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