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, select_fields,
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 mut 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            select_fields(&mut headers, &mut rows, &opts)?;
46
47            match fmt {
48                OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
49                OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
50            }
51        }
52
53        // Default to sales invoices when type is "sales", "debtor", or unspecified
54        _ => {
55            let mut client = SalesClient::new();
56            client.authenticate(&config.api_key).await?;
57            let items = client.get_sales_items().await?;
58
59            let mut headers = vec!["ID".into(), "Description".into()];
60            let mut rows: Vec<Vec<String>> = items
61                .iter()
62                .map(|i| vec![i.id.clone(), i.description.clone()])
63                .collect();
64            apply_pagination(&mut rows, &opts);
65            select_fields(&mut headers, &mut rows, &opts)?;
66
67            match fmt {
68                OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
69                OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
70            }
71        }
72    }
73
74    Ok(())
75}
76
77pub async fn document(
78    config: &Config,
79    admin: Option<&str>,
80    id: &str,
81    format: Option<&str>,
82) -> Result<(), YukiError> {
83    let entry = config.resolve_admin(admin)?;
84    let mut client = AccountingInfoClient::new();
85    client.authenticate(&config.api_key).await?;
86    let xml = client.get_transaction_document(&entry.admin_id, id).await?;
87
88    let result = crate::client::soap_client::SoapClient::parse_single_result(
89        &xml,
90        "GetTransactionDocumentResult",
91    )
92    .unwrap_or(xml);
93
94    let headers = vec!["Transaction".into(), "Document".into()];
95    let rows = vec![vec![id.to_string(), result]];
96
97    let fmt = OutputFormat::from_flag(format, is_tty());
98    match fmt {
99        OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
100        OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
101    }
102    Ok(())
103}
104
105pub async fn show(
106    config: &Config,
107    _admin: Option<&str>,
108    id: &str,
109    format: Option<&str>,
110) -> Result<(), YukiError> {
111    let mut client = AccountingInfoClient::new();
112    client.authenticate(&config.api_key).await?;
113    let details = client.get_transaction_details(id).await?;
114
115    let headers = vec![
116        "ID".into(),
117        "Date".into(),
118        "Amount".into(),
119        "Currency".into(),
120        "GL Account".into(),
121        "Description".into(),
122    ];
123    let rows: Vec<Vec<String>> = details
124        .iter()
125        .map(|d| {
126            vec![
127                d.id.clone(),
128                d.date.clone(),
129                d.amount.clone(),
130                d.currency.clone(),
131                d.gl_account_code.clone(),
132                d.description.clone(),
133            ]
134        })
135        .collect();
136
137    let fmt = OutputFormat::from_flag(format, is_tty());
138    match fmt {
139        OutputFormat::Table => println!("{}", format_table(&headers, &rows)),
140        OutputFormat::Json => println!("{}", format_json(&headers, &rows)),
141    }
142    Ok(())
143}