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}