@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 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 own work, under the
200
- PolyForm Internal Use License 1.0.0 with an additional grant: use them for your own accounts, or for
201
- your clients' accounts if you are an accountant or a bookkeeper, and do not redistribute them.
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
 
@@ -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 { dirname, join, resolve } from "path";
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 cached = resolve(cacheRoot, "diya-gl", path);
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
+ }
@@ -12,7 +12,7 @@
12
12
 
13
13
  export const PROVENANCE_DATA = {
14
14
  formatVersion: "diya-gl/1",
15
- engineVersion: "1.2.47+26b9e6ec",
15
+ engineVersion: "1.2.48+bf25fb49",
16
16
  taxDataHash: "51bdff60e993",
17
17
  reconciledCommit: "54e8731a91279975e61b1a232fbb67fda89515f0",
18
18
  templates: {
@@ -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 reads its true accounting period off the printed boxes, since a book
2689
- // whose dates do not run April to March still has every posting written
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
- (product === "se" && (await seAccountingPeriodFromSheet(set))) || periodCovered(await extractPeriodStartMonth(set, product), lines);
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diy-accounting-uk/diya-gl",
3
- "version": "1.2.47",
3
+ "version": "1.2.48",
4
4
  "type": "module",
5
5
  "description": "Recalculate, read and write DIYA-GL books from the command line",
6
6
  "license": "Apache-2.0",