jaz-clio 4.35.0 → 4.35.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skills/api/SKILL.md +1 -1
- package/assets/skills/cli/SKILL.md +1 -1
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +1 -1
- package/assets/skills/transaction-recipes/SKILL.md +1 -1
- package/dist/commands/accounts.js +6 -6
- package/dist/commands/attachments.js +8 -8
- package/dist/commands/auth.js +41 -41
- package/dist/commands/bank-rules.js +14 -14
- package/dist/commands/bank.js +27 -27
- package/dist/commands/bills.js +35 -35
- package/dist/commands/bookmarks.js +8 -8
- package/dist/commands/calc.js +2 -2
- package/dist/commands/capsules.js +14 -14
- package/dist/commands/cash-entry.js +16 -16
- package/dist/commands/cash-transfer.js +4 -4
- package/dist/commands/cashflow.js +2 -2
- package/dist/commands/contact-groups.js +10 -10
- package/dist/commands/contacts.js +14 -14
- package/dist/commands/currencies.js +2 -2
- package/dist/commands/currency-rates.js +8 -8
- package/dist/commands/custom-fields.js +12 -12
- package/dist/commands/customer-credit-notes.js +30 -30
- package/dist/commands/draft-helpers.js +12 -12
- package/dist/commands/exports.js +4 -4
- package/dist/commands/fixed-assets.js +23 -23
- package/dist/commands/format-helpers.js +5 -0
- package/dist/commands/help-examples.js +2 -2
- package/dist/commands/init.js +8 -8
- package/dist/commands/inventory.js +7 -7
- package/dist/commands/invoices.js +36 -36
- package/dist/commands/items.js +11 -11
- package/dist/commands/jobs.js +33 -33
- package/dist/commands/journals.js +27 -27
- package/dist/commands/kb.js +6 -6
- package/dist/commands/magic.js +40 -40
- package/dist/commands/nano-classifiers.js +8 -8
- package/dist/commands/org-users.js +5 -5
- package/dist/commands/org.js +8 -8
- package/dist/commands/output.js +3 -3
- package/dist/commands/pagination.js +2 -2
- package/dist/commands/parsers.js +20 -20
- package/dist/commands/payments.js +9 -9
- package/dist/commands/picker.js +88 -90
- package/dist/commands/quick-fix.js +8 -8
- package/dist/commands/recipe.js +16 -16
- package/dist/commands/reports.js +11 -11
- package/dist/commands/resolve.js +8 -8
- package/dist/commands/schedulers.js +25 -25
- package/dist/commands/schema.js +15 -24
- package/dist/commands/search.js +4 -4
- package/dist/commands/subscriptions.js +17 -17
- package/dist/commands/supplier-credit-notes.js +28 -28
- package/dist/commands/tags.js +10 -10
- package/dist/commands/tax-profiles.js +11 -11
- package/dist/commands/ui/index.js +1 -0
- package/dist/commands/ui/picker.js +180 -0
- package/dist/commands/ui/table.js +2 -3
- package/dist/commands/ui/theme.js +1 -0
- package/dist/commands/update.js +2 -2
- package/dist/commands/versions.js +3 -3
- package/dist/core/calc/format.js +126 -126
- package/dist/core/jobs/document-collection/tools/ingest/format.js +25 -25
- package/dist/core/jobs/format.js +23 -23
- package/dist/core/jobs/payment-run/tools/bank-file/format.js +7 -7
- package/dist/core/jobs/payment-run/tools/outstanding/format.js +9 -9
- package/dist/core/jobs/statutory-filing/tools/sg-tax/format-sg.js +19 -19
- package/dist/index.js +11 -11
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { danger, success, accent, warning, muted, highlight } from './ui/theme.js';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
3
|
import { listInvoices, getInvoice, searchInvoices, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, applyCreditsToInvoice, downloadInvoicePdf, finalizeInvoice, listInvoicePayments, listInvoiceCredits, reverseInvoiceCredit, } from '../core/api/invoices.js';
|
|
4
4
|
import { listAttachments } from '../core/api/attachments.js';
|
|
@@ -48,19 +48,19 @@ export function registerInvoicesCommand(program) {
|
|
|
48
48
|
console.log(JSON.stringify(inv, null, 2));
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
51
|
-
console.log(
|
|
52
|
-
console.log(
|
|
53
|
-
console.log(
|
|
54
|
-
console.log(
|
|
55
|
-
console.log(
|
|
51
|
+
console.log(highlight('Reference:'), inv.reference || '(none)');
|
|
52
|
+
console.log(highlight('ID:'), inv.resourceId);
|
|
53
|
+
console.log(highlight('Status:'), inv.status);
|
|
54
|
+
console.log(highlight('Date:'), normalizeDate(inv.valueDate));
|
|
55
|
+
console.log(highlight('Due:'), normalizeDate(inv.dueDate));
|
|
56
56
|
if (inv.contactName)
|
|
57
|
-
console.log(
|
|
57
|
+
console.log(highlight('Contact:'), inv.contactName);
|
|
58
58
|
if (inv.totalAmount !== undefined)
|
|
59
|
-
console.log(
|
|
59
|
+
console.log(highlight('Total:'), inv.totalAmount.toFixed(2));
|
|
60
60
|
if (inv.amountDue !== undefined)
|
|
61
|
-
console.log(
|
|
61
|
+
console.log(highlight('Amount Due:'), inv.amountDue.toFixed(2));
|
|
62
62
|
if (inv.lineItems?.length) {
|
|
63
|
-
console.log(
|
|
63
|
+
console.log(highlight('Line Items:'));
|
|
64
64
|
for (const li of inv.lineItems) {
|
|
65
65
|
const qty = li.quantity ?? 1;
|
|
66
66
|
const price = li.unitPrice ?? 0;
|
|
@@ -68,7 +68,7 @@ export function registerInvoicesCommand(program) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
if (inv.notes)
|
|
71
|
-
console.log(
|
|
71
|
+
console.log(highlight('Notes:'), inv.notes);
|
|
72
72
|
}
|
|
73
73
|
})(opts));
|
|
74
74
|
// ── clio invoices search ────────────────────────────────────────
|
|
@@ -178,8 +178,8 @@ export function registerInvoicesCommand(program) {
|
|
|
178
178
|
}
|
|
179
179
|
else {
|
|
180
180
|
const status = opts.finalize ? 'finalized' : 'draft';
|
|
181
|
-
console.log(
|
|
182
|
-
console.log(
|
|
181
|
+
console.log(success(`Invoice created (${status}): ${res.data.reference || res.data.resourceId}`));
|
|
182
|
+
console.log(highlight('ID:'), res.data.resourceId);
|
|
183
183
|
}
|
|
184
184
|
}));
|
|
185
185
|
// ── clio invoices update ────────────────────────────────────────
|
|
@@ -224,7 +224,7 @@ export function registerInvoicesCommand(program) {
|
|
|
224
224
|
console.log(JSON.stringify(res.data, null, 2));
|
|
225
225
|
}
|
|
226
226
|
else {
|
|
227
|
-
console.log(
|
|
227
|
+
console.log(success(`Invoice updated: ${res.data.reference || res.data.resourceId}`));
|
|
228
228
|
}
|
|
229
229
|
})(opts));
|
|
230
230
|
// ── clio invoices delete ────────────────────────────────────────
|
|
@@ -239,7 +239,7 @@ export function registerInvoicesCommand(program) {
|
|
|
239
239
|
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
240
240
|
}
|
|
241
241
|
else {
|
|
242
|
-
console.log(
|
|
242
|
+
console.log(success(`Invoice ${resourceId} deleted.`));
|
|
243
243
|
}
|
|
244
244
|
})(opts));
|
|
245
245
|
// ── clio invoices pay ───────────────────────────────────────────
|
|
@@ -296,7 +296,7 @@ export function registerInvoicesCommand(program) {
|
|
|
296
296
|
console.log(JSON.stringify(res.data, null, 2));
|
|
297
297
|
}
|
|
298
298
|
else {
|
|
299
|
-
console.log(
|
|
299
|
+
console.log(success(`Payment recorded for invoice ${resourceId}`));
|
|
300
300
|
}
|
|
301
301
|
})(opts));
|
|
302
302
|
// ── clio invoices apply-credits ─────────────────────────────────
|
|
@@ -330,7 +330,7 @@ export function registerInvoicesCommand(program) {
|
|
|
330
330
|
console.log(JSON.stringify(res.data, null, 2));
|
|
331
331
|
}
|
|
332
332
|
else {
|
|
333
|
-
console.log(
|
|
333
|
+
console.log(success(`Applied ${credits.length} credit(s) to invoice ${resourceId}`));
|
|
334
334
|
}
|
|
335
335
|
})(opts));
|
|
336
336
|
// ── clio invoices download ──────────────────────────────────────
|
|
@@ -345,7 +345,7 @@ export function registerInvoicesCommand(program) {
|
|
|
345
345
|
console.log(JSON.stringify(res.data, null, 2));
|
|
346
346
|
}
|
|
347
347
|
else {
|
|
348
|
-
console.log(
|
|
348
|
+
console.log(highlight('PDF URL:'), res.data.fileUrl);
|
|
349
349
|
}
|
|
350
350
|
})(opts));
|
|
351
351
|
// ── clio invoices draft ───────────────────────────────────────
|
|
@@ -421,7 +421,7 @@ export function registerInvoicesCommand(program) {
|
|
|
421
421
|
console.log(JSON.stringify({ finalized: false, error: msg }));
|
|
422
422
|
}
|
|
423
423
|
else {
|
|
424
|
-
console.error(
|
|
424
|
+
console.error(danger(msg));
|
|
425
425
|
}
|
|
426
426
|
process.exit(1);
|
|
427
427
|
}
|
|
@@ -484,13 +484,13 @@ export function registerInvoicesCommand(program) {
|
|
|
484
484
|
}
|
|
485
485
|
else {
|
|
486
486
|
if (ready) {
|
|
487
|
-
console.log(
|
|
487
|
+
console.log(success(`✓ ${merged.reference || resourceId} is ready to finalize.`));
|
|
488
488
|
}
|
|
489
489
|
else {
|
|
490
|
-
console.error(
|
|
490
|
+
console.error(warning(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
|
|
491
491
|
for (const f of missingFields) {
|
|
492
492
|
const spec = INVOICE_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
|
|
493
|
-
console.log(` ${f}: ${
|
|
493
|
+
console.log(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
}
|
|
@@ -503,12 +503,12 @@ export function registerInvoicesCommand(program) {
|
|
|
503
503
|
console.log(JSON.stringify({ finalized: false, resourceId, reference: merged.reference || null, ready: false, missingCount, missingFields, validation }, null, 2));
|
|
504
504
|
}
|
|
505
505
|
else {
|
|
506
|
-
console.error(
|
|
506
|
+
console.error(danger(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
|
|
507
507
|
for (const f of missingFields) {
|
|
508
508
|
const spec = INVOICE_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
|
|
509
|
-
console.error(` ${f}: ${
|
|
509
|
+
console.error(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
|
|
510
510
|
}
|
|
511
|
-
console.error(
|
|
511
|
+
console.error(muted(`\n Fix the issues and retry:\n clio invoices draft finalize ${resourceId} ...\n`));
|
|
512
512
|
}
|
|
513
513
|
process.exit(1);
|
|
514
514
|
}
|
|
@@ -522,10 +522,10 @@ export function registerInvoicesCommand(program) {
|
|
|
522
522
|
console.log(JSON.stringify({ finalized: true, resourceId: updated.resourceId, reference: updated.reference || null, status: updated.status, fieldsUpdated }, null, 2));
|
|
523
523
|
}
|
|
524
524
|
else {
|
|
525
|
-
console.log(
|
|
526
|
-
console.log(
|
|
525
|
+
console.log(success(`\n✓ Invoice finalized: ${updated.reference || updated.resourceId}`));
|
|
526
|
+
console.log(highlight(' Status:'), updated.status);
|
|
527
527
|
if (fieldsUpdated.length > 0) {
|
|
528
|
-
console.log(
|
|
528
|
+
console.log(highlight(' Updated:'), fieldsUpdated.join(', '));
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
})(opts));
|
|
@@ -548,11 +548,11 @@ export function registerInvoicesCommand(program) {
|
|
|
548
548
|
console.log(`No attachments for invoice ${inv.reference || resourceId}.`);
|
|
549
549
|
return;
|
|
550
550
|
}
|
|
551
|
-
console.log(
|
|
551
|
+
console.log(highlight(`Attachments for ${inv.reference || resourceId}:\n`));
|
|
552
552
|
for (const att of attachments) {
|
|
553
|
-
console.log(` ${
|
|
553
|
+
console.log(` ${accent(att.resourceId)} ${att.fileName || '(unnamed)'}`);
|
|
554
554
|
if (att.fileUrl)
|
|
555
|
-
console.log(` ${
|
|
555
|
+
console.log(` ${muted(att.fileUrl)}`);
|
|
556
556
|
}
|
|
557
557
|
}
|
|
558
558
|
})(opts));
|
|
@@ -573,9 +573,9 @@ export function registerInvoicesCommand(program) {
|
|
|
573
573
|
console.log('No payments recorded for this invoice.');
|
|
574
574
|
return;
|
|
575
575
|
}
|
|
576
|
-
console.log(
|
|
576
|
+
console.log(highlight(`Payments for invoice ${resourceId}:\n`));
|
|
577
577
|
for (const p of payments) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
578
|
-
console.log(` ${
|
|
578
|
+
console.log(` ${accent(p.resourceId)} ${p.paymentAmount ?? p.amount ?? ''} on ${p.valueDate ?? ''}`);
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
581
|
})(opts));
|
|
@@ -596,9 +596,9 @@ export function registerInvoicesCommand(program) {
|
|
|
596
596
|
console.log('No credits applied to this invoice.');
|
|
597
597
|
return;
|
|
598
598
|
}
|
|
599
|
-
console.log(
|
|
599
|
+
console.log(highlight(`Credits for invoice ${resourceId}:\n`));
|
|
600
600
|
for (const c of credits) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
601
|
-
console.log(` ${
|
|
601
|
+
console.log(` ${accent(c.resourceId)} ${c.amount ?? ''}`);
|
|
602
602
|
}
|
|
603
603
|
}
|
|
604
604
|
})(opts));
|
|
@@ -614,7 +614,7 @@ export function registerInvoicesCommand(program) {
|
|
|
614
614
|
console.log(JSON.stringify({ reversed: true, resourceId, creditResourceId }));
|
|
615
615
|
}
|
|
616
616
|
else {
|
|
617
|
-
console.log(
|
|
617
|
+
console.log(success(`Credit ${creditResourceId} reversed from invoice ${resourceId}.`));
|
|
618
618
|
}
|
|
619
619
|
})(opts));
|
|
620
620
|
}
|
package/dist/commands/items.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { accent, highlight, success } from './ui/theme.js';
|
|
2
2
|
import { listItems, getItem, searchItems, createItem, updateItem, deleteItem, } from '../core/api/items.js';
|
|
3
3
|
import { findExistingItem } from '../core/api/guards.js';
|
|
4
4
|
import { apiAction } from './api-action.js';
|
|
@@ -77,15 +77,15 @@ export function registerItemsCommand(program) {
|
|
|
77
77
|
console.log(JSON.stringify(item, null, 2));
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
|
-
console.log(
|
|
81
|
-
console.log(
|
|
82
|
-
console.log(
|
|
80
|
+
console.log(highlight('Name:'), item.internalName);
|
|
81
|
+
console.log(highlight('Code:'), item.itemCode);
|
|
82
|
+
console.log(highlight('ID:'), item.resourceId);
|
|
83
83
|
const types = [
|
|
84
84
|
item.appliesToSale && 'Sale',
|
|
85
85
|
item.appliesToPurchase && 'Purchase',
|
|
86
86
|
].filter(Boolean).join(', ');
|
|
87
87
|
if (types)
|
|
88
|
-
console.log(
|
|
88
|
+
console.log(highlight('Applies to:'), types);
|
|
89
89
|
}
|
|
90
90
|
})(opts));
|
|
91
91
|
// ── clio items create ─────────────────────────────────────────
|
|
@@ -120,8 +120,8 @@ export function registerItemsCommand(program) {
|
|
|
120
120
|
console.log(JSON.stringify(existing, null, 2));
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
123
|
-
console.log(
|
|
124
|
-
console.log(
|
|
123
|
+
console.log(accent(`Item with code "${itemCode}" already exists`));
|
|
124
|
+
console.log(highlight('ID:'), existing.resourceId);
|
|
125
125
|
}
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
@@ -157,8 +157,8 @@ export function registerItemsCommand(program) {
|
|
|
157
157
|
console.log(JSON.stringify(res.data, null, 2));
|
|
158
158
|
}
|
|
159
159
|
else {
|
|
160
|
-
console.log(
|
|
161
|
-
console.log(
|
|
160
|
+
console.log(success(`Item created: ${opts.name}`));
|
|
161
|
+
console.log(highlight('ID:'), res.data.resourceId);
|
|
162
162
|
}
|
|
163
163
|
}));
|
|
164
164
|
// ── clio items update ─────────────────────────────────────────
|
|
@@ -201,7 +201,7 @@ export function registerItemsCommand(program) {
|
|
|
201
201
|
console.log(JSON.stringify(res.data, null, 2));
|
|
202
202
|
}
|
|
203
203
|
else {
|
|
204
|
-
console.log(
|
|
204
|
+
console.log(success(`Item updated: ${res.data.internalName}`));
|
|
205
205
|
}
|
|
206
206
|
})(opts));
|
|
207
207
|
// ── clio items delete ─────────────────────────────────────────
|
|
@@ -217,7 +217,7 @@ export function registerItemsCommand(program) {
|
|
|
217
217
|
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
218
218
|
}
|
|
219
219
|
else {
|
|
220
|
-
console.log(
|
|
220
|
+
console.log(success(`Item ${resourceId} deleted.`));
|
|
221
221
|
}
|
|
222
222
|
})(opts));
|
|
223
223
|
}
|
package/dist/commands/jobs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { danger, success, warning, muted, highlight } from './ui/theme.js';
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
@@ -47,7 +47,7 @@ function jobAction(fn) {
|
|
|
47
47
|
}
|
|
48
48
|
catch (err) {
|
|
49
49
|
if (err instanceof JobValidationError || err instanceof CalcValidationError || err instanceof TaxValidationError || err instanceof BankFileValidationError) {
|
|
50
|
-
console.error(
|
|
50
|
+
console.error(danger(`Error: ${err.message}`));
|
|
51
51
|
process.exit(1);
|
|
52
52
|
}
|
|
53
53
|
throw err;
|
|
@@ -140,7 +140,7 @@ async function fetchOutstandingForBankFile(opts, format) {
|
|
|
140
140
|
// Auth + client
|
|
141
141
|
const auth = requireAuth(opts.apiKey);
|
|
142
142
|
const client = new JazClient(auth);
|
|
143
|
-
process.stderr.write(
|
|
143
|
+
process.stderr.write(muted(' Fetching outstanding bills...\n'));
|
|
144
144
|
// Build search filter — UNPAID + balance > 0
|
|
145
145
|
const filter = {
|
|
146
146
|
status: { eq: 'UNPAID' },
|
|
@@ -181,12 +181,12 @@ async function fetchOutstandingForBankFile(opts, format) {
|
|
|
181
181
|
dueBefore: opts.dueBefore,
|
|
182
182
|
});
|
|
183
183
|
// Display summary
|
|
184
|
-
process.stderr.write(
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
process.stderr.write(highlight(` Found ${outstanding.supplierCount} supplier${outstanding.supplierCount !== 1 ? 's' : ''}`) +
|
|
185
|
+
highlight(` · ${outstanding.totalBills} bill${outstanding.totalBills !== 1 ? 's' : ''}`) +
|
|
186
|
+
highlight(` · $${outstanding.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n\n`));
|
|
187
187
|
// Build multiselect choices from suppliers
|
|
188
188
|
const choices = outstanding.suppliers.map(s => ({
|
|
189
|
-
title: `${s.contactName} ${
|
|
189
|
+
title: `${s.contactName} ${muted(`${s.billCount} bill${s.billCount !== 1 ? 's' : ''} $${s.totalBalance.toLocaleString(undefined, { minimumFractionDigits: 2 })}`)}`,
|
|
190
190
|
value: s.contactResourceId,
|
|
191
191
|
selected: true,
|
|
192
192
|
}));
|
|
@@ -253,13 +253,13 @@ async function fetchOutstandingForBankFile(opts, format) {
|
|
|
253
253
|
payees,
|
|
254
254
|
};
|
|
255
255
|
if (hasPlaceholders) {
|
|
256
|
-
process.stderr.write(
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
process.stderr.write(warning('\n Note: Payee bank details (accountNumber, bankCode) are not available from the API.\n') +
|
|
257
|
+
warning(' Saving pre-filled template — fill in bank details, then re-run:\n\n') +
|
|
258
|
+
muted(` clio jobs payment-run bank-file --${format} --input ${templatePath}\n\n`));
|
|
259
259
|
writeFileSync(templatePath, JSON.stringify(templateData, null, 2) + '\n', { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
|
|
260
|
-
process.stderr.write(
|
|
261
|
-
process.stderr.write(
|
|
262
|
-
process.stderr.write(
|
|
260
|
+
process.stderr.write(success(` Saved: ${templatePath}\n`));
|
|
261
|
+
process.stderr.write(muted(` ${payees.length} payee${payees.length !== 1 ? 's' : ''} · $${selectedSuppliers.reduce((s, g) => s + g.totalBalance, 0).toLocaleString(undefined, { minimumFractionDigits: 2 })} ${outstanding.currency}\n`));
|
|
262
|
+
process.stderr.write(muted(' Replace FILL_IN_ACCOUNT_NUMBER and FILL_IN_BANK_CODE for each payee.\n\n'));
|
|
263
263
|
process.exit(0);
|
|
264
264
|
}
|
|
265
265
|
return templateData;
|
|
@@ -507,7 +507,7 @@ export function registerJobsCommand(program) {
|
|
|
507
507
|
truncated: res.totalElements > res.data.length,
|
|
508
508
|
};
|
|
509
509
|
if (fetched.truncated && !opts.json) {
|
|
510
|
-
process.stderr.write(
|
|
510
|
+
process.stderr.write(warning(` Warning: ${res.totalElements.toLocaleString()} bills found, showing ${res.data.length.toLocaleString()} (API pagination bug — see api-dx-issues.md #9)\n`));
|
|
511
511
|
}
|
|
512
512
|
// Enrich bills with contact names (batch lookup)
|
|
513
513
|
const uniqueContactIds = Array.from(new Set(fetched.data.map(b => b.contactResourceId).filter(Boolean)));
|
|
@@ -645,7 +645,7 @@ export function registerJobsCommand(program) {
|
|
|
645
645
|
};
|
|
646
646
|
const forceType = opts.type ? typeMap[opts.type.toLowerCase()] : undefined;
|
|
647
647
|
if (opts.type && !forceType) {
|
|
648
|
-
console.error(
|
|
648
|
+
console.error(danger(`Error: Invalid --type "${opts.type}". Use: invoice, bill, credit-note-customer, credit-note-supplier, or bank-statement`));
|
|
649
649
|
process.exit(1);
|
|
650
650
|
}
|
|
651
651
|
// ── Scan + Classify ─────────────────────────────────────────
|
|
@@ -659,7 +659,7 @@ export function registerJobsCommand(program) {
|
|
|
659
659
|
});
|
|
660
660
|
}
|
|
661
661
|
catch (err) {
|
|
662
|
-
console.error(
|
|
662
|
+
console.error(danger(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
663
663
|
process.exit(1);
|
|
664
664
|
}
|
|
665
665
|
// ── Scan-only mode (no --upload) ─────────────────────────────
|
|
@@ -673,21 +673,21 @@ export function registerJobsCommand(program) {
|
|
|
673
673
|
.filter(f => f.documentType === 'UNKNOWN')
|
|
674
674
|
.map(f => ` ${f.folder}/ (${f.count} files)`)
|
|
675
675
|
.join('\n');
|
|
676
|
-
console.error(
|
|
677
|
-
console.error(
|
|
676
|
+
console.error(danger('Error: Cannot upload — some folders could not be classified:'));
|
|
677
|
+
console.error(warning(unknownFolders));
|
|
678
678
|
console.error();
|
|
679
|
-
console.error(
|
|
680
|
-
console.error(
|
|
679
|
+
console.error(muted('Fix: Re-run with --type to force a type for all files,'));
|
|
680
|
+
console.error(muted(' or rename folders to match: invoices/, bills/, bank-statements/'));
|
|
681
681
|
process.exit(1);
|
|
682
682
|
}
|
|
683
683
|
if (plan.summary.uploadable === 0) {
|
|
684
|
-
console.error(
|
|
684
|
+
console.error(danger('Error: No uploadable files found in the source.'));
|
|
685
685
|
process.exit(1);
|
|
686
686
|
}
|
|
687
687
|
const hasBankStatements = plan.folders.some(f => f.documentType === 'BANK_STATEMENT');
|
|
688
688
|
if (hasBankStatements && !opts.bankAccount) {
|
|
689
|
-
console.error(
|
|
690
|
-
console.error(
|
|
689
|
+
console.error(danger('Error: --bank-account is required when uploading bank statements.'));
|
|
690
|
+
console.error(muted('Get your bank account ID: clio bank accounts --json'));
|
|
691
691
|
process.exit(1);
|
|
692
692
|
}
|
|
693
693
|
// ── Encrypted PDF checks ──────────────────────────────────────
|
|
@@ -706,11 +706,11 @@ export function registerJobsCommand(program) {
|
|
|
706
706
|
}));
|
|
707
707
|
}
|
|
708
708
|
else {
|
|
709
|
-
console.error(
|
|
710
|
-
console.error(
|
|
711
|
-
console.error(
|
|
712
|
-
console.error(
|
|
713
|
-
console.error(
|
|
709
|
+
console.error(danger('Error: Encrypted PDFs found but qpdf is not installed.'));
|
|
710
|
+
console.error(warning('Install qpdf to decrypt before upload:'));
|
|
711
|
+
console.error(muted(' macOS: brew install qpdf'));
|
|
712
|
+
console.error(muted(' Ubuntu: sudo apt install qpdf'));
|
|
713
|
+
console.error(muted(' Windows: choco install qpdf'));
|
|
714
714
|
}
|
|
715
715
|
process.exit(1);
|
|
716
716
|
}
|
|
@@ -727,14 +727,14 @@ export function registerJobsCommand(program) {
|
|
|
727
727
|
process.exit(1);
|
|
728
728
|
}
|
|
729
729
|
// Interactive mode — prompt user for each encrypted file
|
|
730
|
-
console.error(
|
|
730
|
+
console.error(warning(`\n${needPassword.length} encrypted PDF(s) need a password:\n`));
|
|
731
731
|
for (const f of needPassword) {
|
|
732
732
|
const password = await p.text({
|
|
733
733
|
message: `PDF password for ${f.folder}/${f.filename}`,
|
|
734
734
|
});
|
|
735
735
|
if (p.isCancel(password) || !password) {
|
|
736
|
-
console.error(
|
|
737
|
-
console.error(
|
|
736
|
+
console.error(danger('Aborted — no password provided.'));
|
|
737
|
+
console.error(muted('Tip: embed password in filename to skip prompts: filename__pw__password.pdf'));
|
|
738
738
|
process.exit(1);
|
|
739
739
|
}
|
|
740
740
|
f.filePassword = password;
|
|
@@ -748,11 +748,11 @@ export function registerJobsCommand(program) {
|
|
|
748
748
|
const resolved = await resolveBankAccount(client, opts.bankAccount);
|
|
749
749
|
bankAccountId = resolved.resourceId;
|
|
750
750
|
if (!apiOpts.json && resolved.name !== resolved.resourceId) {
|
|
751
|
-
process.stderr.write(
|
|
751
|
+
process.stderr.write(muted(` Bank account: ${resolved.name} (${resolved.resourceId})\n`));
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
754
|
if (!apiOpts.json) {
|
|
755
|
-
process.stderr.write(
|
|
755
|
+
process.stderr.write(muted(`\n Uploading ${plan.summary.uploadable} files...\n`));
|
|
756
756
|
}
|
|
757
757
|
const upload = await uploadClassifiedFiles({
|
|
758
758
|
plan,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { danger, success, accent, warning, muted, highlight } from './ui/theme.js';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
3
|
import { listJournals, searchJournals, createJournal, getJournal, updateJournal, deleteJournal, finalizeJournal, } from '../core/api/journals.js';
|
|
4
4
|
import { createTransferTrialBalance } from '../core/api/transfer-trial-balance.js';
|
|
@@ -106,8 +106,8 @@ export function registerJournalsCommand(program) {
|
|
|
106
106
|
}
|
|
107
107
|
else {
|
|
108
108
|
const status = opts.finalize ? 'finalized' : 'draft';
|
|
109
|
-
console.log(
|
|
110
|
-
console.log(
|
|
109
|
+
console.log(success(`Journal created (${status}): ${res.data.reference || res.data.resourceId}`));
|
|
110
|
+
console.log(highlight('ID:'), res.data.resourceId);
|
|
111
111
|
}
|
|
112
112
|
}));
|
|
113
113
|
// ── clio journals get ──────────────────────────────────────────
|
|
@@ -123,13 +123,13 @@ export function registerJournalsCommand(program) {
|
|
|
123
123
|
}
|
|
124
124
|
else {
|
|
125
125
|
const status = formatStatus(j.status);
|
|
126
|
-
console.log(
|
|
127
|
-
console.log(
|
|
128
|
-
console.log(
|
|
129
|
-
console.log(
|
|
130
|
-
console.log(
|
|
126
|
+
console.log(highlight('Journal:'), j.reference || '(no ref)');
|
|
127
|
+
console.log(highlight(' ID:'), accent(j.resourceId));
|
|
128
|
+
console.log(highlight(' Status:'), status);
|
|
129
|
+
console.log(highlight(' Date:'), normalizeDate(j.valueDate));
|
|
130
|
+
console.log(highlight(' Notes:'), j.notes || '—');
|
|
131
131
|
if (j.journalEntries?.length) {
|
|
132
|
-
console.log(
|
|
132
|
+
console.log(highlight(` Entries (${j.journalEntries.length}):`));
|
|
133
133
|
for (const e of j.journalEntries) {
|
|
134
134
|
// GET response has richer fields than the create JournalEntry type
|
|
135
135
|
const entry = e;
|
|
@@ -137,7 +137,7 @@ export function registerJournalsCommand(program) {
|
|
|
137
137
|
const dr = entry.debitAmount;
|
|
138
138
|
const cr = entry.creditAmount;
|
|
139
139
|
const amt = dr != null ? `DR ${dr}` : `CR ${cr}`;
|
|
140
|
-
console.log(` ${
|
|
140
|
+
console.log(` ${muted(String(acct).slice(0, 12) + '...')} ${amt} ${e.description || ''}`);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
|
@@ -186,7 +186,7 @@ export function registerJournalsCommand(program) {
|
|
|
186
186
|
}
|
|
187
187
|
else {
|
|
188
188
|
const status = opts.finalize ? 'finalized' : 'draft';
|
|
189
|
-
console.log(
|
|
189
|
+
console.log(success(`Journal updated (${status}): ${res.data.reference || res.data.resourceId}`));
|
|
190
190
|
}
|
|
191
191
|
})(opts));
|
|
192
192
|
// ── clio journals delete ────────────────────────────────────────
|
|
@@ -201,7 +201,7 @@ export function registerJournalsCommand(program) {
|
|
|
201
201
|
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
202
202
|
}
|
|
203
203
|
else {
|
|
204
|
-
console.log(
|
|
204
|
+
console.log(success(`Journal ${resourceId} deleted.`));
|
|
205
205
|
}
|
|
206
206
|
})(opts));
|
|
207
207
|
// ── clio journals transfer-trial-balance ─────────────────────
|
|
@@ -239,8 +239,8 @@ export function registerJournalsCommand(program) {
|
|
|
239
239
|
console.log(JSON.stringify(res.data, null, 2));
|
|
240
240
|
}
|
|
241
241
|
else {
|
|
242
|
-
console.log(
|
|
243
|
-
console.log(
|
|
242
|
+
console.log(success('Transfer Trial Balance created.'));
|
|
243
|
+
console.log(highlight('ID:'), res.data.resourceId);
|
|
244
244
|
}
|
|
245
245
|
}));
|
|
246
246
|
// ── clio journals draft ──────────────────────────────────────
|
|
@@ -316,7 +316,7 @@ export function registerJournalsCommand(program) {
|
|
|
316
316
|
console.log(JSON.stringify({ finalized: false, error: msg }));
|
|
317
317
|
}
|
|
318
318
|
else {
|
|
319
|
-
console.error(
|
|
319
|
+
console.error(danger(msg));
|
|
320
320
|
}
|
|
321
321
|
process.exit(1);
|
|
322
322
|
}
|
|
@@ -362,13 +362,13 @@ export function registerJournalsCommand(program) {
|
|
|
362
362
|
}
|
|
363
363
|
else {
|
|
364
364
|
if (ready) {
|
|
365
|
-
console.log(
|
|
365
|
+
console.log(success(`✓ ${merged.reference || resourceId} is ready to finalize.`));
|
|
366
366
|
}
|
|
367
367
|
else {
|
|
368
|
-
console.error(
|
|
368
|
+
console.error(warning(`✗ ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:`));
|
|
369
369
|
for (const f of missingFields) {
|
|
370
370
|
const spec = JOURNAL_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
|
|
371
|
-
console.log(` ${f}: ${
|
|
371
|
+
console.log(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
}
|
|
@@ -381,12 +381,12 @@ export function registerJournalsCommand(program) {
|
|
|
381
381
|
console.log(JSON.stringify({ finalized: false, resourceId, reference: merged.reference || null, ready: false, missingCount, missingFields, validation }, null, 2));
|
|
382
382
|
}
|
|
383
383
|
else {
|
|
384
|
-
console.error(
|
|
384
|
+
console.error(danger(`\n✗ Cannot finalize ${merged.reference || resourceId} — ${missingCount} issue${missingCount > 1 ? 's' : ''} remaining:\n`));
|
|
385
385
|
for (const f of missingFields) {
|
|
386
386
|
const spec = JOURNAL_REQUIRED_FIELDS.find((s) => f === s.field || f.endsWith(`.${s.field}`));
|
|
387
|
-
console.error(` ${f}: ${
|
|
387
|
+
console.error(` ${f}: ${danger('MISSING')} — ${spec?.hint ?? ''}`);
|
|
388
388
|
}
|
|
389
|
-
console.error(
|
|
389
|
+
console.error(muted(`\n Fix the issues and retry:\n clio journals draft finalize ${resourceId} ...\n`));
|
|
390
390
|
}
|
|
391
391
|
process.exit(1);
|
|
392
392
|
}
|
|
@@ -400,10 +400,10 @@ export function registerJournalsCommand(program) {
|
|
|
400
400
|
console.log(JSON.stringify({ finalized: true, resourceId: updated.resourceId, reference: updated.reference || null, status: updated.status, fieldsUpdated }, null, 2));
|
|
401
401
|
}
|
|
402
402
|
else {
|
|
403
|
-
console.log(
|
|
404
|
-
console.log(
|
|
403
|
+
console.log(success(`\n✓ Journal finalized: ${updated.reference || updated.resourceId}`));
|
|
404
|
+
console.log(highlight(' Status:'), updated.status);
|
|
405
405
|
if (fieldsUpdated.length > 0) {
|
|
406
|
-
console.log(
|
|
406
|
+
console.log(highlight(' Updated:'), fieldsUpdated.join(', '));
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
})(opts));
|
|
@@ -426,11 +426,11 @@ export function registerJournalsCommand(program) {
|
|
|
426
426
|
console.log(`No attachments for journal ${journal.reference || resourceId}.`);
|
|
427
427
|
return;
|
|
428
428
|
}
|
|
429
|
-
console.log(
|
|
429
|
+
console.log(highlight(`Attachments for ${journal.reference || resourceId}:\n`));
|
|
430
430
|
for (const att of attachments) {
|
|
431
|
-
console.log(` ${
|
|
431
|
+
console.log(` ${accent(att.resourceId)} ${att.fileName || '(unnamed)'}`);
|
|
432
432
|
if (att.fileUrl)
|
|
433
|
-
console.log(` ${
|
|
433
|
+
console.log(` ${muted(att.fileUrl)}`);
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
})(opts));
|
package/dist/commands/kb.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
1
|
import { loadIndex, searchArticles, } from '../core/kb/index.js';
|
|
3
2
|
import { loadEmbeddings, semanticSearch, isSemanticAvailable, } from '../core/kb/embeddings.js';
|
|
3
|
+
import { accent, danger, highlight, muted } from './ui/theme.js';
|
|
4
4
|
export function registerKbCommand(program) {
|
|
5
5
|
program
|
|
6
6
|
.command('help-center')
|
|
@@ -46,22 +46,22 @@ export function registerKbCommand(program) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
console.log('');
|
|
49
|
-
console.log(
|
|
49
|
+
console.log(highlight(` ${results.length} result${results.length !== 1 ? 's' : ''} for "${query}"`));
|
|
50
50
|
console.log('');
|
|
51
51
|
for (let i = 0; i < results.length; i++) {
|
|
52
52
|
const r = results[i];
|
|
53
|
-
console.log(
|
|
53
|
+
console.log(accent(` ${i + 1}. ${r.article.title}`) + muted(` (${r.section.name})`));
|
|
54
54
|
if (r.article.snippet) {
|
|
55
|
-
console.log(
|
|
55
|
+
console.log(muted(` ${r.article.snippet.slice(0, 120)}${r.article.snippet.length > 120 ? '...' : ''}`));
|
|
56
56
|
}
|
|
57
57
|
if (r.article.sourceUrl) {
|
|
58
|
-
console.log(
|
|
58
|
+
console.log(muted(` ${r.article.sourceUrl}`));
|
|
59
59
|
}
|
|
60
60
|
console.log('');
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
catch (err) {
|
|
64
|
-
console.error(
|
|
64
|
+
console.error(danger(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
67
|
});
|