jaz-cli 2.8.0 → 2.9.0

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.
Files changed (45) hide show
  1. package/assets/skills/api/SKILL.md +1 -1
  2. package/assets/skills/conversion/SKILL.md +1 -1
  3. package/assets/skills/jobs/SKILL.md +58 -1
  4. package/assets/skills/jobs/references/sg-tax/add-backs-guide.md +354 -0
  5. package/assets/skills/jobs/references/sg-tax/capital-allowances-guide.md +343 -0
  6. package/assets/skills/jobs/references/sg-tax/data-extraction.md +408 -0
  7. package/assets/skills/jobs/references/sg-tax/enhanced-deductions.md +248 -0
  8. package/assets/skills/jobs/references/sg-tax/exemptions-and-rebates.md +197 -0
  9. package/assets/skills/jobs/references/sg-tax/form-cs-fields.md +191 -0
  10. package/assets/skills/jobs/references/sg-tax/ifrs16-tax-adjustment.md +194 -0
  11. package/assets/skills/jobs/references/sg-tax/losses-and-carry-forwards.md +269 -0
  12. package/assets/skills/jobs/references/sg-tax/overview.md +207 -0
  13. package/assets/skills/jobs/references/sg-tax/wizard-workflow.md +391 -0
  14. package/assets/skills/transaction-recipes/SKILL.md +1 -1
  15. package/dist/__tests__/jobs-audit-prep.test.js +3 -3
  16. package/dist/__tests__/jobs-bank-recon.test.js +5 -5
  17. package/dist/__tests__/jobs-credit-control.test.js +1 -1
  18. package/dist/__tests__/jobs-fa-review.test.js +1 -1
  19. package/dist/__tests__/jobs-payment-run.test.js +3 -3
  20. package/dist/__tests__/jobs-supplier-recon.test.js +6 -6
  21. package/dist/__tests__/tax-sg-capital-allowances.test.js +389 -0
  22. package/dist/__tests__/tax-sg-exemptions.test.js +232 -0
  23. package/dist/__tests__/tax-sg-form-cs.test.js +687 -0
  24. package/dist/__tests__/tax-validate.test.js +208 -0
  25. package/dist/commands/init.js +7 -2
  26. package/dist/commands/jobs.js +1 -1
  27. package/dist/commands/tax.js +195 -0
  28. package/dist/index.js +2 -0
  29. package/dist/jobs/audit-prep.js +4 -4
  30. package/dist/jobs/bank-recon.js +4 -4
  31. package/dist/jobs/credit-control.js +5 -5
  32. package/dist/jobs/fa-review.js +4 -4
  33. package/dist/jobs/gst-vat.js +3 -3
  34. package/dist/jobs/payment-run.js +4 -4
  35. package/dist/jobs/supplier-recon.js +3 -3
  36. package/dist/tax/format.js +18 -0
  37. package/dist/tax/sg/capital-allowances.js +160 -0
  38. package/dist/tax/sg/constants.js +63 -0
  39. package/dist/tax/sg/exemptions.js +76 -0
  40. package/dist/tax/sg/form-cs.js +349 -0
  41. package/dist/tax/sg/format-sg.js +134 -0
  42. package/dist/tax/types.js +9 -0
  43. package/dist/tax/validate.js +124 -0
  44. package/dist/utils/template.js +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,208 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { TaxValidationError, validateYa, validateNonNegative, validateExemptionType, validateAssetCategory, validateDateFormat, validateFormCsInput, validateCaInput, } from '../tax/validate.js';
3
+ // ── validateYa ──────────────────────────────────────────────────
4
+ describe('validateYa', () => {
5
+ it('accepts valid YA 2026', () => {
6
+ expect(() => validateYa(2026)).not.toThrow();
7
+ });
8
+ it('rejects YA below range (2019)', () => {
9
+ expect(() => validateYa(2019)).toThrow('between 2020 and 2100');
10
+ });
11
+ it('rejects YA above range (2101)', () => {
12
+ expect(() => validateYa(2101)).toThrow('between 2020 and 2100');
13
+ });
14
+ it('rejects NaN', () => {
15
+ expect(() => validateYa(NaN)).toThrow('must be an integer');
16
+ });
17
+ it('rejects non-integer (2025.5)', () => {
18
+ expect(() => validateYa(2025.5)).toThrow('must be an integer');
19
+ });
20
+ });
21
+ // ── validateNonNegative ─────────────────────────────────────────
22
+ describe('validateNonNegative', () => {
23
+ it('accepts zero', () => {
24
+ expect(() => validateNonNegative(0, 'amount')).not.toThrow();
25
+ });
26
+ it('accepts positive number', () => {
27
+ expect(() => validateNonNegative(100, 'amount')).not.toThrow();
28
+ });
29
+ it('rejects negative number', () => {
30
+ expect(() => validateNonNegative(-1, 'amount')).toThrow('must be zero or positive');
31
+ });
32
+ it('rejects NaN', () => {
33
+ expect(() => validateNonNegative(NaN, 'amount')).toThrow('must be zero or positive');
34
+ });
35
+ it('rejects Infinity', () => {
36
+ expect(() => validateNonNegative(Infinity, 'amount')).toThrow('must be zero or positive');
37
+ });
38
+ });
39
+ // ── validateExemptionType ───────────────────────────────────────
40
+ describe('validateExemptionType', () => {
41
+ it('accepts sute', () => {
42
+ expect(() => validateExemptionType('sute')).not.toThrow();
43
+ });
44
+ it('accepts pte', () => {
45
+ expect(() => validateExemptionType('pte')).not.toThrow();
46
+ });
47
+ it('accepts none', () => {
48
+ expect(() => validateExemptionType('none')).not.toThrow();
49
+ });
50
+ it('rejects invalid type', () => {
51
+ expect(() => validateExemptionType('invalid')).toThrow('Must be one of: sute, pte, none');
52
+ });
53
+ });
54
+ // ── validateAssetCategory ───────────────────────────────────────
55
+ describe('validateAssetCategory', () => {
56
+ it('accepts computer', () => {
57
+ expect(() => validateAssetCategory('computer')).not.toThrow();
58
+ });
59
+ it('rejects invalid category', () => {
60
+ expect(() => validateAssetCategory('invalid')).toThrow('Must be one of: computer, automation, low-value, general, ip, renovation');
61
+ });
62
+ });
63
+ // ── validateDateFormat ──────────────────────────────────────────
64
+ describe('validateDateFormat', () => {
65
+ it('accepts valid date 2025-01-01', () => {
66
+ expect(() => validateDateFormat('2025-01-01', 'startDate')).not.toThrow();
67
+ });
68
+ it('rejects invalid month (2025-13-01)', () => {
69
+ expect(() => validateDateFormat('2025-13-01', 'startDate')).toThrow(TaxValidationError);
70
+ });
71
+ it('rejects invalid day (2025-02-30)', () => {
72
+ expect(() => validateDateFormat('2025-02-30', 'startDate')).toThrow(TaxValidationError);
73
+ });
74
+ it('rejects non-date string', () => {
75
+ expect(() => validateDateFormat('not-a-date', 'startDate')).toThrow('YYYY-MM-DD');
76
+ });
77
+ });
78
+ // ── validateFormCsInput ─────────────────────────────────────────
79
+ describe('validateFormCsInput', () => {
80
+ const validInput = {
81
+ ya: 2026,
82
+ basisPeriodStart: '2025-01-01',
83
+ basisPeriodEnd: '2025-12-31',
84
+ revenue: 500000,
85
+ accountingProfit: 100000,
86
+ exemptionType: 'pte',
87
+ addBacks: {
88
+ depreciation: 0,
89
+ amortization: 0,
90
+ rouDepreciation: 0,
91
+ leaseInterest: 0,
92
+ generalProvisions: 0,
93
+ donations: 0,
94
+ entertainment: 0,
95
+ penalties: 0,
96
+ privateCar: 0,
97
+ capitalExpOnPnl: 0,
98
+ unrealizedFxLoss: 0,
99
+ otherNonDeductible: 0,
100
+ },
101
+ deductions: {
102
+ actualLeasePayments: 0,
103
+ unrealizedFxGain: 0,
104
+ exemptDividends: 0,
105
+ exemptIncome: 0,
106
+ otherDeductions: 0,
107
+ },
108
+ capitalAllowances: {
109
+ currentYearClaim: 0,
110
+ balanceBroughtForward: 0,
111
+ },
112
+ enhancedDeductions: {
113
+ rdExpenditure: 0,
114
+ rdMultiplier: 2.5,
115
+ ipRegistration: 0,
116
+ ipMultiplier: 2.0,
117
+ donations250Base: 0,
118
+ s14qRenovation: 0,
119
+ },
120
+ losses: { broughtForward: 0 },
121
+ donationsCarryForward: { broughtForward: 0 },
122
+ };
123
+ it('accepts valid complete input', () => {
124
+ expect(() => validateFormCsInput(validInput)).not.toThrow();
125
+ });
126
+ it('rejects negative depreciation add-back', () => {
127
+ const bad = {
128
+ ...validInput,
129
+ addBacks: { ...validInput.addBacks, depreciation: -100 },
130
+ };
131
+ expect(() => validateFormCsInput(bad)).toThrow('must be zero or positive');
132
+ });
133
+ it('rejects basisPeriodStart after basisPeriodEnd', () => {
134
+ const bad = {
135
+ ...validInput,
136
+ basisPeriodStart: '2025-12-31',
137
+ basisPeriodEnd: '2025-01-01',
138
+ };
139
+ expect(() => validateFormCsInput(bad)).toThrow('must be on or before');
140
+ });
141
+ it('accepts basisPeriodStart equal to basisPeriodEnd', () => {
142
+ const same = {
143
+ ...validInput,
144
+ basisPeriodStart: '2025-06-15',
145
+ basisPeriodEnd: '2025-06-15',
146
+ };
147
+ expect(() => validateFormCsInput(same)).not.toThrow();
148
+ });
149
+ });
150
+ // ── validateCaInput ─────────────────────────────────────────────
151
+ describe('validateCaInput', () => {
152
+ const validAsset = {
153
+ description: 'Laptop',
154
+ cost: 3000,
155
+ acquisitionDate: '2025-03-15',
156
+ category: 'computer',
157
+ priorYearsClaimed: 0,
158
+ };
159
+ const validInput = {
160
+ ya: 2026,
161
+ assets: [validAsset],
162
+ unabsorbedBroughtForward: 0,
163
+ };
164
+ it('accepts valid input with one asset', () => {
165
+ expect(() => validateCaInput(validInput)).not.toThrow();
166
+ });
167
+ it('rejects empty assets array', () => {
168
+ expect(() => validateCaInput({ ...validInput, assets: [] })).toThrow('At least one asset');
169
+ });
170
+ it('rejects priorYearsClaimed exceeding cost', () => {
171
+ const bad = {
172
+ ...validInput,
173
+ assets: [{ ...validAsset, cost: 1000, priorYearsClaimed: 1500 }],
174
+ };
175
+ expect(() => validateCaInput(bad)).toThrow('exceeds cost');
176
+ });
177
+ it('rejects low-value asset with cost above $5,000', () => {
178
+ const bad = {
179
+ ...validInput,
180
+ assets: [{ ...validAsset, category: 'low-value', cost: 6000 }],
181
+ };
182
+ expect(() => validateCaInput(bad)).toThrow('exceeds $5,000 threshold');
183
+ });
184
+ it('rejects IP asset with invalid ipWriteOffYears (7)', () => {
185
+ const bad = {
186
+ ...validInput,
187
+ assets: [{
188
+ ...validAsset,
189
+ category: 'ip',
190
+ ipWriteOffYears: 7,
191
+ }],
192
+ };
193
+ expect(() => validateCaInput(bad)).toThrow('must be 5, 10, or 15');
194
+ });
195
+ it('accepts IP asset with valid ipWriteOffYears (5, 10, 15)', () => {
196
+ for (const years of [5, 10, 15]) {
197
+ const ok = {
198
+ ...validInput,
199
+ assets: [{
200
+ ...validAsset,
201
+ category: 'ip',
202
+ ipWriteOffYears: years,
203
+ }],
204
+ };
205
+ expect(() => validateCaInput(ok)).not.toThrow();
206
+ }
207
+ });
208
+ });
@@ -16,7 +16,7 @@ export async function initCommand(options) {
16
16
  choices: [
17
17
  {
18
18
  title: `All (Recommended)`,
19
- description: 'API reference + data conversion + transaction recipes',
19
+ description: 'API reference + data conversion + transaction recipes + accounting jobs',
20
20
  value: 'all',
21
21
  },
22
22
  {
@@ -34,6 +34,11 @@ export async function initCommand(options) {
34
34
  description: SKILL_DESCRIPTIONS['transaction-recipes'],
35
35
  value: 'transaction-recipes',
36
36
  },
37
+ {
38
+ title: 'Jobs only',
39
+ description: SKILL_DESCRIPTIONS.jobs,
40
+ value: 'jobs',
41
+ },
37
42
  ],
38
43
  initial: 0,
39
44
  });
@@ -44,7 +49,7 @@ export async function initCommand(options) {
44
49
  skillType = response.skill;
45
50
  }
46
51
  const skillLabel = skillType === 'all'
47
- ? 'api + conversion + transaction-recipes'
52
+ ? 'api + conversion + transaction-recipes + jobs'
48
53
  : skillType;
49
54
  logger.info(`Installing: ${chalk.cyan(skillLabel)}`);
50
55
  const spinner = ora('Installing skill files...').start();
@@ -159,7 +159,7 @@ export function registerJobsCommand(program) {
159
159
  jobs
160
160
  .command('audit-prep')
161
161
  .description('Audit preparation pack blueprint')
162
- .requiredOption('--period <YYYY>', 'Fiscal year (e.g., 2025)')
162
+ .requiredOption('--period <YYYY|YYYY-QN>', 'Fiscal year or quarter (e.g., 2025 or 2025-Q3)')
163
163
  .option('--currency <code>', 'Currency code (e.g. SGD, USD)')
164
164
  .option('--json', 'Output as JSON')
165
165
  .action(jobAction((opts) => {
@@ -0,0 +1,195 @@
1
+ /**
2
+ * `jaz tax` command group — Singapore corporate income tax.
3
+ *
4
+ * Commands:
5
+ * jaz tax sg-cs — Form C-S / C-S Lite computation
6
+ * jaz tax sg-ca — Capital allowance schedule
7
+ */
8
+ import chalk from 'chalk';
9
+ import { readFileSync } from 'fs';
10
+ import { TaxValidationError } from '../tax/validate.js';
11
+ import { computeFormCs } from '../tax/sg/form-cs.js';
12
+ import { computeCapitalAllowances } from '../tax/sg/capital-allowances.js';
13
+ import { printTaxResult, printTaxJson } from '../tax/format.js';
14
+ /** Wrap tax action with validation error handling. */
15
+ function taxAction(fn) {
16
+ return (opts) => {
17
+ try {
18
+ fn(opts);
19
+ }
20
+ catch (err) {
21
+ if (err instanceof TaxValidationError) {
22
+ console.error(chalk.red(`Error: ${err.message}`));
23
+ process.exit(1);
24
+ }
25
+ throw err;
26
+ }
27
+ };
28
+ }
29
+ /** Read JSON input from --input file or stdin. */
30
+ function readJsonInput(opts) {
31
+ const inputFile = opts.input;
32
+ if (inputFile) {
33
+ const raw = readFileSync(inputFile, 'utf-8');
34
+ return JSON.parse(raw);
35
+ }
36
+ // Check if stdin has data (piped input) — use fd 0 for cross-platform support
37
+ if (!process.stdin.isTTY) {
38
+ try {
39
+ const raw = readFileSync(0, 'utf-8').trim();
40
+ if (raw)
41
+ return JSON.parse(raw);
42
+ }
43
+ catch {
44
+ // No stdin data available
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ /** Build a default SgFormCsInput structure with zeros. */
50
+ function buildDefaultInput(opts) {
51
+ return {
52
+ ya: opts.ya,
53
+ basisPeriodStart: opts.basisStart ?? `${opts.ya - 1}-01-01`,
54
+ basisPeriodEnd: opts.basisEnd ?? `${opts.ya - 1}-12-31`,
55
+ currency: opts.currency,
56
+ revenue: opts.revenue ?? 0,
57
+ accountingProfit: opts.profit ?? 0,
58
+ addBacks: {
59
+ depreciation: opts.depreciation ?? 0,
60
+ amortization: opts.amortization ?? 0,
61
+ rouDepreciation: opts.rouDepreciation ?? 0,
62
+ leaseInterest: opts.leaseInterest ?? 0,
63
+ generalProvisions: opts.provisions ?? 0,
64
+ donations: opts.donations ?? 0,
65
+ entertainment: opts.entertainment ?? 0,
66
+ penalties: opts.penalties ?? 0,
67
+ privateCar: opts.privateCar ?? 0,
68
+ capitalExpOnPnl: 0,
69
+ unrealizedFxLoss: 0,
70
+ otherNonDeductible: 0,
71
+ },
72
+ deductions: {
73
+ actualLeasePayments: opts.leasePayments ?? 0,
74
+ unrealizedFxGain: 0,
75
+ exemptDividends: 0,
76
+ exemptIncome: 0,
77
+ otherDeductions: 0,
78
+ },
79
+ capitalAllowances: {
80
+ currentYearClaim: opts.ca ?? 0,
81
+ balanceBroughtForward: opts.caBf ?? 0,
82
+ },
83
+ enhancedDeductions: {
84
+ rdExpenditure: 0,
85
+ rdMultiplier: 2.5,
86
+ ipRegistration: 0,
87
+ ipMultiplier: 2.0,
88
+ donations250Base: opts.donations ?? 0,
89
+ s14qRenovation: 0,
90
+ },
91
+ losses: {
92
+ broughtForward: opts.lossesBf ?? 0,
93
+ },
94
+ donationsCarryForward: {
95
+ broughtForward: opts.donationsBf ?? 0,
96
+ },
97
+ exemptionType: opts.exemption ?? 'pte',
98
+ };
99
+ }
100
+ export function registerTaxCommand(program) {
101
+ const tax = program
102
+ .command('tax')
103
+ .description('Corporate income tax computation — Singapore Form C-S / C-S Lite');
104
+ // ── jaz tax sg-cs ────────────────────────────────────────────────
105
+ tax
106
+ .command('sg-cs')
107
+ .description('Singapore Form C-S / C-S Lite corporate income tax computation')
108
+ .option('--input <file>', 'JSON input file (full SgFormCsInput structure)')
109
+ .option('--ya <year>', 'Year of Assessment', parseInt)
110
+ .option('--revenue <amount>', 'Total revenue', parseFloat)
111
+ .option('--profit <amount>', 'Accounting net profit/loss', parseFloat)
112
+ .option('--depreciation <amount>', 'Accounting depreciation (add-back)', parseFloat)
113
+ .option('--amortization <amount>', 'Intangible amortization (add-back)', parseFloat)
114
+ .option('--rou-depreciation <amount>', 'IFRS 16 ROU depreciation (add-back)', parseFloat)
115
+ .option('--lease-interest <amount>', 'IFRS 16 lease interest (add-back)', parseFloat)
116
+ .option('--lease-payments <amount>', 'Actual lease payments (deduction)', parseFloat)
117
+ .option('--provisions <amount>', 'General provisions (add-back)', parseFloat)
118
+ .option('--donations <amount>', 'IPC donations (add-back, claimed at 250%)', parseFloat)
119
+ .option('--entertainment <amount>', 'Non-deductible entertainment', parseFloat)
120
+ .option('--penalties <amount>', 'Penalties & fines', parseFloat)
121
+ .option('--private-car <amount>', 'S-plated vehicle expenses', parseFloat)
122
+ .option('--ca <amount>', 'Current year capital allowances', parseFloat)
123
+ .option('--ca-bf <amount>', 'Unabsorbed CA brought forward', parseFloat)
124
+ .option('--losses-bf <amount>', 'Unabsorbed trade losses b/f', parseFloat)
125
+ .option('--donations-bf <amount>', 'Unabsorbed donations b/f', parseFloat)
126
+ .option('--exemption <type>', 'Exemption type: sute, pte (default), none', 'pte')
127
+ .option('--basis-start <date>', 'Basis period start (YYYY-MM-DD)')
128
+ .option('--basis-end <date>', 'Basis period end (YYYY-MM-DD)')
129
+ .option('--currency <code>', 'Currency code (default: SGD)')
130
+ .option('--json', 'Output as JSON')
131
+ .action(taxAction((opts) => {
132
+ // Full JSON input mode (from file or stdin)
133
+ const jsonInput = readJsonInput(opts);
134
+ let input;
135
+ if (jsonInput) {
136
+ input = jsonInput;
137
+ }
138
+ else {
139
+ // Simple flag mode
140
+ if (!opts.ya) {
141
+ console.error(chalk.red('Error: --ya (Year of Assessment) is required'));
142
+ process.exit(1);
143
+ }
144
+ input = buildDefaultInput(opts);
145
+ }
146
+ const result = computeFormCs(input);
147
+ opts.json ? printTaxJson(result) : printTaxResult(result);
148
+ }));
149
+ // ── jaz tax sg-ca ────────────────────────────────────────────────
150
+ tax
151
+ .command('sg-ca')
152
+ .description('Singapore capital allowance schedule (per-asset computation)')
153
+ .option('--input <file>', 'JSON input file (full SgCapitalAllowanceInput structure)')
154
+ .option('--ya <year>', 'Year of Assessment', parseInt)
155
+ .option('--cost <amount>', 'Asset cost (simple single-asset mode)', parseFloat)
156
+ .option('--category <cat>', 'Asset category: computer, automation, low-value, general, ip, renovation')
157
+ .option('--acquired <date>', 'Acquisition date (YYYY-MM-DD)')
158
+ .option('--prior-claimed <amount>', 'CA already claimed in prior YAs', parseFloat, 0)
159
+ .option('--ip-years <years>', 'IP write-off period (5, 10, or 15)', parseInt)
160
+ .option('--unabsorbed-bf <amount>', 'Unabsorbed CA brought forward', parseFloat, 0)
161
+ .option('--currency <code>', 'Currency code (default: SGD)')
162
+ .option('--json', 'Output as JSON')
163
+ .action(taxAction((opts) => {
164
+ const jsonInput = readJsonInput(opts);
165
+ let input;
166
+ if (jsonInput) {
167
+ input = jsonInput;
168
+ }
169
+ else {
170
+ if (!opts.ya) {
171
+ console.error(chalk.red('Error: --ya (Year of Assessment) is required'));
172
+ process.exit(1);
173
+ }
174
+ if (!opts.cost || !opts.category || !opts.acquired) {
175
+ console.error(chalk.red('Error: --cost, --category, and --acquired are required in simple mode'));
176
+ process.exit(1);
177
+ }
178
+ input = {
179
+ ya: opts.ya,
180
+ currency: opts.currency,
181
+ unabsorbedBroughtForward: opts.unabsorbedBf,
182
+ assets: [{
183
+ description: `${opts.category.charAt(0).toUpperCase() + opts.category.slice(1)} asset`,
184
+ cost: opts.cost,
185
+ acquisitionDate: opts.acquired,
186
+ category: opts.category,
187
+ priorYearsClaimed: opts.priorClaimed,
188
+ ipWriteOffYears: opts.ipYears,
189
+ }],
190
+ };
191
+ }
192
+ const result = computeCapitalAllowances(input);
193
+ opts.json ? printTaxJson(result) : printTaxResult(result);
194
+ }));
195
+ }
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { versionsCommand } from './commands/versions.js';
8
8
  import { updateCommand } from './commands/update.js';
9
9
  import { registerCalcCommand } from './commands/calc.js';
10
10
  import { registerJobsCommand } from './commands/jobs.js';
11
+ import { registerTaxCommand } from './commands/tax.js';
11
12
  import { SKILL_TYPES } from './types/index.js';
12
13
  const __filename = fileURLToPath(import.meta.url);
13
14
  const __dirname = dirname(__filename);
@@ -53,4 +54,5 @@ program
53
54
  });
54
55
  registerCalcCommand(program);
55
56
  registerJobsCommand(program);
57
+ registerTaxCommand(program);
56
58
  program.parse();
@@ -84,7 +84,7 @@ export function generateAuditPrepBlueprint(opts) {
84
84
  order: 6,
85
85
  description: 'Generate AR aging schedule',
86
86
  category: 'report',
87
- apiCall: 'POST /generate-reports/ar-aging',
87
+ apiCall: 'POST /generate-reports/ar-report',
88
88
  apiBody: {
89
89
  endDate: p.endDate,
90
90
  },
@@ -94,7 +94,7 @@ export function generateAuditPrepBlueprint(opts) {
94
94
  order: 7,
95
95
  description: 'Generate AP aging schedule',
96
96
  category: 'report',
97
- apiCall: 'POST /generate-reports/ap-aging',
97
+ apiCall: 'POST /generate-reports/ap-report',
98
98
  apiBody: {
99
99
  endDate: p.endDate,
100
100
  },
@@ -104,7 +104,7 @@ export function generateAuditPrepBlueprint(opts) {
104
104
  order: 8,
105
105
  description: 'Generate Fixed Asset register',
106
106
  category: 'report',
107
- apiCall: 'POST /generate-reports/fixed-assets',
107
+ apiCall: 'POST /generate-reports/fixed-assets-summary',
108
108
  apiBody: {
109
109
  endDate: p.endDate,
110
110
  },
@@ -136,7 +136,7 @@ export function generateAuditPrepBlueprint(opts) {
136
136
  category: 'verify',
137
137
  apiCall: 'POST /bank-records/search',
138
138
  apiBody: {
139
- filters: {
139
+ filter: {
140
140
  reconciliationStatus: 'UNRECONCILED',
141
141
  },
142
142
  },
@@ -28,7 +28,7 @@ export function generateBankReconBlueprint(opts = {}) {
28
28
  category: 'verify',
29
29
  apiCall: 'POST /chart-of-accounts/search',
30
30
  apiBody: {
31
- filters: {
31
+ filter: {
32
32
  classificationType: 'Bank Accounts',
33
33
  ...accountFilter,
34
34
  },
@@ -50,7 +50,7 @@ export function generateBankReconBlueprint(opts = {}) {
50
50
  category: 'verify',
51
51
  apiCall: 'POST /bank-records/search',
52
52
  apiBody: {
53
- filters: {
53
+ filter: {
54
54
  reconciliationStatus: 'UNRECONCILED',
55
55
  ...accountFilter,
56
56
  ...periodFilter,
@@ -64,7 +64,7 @@ export function generateBankReconBlueprint(opts = {}) {
64
64
  category: 'verify',
65
65
  apiCall: 'POST /bank-records/search',
66
66
  apiBody: {
67
- filters: {
67
+ filter: {
68
68
  reconciliationStatus: 'UNRECONCILED',
69
69
  ...accountFilter,
70
70
  },
@@ -126,7 +126,7 @@ export function generateBankReconBlueprint(opts = {}) {
126
126
  category: 'verify',
127
127
  apiCall: 'POST /bank-records/search',
128
128
  apiBody: {
129
- filters: {
129
+ filter: {
130
130
  reconciliationStatus: 'UNRECONCILED',
131
131
  ...accountFilter,
132
132
  },
@@ -21,7 +21,7 @@ export function generateCreditControlBlueprint(opts = {}) {
21
21
  order: 1,
22
22
  description: 'Generate AR aging report',
23
23
  category: 'report',
24
- apiCall: 'POST /generate-reports/ar-aging',
24
+ apiCall: 'POST /generate-reports/ar-report',
25
25
  notes: 'Current, 1-30, 31-60, 61-90, 90+ day buckets. Focus on buckets beyond threshold.',
26
26
  verification: 'AR aging report generated — note total receivables and overdue amount',
27
27
  },
@@ -31,7 +31,7 @@ export function generateCreditControlBlueprint(opts = {}) {
31
31
  category: 'verify',
32
32
  apiCall: 'POST /invoices/search',
33
33
  apiBody: {
34
- filters: {
34
+ filter: {
35
35
  status: ['APPROVED'],
36
36
  overdue: true,
37
37
  },
@@ -52,7 +52,7 @@ export function generateCreditControlBlueprint(opts = {}) {
52
52
  category: 'review',
53
53
  apiCall: 'POST /invoices/search',
54
54
  apiBody: {
55
- filters: {
55
+ filter: {
56
56
  status: ['APPROVED'],
57
57
  overdue: true,
58
58
  },
@@ -84,7 +84,7 @@ export function generateCreditControlBlueprint(opts = {}) {
84
84
  order: 5,
85
85
  description: 'Identify doubtful debts',
86
86
  category: 'review',
87
- apiCall: 'POST /generate-reports/ar-aging',
87
+ apiCall: 'POST /generate-reports/ar-report',
88
88
  notes: 'Review 90+ day bucket for potential bad debts. Consider: customer financial health, dispute status, collateral, historical write-off rates.',
89
89
  verification: 'Doubtful debt candidates identified and documented',
90
90
  },
@@ -108,7 +108,7 @@ export function generateCreditControlBlueprint(opts = {}) {
108
108
  order: 7,
109
109
  description: 'Review AR aging after credit control actions',
110
110
  category: 'verify',
111
- apiCall: 'POST /generate-reports/ar-aging',
111
+ apiCall: 'POST /generate-reports/ar-report',
112
112
  notes: 'Compare to opening AR aging. Document: payments received, payment plans agreed, write-offs processed, provisions updated.',
113
113
  verification: 'AR aging reviewed, all actions documented, follow-up dates set',
114
114
  },
@@ -15,7 +15,7 @@ export function generateFaReviewBlueprint(opts = {}) {
15
15
  order: 1,
16
16
  description: 'Pull fixed asset summary report',
17
17
  category: 'report',
18
- apiCall: 'POST /generate-reports/fixed-assets',
18
+ apiCall: 'POST /generate-reports/fixed-assets-summary',
19
19
  notes: 'Review: asset description, cost, accumulated depreciation, NBV, useful life remaining, depreciation method per asset',
20
20
  verification: 'FA register generated — note total cost, total accumulated depreciation, total NBV',
21
21
  },
@@ -38,7 +38,7 @@ export function generateFaReviewBlueprint(opts = {}) {
38
38
  order: 3,
39
39
  description: 'Verify depreciation runs are up to date',
40
40
  category: 'verify',
41
- apiCall: 'POST /generate-reports/fixed-assets',
41
+ apiCall: 'POST /generate-reports/fixed-assets-summary',
42
42
  calcCommand: 'jaz calc depreciation',
43
43
  notes: 'Check that monthly/annual depreciation has been posted for all assets through the current period. Recalculate sample of assets to verify amounts.',
44
44
  verification: 'Depreciation is current — no missed periods. Sample recalculations match posted amounts.',
@@ -47,7 +47,7 @@ export function generateFaReviewBlueprint(opts = {}) {
47
47
  order: 4,
48
48
  description: 'Check fully depreciated assets',
49
49
  category: 'review',
50
- apiCall: 'POST /generate-reports/fixed-assets',
50
+ apiCall: 'POST /generate-reports/fixed-assets-summary',
51
51
  notes: 'Identify assets with NBV = 0 or NBV = salvage value. Review whether: (1) asset is still in use, (2) asset should be disposed/written off, (3) useful life estimate needs revision.',
52
52
  verification: 'All fully depreciated assets reviewed — disposals and write-offs identified',
53
53
  },
@@ -96,7 +96,7 @@ export function generateFaReviewBlueprint(opts = {}) {
96
96
  order: 8,
97
97
  description: 'Check NBV reasonableness',
98
98
  category: 'review',
99
- apiCall: 'POST /generate-reports/fixed-assets',
99
+ apiCall: 'POST /generate-reports/fixed-assets-summary',
100
100
  notes: [
101
101
  'Review remaining NBV for reasonableness:',
102
102
  '(1) No negative NBV,',
@@ -48,7 +48,7 @@ export function generateGstVatBlueprint(opts) {
48
48
  category: 'verify',
49
49
  apiCall: 'POST /invoices/search',
50
50
  apiBody: {
51
- filters: {
51
+ filter: {
52
52
  dateFrom: qp.startDate,
53
53
  dateTo: qp.endDate,
54
54
  status: ['APPROVED', 'PAID'],
@@ -63,7 +63,7 @@ export function generateGstVatBlueprint(opts) {
63
63
  category: 'verify',
64
64
  apiCall: 'POST /invoices/search',
65
65
  apiBody: {
66
- filters: {
66
+ filter: {
67
67
  dateFrom: qp.startDate,
68
68
  dateTo: qp.endDate,
69
69
  },
@@ -84,7 +84,7 @@ export function generateGstVatBlueprint(opts) {
84
84
  category: 'verify',
85
85
  apiCall: 'POST /bills/search',
86
86
  apiBody: {
87
- filters: {
87
+ filter: {
88
88
  dateFrom: qp.startDate,
89
89
  dateTo: qp.endDate,
90
90
  status: ['APPROVED', 'PAID'],