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}