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    /// Describe supported API areas and safety behavior without loading configuration
138    Capabilities,
139}
140
141#[derive(Subcommand)]
142pub enum AdminCommands {
143    /// List all available administrations.
144    List {
145        /// Maximum number of results to return.
146        #[arg(long)]
147        limit: Option<usize>,
148
149        /// Number of results to skip (for pagination).
150        #[arg(long)]
151        offset: Option<usize>,
152
153        /// Comma-separated list of fields to include in output.
154        #[arg(long)]
155        fields: Option<String>,
156    },
157
158    /// Switch the active administration.
159    Switch {
160        /// Name of the administration to activate.
161        name: String,
162    },
163}
164
165#[derive(Subcommand)]
166pub enum InvoiceCommands {
167    /// List invoices, optionally filtered by period and type.
168    List {
169        /// Accounting period (e.g. 2025-01).
170        #[arg(long)]
171        period: Option<String>,
172
173        /// Invoice type filter (e.g. sales, purchase).
174        #[arg(long)]
175        invoice_type: Option<String>,
176
177        /// Maximum number of results to return.
178        #[arg(long)]
179        limit: Option<usize>,
180
181        /// Number of results to skip (for pagination).
182        #[arg(long)]
183        offset: Option<usize>,
184
185        /// Comma-separated list of fields to include in output.
186        #[arg(long)]
187        fields: Option<String>,
188    },
189
190    /// Show details for a single invoice.
191    Show {
192        /// Invoice ID.
193        id: String,
194    },
195
196    /// Show the document linked to a transaction.
197    Document {
198        /// Transaction ID.
199        id: String,
200    },
201}
202
203#[derive(Subcommand)]
204pub enum DocumentCommands {
205    /// List documents in a folder or of a given type.
206    List {
207        /// Archive folder: uitzoeken, inkoop, verkoop, bank, personeel, belasting,
208        /// overig-financieel, or a numeric folder ID.
209        #[arg(long)]
210        folder: Option<String>,
211
212        /// Document type filter (numeric document type ID).
213        #[arg(long)]
214        doc_type: Option<String>,
215
216        /// Maximum number of results to return.
217        #[arg(long)]
218        limit: Option<usize>,
219
220        /// Number of results to skip (for pagination).
221        #[arg(long)]
222        offset: Option<usize>,
223
224        /// Comma-separated list of fields to include in output.
225        #[arg(long)]
226        fields: Option<String>,
227    },
228
229    /// Search documents by a query string.
230    Search {
231        /// Search query.
232        query: String,
233    },
234
235    /// Check if an invoice exists in the archive (by amount, date, and optional contact).
236    Exists {
237        /// Invoice amount to search for.
238        #[arg(long)]
239        amount: f64,
240        /// Invoice date (YYYY-MM-DD). Matches within +/-7 days.
241        #[arg(long)]
242        date: String,
243        /// Contact/supplier name to narrow the search.
244        #[arg(long)]
245        contact: Option<String>,
246    },
247}
248
249#[derive(Subcommand)]
250pub enum ContactCommands {
251    /// Search contacts by name or other criteria.
252    Search {
253        /// Search query.
254        query: String,
255    },
256
257    /// List contacts filtered by type.
258    List {
259        /// Contact type (e.g. customer, supplier).
260        #[arg(long)]
261        contact_type: Option<String>,
262
263        /// Maximum number of results to return.
264        #[arg(long)]
265        limit: Option<usize>,
266
267        /// Number of results to skip (for pagination).
268        #[arg(long)]
269        offset: Option<usize>,
270
271        /// Comma-separated list of fields to include in output.
272        #[arg(long)]
273        fields: Option<String>,
274    },
275}
276
277#[derive(Subcommand)]
278pub enum AccountCommands {
279    /// Show the balance of a general ledger account for a period.
280    Balance {
281        /// GL account code.
282        #[arg(long)]
283        account: Option<String>,
284
285        /// Accounting period (e.g. 2025-01).
286        #[arg(long)]
287        period: Option<String>,
288    },
289
290    /// List transactions for a general ledger account.
291    Transactions {
292        /// GL account code.
293        #[arg(long)]
294        account: Option<String>,
295
296        /// Accounting period (e.g. 2025-01).
297        #[arg(long)]
298        period: Option<String>,
299
300        /// Maximum number of results to return.
301        #[arg(long)]
302        limit: Option<usize>,
303
304        /// Number of results to skip (for pagination).
305        #[arg(long)]
306        offset: Option<usize>,
307
308        /// Comma-separated list of fields to include in output.
309        #[arg(long)]
310        fields: Option<String>,
311    },
312
313    /// Show the chart of accounts (GL account scheme).
314    Scheme,
315
316    /// Show net revenue for a period.
317    Revenue {
318        /// Accounting period (e.g. 2025, 2025-Q1, 2025-01).
319        #[arg(long)]
320        period: Option<String>,
321    },
322
323    /// Show opening balances per GL account for a book year.
324    StartBalance {
325        /// Book year (e.g. 2025).
326        #[arg(long)]
327        year: Option<String>,
328    },
329}
330
331#[derive(Subcommand)]
332pub enum ProjectCommands {
333    /// List all projects.
334    List,
335
336    /// Show balance for a project.
337    Balance {
338        /// Project code.
339        project: String,
340
341        /// GL account code filter.
342        #[arg(long)]
343        account: Option<String>,
344
345        /// Accounting period (e.g. 2025, 2025-Q1).
346        #[arg(long)]
347        period: Option<String>,
348    },
349}
350
351#[derive(Subcommand)]
352pub enum VatCommands {
353    /// List VAT returns for a given year.
354    Returns {
355        /// Fiscal year (e.g. 2025).
356        year: Option<String>,
357    },
358
359    /// List active VAT codes.
360    Codes,
361}
362
363#[derive(Subcommand)]
364pub enum CheckCommands {
365    /// Check outstanding BTW (VAT) items for a period.
366    Btw {
367        /// Accounting period (e.g. 2025-01).
368        period: Option<String>,
369    },
370
371    /// Find bank transactions without matching booked invoices.
372    Unmatched {
373        /// Accounting period (e.g. 2025-Q1).
374        #[arg(long)]
375        period: Option<String>,
376        /// GL account code for the bank account (default: 11001).
377        #[arg(long, default_value = "11001")]
378        bank_account: String,
379    },
380
381    /// Check if a specific invoice reference is still outstanding.
382    Outstanding {
383        /// Invoice reference to check.
384        reference: String,
385    },
386}
387
388#[derive(Subcommand)]
389pub enum UploadCommands {
390    /// Upload a document with optional invoice metadata.
391    File {
392        /// Path to the file to upload.
393        file: String,
394
395        /// Target folder: uitzoeken (default), inkoop, verkoop, bank, personeel, belasting, overig-financieel.
396        #[arg(long, default_value = "uitzoeken")]
397        folder: String,
398
399        /// Invoice amount (e.g. 114.27); enables richer metadata upload.
400        #[arg(long)]
401        amount: Option<f64>,
402
403        /// Cost category ID (e.g. 45100).
404        #[arg(long)]
405        category: Option<String>,
406
407        /// Payment method ID (e.g. 4 for pinpas).
408        #[arg(long = "payment-method")]
409        payment_method: Option<String>,
410
411        /// Project ID.
412        #[arg(long)]
413        project: Option<String>,
414
415        /// Remarks or notes.
416        #[arg(long)]
417        remarks: Option<String>,
418
419        /// Currency code (default: EUR).
420        #[arg(long, default_value = "EUR")]
421        currency: String,
422    },
423
424    /// List available cost categories.
425    Categories,
426
427    /// List available payment methods.
428    PaymentMethods,
429}