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
125#[derive(Subcommand)]
126pub enum AdminCommands {
127    /// List all available administrations.
128    List,
129
130    /// Switch the active administration.
131    Switch {
132        /// Name of the administration to activate.
133        name: String,
134    },
135}
136
137#[derive(Subcommand)]
138pub enum InvoiceCommands {
139    /// List invoices, optionally filtered by period and type.
140    List {
141        /// Accounting period (e.g. 2025-01).
142        #[arg(long)]
143        period: Option<String>,
144
145        /// Invoice type filter (e.g. sales, purchase).
146        #[arg(long)]
147        invoice_type: Option<String>,
148    },
149
150    /// Show details for a single invoice.
151    Show {
152        /// Invoice ID.
153        id: String,
154    },
155
156    /// Show the document linked to a transaction.
157    Document {
158        /// Transaction ID.
159        id: String,
160    },
161}
162
163#[derive(Subcommand)]
164pub enum DocumentCommands {
165    /// List documents in a folder or of a given type.
166    List {
167        /// Archive folder name.
168        #[arg(long)]
169        folder: Option<String>,
170
171        /// Document type filter.
172        #[arg(long)]
173        doc_type: Option<String>,
174    },
175
176    /// Search documents by a query string.
177    Search {
178        /// Search query.
179        query: String,
180    },
181
182    /// Check if an invoice exists in the archive (by amount, date, and optional contact).
183    Exists {
184        /// Invoice amount to search for.
185        #[arg(long)]
186        amount: f64,
187        /// Invoice date (YYYY-MM-DD). Matches within ±7 days.
188        #[arg(long)]
189        date: String,
190        /// Contact/supplier name to narrow the search.
191        #[arg(long)]
192        contact: Option<String>,
193    },
194}
195
196#[derive(Subcommand)]
197pub enum ContactCommands {
198    /// Search contacts by name or other criteria.
199    Search {
200        /// Search query.
201        query: String,
202    },
203
204    /// List contacts filtered by type.
205    List {
206        /// Contact type (e.g. customer, supplier).
207        #[arg(long)]
208        contact_type: Option<String>,
209    },
210}
211
212#[derive(Subcommand)]
213pub enum AccountCommands {
214    /// Show the balance of a general ledger account for a period.
215    Balance {
216        /// GL account code.
217        #[arg(long)]
218        account: Option<String>,
219
220        /// Accounting period (e.g. 2025-01).
221        #[arg(long)]
222        period: Option<String>,
223    },
224
225    /// List transactions for a general ledger account.
226    Transactions {
227        /// GL account code.
228        #[arg(long)]
229        account: Option<String>,
230
231        /// Accounting period (e.g. 2025-01).
232        #[arg(long)]
233        period: Option<String>,
234    },
235
236    /// Show the chart of accounts (GL account scheme).
237    Scheme,
238
239    /// Show net revenue for a period.
240    Revenue {
241        /// Accounting period (e.g. 2025, 2025-Q1, 2025-01).
242        #[arg(long)]
243        period: Option<String>,
244    },
245
246    /// Show opening balances per GL account for a book year.
247    StartBalance {
248        /// Book year (e.g. 2025).
249        #[arg(long)]
250        year: Option<String>,
251    },
252}
253
254#[derive(Subcommand)]
255pub enum ProjectCommands {
256    /// List all projects.
257    List,
258
259    /// Show balance for a project.
260    Balance {
261        /// Project code.
262        project: String,
263
264        /// GL account code filter.
265        #[arg(long)]
266        account: Option<String>,
267
268        /// Accounting period (e.g. 2025, 2025-Q1).
269        #[arg(long)]
270        period: Option<String>,
271    },
272}
273
274#[derive(Subcommand)]
275pub enum VatCommands {
276    /// List VAT returns for a given year.
277    Returns {
278        /// Fiscal year (e.g. 2025).
279        year: Option<String>,
280    },
281
282    /// List active VAT codes.
283    Codes,
284}
285
286#[derive(Subcommand)]
287pub enum CheckCommands {
288    /// Check outstanding BTW (VAT) items for a period.
289    Btw {
290        /// Accounting period (e.g. 2025-01).
291        period: Option<String>,
292    },
293
294    /// Find bank transactions without matching booked invoices.
295    Unmatched {
296        /// Accounting period (e.g. 2025-Q1).
297        #[arg(long)]
298        period: Option<String>,
299        /// GL account code for the bank account (default: 11001).
300        #[arg(long, default_value = "11001")]
301        bank_account: String,
302    },
303
304    /// Check if a specific invoice reference is still outstanding.
305    Outstanding {
306        /// Invoice reference to check.
307        reference: String,
308    },
309}
310
311#[derive(Subcommand)]
312pub enum UploadCommands {
313    /// Upload a document with optional invoice metadata.
314    File {
315        /// Path to the file to upload.
316        file: String,
317
318        /// Target folder: uitzoeken (default), inkoop, verkoop, bank, personeel, belasting, overig-financieel.
319        #[arg(long, default_value = "uitzoeken")]
320        folder: String,
321
322        /// Invoice amount (e.g. 114.27); enables richer metadata upload.
323        #[arg(long)]
324        amount: Option<f64>,
325
326        /// Cost category ID (e.g. 45100).
327        #[arg(long)]
328        category: Option<String>,
329
330        /// Payment method ID (e.g. 4 for pinpas).
331        #[arg(long = "payment-method")]
332        payment_method: Option<String>,
333
334        /// Project ID.
335        #[arg(long)]
336        project: Option<String>,
337
338        /// Remarks or notes.
339        #[arg(long)]
340        remarks: Option<String>,
341
342        /// Currency code (default: EUR).
343        #[arg(long, default_value = "EUR")]
344        currency: String,
345    },
346
347    /// List available cost categories.
348    Categories,
349
350    /// List available payment methods.
351    PaymentMethods,
352}