@diy-accounting-uk/diya-gl 1.0.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.
- package/README.md +63 -0
- package/bin/diya-gl-mcp.js +10 -0
- package/bin/diya-gl-read-workbook.js +11 -0
- package/bin/diya-gl-recalc.js +10 -0
- package/bin/diya-gl-write-workbook.js +12 -0
- package/bin/diya-gl.js +24 -0
- package/bin/run-dist-bin.js +23 -0
- package/dist/app/bin/build-diya-gl-spec.js +680 -0
- package/dist/app/bin/build-packages.js +149 -0
- package/dist/app/bin/build-reconciliation-pages.js +1052 -0
- package/dist/app/bin/build-sitemaps.js +51 -0
- package/dist/app/bin/compliance-report.js +125 -0
- package/dist/app/bin/cross-package-reconciliation.js +92 -0
- package/dist/app/bin/diya-gl-mcp.js +27 -0
- package/dist/app/bin/export.js +318 -0
- package/dist/app/bin/extract-scenarios.js +1348 -0
- package/dist/app/bin/generate.js +353 -0
- package/dist/app/bin/judge-reconciliation.js +649 -0
- package/dist/app/bin/reconcile.js +247 -0
- package/dist/app/bin/report.js +291 -0
- package/dist/app/bin/verify-roundtrip.js +941 -0
- package/dist/app/bin/verify-stability.js +429 -0
- package/dist/app/bin/write-workbook.js +106 -0
- package/dist/app/data/filing/ct-computation-v1.1.toml +1033 -0
- package/dist/app/data/filing/ct600-v3.toml +2329 -0
- package/dist/app/data/filing/frs105-formats.toml +244 -0
- package/dist/app/data/fixture-master-gaps.json +247 -0
- package/dist/app/data/hmrc/SOURCE.md +14 -0
- package/dist/app/data/hmrc/form-layouts/bst.json +116 -0
- package/dist/app/data/hmrc/form-layouts/ltd.json +452 -0
- package/dist/app/data/hmrc/form-layouts/se.json +485 -0
- package/dist/app/data/hmrc/form-layouts/taxi.json +117 -0
- package/dist/app/data/hmrc/sa103-mtd-mapping.json +1078 -0
- package/dist/app/data/hmrc/sa103f_mapping_v3.csv +111 -0
- package/dist/app/data/judge-rubric.md +52 -0
- package/dist/app/data/ltd-2020.toml +49 -0
- package/dist/app/data/ltd-2021.toml +49 -0
- package/dist/app/data/ltd-2022.toml +49 -0
- package/dist/app/data/ltd-2023.toml +49 -0
- package/dist/app/data/ltd-2024.toml +49 -0
- package/dist/app/data/ltd-2025.toml +49 -0
- package/dist/app/data/ltd-2026.toml +53 -0
- package/dist/app/data/ltd-2027.toml +51 -0
- package/dist/app/data/render-unrepresentable/bst.json +24 -0
- package/dist/app/data/render-unrepresentable/ltd.json +863 -0
- package/dist/app/data/render-unrepresentable/se.json +455 -0
- package/dist/app/data/render-unrepresentable/taxi.json +16 -0
- package/dist/app/data/roundtrip-budget.json +38 -0
- package/dist/app/data/roundtrip-matrix-budget.json +26 -0
- package/dist/app/data/roundtrip-unrepresentable.json +401 -0
- package/dist/app/data/se-2020-2021.toml +52 -0
- package/dist/app/data/se-2021-2022.toml +52 -0
- package/dist/app/data/se-2022-2023.toml +55 -0
- package/dist/app/data/se-2023-2024.toml +54 -0
- package/dist/app/data/se-2024-2025.toml +51 -0
- package/dist/app/data/se-2025-2026.toml +51 -0
- package/dist/app/data/se-2026-2027.toml +51 -0
- package/dist/app/data/volatile-cells.json +16 -0
- package/dist/app/lib/anchors/bst.js +79 -0
- package/dist/app/lib/anchors/ltd.js +626 -0
- package/dist/app/lib/anchors/run.js +100 -0
- package/dist/app/lib/anchors/se.js +456 -0
- package/dist/app/lib/anchors/taxi.js +164 -0
- package/dist/app/lib/app-resources.js +80 -0
- package/dist/app/lib/book-checks/ltd.js +408 -0
- package/dist/app/lib/book-checks/se.js +687 -0
- package/dist/app/lib/book-checks/taxi.js +215 -0
- package/dist/app/lib/book-checks.js +872 -0
- package/dist/app/lib/books-engine.js +197 -0
- package/dist/app/lib/books-interchange.js +533 -0
- package/dist/app/lib/calculators/bst.js +381 -0
- package/dist/app/lib/calculators/ltd.js +1938 -0
- package/dist/app/lib/calculators/se.js +1239 -0
- package/dist/app/lib/calculators/shared.js +131 -0
- package/dist/app/lib/calculators/taxi.js +461 -0
- package/dist/app/lib/canonical-report-value.js +77 -0
- package/dist/app/lib/compliance-report.js +341 -0
- package/dist/app/lib/diya-gl-calculator.js +52 -0
- package/dist/app/lib/diya-gl-canonical.js +355 -0
- package/dist/app/lib/diya-gl-edits-ltd.js +144 -0
- package/dist/app/lib/diya-gl-edits.js +361 -0
- package/dist/app/lib/diya-gl-loader.js +774 -0
- package/dist/app/lib/diya-gl-schema.js +212 -0
- package/dist/app/lib/generator.js +1912 -0
- package/dist/app/lib/guide.js +33 -0
- package/dist/app/lib/headlines.js +293 -0
- package/dist/app/lib/link-caches.js +421 -0
- package/dist/app/lib/ltd-layout.js +139 -0
- package/dist/app/lib/mcp/diya-gl-tools.js +330 -0
- package/dist/app/lib/mcp/jsonrpc-stdio.js +66 -0
- package/dist/app/lib/mcp/server.js +65 -0
- package/dist/app/lib/overtype-sidecar.js +180 -0
- package/dist/app/lib/package-builder.js +202 -0
- package/dist/app/lib/payslips-date-chain.js +481 -0
- package/dist/app/lib/payslips-layout.js +235 -0
- package/dist/app/lib/period-shift.js +111 -0
- package/dist/app/lib/product-workbook.js +317 -0
- package/dist/app/lib/products.js +27 -0
- package/dist/app/lib/provenance-data.js +36 -0
- package/dist/app/lib/provenance.js +92 -0
- package/dist/app/lib/report-generator.js +374 -0
- package/dist/app/lib/report-indicators.js +468 -0
- package/dist/app/lib/report-serializer.js +392 -0
- package/dist/app/lib/scenario-extractor.js +1403 -0
- package/dist/app/lib/scenario-loader.js +84 -0
- package/dist/app/lib/sitemap-builder.js +59 -0
- package/dist/app/lib/spreadsheet-runner.js +566 -0
- package/dist/app/lib/tax/capital-allowances.js +77 -0
- package/dist/app/lib/tax/corporation-tax.js +144 -0
- package/dist/app/lib/tax/income-tax.js +122 -0
- package/dist/app/lib/tax/mileage.js +69 -0
- package/dist/app/lib/tax/national-insurance.js +31 -0
- package/dist/app/lib/tax/vat.js +188 -0
- package/dist/app/lib/tax-year.js +31 -0
- package/dist/app/lib/template-formula-map.js +253 -0
- package/dist/app/lib/workbook-set.js +153 -0
- package/dist/app/lib/xlsx-exporter.js +2815 -0
- package/dist/app/lib/xlsx-parts.js +111 -0
- package/dist/app/lib/xlsx-reader.js +84 -0
- package/dist/app/products/bst.js +727 -0
- package/dist/app/products/ltd.js +4270 -0
- package/dist/app/products/se.js +3403 -0
- package/dist/app/products/taxi.js +912 -0
- package/dist/app/templates/bst/bst-excel.xlsx +0 -0
- package/dist/app/templates/bst/meta.toml +27 -0
- package/dist/app/templates/ltd/Cashaccount.xlsx +0 -0
- package/dist/app/templates/ltd/Companysecretary.xlsx +0 -0
- package/dist/app/templates/ltd/Creditcardaccount.xlsx +0 -0
- package/dist/app/templates/ltd/Currentaccount.xlsx +0 -0
- package/dist/app/templates/ltd/Dividend Voucher.docx +0 -0
- package/dist/app/templates/ltd/Financialaccounts.xlsx +0 -0
- package/dist/app/templates/ltd/Fixedassets.xlsx +0 -0
- package/dist/app/templates/ltd/Payslips.xlsx +0 -0
- package/dist/app/templates/ltd/Purchases.xlsx +0 -0
- package/dist/app/templates/ltd/Sales.xlsx +0 -0
- package/dist/app/templates/ltd/Salesinvoice.xlsx +0 -0
- package/dist/app/templates/ltd/Savingaccount.xlsx +0 -0
- package/dist/app/templates/ltd/Vatreturns.xlsx +0 -0
- package/dist/app/templates/ltd/expensesform.xlsx +0 -0
- package/dist/app/templates/ltd/meta.toml +54 -0
- package/dist/app/templates/meta.toml +12 -0
- package/dist/app/templates/se/Bank.xlsx +0 -0
- package/dist/app/templates/se/Cash.xlsx +0 -0
- package/dist/app/templates/se/Financialaccounts.xlsx +0 -0
- package/dist/app/templates/se/Fixedassets.xlsx +0 -0
- package/dist/app/templates/se/Payslips.xlsx +0 -0
- package/dist/app/templates/se/Purchases.xlsx +0 -0
- package/dist/app/templates/se/Sales.xlsx +0 -0
- package/dist/app/templates/se/Salesinvoice.xlsx +0 -0
- package/dist/app/templates/se/Vat.xlsx +0 -0
- package/dist/app/templates/se/meta.toml +43 -0
- package/dist/app/templates/taxi/meta.toml +43 -0
- package/dist/app/templates/taxi/taxi-excel.xlsx +0 -0
- package/dist/web/spreadsheets.diyaccounting.co.uk/public/schema/diya-gl-book-v2.schema.json +713 -0
- package/dist/web/spreadsheets.diyaccounting.co.uk/public/schema/diya-gl-docs.md +930 -0
- package/dist/web/spreadsheets.diyaccounting.co.uk/public/schema/diya-gl-lines-v2.schema.json +220 -0
- package/package.json +37 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
// Copyright (C) 2026 DIY Accounting Ltd
|
|
4
|
+
//
|
|
5
|
+
// generate.js — CLI entry point for spreadsheet and guide generation.
|
|
6
|
+
// Dispatches to product modules in app/products/ which define their own metadata.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// node app/bin/generate.js # all packages, all years
|
|
10
|
+
// node app/bin/generate.js --package bst # Basic Sole Trader only
|
|
11
|
+
// node app/bin/generate.js --package taxi # Taxi Driver only
|
|
12
|
+
// node app/bin/generate.js --years se-2024-2025 se-2025-2026 # specific years
|
|
13
|
+
// node app/bin/generate.js --skip-guide # spreadsheets only, no PDF
|
|
14
|
+
// node app/bin/generate.js --source-date-epoch 1711670400 # override PDF timestamp
|
|
15
|
+
|
|
16
|
+
import { parse as parseTOML } from "smol-toml";
|
|
17
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from "fs";
|
|
18
|
+
import { resolve, dirname } from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
import { generateSpreadsheet, packageNaming, applyYearEndSequence, monthEnd } from "../lib/generator.js";
|
|
21
|
+
import { generatePdf } from "../lib/guide.js";
|
|
22
|
+
import { runSpreadsheet, runMultiFileSpreadsheet } from "../lib/spreadsheet-runner.js";
|
|
23
|
+
import { loadDiyaGlData, diyaGlToScenario } from "../lib/diya-gl-loader.js";
|
|
24
|
+
import { saveWorkbook } from "../lib/product-workbook.js";
|
|
25
|
+
import { productModule } from "../lib/products.js";
|
|
26
|
+
import { PRODUCT as BST } from "../products/bst.js";
|
|
27
|
+
import { PRODUCT as TAXI } from "../products/taxi.js";
|
|
28
|
+
import { PRODUCT as SE } from "../products/se.js";
|
|
29
|
+
import { PRODUCT as LTD } from "../products/ltd.js";
|
|
30
|
+
|
|
31
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const APP_DIR = resolve(__dirname, "..");
|
|
33
|
+
const ROOT = resolve(APP_DIR, "..");
|
|
34
|
+
const DATA_DIR = resolve(APP_DIR, "data");
|
|
35
|
+
const OUTPUT_DIR = resolve(ROOT, "packages");
|
|
36
|
+
|
|
37
|
+
const PRODUCTS = {
|
|
38
|
+
bst: BST,
|
|
39
|
+
taxi: TAXI,
|
|
40
|
+
se: SE,
|
|
41
|
+
ltd: LTD,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
async function generateProduct(productDir, tomlPath, sourceDateEpoch, skipGuide, outputDir, opts = {}) {
|
|
45
|
+
const productMeta = parseTOML(readFileSync(resolve(productDir, "meta.toml"), "utf8"));
|
|
46
|
+
const sharedMeta = parseTOML(readFileSync(resolve(APP_DIR, "templates", "meta.toml"), "utf8"));
|
|
47
|
+
|
|
48
|
+
const taxData = opts.overrideTaxData || parseTOML(readFileSync(tomlPath, "utf8"));
|
|
49
|
+
const ty = taxData.tax_year || taxData.financial_year;
|
|
50
|
+
const yearEndMonth = opts.yearEndMonth || 0;
|
|
51
|
+
const endDate = new Date(ty.end);
|
|
52
|
+
|
|
53
|
+
// Skip packages whose year-end is more than 14 months from now.
|
|
54
|
+
// 14 months ensures the next tax year's packages are available from early March
|
|
55
|
+
// (covers the budget-in-March scenario where SE Apr 2027 is needed by 6 Mar 2026).
|
|
56
|
+
// The website download default uses a tighter 13-month window for the pre-selected year.
|
|
57
|
+
const cutoff = new Date();
|
|
58
|
+
cutoff.setUTCMonth(cutoff.getUTCMonth() + 14);
|
|
59
|
+
cutoff.setUTCDate(0); // last day of the month 13 months from now
|
|
60
|
+
if (endDate > cutoff) {
|
|
61
|
+
console.log(
|
|
62
|
+
`\nSkipping ${productMeta.product.name} for ${ty.label} (year-end ${endDate.toISOString().slice(0, 10)} is beyond cutoff ${cutoff.toISOString().slice(0, 10)})`,
|
|
63
|
+
);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`\nGenerating ${productMeta.product.name} for ${ty.label}...`);
|
|
68
|
+
|
|
69
|
+
const { dirName, xlsxFilename } = packageNaming(productMeta, sharedMeta, endDate);
|
|
70
|
+
|
|
71
|
+
const outDir = resolve(outputDir, dirName);
|
|
72
|
+
mkdirSync(outDir, { recursive: true });
|
|
73
|
+
|
|
74
|
+
if (productMeta.template.files) {
|
|
75
|
+
for (const templateFile of productMeta.template.files) {
|
|
76
|
+
let buffer = readFileSync(resolve(productDir, templateFile));
|
|
77
|
+
const fileKey = templateFile.replace(".xlsx", "").replace(".docx", "").toLowerCase();
|
|
78
|
+
const sheetsConfig = productMeta.sheets?.[fileKey];
|
|
79
|
+
|
|
80
|
+
if (sheetsConfig && Object.keys(sheetsConfig).length > 0) {
|
|
81
|
+
buffer = await generateSpreadsheet(buffer, taxData, sheetsConfig);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
buffer = await applyYearEndSequence(buffer, templateFile, sheetsConfig, yearEndMonth, endDate, ty);
|
|
85
|
+
|
|
86
|
+
writeFileSync(resolve(outDir, templateFile), buffer);
|
|
87
|
+
}
|
|
88
|
+
console.log(` Written: ${dirName}/`);
|
|
89
|
+
console.log(` ${productMeta.template.files.length} files`);
|
|
90
|
+
} else {
|
|
91
|
+
// Single-file product (BST, Taxi)
|
|
92
|
+
const templatePath = resolve(productDir, productMeta.template.spreadsheet);
|
|
93
|
+
const templateBuffer = readFileSync(templatePath);
|
|
94
|
+
|
|
95
|
+
const xlsxBuffer = await generateSpreadsheet(templateBuffer, taxData, productMeta.sheets);
|
|
96
|
+
|
|
97
|
+
writeFileSync(resolve(outDir, xlsxFilename), xlsxBuffer);
|
|
98
|
+
console.log(` Written: ${dirName}/`);
|
|
99
|
+
console.log(` ${xlsxFilename}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Generate PDF guides
|
|
103
|
+
if (!skipGuide) {
|
|
104
|
+
const guides = [];
|
|
105
|
+
if (productMeta.template.guide) {
|
|
106
|
+
guides.push({ md: productMeta.template.guide, pdf: productMeta.output.guide_filename });
|
|
107
|
+
}
|
|
108
|
+
if (productMeta.template.payslip_guide) {
|
|
109
|
+
guides.push({ md: productMeta.template.payslip_guide, pdf: productMeta.output.payslip_guide_filename });
|
|
110
|
+
}
|
|
111
|
+
for (const { md, pdf } of guides) {
|
|
112
|
+
const guideMd = resolve(productDir, md);
|
|
113
|
+
const guidePdf = resolve(outDir, pdf);
|
|
114
|
+
try {
|
|
115
|
+
await generatePdf(guideMd, guidePdf, sourceDateEpoch);
|
|
116
|
+
console.log(` Guide: ${pdf}`);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.warn(` Warning: PDF guide generation failed (${pdf}) — ${e.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { dirName, taxYear: ty.label, taxData };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseArgs(argv) {
|
|
127
|
+
const args = argv.slice(2);
|
|
128
|
+
|
|
129
|
+
let packageFilter = "all";
|
|
130
|
+
const pkgIdx = args.indexOf("--package");
|
|
131
|
+
if (pkgIdx !== -1 && args[pkgIdx + 1]) {
|
|
132
|
+
packageFilter = args[pkgIdx + 1];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let tomlFiles;
|
|
136
|
+
const yearsIdx = args.indexOf("--years");
|
|
137
|
+
if (yearsIdx !== -1) {
|
|
138
|
+
const years = [];
|
|
139
|
+
for (let i = yearsIdx + 1; i < args.length; i++) {
|
|
140
|
+
if (args[i].startsWith("--")) break;
|
|
141
|
+
years.push(args[i]);
|
|
142
|
+
}
|
|
143
|
+
tomlFiles = years.map((y) => resolve(DATA_DIR, `${y}.toml`));
|
|
144
|
+
} else {
|
|
145
|
+
tomlFiles = readdirSync(DATA_DIR)
|
|
146
|
+
.filter((f) => f.endsWith(".toml"))
|
|
147
|
+
.sort()
|
|
148
|
+
.map((f) => resolve(DATA_DIR, f));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let sourceDateEpoch;
|
|
152
|
+
const epochIdx = args.indexOf("--source-date-epoch");
|
|
153
|
+
if (epochIdx !== -1 && args[epochIdx + 1]) {
|
|
154
|
+
sourceDateEpoch = parseInt(args[epochIdx + 1], 10);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const skipGuide = args.includes("--skip-guide");
|
|
158
|
+
|
|
159
|
+
let yearEndFilter = null;
|
|
160
|
+
const yeIdx = args.indexOf("--year-end");
|
|
161
|
+
if (yeIdx !== -1) {
|
|
162
|
+
yearEndFilter = [];
|
|
163
|
+
for (let i = yeIdx + 1; i < args.length; i++) {
|
|
164
|
+
if (args[i].startsWith("--")) break;
|
|
165
|
+
yearEndFilter.push(args[i]);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let dataDir = null;
|
|
170
|
+
const dataIdx = args.indexOf("--data");
|
|
171
|
+
if (dataIdx !== -1 && args[dataIdx + 1]) {
|
|
172
|
+
dataDir = args[dataIdx + 1];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let outputDir = null;
|
|
176
|
+
const outIdx = args.indexOf("--output-dir");
|
|
177
|
+
if (outIdx !== -1) {
|
|
178
|
+
const value = args[outIdx + 1];
|
|
179
|
+
if (!value || value.startsWith("--")) {
|
|
180
|
+
throw new Error("--output-dir requires a path argument");
|
|
181
|
+
}
|
|
182
|
+
outputDir = value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let offset = null;
|
|
186
|
+
const offIdx = args.indexOf("--offset");
|
|
187
|
+
if (offIdx !== -1 && args[offIdx + 1]) {
|
|
188
|
+
offset = args[offIdx + 1];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { packageFilter, tomlFiles, sourceDateEpoch, skipGuide, yearEndFilter, dataDir, outputDir, offset };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function main() {
|
|
195
|
+
console.log("=== generate.js ===");
|
|
196
|
+
|
|
197
|
+
const {
|
|
198
|
+
packageFilter,
|
|
199
|
+
tomlFiles,
|
|
200
|
+
sourceDateEpoch,
|
|
201
|
+
skipGuide,
|
|
202
|
+
yearEndFilter,
|
|
203
|
+
dataDir,
|
|
204
|
+
outputDir: outputDirOverride,
|
|
205
|
+
offset,
|
|
206
|
+
} = parseArgs(process.argv);
|
|
207
|
+
|
|
208
|
+
// Determine which products to generate
|
|
209
|
+
const productsToGenerate = packageFilter === "all" ? Object.entries(PRODUCTS) : [[packageFilter, PRODUCTS[packageFilter]]];
|
|
210
|
+
|
|
211
|
+
for (const [key, product] of productsToGenerate) {
|
|
212
|
+
if (!product) {
|
|
213
|
+
console.error(`Unknown package: ${key}. Available: ${Object.keys(PRODUCTS).join(", ")}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const outputDir = outputDirOverride ? resolve(outputDirOverride) : OUTPUT_DIR;
|
|
219
|
+
|
|
220
|
+
console.log("Package: ", packageFilter === "all" ? "all" : packageFilter);
|
|
221
|
+
console.log(
|
|
222
|
+
"Tax data:",
|
|
223
|
+
tomlFiles.map((f) => f.split("/").pop()),
|
|
224
|
+
);
|
|
225
|
+
console.log("Output: ", outputDir);
|
|
226
|
+
|
|
227
|
+
mkdirSync(outputDir, { recursive: true });
|
|
228
|
+
|
|
229
|
+
const results = [];
|
|
230
|
+
for (const [, product] of productsToGenerate) {
|
|
231
|
+
const productDir = resolve(APP_DIR, "templates", product.dir);
|
|
232
|
+
const productMeta = parseTOML(readFileSync(resolve(productDir, "meta.toml"), "utf8"));
|
|
233
|
+
|
|
234
|
+
for (const tomlFile of tomlFiles) {
|
|
235
|
+
const tomlName = tomlFile.split("/").pop();
|
|
236
|
+
if (!tomlName.startsWith(productMeta.product.tax_regime + "-")) continue;
|
|
237
|
+
|
|
238
|
+
if (productMeta.product.tax_regime === "ltd") {
|
|
239
|
+
const taxData = parseTOML(readFileSync(tomlFile, "utf8"));
|
|
240
|
+
const fyStart = new Date(taxData.financial_year.start);
|
|
241
|
+
const fyEnd = new Date(taxData.financial_year.end);
|
|
242
|
+
|
|
243
|
+
for (let m = 0; m < 12; m++) {
|
|
244
|
+
const meYear = fyStart.getUTCFullYear() + Math.floor((fyStart.getUTCMonth() + m) / 12);
|
|
245
|
+
const meMonth = (fyStart.getUTCMonth() + m) % 12;
|
|
246
|
+
const yearEndDate = monthEnd(meYear, meMonth + 1);
|
|
247
|
+
|
|
248
|
+
if (yearEndDate > fyEnd) break;
|
|
249
|
+
|
|
250
|
+
const yeStr = yearEndDate.toISOString().slice(0, 10);
|
|
251
|
+
if (yearEndFilter && !yearEndFilter.includes(yeStr)) continue;
|
|
252
|
+
|
|
253
|
+
const overrideTaxData = JSON.parse(JSON.stringify(taxData));
|
|
254
|
+
overrideTaxData.financial_year.end = yearEndDate.toISOString().slice(0, 10);
|
|
255
|
+
|
|
256
|
+
const yearEndMonth = yearEndDate.getUTCMonth() + 1;
|
|
257
|
+
const result = await generateProduct(productDir, tomlFile, sourceDateEpoch, skipGuide, outputDir, {
|
|
258
|
+
overrideTaxData,
|
|
259
|
+
yearEndMonth,
|
|
260
|
+
});
|
|
261
|
+
if (result) results.push(result);
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
if (yearEndFilter) {
|
|
265
|
+
const td = parseTOML(readFileSync(tomlFile, "utf8"));
|
|
266
|
+
const tyEnd = (td.tax_year || td.financial_year).end;
|
|
267
|
+
const tyEndStr = typeof tyEnd === "string" ? tyEnd : new Date(tyEnd).toISOString().slice(0, 10);
|
|
268
|
+
if (!yearEndFilter.includes(tyEndStr)) continue;
|
|
269
|
+
}
|
|
270
|
+
const result = await generateProduct(productDir, tomlFile, sourceDateEpoch, skipGuide, outputDir);
|
|
271
|
+
if (result) results.push(result);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log(`\n=== Generated ${results.length} packages ===`);
|
|
277
|
+
for (const r of results) {
|
|
278
|
+
console.log(` ${r.taxYear}: ${r.dirName}/`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// If --data provided, inject diya-gl data into the generated package and recalculate
|
|
282
|
+
if (dataDir && results.length > 0) {
|
|
283
|
+
if (!PRODUCTS[packageFilter]) {
|
|
284
|
+
console.error(`--data requires --package (not 'all'). Got: ${packageFilter}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
const productMod = productModule(packageFilter);
|
|
288
|
+
|
|
289
|
+
const { book, lines } = loadDiyaGlData(resolve(dataDir), offset);
|
|
290
|
+
|
|
291
|
+
// Use the last generated package (most recent year-end)
|
|
292
|
+
const lastResult = results[results.length - 1];
|
|
293
|
+
const pkgDir = resolve(outputDir, lastResult.dirName);
|
|
294
|
+
// export.js and CI read the populated package flat from --output-dir (no
|
|
295
|
+
// dirName nesting), so an override still lands the recalculated files
|
|
296
|
+
// there directly; without one, recalculation happens in place.
|
|
297
|
+
const finalOutputDir = outputDirOverride ? resolve(outputDirOverride) : pkgDir;
|
|
298
|
+
|
|
299
|
+
const reads = productMod.standardReads();
|
|
300
|
+
|
|
301
|
+
console.log(`\n=== Injecting diya-gl data into ${lastResult.dirName} ===`);
|
|
302
|
+
|
|
303
|
+
if (packageFilter === "bst") {
|
|
304
|
+
// saveWorkbook takes the tax data this run selected rather than
|
|
305
|
+
// deriving it from the book, so --years still decides the year.
|
|
306
|
+
const { workbook, filename } = await saveWorkbook(book, lines, { taxData: lastResult.taxData });
|
|
307
|
+
await runSpreadsheet(workbook, {}, reads, {
|
|
308
|
+
saveRecalculatedTo: resolve(finalOutputDir, filename),
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
const scenario = diyaGlToScenario(book, lines, packageFilter);
|
|
312
|
+
|
|
313
|
+
// Extract year-end info from the directory name
|
|
314
|
+
const yearEndMatch = lastResult.dirName.match(/(\d{4})-(\d{2})-(\d{2})/);
|
|
315
|
+
const endYear = yearEndMatch ? parseInt(yearEndMatch[1], 10) : null;
|
|
316
|
+
const endMonth = yearEndMatch ? parseInt(yearEndMatch[2], 10) : null;
|
|
317
|
+
const startYear = endYear ? endYear - 1 : null;
|
|
318
|
+
|
|
319
|
+
const writes = productMod.cellWrites(scenario, startYear, endMonth);
|
|
320
|
+
|
|
321
|
+
if (productMod.MULTI_FILE) {
|
|
322
|
+
const { readdirSync: readdirSyncFs } = await import("fs");
|
|
323
|
+
const xlsxFiles = readdirSyncFs(pkgDir).filter((f) => f.endsWith(".xlsx"));
|
|
324
|
+
const fileBuffers = {};
|
|
325
|
+
for (const f of xlsxFiles) {
|
|
326
|
+
fileBuffers[f] = readFileSync(resolve(pkgDir, f));
|
|
327
|
+
}
|
|
328
|
+
await runMultiFileSpreadsheet(fileBuffers, writes, reads, "Financialaccounts.xlsx", {
|
|
329
|
+
saveRecalculatedTo: finalOutputDir,
|
|
330
|
+
});
|
|
331
|
+
} else {
|
|
332
|
+
const xlsxFiles = readdirSync(pkgDir).filter((f) => f.endsWith(".xlsx"));
|
|
333
|
+
if (xlsxFiles.length === 0) {
|
|
334
|
+
console.error(`No xlsx found in ${pkgDir}`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
const xlsxBuffer = readFileSync(resolve(pkgDir, xlsxFiles[0]));
|
|
338
|
+
await runSpreadsheet(xlsxBuffer, writes, reads, {
|
|
339
|
+
saveRecalculatedTo: resolve(finalOutputDir, xlsxFiles[0]),
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
console.log(`Populated package written to ${finalOutputDir}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
349
|
+
main().catch((err) => {
|
|
350
|
+
console.error(err);
|
|
351
|
+
process.exit(1);
|
|
352
|
+
});
|
|
353
|
+
}
|