@diy-accounting-uk/diya-gl 1.2.47 → 1.2.48
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 +4 -3
- package/dist/app/bin/view.js +3 -17
- package/dist/app/data/releases.json +26 -0
- package/dist/app/lib/app-resources.js +9 -3
- package/dist/app/lib/package-version.js +28 -0
- package/dist/app/lib/provenance-data.js +1 -1
- package/dist/app/lib/xlsx-exporter.js +27 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -196,9 +196,10 @@ format text is CC BY 4.0, so you can implement the format in anything with attri
|
|
|
196
196
|
The workbook templates are not part of this package. `write-workbook` and the MCP `save_workbook`
|
|
197
197
|
tool fetch the template they need from
|
|
198
198
|
[spreadsheets.diyaccounting.co.uk](https://spreadsheets.diyaccounting.co.uk) the first time they run,
|
|
199
|
-
print the terms, and cache it for later runs. The templates are the company's
|
|
200
|
-
PolyForm Internal Use License 1.0.0 with an additional grant: use them for your
|
|
201
|
-
your clients' accounts if you are an accountant or a bookkeeper, and do not
|
|
199
|
+
print the terms, and cache it for later runs of the same version. The templates are the company's
|
|
200
|
+
own work, under the PolyForm Internal Use License 1.0.0 with an additional grant: use them for your
|
|
201
|
+
own accounts, or for your clients' accounts if you are an accountant or a bookkeeper, and do not
|
|
202
|
+
redistribute them.
|
|
202
203
|
|
|
203
204
|
This repository does not accept contributions.
|
|
204
205
|
|
package/dist/app/bin/view.js
CHANGED
|
@@ -15,30 +15,16 @@
|
|
|
15
15
|
// one directory per package version and origin host.
|
|
16
16
|
|
|
17
17
|
import { spawn } from "child_process";
|
|
18
|
-
import { existsSync, readFileSync } from "fs";
|
|
19
18
|
import { tmpdir } from "os";
|
|
20
|
-
import {
|
|
21
|
-
import { fileURLToPath } from "url";
|
|
19
|
+
import { join, resolve } from "path";
|
|
22
20
|
import { readBookFragment } from "./link.js";
|
|
23
21
|
import { FRAGMENT_KEY } from "../lib/diya-gl-link.js";
|
|
24
22
|
import { DEFAULT_ORIGIN, startViewServer } from "../lib/diya-gl-view-server.js";
|
|
23
|
+
import { packageVersion } from "../lib/package-version.js";
|
|
25
24
|
|
|
26
25
|
const USAGE =
|
|
27
26
|
"Usage: diya-gl view <book directory | diya-gl zip | diya-gl JSON file | workbook> [--port <number>] [--origin <url>] [--no-open]";
|
|
28
27
|
|
|
29
|
-
// The nearest package.json above this file: the package's own in dist/, the
|
|
30
|
-
// repository's in a checkout. Both carry the version the cache is keyed on.
|
|
31
|
-
function packageVersion() {
|
|
32
|
-
let directory = dirname(fileURLToPath(import.meta.url));
|
|
33
|
-
for (;;) {
|
|
34
|
-
const candidate = join(directory, "package.json");
|
|
35
|
-
if (existsSync(candidate)) return JSON.parse(readFileSync(candidate, "utf8")).version;
|
|
36
|
-
const parent = dirname(directory);
|
|
37
|
-
if (parent === directory) throw new Error("No package.json found above " + import.meta.url);
|
|
38
|
-
directory = parent;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
28
|
function parseArgs(argv) {
|
|
43
29
|
const args = argv.slice(2);
|
|
44
30
|
let port = 0;
|
|
@@ -86,7 +72,7 @@ function openInBrowser(url) {
|
|
|
86
72
|
async function main() {
|
|
87
73
|
const { source, port, origin, open } = parseArgs(process.argv);
|
|
88
74
|
const { product, fragment, length, warning } = await readBookFragment(source);
|
|
89
|
-
const cacheDir = join(tmpdir(), "diya-gl-view", packageVersion(), new URL(origin).host.replace(/[^A-Za-z0-9.-]/g, "_"));
|
|
75
|
+
const cacheDir = join(tmpdir(), "diya-gl-view", await packageVersion(), new URL(origin).host.replace(/[^A-Za-z0-9.-]/g, "_"));
|
|
90
76
|
const running = await startViewServer({ cacheDir, origin, port });
|
|
91
77
|
const url = `http://${running.host}:${running.port}/${product}.html#${FRAGMENT_KEY}=${fragment}`;
|
|
92
78
|
console.log(url);
|
|
@@ -1455,6 +1455,32 @@
|
|
|
1455
1455
|
"scorecard": "101608 passed, 0 warnings, 0 failed"
|
|
1456
1456
|
}
|
|
1457
1457
|
}
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
"tag": "diya-gl-v1.2.47",
|
|
1461
|
+
"date": "2026-10-05",
|
|
1462
|
+
"formatVersion": "diya-gl/1",
|
|
1463
|
+
"engineVersion": "1.2.47+26b9e6ec",
|
|
1464
|
+
"taxDataHash": "51bdff60e993",
|
|
1465
|
+
"reconciledCommit": "54e8731a91279975e61b1a232fbb67fda89515f0",
|
|
1466
|
+
"templates": {
|
|
1467
|
+
"bst": {
|
|
1468
|
+
"hash": "0d003e10e00c",
|
|
1469
|
+
"scorecard": "812 passed, 0 warnings, 0 failed"
|
|
1470
|
+
},
|
|
1471
|
+
"taxi": {
|
|
1472
|
+
"hash": "1ff69654d64d",
|
|
1473
|
+
"scorecard": "700 passed, 0 warnings, 0 failed"
|
|
1474
|
+
},
|
|
1475
|
+
"se": {
|
|
1476
|
+
"hash": "064605ae76ed",
|
|
1477
|
+
"scorecard": "8015 passed, 0 warnings, 0 failed"
|
|
1478
|
+
},
|
|
1479
|
+
"ltd": {
|
|
1480
|
+
"hash": "a65ab09cf35c",
|
|
1481
|
+
"scorecard": "101608 passed, 0 warnings, 0 failed"
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1458
1484
|
}
|
|
1459
1485
|
]
|
|
1460
1486
|
}
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
// Node's own modules load on the first read rather than with this file, so a
|
|
20
20
|
// bundle whose caller supplies a loader never pulls them in.
|
|
21
21
|
|
|
22
|
+
import { packageVersion } from "./package-version.js";
|
|
23
|
+
|
|
22
24
|
// The resource space is app/ with one named exception. The two v2 schemas are
|
|
23
25
|
// published by the site, not carried in app/, and the site serves them at
|
|
24
26
|
// /schema/ — so "schema/" is a root of its own rather than a directory under
|
|
@@ -31,7 +33,10 @@ const SCHEMA_ROOT_FROM_APP = ["..", "web", "spreadsheets.diyaccounting.co.uk", "
|
|
|
31
33
|
// checkout has app/templates and reads it straight off disk. A packaged install
|
|
32
34
|
// has not, so the loader takes the same file from the site, which already
|
|
33
35
|
// serves the templates the DIYA-GL pages use, and keeps it in a user cache so
|
|
34
|
-
// every later run works with no network.
|
|
36
|
+
// every later run of the same version works with no network. The cache is keyed
|
|
37
|
+
// on the package version and the source host, because the anchors the reader
|
|
38
|
+
// checks a package against move with the version: a template cached by an
|
|
39
|
+
// older version writes a package the newer reader refuses.
|
|
35
40
|
//
|
|
36
41
|
// Both the current /diya-gl/ and legacy /books/ paths resolve — the old one
|
|
37
42
|
// through a redirect — so published versions built before the path moved keep
|
|
@@ -71,11 +76,12 @@ async function fetchTemplate(path) {
|
|
|
71
76
|
const { dirname, resolve } = await import("path");
|
|
72
77
|
const { homedir } = await import("os");
|
|
73
78
|
|
|
79
|
+
const source = templateSource();
|
|
74
80
|
const cacheRoot = process.env.XDG_CACHE_HOME || resolve(homedir(), ".cache");
|
|
75
|
-
const
|
|
81
|
+
const sourceHost = new URL(source).host.replace(/[^A-Za-z0-9.-]/g, "_");
|
|
82
|
+
const cached = resolve(cacheRoot, "diya-gl", await packageVersion(), sourceHost, path);
|
|
76
83
|
if (existsSync(cached)) return readFileSync(cached);
|
|
77
84
|
|
|
78
|
-
const source = templateSource();
|
|
79
85
|
announceTemplateTerms(source);
|
|
80
86
|
|
|
81
87
|
const url = `${source}/${path}`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2006-2026 DIY Accounting Limited
|
|
3
|
+
//
|
|
4
|
+
// package-version.js — the version of the engine that is running, read from
|
|
5
|
+
// the nearest package.json above this file: the package's own in an install,
|
|
6
|
+
// the repository's in a checkout. Both carry the same version, and the caches
|
|
7
|
+
// of files fetched from the site are keyed on it.
|
|
8
|
+
//
|
|
9
|
+
// Node's own modules load on the call rather than with this file, so a bundle
|
|
10
|
+
// that never asks for the version never pulls them in.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @returns {Promise<string>}
|
|
14
|
+
*/
|
|
15
|
+
export async function packageVersion() {
|
|
16
|
+
const { existsSync, readFileSync } = await import("fs");
|
|
17
|
+
const { dirname, join } = await import("path");
|
|
18
|
+
const { fileURLToPath } = await import("url");
|
|
19
|
+
|
|
20
|
+
let directory = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
for (;;) {
|
|
22
|
+
const candidate = join(directory, "package.json");
|
|
23
|
+
if (existsSync(candidate)) return JSON.parse(readFileSync(candidate, "utf8")).version;
|
|
24
|
+
const parent = dirname(directory);
|
|
25
|
+
if (parent === directory) throw new Error("No package.json found above " + import.meta.url);
|
|
26
|
+
directory = parent;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -2258,6 +2258,27 @@ async function seAccountingPeriodFromSheet(set) {
|
|
|
2258
2258
|
return { start, end };
|
|
2259
2259
|
}
|
|
2260
2260
|
|
|
2261
|
+
// The Taxi package's own accounting period. Its end is the box "Date your
|
|
2262
|
+
// books or accounts are made up to" (Business Details!N25, a formula on
|
|
2263
|
+
// Admin!B17, the 5 April the writer sets for the package's tax year); the
|
|
2264
|
+
// period opens the day after the same date a year earlier. periodCovered()'s
|
|
2265
|
+
// guess would run the period 1 April to 31 March instead.
|
|
2266
|
+
const TAXI_PERIOD_END_CELL = { sheet: "Business Details", cell: "N25" };
|
|
2267
|
+
|
|
2268
|
+
async function taxiAccountingPeriodFromSheet(set) {
|
|
2269
|
+
const workbookName = singleWorkbookName(set);
|
|
2270
|
+
const sheet = await openSheet(await openWorkbook(set, workbookName), TAXI_PERIOD_END_CELL.sheet);
|
|
2271
|
+
const end = sheet ? dateAt(sheet.xml, TAXI_PERIOD_END_CELL.cell, sheet.sharedStrings) : undefined;
|
|
2272
|
+
if (!end) {
|
|
2273
|
+
throw new Error(
|
|
2274
|
+
`${workbookName}: no date in ${TAXI_PERIOD_END_CELL.sheet}!${TAXI_PERIOD_END_CELL.cell}, the date the books are made up to`,
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
const [year, month, day] = end.split("-").map(Number);
|
|
2278
|
+
const start = new Date(Date.UTC(year - 1, month - 1, day + 1)).toISOString().slice(0, 10);
|
|
2279
|
+
return { start, end };
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2261
2282
|
function assign(target, key, value) {
|
|
2262
2283
|
if (value !== undefined && value !== "" && value !== 0) target[key] = value;
|
|
2263
2284
|
}
|
|
@@ -2685,11 +2706,13 @@ export async function extractBook(set, product, lines, cellMap, options = {}) {
|
|
|
2685
2706
|
}
|
|
2686
2707
|
}
|
|
2687
2708
|
|
|
2688
|
-
// SE
|
|
2689
|
-
// whose dates do not run April to March still has every
|
|
2690
|
-
// onto the package's own April-March tab grid.
|
|
2709
|
+
// SE and Taxi read their true accounting period off the printed boxes,
|
|
2710
|
+
// since a book whose dates do not run April to March still has every
|
|
2711
|
+
// posting written onto the package's own April-March tab grid.
|
|
2691
2712
|
const period =
|
|
2692
|
-
|
|
2713
|
+
product === "taxi"
|
|
2714
|
+
? await taxiAccountingPeriodFromSheet(set)
|
|
2715
|
+
: (product === "se" && (await seAccountingPeriodFromSheet(set))) || periodCovered(await extractPeriodStartMonth(set, product), lines);
|
|
2693
2716
|
const book = {
|
|
2694
2717
|
documentInfo: {
|
|
2695
2718
|
entriesType: "journal",
|