Skip to main content

yuki_cli/cli/
mod.rs

1pub mod accounts;
2pub mod admin;
3pub mod check;
4pub mod contacts;
5pub mod documents;
6pub mod init;
7pub mod invoices;
8pub mod projects;
9pub mod upload;
10pub mod vat;
11
12use clap::{Parser, Subcommand};
13
14use crate::client::accounting::AccountingClient;
15use crate::config::{AdminEntry, Config};
16use crate::error::YukiError;
17
18/// Authenticate a client and set the active administration domain.
19///
20/// Returns both the configured client and the resolved `AdminEntry` so callers
21/// can pass `admin_id` to operations that require `administrationID`.
22pub async fn setup_domain(
23    config: &Config,
24    admin: Option<&str>,
25) -> Result<(AccountingClient, AdminEntry), YukiError> {
26    let entry = config.resolve_admin(admin)?;
27    let mut client = AccountingClient::new();
28    client.authenticate(&config.api_key).await?;
29    client.set_current_domain(&entry.domain_id).await?;
30    Ok((client, entry))
31}
32
33/// Top-level CLI entry point for the Yuki bookkeeping API client.
34#[derive(Parser)]
35#[command(
36    name = "yuki",
37    version,
38    about = "CLI client for the Yuki bookkeeping API"
39)]
40pub struct Cli {
41    /// Override the active administration by name.
42    #[arg(long = "admin", global = true)]
43    pub admin: Option<String>,
44
45    /// Output format: auto, text, or json.
46    #[arg(long = "output", short = 'o', global = true)]
47    pub output: Option<String>,
48
49    /// Suppress all output except errors.
50    #[arg(long, short, global = true)]
51    pub quiet: bool,
52
53    /// Skip confirmation prompts (for use in scripts and pipelines).
54    #[arg(long = "yes", short = 'y', global = true)]
55    pub yes: bool,
56
57    #[command(subcommand)]
58    pub command: Commands,
59}
60
61#[derive(Subcommand)]
62pub enum Commands {
63    /// Initialize yuki configuration for this machine.
64    Init {
65        /// API key (skips interactive prompt if provided).
66        #[arg(long)]
67        api_key: Option<String>,
68
69        /// Default administration name (auto-selects if only one available).
70        #[arg(long)]
71        default_admin: Option<String>,
72    },
73
74    /// Manage Yuki administrations.
75    Admin {
76        #[command(subcommand)]
77        command: AdminCommands,
78    },
79
80    /// Work with sales invoices.
81    Invoices {
82        #[command(subcommand)]
83        command: InvoiceCommands,
84    },
85
86    /// Work with archived documents.
87    Documents {
88        #[command(subcommand)]
89        command: DocumentCommands,
90    },
91
92    /// Work with contacts (customers and suppliers).
93    Contacts {
94        #[command(subcommand)]
95        command: ContactCommands,
96    },
97
98    /// Work with general ledger accounts.
99    Accounts {
100        #[command(subcommand)]
101        command: AccountCommands,
102    },
103
104    /// Work with VAT returns and codes.
105    Vat {
106        #[command(subcommand)]
107        command: VatCommands,
108    },
109
110    /// Work with projects.
111    Projects {
112        #[command(subcommand)]
113        command: ProjectCommands,
114    },
115
116    /// Run compliance and period checks.
117    Check {
118        #[command(subcommand)]
119        command: CheckCommands,
120    },
121
122    /// Upload documents to the Yuki archive.
123    Upload {
124        #[command(subcommand)]
125        command: UploadCommands,
126    },
127
128    /// Generate shell completions
129    Completions {
130        /// Shell to generate completions for
131        shell: clap_complete::Shell,
132    },
133
134    /// Output JSON schema for agent integration
135    Schema,
136}
137
138#[derive(Subcommand)]
139pub enum AdminCommands {
140    /// List all available administrations.
141    List {
142        /// Maximum number of results to return.
143        #[arg(long)]
144        limit: Option<usize>,
145
146        /// Number of results to skip (for pagination).
147        #[arg(long)]
148        offset: Option<usize>,
149
150        /// Comma-separated list of fields to include in output.
151        #[arg(long)]
152        fields: Option<String>,
153    },
154
155    /// Switch the active administration.
156    Switch {
157        /// Name of the administration to activate.
158        name: String,
159    },
160}
161
162#[derive(Subcommand)]
163pub enum InvoiceCommands {
164    /// List invoices, optionally filtered by period and type.
165    List {
166        /// Accounting period (e.g. 2025-01).
167        #[arg(long)]
168        period: Option<String>,
169
170        /// Invoice type filter (e.g. sales, purchase).
171        #[arg(long)]
172        invoice_type: Option<String>,
173
174        /// Maximum number of results to return.
175        #[arg(long)]
176        limit: Option<usize>,
177
178        /// Number of results to skip (for pagination).
179        #[arg(long)]
180        offset: Option<usize>,
181
182        /// Comma-separated list of fields to include in output.
183        #[arg(long)]
184        fields: Option<String>,
185    },
186
187    /// Show details for a single invoice.
188    Show {
189        /// Invoice ID.
190        id: String,
191    },
192
193    /// Show the document linked to a transaction.
194    Document {
195        /// Transaction ID.
196        id: String,
197    },
198}
199
200#[derive(Subcommand)]
201pub enum DocumentCommands {
202    /// List documents in a folder or of a given type.
203    List {
204        /// Archive folder: uitzoeken, inkoop, verkoop, bank, personeel, belasting,
205        /// overig-financieel, or a numeric folder ID.
206        #[arg(long)]
207        folder: Option<String>,
208
209        /// Document type filter (numeric document type ID).
210        #[arg(long)]
211        doc_type: Option<String>,
212
213        /// Maximum number of results to return.
214        #[arg(long)]
215        limit: Option<usize>,
216
217        /// Number of results to skip (for pagination).
218        #[arg(long)]
219        offset: Option<usize>,
220
221        /// Comma-separated list of fields to include in output.
222        #[arg(long)]
223        fields: Option<String>,
224    },
225
226    /// Search documents by a query string.
227    Search {
228        /// Search query.
229        query: String,
230    },
231
232    /// Check if an invoice exists in the archive (by amount, date, and optional contact).
233    Exists {
234        /// Invoice amount to search for.
235        #[arg(long)]
236        amount: f64,
237        /// Invoice date (YYYY-MM-DD). Matches within +/-7 days.
238        #[arg(long)]
239        date: String,
240        /// Contact/supplier name to narrow the search.
241        #[arg(long)]
242        contact: Option<String>,
243    },
244}
245
246#[derive(Subcommand)]
247pub enum ContactCommands {
248    /// Search contacts by name or other criteria.
249    Search {
250        /// Search query.
251        query: String,
252    },
253
254    /// List contacts filtered by type.
255    List {
256        /// Contact type (e.g. customer, supplier).
257        #[arg(long)]
258        contact_type: Option<String>,
259
260        /// Maximum number of results to return.
261        #[arg(long)]
262        limit: Option<usize>,
263
264        /// Number of results to skip (for pagination).
265        #[arg(long)]
266        offset: Option<usize>,
267
268        /// Comma-separated list of fields to include in output.
269        #[arg(long)]
270        fields: Option<String>,
271    },
272}
273
274#[derive(Subcommand)]
275pub enum AccountCommands {
276    /// Show the balance of a general ledger account for a period.
277    Balance {
278        /// GL account code.
279        #[arg(long)]
280        account: Option<String>,
281
282        /// Accounting period (e.g. 2025-01).
283        #[arg(long)]
284        period: Option<String>,
285    },
286
287    /// List transactions for a general ledger account.
288    Transactions {
289        /// GL account code.
290        #[arg(long)]
291        account: Option<String>,
292
293        /// Accounting period (e.g. 2025-01).
294        #[arg(long)]
295        period: Option<String>,
296
297        /// Maximum number of results to return.
298        #[arg(long)]
299        limit: Option<usize>,
300
301        /// Number of results to skip (for pagination).
302        #[arg(long)]
303        offset: Option<usize>,
304
305        /// Comma-separated list of fields to include in output.
306        #[arg(long)]
307        fields: Option<String>,
308    },
309
310    /// Show the chart of accounts (GL account scheme).
311    Scheme,
312
313    /// Show net revenue for a period.
314    Revenue {
315        /// Accounting period (e.g. 2025, 2025-Q1, 2025-01).
316        #[arg(long)]
317        period: Option<String>,
318    },
319
320    /// Show opening balances per GL account for a book year.
321    StartBalance {
322        /// Book year (e.g. 2025).
323        #[arg(long)]
324        year: Option<String>,
325    },
326}
327
328#[derive(Subcommand)]
329pub enum ProjectCommands {
330    /// List all projects.
331    List,
332
333    /// Show balance for a project.
334    Balance {
335        /// Project code.
336        project: String,
337
338        /// GL account code filter.
339        #[arg(long)]
340        account: Option<String>,
341
342        /// Accounting period (e.g. 2025, 2025-Q1).
343        #[arg(long)]
344        period: Option<String>,
345    },
346}
347
348#[derive(Subcommand)]
349pub enum VatCommands {
350    /// List VAT returns for a given year.
351    Returns {
352        /// Fiscal year (e.g. 2025).
353        year: Option<String>,
354    },
355
356    /// List active VAT codes.
357    Codes,
358}
359
360#[derive(Subcommand)]
361pub enum CheckCommands {
362    /// Check outstanding BTW (VAT) items for a period.
363    Btw {
364        /// Accounting period (e.g. 2025-01).
365        period: Option<String>,
366    },
367
368    /// Find bank transactions without matching booked invoices.
369    Unmatched {
370        /// Accounting period (e.g. 2025-Q1).
371        #[arg(long)]
372        period: Option<String>,
373        /// GL account code for the bank account (default: 11001).
374        #[arg(long, default_value = "11001")]
375        bank_account: String,
376    },
377
378    /// Check if a specific invoice reference is still outstanding.
379    Outstanding {
380        /// Invoice reference to check.
381        reference: String,
382    },
383}
384
385#[derive(Subcommand)]
386pub enum UploadCommands {
387    /// Upload a document with optional invoice metadata.
388    File {
389        /// Path to the file to upload.
390        file: String,
391
392        /// Target folder: uitzoeken (default), inkoop, verkoop, bank, personeel, belasting, overig-financieel.
393        #[arg(long, default_value = "uitzoeken")]
394        folder: String,
395
396        /// Invoice amount (e.g. 114.27); enables richer metadata upload.
397        #[arg(long)]
398        amount: Option<f64>,
399
400        /// Cost category ID (e.g. 45100).
401        #[arg(long)]
402        category: Option<String>,
403
404        /// Payment method ID (e.g. 4 for pinpas).
405        #[arg(long = "payment-method")]
406        payment_method: Option<String>,
407
408        /// Project ID.
409        #[arg(long)]
410        project: Option<String>,
411
412        /// Remarks or notes.
413        #[arg(long)]
414        remarks: Option<String>,
415
416        /// Currency code (default: EUR).
417        #[arg(long, default_value = "EUR")]
418        currency: String,
419    },
420
421    /// List available cost categories.
422    Categories,
423
424    /// List available payment methods.
425    PaymentMethods,
426}