@diy-accounting-uk/diya-gl 1.2.31 → 1.2.33
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/dist/app/data/releases.json +52 -0
- package/dist/app/lib/book-checks/se.js +2 -2
- package/dist/app/lib/book-checks.js +2 -2
- package/dist/app/lib/calculators/bst.js +3 -3
- package/dist/app/lib/calculators/shared.js +2 -2
- package/dist/app/lib/calculators/taxi.js +5 -4
- package/dist/app/lib/diya-gl-loader.js +5 -4
- package/dist/app/lib/provenance-data.js +1 -1
- package/dist/app/lib/scenario-extractor.js +25 -6
- package/package.json +1 -1
|
@@ -1039,6 +1039,58 @@
|
|
|
1039
1039
|
"scorecard": "101605 passed, 0 warnings, 0 failed"
|
|
1040
1040
|
}
|
|
1041
1041
|
}
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
"tag": "diya-gl-v1.2.31",
|
|
1045
|
+
"date": "2026-09-23",
|
|
1046
|
+
"formatVersion": "diya-gl/1",
|
|
1047
|
+
"engineVersion": "1.2.31+80457d57",
|
|
1048
|
+
"taxDataHash": "3221a7c2f39c",
|
|
1049
|
+
"reconciledCommit": "54e8731a91279975e61b1a232fbb67fda89515f0",
|
|
1050
|
+
"templates": {
|
|
1051
|
+
"bst": {
|
|
1052
|
+
"hash": "0d003e10e00c",
|
|
1053
|
+
"scorecard": "812 passed, 0 warnings, 0 failed"
|
|
1054
|
+
},
|
|
1055
|
+
"taxi": {
|
|
1056
|
+
"hash": "1ff69654d64d",
|
|
1057
|
+
"scorecard": "686 passed, 0 warnings, 0 failed"
|
|
1058
|
+
},
|
|
1059
|
+
"se": {
|
|
1060
|
+
"hash": "064605ae76ed",
|
|
1061
|
+
"scorecard": "7982 passed, 0 warnings, 0 failed"
|
|
1062
|
+
},
|
|
1063
|
+
"ltd": {
|
|
1064
|
+
"hash": "caa14632083b",
|
|
1065
|
+
"scorecard": "101605 passed, 0 warnings, 0 failed"
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
"tag": "diya-gl-v1.2.32",
|
|
1071
|
+
"date": "2026-09-24",
|
|
1072
|
+
"formatVersion": "diya-gl/1",
|
|
1073
|
+
"engineVersion": "1.2.32+92a3639b",
|
|
1074
|
+
"taxDataHash": "3221a7c2f39c",
|
|
1075
|
+
"reconciledCommit": "54e8731a91279975e61b1a232fbb67fda89515f0",
|
|
1076
|
+
"templates": {
|
|
1077
|
+
"bst": {
|
|
1078
|
+
"hash": "0d003e10e00c",
|
|
1079
|
+
"scorecard": "812 passed, 0 warnings, 0 failed"
|
|
1080
|
+
},
|
|
1081
|
+
"taxi": {
|
|
1082
|
+
"hash": "1ff69654d64d",
|
|
1083
|
+
"scorecard": "686 passed, 0 warnings, 0 failed"
|
|
1084
|
+
},
|
|
1085
|
+
"se": {
|
|
1086
|
+
"hash": "064605ae76ed",
|
|
1087
|
+
"scorecard": "7982 passed, 0 warnings, 0 failed"
|
|
1088
|
+
},
|
|
1089
|
+
"ltd": {
|
|
1090
|
+
"hash": "caa14632083b",
|
|
1091
|
+
"scorecard": "101605 passed, 0 warnings, 0 failed"
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1042
1094
|
}
|
|
1043
1095
|
]
|
|
1044
1096
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// up from the book's own entityInformation["diya-gl:product"].
|
|
18
18
|
|
|
19
19
|
import { changeLineBankAccount } from "../diya-gl-edits.js";
|
|
20
|
-
import { SE_PURCHASE_CODE_MAP, LTD_SALES_CODE_MAP, isStraddlingLine } from "../scenario-extractor.js";
|
|
20
|
+
import { SE_PURCHASE_CODE_MAP, LTD_SALES_CODE_MAP, isStraddlingLine, signedAmount } from "../scenario-extractor.js";
|
|
21
21
|
|
|
22
22
|
// The workbook each bank account is kept in. A Self Employed package
|
|
23
23
|
// carries two of them where a Company package carries four, so an account
|
|
@@ -558,7 +558,7 @@ function isVatRegistered(book) {
|
|
|
558
558
|
// year and belongs to a different year's turnover.
|
|
559
559
|
function netOfVatTurnover(lines, rate) {
|
|
560
560
|
let total = 0;
|
|
561
|
-
for (const line of lines) if (line.sourceJournalID === "sales" && !isStraddlingLine(line)) total += line
|
|
561
|
+
for (const line of lines) if (line.sourceJournalID === "sales" && !isStraddlingLine(line)) total += signedAmount(line) / (1 + rate);
|
|
562
562
|
return total;
|
|
563
563
|
}
|
|
564
564
|
|
|
@@ -19,7 +19,7 @@ import { changeLinePostingDate, changeLineAccount, changeLineAmount, addSaleLine
|
|
|
19
19
|
import { LTD_PRODUCT_RULES } from "./book-checks/ltd.js";
|
|
20
20
|
import { TAXI_PRODUCT_RULES } from "./book-checks/taxi.js";
|
|
21
21
|
import { SE_PRODUCT_RULES } from "./book-checks/se.js";
|
|
22
|
-
import { isStraddlingLine } from "./scenario-extractor.js";
|
|
22
|
+
import { isStraddlingLine, signedAmount } from "./scenario-extractor.js";
|
|
23
23
|
|
|
24
24
|
// ============================== shared helpers ==============================
|
|
25
25
|
|
|
@@ -323,7 +323,7 @@ function runChecks(ctx) {
|
|
|
323
323
|
// year's turnover and not to this one.
|
|
324
324
|
function salesTotal(lines) {
|
|
325
325
|
let total = 0;
|
|
326
|
-
for (const line of lines) if (line.sourceJournalID === "sales" && !isStraddlingLine(line)) total += line
|
|
326
|
+
for (const line of lines) if (line.sourceJournalID === "sales" && !isStraddlingLine(line)) total += signedAmount(line);
|
|
327
327
|
return total;
|
|
328
328
|
}
|
|
329
329
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// read returns, so app/products/bst.js's reportSections() and
|
|
7
7
|
// checkCompliance() work unchanged on either source.
|
|
8
8
|
|
|
9
|
-
import { BST_SALES_ACCOUNTS, CIS_DEDUCTION_FIELD, MONTH_ORDER, getMonthKey } from "../scenario-extractor.js";
|
|
9
|
+
import { BST_SALES_ACCOUNTS, CIS_DEDUCTION_FIELD, MONTH_ORDER, getMonthKey, signedAmount } from "../scenario-extractor.js";
|
|
10
10
|
import { resolveBstPurchaseCodeMap } from "../diya-gl-loader.js";
|
|
11
11
|
import { fixedAssetAdditions } from "../scenario-loader.js";
|
|
12
12
|
import { calculateIncomeTax } from "../tax/income-tax.js";
|
|
@@ -91,14 +91,14 @@ export function calculateBstResults(book, lines, taxData, scenario) {
|
|
|
91
91
|
const purchaseLines = lines.filter((l) => l.sourceJournalID === "purchases" && purchaseCodeMap[l.accountMainID] !== undefined);
|
|
92
92
|
|
|
93
93
|
// Total sales (BST: gross, no VAT split)
|
|
94
|
-
const totalSales = Math.round(salesLines.reduce((s, l) => s + l
|
|
94
|
+
const totalSales = Math.round(salesLines.reduce((s, l) => s + signedAmount(l), 0));
|
|
95
95
|
|
|
96
96
|
// Monthly sales
|
|
97
97
|
const monthlySales = {};
|
|
98
98
|
for (const month of MONTH_ORDER) monthlySales[month] = 0;
|
|
99
99
|
for (const line of salesLines) {
|
|
100
100
|
const month = getMonthKey(line.postingDate);
|
|
101
|
-
monthlySales[month] += line
|
|
101
|
+
monthlySales[month] += signedAmount(line);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Purchase expenses by code. A mileage-log entry is left out: it buys
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// shared.js — Aggregation helpers used by more than one product calculator.
|
|
5
5
|
|
|
6
|
-
import { getMonthKey } from "../scenario-extractor.js";
|
|
6
|
+
import { getMonthKey, signedAmount } from "../scenario-extractor.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Group lines by accountMainID and month, computing totals.
|
|
@@ -49,7 +49,7 @@ export function aggregateByCode(lines, codeMap) {
|
|
|
49
49
|
const byCode = {};
|
|
50
50
|
for (const line of lines) {
|
|
51
51
|
const code = codeMap[line.accountMainID];
|
|
52
|
-
if (code) byCode[code] = (byCode[code] || 0) + line
|
|
52
|
+
if (code) byCode[code] = (byCode[code] || 0) + signedAmount(line);
|
|
53
53
|
}
|
|
54
54
|
return byCode;
|
|
55
55
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
MONTH_ORDER,
|
|
11
11
|
getMonthKey,
|
|
12
12
|
buildTaxMonthByDate,
|
|
13
|
+
signedAmount,
|
|
13
14
|
} from "../scenario-extractor.js";
|
|
14
15
|
import { fixedAssetAdditions } from "../scenario-loader.js";
|
|
15
16
|
import { calculateIncomeTax } from "../tax/income-tax.js";
|
|
@@ -89,7 +90,7 @@ function aggregateByCodeAndMonth(lines, codeMap) {
|
|
|
89
90
|
const code = codeMap[line.accountMainID];
|
|
90
91
|
if (!code) continue;
|
|
91
92
|
const month = getMonthKey(line.postingDate);
|
|
92
|
-
byMonth[month][code] = (byMonth[month][code] || 0) + line
|
|
93
|
+
byMonth[month][code] = (byMonth[month][code] || 0) + signedAmount(line);
|
|
93
94
|
}
|
|
94
95
|
return byMonth;
|
|
95
96
|
}
|
|
@@ -117,17 +118,17 @@ export function calculateTaxiResults(book, lines, taxData, scenario) {
|
|
|
117
118
|
const byCode = aggregateByCode(cashPurchaseLines, TAXI_PURCHASE_CODE_MAP);
|
|
118
119
|
const byCodeAndMonth = aggregateByCodeAndMonth(cashPurchaseLines, TAXI_PURCHASE_CODE_MAP);
|
|
119
120
|
|
|
120
|
-
const totalSales = Math.floor(salesLines.reduce((s, l) => s + l
|
|
121
|
+
const totalSales = Math.floor(salesLines.reduce((s, l) => s + signedAmount(l), 0));
|
|
121
122
|
const monthlySales = {};
|
|
122
123
|
for (const month of MONTH_ORDER) monthlySales[month] = 0;
|
|
123
|
-
for (const line of salesLines) monthlySales[byDate.get(line.postingDate)] += line
|
|
124
|
+
for (const line of salesLines) monthlySales[byDate.get(line.postingDate)] += signedAmount(line);
|
|
124
125
|
|
|
125
126
|
// Other business income, month by month on the same week-based tab a
|
|
126
127
|
// fare's date falls in -- the Sales tab's rental and other-income rows
|
|
127
128
|
// belong to a week exactly as a fare day does.
|
|
128
129
|
const monthlyOther = {};
|
|
129
130
|
for (const month of MONTH_ORDER) monthlyOther[month] = 0;
|
|
130
|
-
for (const line of otherIncomeLines) monthlyOther[byDate.get(line.postingDate)] += line
|
|
131
|
+
for (const line of otherIncomeLines) monthlyOther[byDate.get(line.postingDate)] += signedAmount(line);
|
|
131
132
|
|
|
132
133
|
// Capital allowances, from the fixed asset register cellWrites registers
|
|
133
134
|
// (the same additions cellWrites itself derives via fixedAssetAdditions()).
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
computeSpreadsheetNetSales,
|
|
32
32
|
splitStraddlingLines,
|
|
33
33
|
deriveStraddlingEntries,
|
|
34
|
+
signedAmount,
|
|
34
35
|
} from "./scenario-extractor.js";
|
|
35
36
|
import { totalBusinessMiles, calculateMileageAllowance, HMRC_CAR_MILEAGE_RATES } from "./tax/mileage.js";
|
|
36
37
|
import { compareLines } from "./diya-gl-canonical.js";
|
|
@@ -314,14 +315,14 @@ export function diyaGlToScenario(book, lines, product) {
|
|
|
314
315
|
const turnoverLines = salesLines.filter((l) => TURNOVER_ACCOUNTS.has(l.accountMainID));
|
|
315
316
|
totalSales = vatRegistered
|
|
316
317
|
? computeSpreadsheetNetSales(turnoverLines)
|
|
317
|
-
: Math.round(turnoverLines.reduce((sum, l) => sum + l
|
|
318
|
+
: Math.round(turnoverLines.reduce((sum, l) => sum + signedAmount(l), 0));
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
// Compute expense totals by code.
|
|
321
322
|
const byCode = {};
|
|
322
323
|
purchaseLines.forEach((l) => {
|
|
323
324
|
const code = purchaseCodeMap[l.accountMainID];
|
|
324
|
-
if (code) byCode[code] = (byCode[code] || 0) + l
|
|
325
|
+
if (code) byCode[code] = (byCode[code] || 0) + signedAmount(l);
|
|
325
326
|
});
|
|
326
327
|
|
|
327
328
|
// Build metadata from book.toml
|
|
@@ -382,7 +383,7 @@ export function diyaGlToScenario(book, lines, product) {
|
|
|
382
383
|
.filter((l) => !(l.measurableUnitOfMeasure === "miles" && typeof l.measurableQuantity === "number"))
|
|
383
384
|
.forEach((l) => {
|
|
384
385
|
const code = purchaseCodeMap[l.accountMainID];
|
|
385
|
-
if (code) bstByCode[code] = (bstByCode[code] || 0) + l
|
|
386
|
+
if (code) bstByCode[code] = (bstByCode[code] || 0) + signedAmount(l);
|
|
386
387
|
});
|
|
387
388
|
if (businessMiles) bstByCode.m = (bstByCode.m || 0) + calculateMileageAllowance(businessMiles, HMRC_CAR_MILEAGE_RATES);
|
|
388
389
|
|
|
@@ -415,7 +416,7 @@ export function diyaGlToScenario(book, lines, product) {
|
|
|
415
416
|
const cashMotor = purchaseLines
|
|
416
417
|
.filter((l) => !(product === "se" && l.measurableUnitOfMeasure === "miles" && typeof l.measurableQuantity === "number"))
|
|
417
418
|
.filter((l) => purchaseCodeMap[l.accountMainID] === "v")
|
|
418
|
-
.reduce((sum, l) => sum + l
|
|
419
|
+
.reduce((sum, l) => sum + signedAmount(l), 0);
|
|
419
420
|
const mileageClaim = product === "se" ? calculateMileageAllowance(businessMiles, HMRC_CAR_MILEAGE_RATES) : 0;
|
|
420
421
|
expected.total_motor_net = Math.round(cashMotor / vatDivisor + mileageClaim);
|
|
421
422
|
expected.total_legal_net = Math.round((byCode.l || 0) / vatDivisor);
|
|
@@ -514,11 +514,30 @@ export function tomlLocalDate(value) {
|
|
|
514
514
|
return value instanceof Date ? value.toISOString().slice(0, 10) : value;
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
// Bad debts written off (4005, sales code "o") and fixed-asset disposals
|
|
518
|
+
// (4006, code "fs") already carry their own sign in the sales code's own
|
|
519
|
+
// P&L formula (see LTD_SALES_CODE_MAP and the "o"/"fs" rows CONTEXT_SELF_
|
|
520
|
+
// EMPLOYED.md and CONTEXT_LIMITED_COMPANY.md describe), so negating a
|
|
521
|
+
// credit note against either account here would flip an already-correct
|
|
522
|
+
// figure a second time.
|
|
523
|
+
const SALES_ACCOUNTS_WITH_OWN_SIGN = new Set(["4005", "4006"]);
|
|
524
|
+
|
|
525
|
+
// A credit note reverses part of the sale or purchase it names. The schema
|
|
526
|
+
// fixes a line's amount at zero or more -- only documentType says which way
|
|
527
|
+
// it points -- so every total that sums a sales or purchases line's amount
|
|
528
|
+
// reads this instead of the raw field, or a refund inflates turnover (and a
|
|
529
|
+
// supplier's credit inflates spend) instead of reducing it.
|
|
530
|
+
export function signedAmount(line) {
|
|
531
|
+
if (line.documentType !== "credit-note") return line.amount;
|
|
532
|
+
if (line.sourceJournalID === "sales" && SALES_ACCOUNTS_WITH_OWN_SIGN.has(String(line.accountMainID))) return line.amount;
|
|
533
|
+
return -line.amount;
|
|
534
|
+
}
|
|
535
|
+
|
|
517
536
|
export function computeNetSales(salesLines) {
|
|
518
537
|
let netTotal = 0;
|
|
519
538
|
for (const line of salesLines) {
|
|
520
539
|
const rate = line.taxRate || 0;
|
|
521
|
-
netTotal += line
|
|
540
|
+
netTotal += signedAmount(line) / (1 + rate);
|
|
522
541
|
}
|
|
523
542
|
return Math.round(netTotal);
|
|
524
543
|
}
|
|
@@ -527,14 +546,14 @@ export function computeNetSales(salesLines) {
|
|
|
527
546
|
export function computeSpreadsheetNetSales(salesLines) {
|
|
528
547
|
let netTotal = 0;
|
|
529
548
|
for (const line of salesLines) {
|
|
530
|
-
netTotal += line
|
|
549
|
+
netTotal += signedAmount(line) / 1.2;
|
|
531
550
|
}
|
|
532
551
|
return Math.round(netTotal);
|
|
533
552
|
}
|
|
534
553
|
|
|
535
554
|
// BST: amounts are entered as-is (no VAT split), so total = sum of amounts
|
|
536
555
|
export function computeGrossSales(salesLines) {
|
|
537
|
-
return Math.round(salesLines.reduce((sum, line) => sum + line
|
|
556
|
+
return Math.round(salesLines.reduce((sum, line) => sum + signedAmount(line), 0));
|
|
538
557
|
}
|
|
539
558
|
|
|
540
559
|
// ============================================================================
|
|
@@ -816,7 +835,7 @@ export function buildGrouped(
|
|
|
816
835
|
date: line.postingDate,
|
|
817
836
|
customer: line.detailComment,
|
|
818
837
|
code,
|
|
819
|
-
amount: line
|
|
838
|
+
amount: signedAmount(line),
|
|
820
839
|
};
|
|
821
840
|
if (carriesSourceFields) {
|
|
822
841
|
sale.account = line.accountMainID;
|
|
@@ -836,7 +855,7 @@ export function buildGrouped(
|
|
|
836
855
|
date: line.postingDate,
|
|
837
856
|
supplier: line.detailComment,
|
|
838
857
|
code,
|
|
839
|
-
amount: line
|
|
858
|
+
amount: signedAmount(line),
|
|
840
859
|
};
|
|
841
860
|
if (carriesSourceFields) {
|
|
842
861
|
purchase.account = line.accountMainID;
|
|
@@ -891,7 +910,7 @@ export function totalsByCode(lines, purchaseCodeMap) {
|
|
|
891
910
|
if (line.sourceJournalID !== "purchases") continue;
|
|
892
911
|
const code = purchaseCodeMap[line.accountMainID];
|
|
893
912
|
if (!code) continue;
|
|
894
|
-
totals[code] = Math.round(((totals[code] || 0) + line
|
|
913
|
+
totals[code] = Math.round(((totals[code] || 0) + signedAmount(line)) * 100) / 100;
|
|
895
914
|
}
|
|
896
915
|
return totals;
|
|
897
916
|
}
|