Five more resources following clients.rs's template exactly, same as the previous batch. credits.rs is otherwise identical to quotes.rs since Credit is also a type alias for Invoice in the SDK (same table, distinguished by entity_type upstream). Verified against a live local server: full create/list/get/update/ delete round trip for all five. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
142 lines
4.2 KiB
Rust
142 lines
4.2 KiB
Rust
//! `purchase-orders` subcommand. Same shape as `clients.rs` — see the
|
|
//! "Adding a resource" section in CLAUDE.md.
|
|
|
|
use invoiceninja_sdk::models::{ListParams, PurchaseOrder};
|
|
use invoiceninja_sdk::InvoiceNinjaClient;
|
|
|
|
use crate::body;
|
|
use crate::cli::{Action, BodyArgs, ListArgs, OutputFormat, ResourceArgs};
|
|
use crate::output;
|
|
|
|
pub async fn run(
|
|
client: &InvoiceNinjaClient,
|
|
args: ResourceArgs,
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
match args.action {
|
|
Action::List(list_args) => list(client, list_args, format).await,
|
|
Action::Get { id } => get(client, &id, format).await,
|
|
Action::Create(body_args) => create(client, body_args, format).await,
|
|
Action::Update { id, body } => update(client, &id, body, format).await,
|
|
Action::Delete { id } => delete(client, &id).await,
|
|
}
|
|
}
|
|
|
|
async fn list(
|
|
client: &InvoiceNinjaClient,
|
|
args: ListArgs,
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut params = ListParams::new();
|
|
if let Some(page) = args.page {
|
|
params = params.page(page);
|
|
}
|
|
if let Some(per_page) = args.per_page {
|
|
params = params.per_page(per_page);
|
|
}
|
|
if let Some(sort) = args.sort {
|
|
params = params.sort(sort);
|
|
}
|
|
if let Some(filter) = args.filter {
|
|
params = params.filter(filter);
|
|
}
|
|
if let Some(status) = args.status {
|
|
params = params.status(status);
|
|
}
|
|
if let Some(include) = args.include {
|
|
params = params.include(include);
|
|
}
|
|
for (k, v) in args.params {
|
|
params = params.param(k, v);
|
|
}
|
|
|
|
let purchase_orders = if args.all {
|
|
client.list_all_purchase_orders(¶ms).await?
|
|
} else {
|
|
client.list_purchase_orders(¶ms).await?
|
|
};
|
|
|
|
render(&purchase_orders, format)
|
|
}
|
|
|
|
async fn get(
|
|
client: &InvoiceNinjaClient,
|
|
id: &str,
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let found = client.get_purchase_order(id).await?;
|
|
render(std::slice::from_ref(&found), format)
|
|
}
|
|
|
|
async fn create(
|
|
client: &InvoiceNinjaClient,
|
|
body_args: BodyArgs,
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let value = body::load(&body_args)?;
|
|
let model: PurchaseOrder = serde_json::from_value(value)?;
|
|
let created = client.create_purchase_order(&model).await?;
|
|
render(std::slice::from_ref(&created), format)
|
|
}
|
|
|
|
async fn update(
|
|
client: &InvoiceNinjaClient,
|
|
id: &str,
|
|
body_args: BodyArgs,
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let patch = body::load(&body_args)?;
|
|
let current = client.get_purchase_order(id).await?;
|
|
let merged = body::merge_shallow(serde_json::to_value(¤t)?, patch);
|
|
let model: PurchaseOrder = serde_json::from_value(merged)?;
|
|
let updated = client.update_purchase_order(id, &model).await?;
|
|
render(std::slice::from_ref(&updated), format)
|
|
}
|
|
|
|
async fn delete(client: &InvoiceNinjaClient, id: &str) -> Result<(), Box<dyn std::error::Error>> {
|
|
client.delete_purchase_order(id).await?;
|
|
println!("deleted {id}");
|
|
Ok(())
|
|
}
|
|
|
|
fn render(
|
|
purchase_orders: &[PurchaseOrder],
|
|
format: OutputFormat,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
match format {
|
|
OutputFormat::Json => output::print_json(purchase_orders)?,
|
|
OutputFormat::Table => print_table(purchase_orders),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn print_table(purchase_orders: &[PurchaseOrder]) {
|
|
if purchase_orders.is_empty() {
|
|
println!("(no records)");
|
|
return;
|
|
}
|
|
println!(
|
|
"{:<20} {:<15} {:<20} {:<8} {:>12} {:>12}",
|
|
"id", "number", "vendor_id", "status", "amount", "balance"
|
|
);
|
|
for po in purchase_orders {
|
|
println!(
|
|
"{:<20} {:<15} {:<20} {:<8} {:>12.2} {:>12.2}",
|
|
po.id.as_deref().unwrap_or(""),
|
|
truncate(&po.number, 15),
|
|
truncate(&po.vendor_id, 20),
|
|
po.status_id,
|
|
po.amount,
|
|
po.balance
|
|
);
|
|
}
|
|
}
|
|
|
|
fn truncate(s: &str, max: usize) -> String {
|
|
if s.chars().count() <= max {
|
|
s.to_string()
|
|
} else {
|
|
s.chars().take(max.saturating_sub(1)).collect::<String>() + "…"
|
|
}
|
|
}
|