Skip to main content

yuki_cli/cli/
invoices.rs

1use crate::cli::setup_domain;
2use crate::client::accounting_info::AccountingInfoClient;
3use crate::client::sales::SalesClient;
4use crate::config::Config;
5use crate::error::YukiError;
6use crate::output::{
7    ListOptions, OutputFormat, apply_pagination, format_json, format_table, is_tty,
8};
9
10pub async fn list(
11    config: &Config,
12    admin: Option<&str>,
13    _period: Option<&str>,
14    invoice_type: Option<&str>,
15    format: Option<&str>,
16    opts: ListOptions<'_>,
17) -> Result<(), YukiError> {
18    let fmt = OutputFormat::from_flag(format, is_tty());
19
20    match invoice_type {
21        Some("purchase") | Some("creditor") => {
22            let (client, entry) = setup_domain(config, admin).await?;
23            let items = client.outstanding_creditor_items(&entry.admin_id).await?;
24
25            let headers = vec![
26                "Contact".into(),
27                "Description".into(),
28                "Date".into(),
29                "Amount".into(),
30                "Open".into(),
31            ];
32            let mut rows: Vec<Vec<String>> = items
33                .iter()
34                .map(|i| {
35                    vec![
36                        i.contact_name.clone(),
37                        i.description.clone(),
38                        i.date.clone(),
39                        i.amount.clone(),
40                        i.open_amount.clone(),
41                    ]
42                })
43                .collect();
44            apply_pagination(&mut rows, &opts);
45
46            match fmt {
47                OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
48                OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
49            }
50        }
51
52        // Default to sales invoices when type is "sales", "debtor", or unspecified
53        _ => {
54            let mut client = SalesClient::new();
55            client.authenticate(&config.api_key).await?;
56            let items = client.get_sales_items().await?;
57
58            let headers = vec!["ID".into(), "Description".into()];
59            let mut rows: Vec<Vec<String>> = items
60                .iter()
61                .map(|i| vec![i.id.clone(), i.description.clone()])
62                .collect();
63            apply_pagination(&mut rows, &opts);
64
65            match fmt {
66                OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
67                OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
68            }
69        }
70    }
71
72    Ok(())
73}
74
75pub async fn document(
76    config: &Config,
77    admin: Option<&str>,
78    id: &str,
79    format: Option<&str>,
80) -> Result<(), YukiError> {
81    let entry = config.resolve_admin(admin)?;
82    let mut client = AccountingInfoClient::new();
83    client.authenticate(&config.api_key).await?;
84    let xml = client.get_transaction_document(&entry.admin_id, id).await?;
85
86    let result = crate::client::soap_client::SoapClient::parse_single_result(
87        &xml,
88        "GetTransactionDocumentResult",
89    )
90    .unwrap_or(xml);
91
92    let headers = vec!["Transaction".into(), "Document".into()];
93    let rows = vec![vec![id.to_string(), result]];
94
95    let fmt = OutputFormat::from_flag(format, is_tty());
96    match fmt {
97        OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
98        OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
99    }
100    Ok(())
101}
102
103pub async fn show(
104    config: &Config,
105    _admin: Option<&str>,
106    id: &str,
107    format: Option<&str>,
108) -> Result<(), YukiError> {
109    let mut client = AccountingInfoClient::new();
110    client.authenticate(&config.api_key).await?;
111    let details = client.get_transaction_details(id).await?;
112
113    let headers = vec![
114        "ID".into(),
115        "Date".into(),
116        "Amount".into(),
117        "Currency".into(),
118        "GL Account".into(),
119        "Description".into(),
120    ];
121    let rows: Vec<Vec<String>> = details
122        .iter()
123        .map(|d| {
124            vec![
125                d.id.clone(),
126                d.date.clone(),
127                d.amount.clone(),
128                d.currency.clone(),
129                d.gl_account_code.clone(),
130                d.description.clone(),
131            ]
132        })
133        .collect();
134
135    let fmt = OutputFormat::from_flag(format, is_tty());
136    match fmt {
137        OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
138        OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
139    }
140    Ok(())
141}