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: table or json.
46    #[arg(long, global = true)]
47    pub format: Option<String>,
48
49    /// Suppress all output except errors.
50    #[arg(long, short, global = true)]
51    pub quiet: bool,
52
53    #[command(subcommand)]
54    pub command: Commands,
55}
56
57#[derive(Subcommand)]
58pub enum Commands {
59    /// Initialize yuki configuration for this machine.
60    Init {
61        /// API key (skips interactive prompt if provided).
62        #[arg(long)]
63        api_key: Option<String>,
64
65        /// Default administration name (auto-selects if only one available).
66        #[arg(long)]
67        default_admin: Option<String>,
68    },
69
70    /// Manage Yuki administrations.
71    Admin {
72        #[command(subcommand)]
73        command: AdminCommands,
74    },
75
76    /// Work with sales invoices.
77    Invoices {
78        #[command(subcommand)]
79        command: InvoiceCommands,
80    },
81
82    /// Work with archived documents.
83    Documents {
84        #[command(subcommand)]
85        command: DocumentCommands,
86    },
87
88    /// Work with contacts (customers and suppliers).
89    Contacts {
90        #[command(subcommand)]
91        command: ContactCommands,
92    },
93
94    /// Work with general ledger accounts.
95    Accounts {
96        #[command(subcommand)]
97        command: AccountCommands,
98    },
99
100    /// Work with VAT returns and codes.
101    Vat {
102        #[command(subcommand)]
103        command: VatCommands,
104    },
105
106    /// Work with projects.
107    Projects {
108        #[command(subcommand)]
109        command: ProjectCommands,
110    },
111
112    /// Run compliance and period checks.
113    Check {
114        #[command(subcommand)]
115        command: CheckCommands,
116    },
117
118    /// Upload documents to the Yuki archive.
119    Upload {
120        #[command(subcommand)]
121        command: UploadCommands,
122    },
123
124    /// Generate shell completions
125    Completions {
126        /// Shell to generate completions for
127        shell: clap_complete::Shell,
128    },
129
130    /// Output JSON schema for agent integration
131    Schema,
132}
133
134#[derive(Subcommand)]
135pub enum AdminCommands {
136    /// List all available administrations.
137    List,
138
139    /// Switch the active administration.
140    Switch {
141        /// Name of the administration to activate.
142        name: String,
143    },
144}
145
146#[derive(Subcommand)]
147pub enum InvoiceCommands {
148    /// List invoices, optionally filtered by period and type.
149    List {
150        /// Accounting period (e.g. 2025-01).
151        #[arg(long)]
152        period: Option<String>,
153
154        /// Invoice type filter (e.g. sales, purchase).
155        #[arg(long)]
156        invoice_type: Option<String>,
157    },
158
159    /// Show details for a single invoice.
160    Show {
161        /// Invoice ID.
162        id: String,
163    },
164
165    /// Show the document linked to a transaction.
166    Document {
167        /// Transaction ID.
168        id: String,
169    },
170}
171
172#[derive(Subcommand)]
173pub enum DocumentCommands {
174    /// List documents in a folder or of a given type.
175    List {
176        /// Archive folder name.
177        #[arg(long)]
178        folder: Option<String>,
179
180        /// Document type filter.
181        #[arg(long)]
182        doc_type: Option<String>,
183    },
184
185    /// Search documents by a query string.
186    Search {
187        /// Search query.
188        query: String,
189    },
190
191    /// Check if an invoice exists in the archive (by amount, date, and optional contact).
192    Exists {
193        /// Invoice amount to search for.
194        #[arg(long)]
195        amount: f64,
196        /// Invoice date (YYYY-MM-DD). Matches within ±7 days.
197        #[arg(long)]
198        date: String,
199        /// Contact/supplier name to narrow the search.
200        #[arg(long)]
201        contact: Option<String>,
202    },
203}
204
205#[derive(Subcommand)]
206pub enum ContactCommands {
207    /// Search contacts by name or other criteria.
208    Search {
209        /// Search query.
210        query: String,
211    },
212
213    /// List contacts filtered by type.
214    List {
215        /// Contact type (e.g. customer, supplier).
216        #[arg(long)]
217        contact_type: Option<String>,
218    },
219}
220
221#[derive(Subcommand)]
222pub enum AccountCommands {
223    /// Show the balance of a general ledger account for a period.
224    Balance {
225        /// GL account code.
226        #[arg(long)]
227        account: Option<String>,
228
229        /// Accounting period (e.g. 2025-01).
230        #[arg(long)]
231        period: Option<String>,
232    },
233
234    /// List transactions for a general ledger account.
235    Transactions {
236        /// GL account code.
237        #[arg(long)]
238        account: Option<String>,
239
240        /// Accounting period (e.g. 2025-01).
241        #[arg(long)]
242        period: Option<String>,
243    },
244
245    /// Show the chart of accounts (GL account scheme).
246    Scheme,
247
248    /// Show net revenue for a period.
249    Revenue {
250        /// Accounting period (e.g. 2025, 2025-Q1, 2025-01).
251        #[arg(long)]
252        period: Option<String>,
253    },
254
255    /// Show opening balances per GL account for a book year.
256    StartBalance {
257        /// Book year (e.g. 2025).
258        #[arg(long)]
259        year: Option<String>,
260    },
261}
262
263#[derive(Subcommand)]
264pub enum ProjectCommands {
265    /// List all projects.
266    List,
267
268    /// Show balance for a project.
269    Balance {
270        /// Project code.
271        project: String,
272
273        /// GL account code filter.
274        #[arg(long)]
275        account: Option<String>,
276
277        /// Accounting period (e.g. 2025, 2025-Q1).
278        #[arg(long)]
279        period: Option<String>,
280    },
281}
282
283#[derive(Subcommand)]
284pub enum VatCommands {
285    /// List VAT returns for a given year.
286    Returns {
287        /// Fiscal year (e.g. 2025).
288        year: Option<String>,
289    },
290
291    /// List active VAT codes.
292    Codes,
293}
294
295#[derive(Subcommand)]
296pub enum CheckCommands {
297    /// Check outstanding BTW (VAT) items for a period.
298    Btw {
299        /// Accounting period (e.g. 2025-01).
300        period: Option<String>,
301    },
302
303    /// Find bank transactions without matching booked invoices.
304    Unmatched {
305        /// Accounting period (e.g. 2025-Q1).
306        #[arg(long)]
307        period: Option<String>,
308        /// GL account code for the bank account (default: 11001).
309        #[arg(long, default_value = "11001")]
310        bank_account: String,
311    },
312
313    /// Check if a specific invoice reference is still outstanding.
314    Outstanding {
315        /// Invoice reference to check.
316        reference: String,
317    },
318}
319
320#[derive(Subcommand)]
321pub enum UploadCommands {
322    /// Upload a document with optional invoice metadata.
323    File {
324        /// Path to the file to upload.
325        file: String,
326
327        /// Target folder: uitzoeken (default), inkoop, verkoop, bank, personeel, belasting, overig-financieel.
328        #[arg(long, default_value = "uitzoeken")]
329        folder: String,
330
331        /// Invoice amount (e.g. 114.27); enables richer metadata upload.
332        #[arg(long)]
333        amount: Option<f64>,
334
335        /// Cost category ID (e.g. 45100).
336        #[arg(long)]
337        category: Option<String>,
338
339        /// Payment method ID (e.g. 4 for pinpas).
340        #[arg(long = "payment-method")]
341        payment_method: Option<String>,
342
343        /// Project ID.
344        #[arg(long)]
345        project: Option<String>,
346
347        /// Remarks or notes.
348        #[arg(long)]
349        remarks: Option<String>,
350
351        /// Currency code (default: EUR).
352        #[arg(long, default_value = "EUR")]
353        currency: String,
354    },
355
356    /// List available cost categories.
357    Categories,
358
359    /// List available payment methods.
360    PaymentMethods,
361}