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}