@diy-accounting-uk/diya-gl 1.2.10 → 1.2.11
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/filing/ct-computation-v1.1.toml +1 -0
- package/dist/app/data/hmrc/form-layouts/ltd.json +3 -0
- package/dist/app/data/hmrc/sa103-mtd-mapping.json +7 -7
- package/dist/app/data/releases.json +26 -0
- package/dist/app/data/render-unrepresentable/ltd.json +6 -5
- package/dist/app/data/render-unrepresentable/se.json +5 -14
- package/dist/app/data/roundtrip-unrepresentable.json +2 -2
- package/dist/app/data/se-2020-2021.toml +1 -0
- package/dist/app/data/se-2021-2022.toml +1 -0
- package/dist/app/data/se-2022-2023.toml +1 -0
- package/dist/app/data/se-2023-2024.toml +1 -0
- package/dist/app/data/se-2024-2025.toml +1 -0
- package/dist/app/data/se-2025-2026.toml +1 -0
- package/dist/app/data/se-2026-2027.toml +1 -0
- package/dist/app/lib/anchors/se.js +15 -7
- package/dist/app/lib/calculators/ltd.js +26 -4
- package/dist/app/lib/calculators/se-derivations.js +93 -29
- package/dist/app/lib/calculators/se.js +52 -26
- package/dist/app/lib/diya-gl-loader.js +13 -2
- package/dist/app/lib/generator.js +4 -1
- package/dist/app/lib/provenance-data.js +7 -7
- package/dist/app/lib/scenario-extractor.js +18 -1
- package/dist/app/lib/xlsx-exporter.js +57 -6
- package/dist/app/products/ltd.js +45 -9
- package/dist/app/products/se.js +232 -22
- package/dist/web/spreadsheets.diyaccounting.co.uk/public/schema/diya-gl-book-v2.schema.json +48 -1
- package/package.json +1 -1
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
"form": "SA103F",
|
|
445
445
|
"box": "51",
|
|
446
446
|
"label": "Capital allowances at 6% on equipment, including cars with higher CO2 emissions",
|
|
447
|
-
"cell":
|
|
447
|
+
"cell": "SE Full!D147",
|
|
448
448
|
"field": "allowances.capitalAllowanceSpecialRatePool | allowances.capitalAllowanceSingleAssetPool",
|
|
449
449
|
"route": "annual"
|
|
450
450
|
},
|
|
@@ -452,7 +452,7 @@
|
|
|
452
452
|
"form": "SA103F",
|
|
453
453
|
"box": "52",
|
|
454
454
|
"label": "Zero-emission goods vehicle allowance",
|
|
455
|
-
"cell":
|
|
455
|
+
"cell": "SE Full!D152",
|
|
456
456
|
"field": "allowances.zeroEmissionsGoodsVehicleAllowance",
|
|
457
457
|
"route": "annual"
|
|
458
458
|
},
|
|
@@ -460,7 +460,7 @@
|
|
|
460
460
|
"form": "SA103F",
|
|
461
461
|
"box": "52.1",
|
|
462
462
|
"label": "Zero-emission car allowance",
|
|
463
|
-
"cell":
|
|
463
|
+
"cell": "SE Full!D156",
|
|
464
464
|
"field": "allowances.zeroEmissionsCarAllowance",
|
|
465
465
|
"route": "annual"
|
|
466
466
|
},
|
|
@@ -468,7 +468,7 @@
|
|
|
468
468
|
"form": "SA103F",
|
|
469
469
|
"box": "53",
|
|
470
470
|
"label": "The Structures and Buildings Allowance",
|
|
471
|
-
"cell":
|
|
471
|
+
"cell": "SE Full!D160",
|
|
472
472
|
"field": "allowances.structuredBuildingAllowance",
|
|
473
473
|
"route": "annual"
|
|
474
474
|
},
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
"form": "SA103F",
|
|
485
485
|
"box": "54",
|
|
486
486
|
"label": "Electric charge-point allowance",
|
|
487
|
-
"cell":
|
|
487
|
+
"cell": "SE Full!O139",
|
|
488
488
|
"field": "allowances.enhancedCapitalAllowance | allowances.electricChargePointAllowance",
|
|
489
489
|
"route": "annual"
|
|
490
490
|
},
|
|
@@ -545,7 +545,7 @@
|
|
|
545
545
|
"form": "SA103F",
|
|
546
546
|
"box": "62",
|
|
547
547
|
"label": "Income, receipts and other profits included in business income or expenses but not taxable as business profits",
|
|
548
|
-
"cell":
|
|
548
|
+
"cell": "SE Full!D179",
|
|
549
549
|
"field": "adjustments.includedNonTaxableProfits",
|
|
550
550
|
"route": "annual"
|
|
551
551
|
},
|
|
@@ -598,7 +598,7 @@
|
|
|
598
598
|
"form": "SA103F",
|
|
599
599
|
"box": "71",
|
|
600
600
|
"label": "Adjustment for change of accounting practice",
|
|
601
|
-
"cell":
|
|
601
|
+
"cell": "SE Full!D210",
|
|
602
602
|
"field": "adjustments.accountingAdjustment",
|
|
603
603
|
"route": "annual"
|
|
604
604
|
},
|
|
@@ -493,6 +493,32 @@
|
|
|
493
493
|
"scorecard": "101565 passed, 0 warnings, 0 failed"
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"tag": "diya-gl-v1.2.10",
|
|
499
|
+
"date": "2026-09-13",
|
|
500
|
+
"formatVersion": "diya-gl/1",
|
|
501
|
+
"engineVersion": "1.2.10+2c047a2a",
|
|
502
|
+
"taxDataHash": "84585062cad8",
|
|
503
|
+
"reconciledCommit": "1839cf9537c130410e2a180626e7729aca82ffc9",
|
|
504
|
+
"templates": {
|
|
505
|
+
"bst": {
|
|
506
|
+
"hash": "0d003e10e00c",
|
|
507
|
+
"scorecard": "812 passed, 0 warnings, 0 failed"
|
|
508
|
+
},
|
|
509
|
+
"taxi": {
|
|
510
|
+
"hash": "1ff69654d64d",
|
|
511
|
+
"scorecard": "686 passed, 0 warnings, 0 failed"
|
|
512
|
+
},
|
|
513
|
+
"se": {
|
|
514
|
+
"hash": "185e5ad6b61d",
|
|
515
|
+
"scorecard": "7692 passed, 0 warnings, 0 failed"
|
|
516
|
+
},
|
|
517
|
+
"ltd": {
|
|
518
|
+
"hash": "81ffdc956e5a",
|
|
519
|
+
"scorecard": "101565 passed, 0 warnings, 0 failed"
|
|
520
|
+
}
|
|
521
|
+
}
|
|
496
522
|
}
|
|
497
523
|
]
|
|
498
524
|
}
|
|
@@ -684,6 +684,7 @@
|
|
|
684
684
|
"cell/expensesform.xlsx!Month 10!C30": "Expense form monthly entries; the admin view shows rates only.",
|
|
685
685
|
"cell/expensesform.xlsx!Month 11!C30": "Expense form monthly entries; the admin view shows rates only.",
|
|
686
686
|
"cell/expensesform.xlsx!Month 12!C30": "Expense form monthly entries; the admin view shows rates only.",
|
|
687
|
+
"section/accounting-profit-to-tax-profit-bridge/add-back-business-entertainment": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
687
688
|
"section/accounting-profit-to-tax-profit-bridge/add-back-depreciation-charged-in-the-year": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
688
689
|
"section/accounting-profit-to-tax-profit-bridge/add-back-goodwill-written-off": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
689
690
|
"section/accounting-profit-to-tax-profit-bridge/add-gross-bank-interest-received": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
@@ -695,11 +696,11 @@
|
|
|
695
696
|
"section/accounting-profit-to-tax-profit-bridge/tax-profit-the-bridge-computes": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
696
697
|
"section/accounting-profit-to-tax-profit-bridge/tax-profit-the-sheet-carries": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
697
698
|
"section/ct600-as-filed/box-395-tax": "Box 395 tax is the CT600 straddle-period figure (an accounting period spanning two financial years); like its sibling straddle boxes, the CT600 form carries no box for it.",
|
|
698
|
-
"section/journal-category-vat-netting/advertising-promotion-purchases-a/downstream": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
699
|
-
"section/journal-category-vat-netting/advertising-promotion-purchases-a/gross": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
700
|
-
"section/journal-category-vat-netting/advertising-promotion-purchases-a/net": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
701
|
-
"section/journal-category-vat-netting/advertising-promotion-purchases-a/residue": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
702
|
-
"section/journal-category-vat-netting/advertising-promotion-purchases-a/vat": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
699
|
+
"section/journal-category-vat-netting/advertising-promotion-entertainment-purchases-a/downstream": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
700
|
+
"section/journal-category-vat-netting/advertising-promotion-entertainment-purchases-a/gross": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
701
|
+
"section/journal-category-vat-netting/advertising-promotion-entertainment-purchases-a/net": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
702
|
+
"section/journal-category-vat-netting/advertising-promotion-entertainment-purchases-a/residue": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
703
|
+
"section/journal-category-vat-netting/advertising-promotion-entertainment-purchases-a/vat": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
703
704
|
"section/journal-category-vat-netting/bad-debts-written-off-sales-o/downstream": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
704
705
|
"section/journal-category-vat-netting/bad-debts-written-off-sales-o/gross": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
705
706
|
"section/journal-category-vat-netting/bad-debts-written-off-sales-o/net": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/ltd.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
@@ -338,6 +338,10 @@
|
|
|
338
338
|
"section/accounting-profit-to-tax-profit-bridge/less-other-capital-allowances-box-25": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
339
339
|
"section/accounting-profit-to-tax-profit-bridge/less-small-balance-allowance-box-24": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
340
340
|
"section/accounting-profit-to-tax-profit-bridge/less-the-full-return-s-own-box-62-adjustment": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
341
|
+
"section/accounting-profit-to-tax-profit-bridge/less-the-full-return-s-own-electric-charge-point-allowance-box-54": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
342
|
+
"section/accounting-profit-to-tax-profit-bridge/less-the-full-return-s-own-structures-and-buildings-allowance-box-53": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
343
|
+
"section/accounting-profit-to-tax-profit-bridge/less-the-full-return-s-own-zero-emission-car-allowance-box-52-1": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
344
|
+
"section/accounting-profit-to-tax-profit-bridge/less-the-full-return-s-own-zero-emission-goods-vehicle-allowance-box-52": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
341
345
|
"section/accounting-profit-to-tax-profit-bridge/profit-before-tax-per-the-profit-and-loss-account": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
342
346
|
"section/accounting-profit-to-tax-profit-bridge/residue": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
343
347
|
"section/accounting-profit-to-tax-profit-bridge/tax-profit-the-bridge-computes": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page.",
|
|
@@ -457,18 +461,5 @@
|
|
|
457
461
|
"section/journal-category-vat-netting/business-entertainment-memo-purchases-e/net": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/se.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
458
462
|
"section/journal-category-vat-netting/business-entertainment-memo-purchases-e/residue": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/se.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
459
463
|
"section/journal-category-vat-netting/business-entertainment-memo-purchases-e/vat": "The journal-category-vat-netting report is a reconciliation table (categoryNettingLines, products/se.js) explaining how a gross journal figure nets into a statement cell; no page view is this table.",
|
|
460
|
-
"section/accounting-profit-to-tax-profit-bridge/add-disallowable-expenses-added-back-box-46": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page."
|
|
461
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!B49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
462
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!C49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
463
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!D49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
464
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!E49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
465
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!F49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
466
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!G49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
467
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!H49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
468
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!I49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
469
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!J49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
470
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!K49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
471
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!L49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
472
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!M49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it.",
|
|
473
|
-
"cell/Financialaccounts.xlsx!Profit & Loss Account!N49": "The Profit & Loss view renders the statement itself, B5 to B39. Row 49 sits below it as the memo the sheet keeps for disallowable entertainment, so the add-back has a figure to read; it is not a statement line and no page view prints it."
|
|
464
|
+
"section/accounting-profit-to-tax-profit-bridge/add-disallowable-expenses-added-back-box-46": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page."
|
|
474
465
|
}
|
|
@@ -279,8 +279,8 @@
|
|
|
279
279
|
},
|
|
280
280
|
{
|
|
281
281
|
"path": "tax.capitalAllowances.specialRateWDA",
|
|
282
|
-
"products": ["bst", "taxi"
|
|
283
|
-
"reason": "The
|
|
282
|
+
"products": ["bst", "taxi"],
|
|
283
|
+
"reason": "The Basic Sole Trader and Taxi Driver Fixed Assets sheets keep one writing down pool at Admin's single rate. The Self Employed Schedule keeps a special rate pool at Admin!G6, which the year files state as writing_down_allowance_special."
|
|
284
284
|
},
|
|
285
285
|
{
|
|
286
286
|
"path": "tax.incomeTax.additionalRate",
|
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
STOCK_CLOSING_COUNT_CELL,
|
|
28
28
|
BUSINESS_DESCRIPTION_CELL,
|
|
29
29
|
DISALLOWABLE_PERCENT_CELLS,
|
|
30
|
+
ANNUAL_ALLOWANCE_CELLS,
|
|
31
|
+
ANNUAL_ADJUSTMENT_CELLS,
|
|
32
|
+
GOODS_FOR_OWN_USE_CELL,
|
|
33
|
+
SPECIAL_RATE_POOL_MARKER_COLUMN,
|
|
30
34
|
SALESINVOICE_VAT_REG_CELL,
|
|
31
35
|
SALESINVOICE_TELEPHONE_CELL,
|
|
32
36
|
SALESINVOICE_PRODUCT_DETAILS_COLUMNS,
|
|
@@ -352,13 +356,13 @@ function isPayslipsInputCell(sheet, cellRef) {
|
|
|
352
356
|
}
|
|
353
357
|
|
|
354
358
|
// Schedule: an opening asset's description/cost/acc-dep/tax-wdv (C, E, F, O)
|
|
355
|
-
// on its existing-asset row, or an
|
|
356
|
-
//
|
|
357
|
-
// Plant & Machinery row. See
|
|
358
|
-
// writer.
|
|
359
|
+
// and its special rate pool marker (AB) on its existing-asset row, or an
|
|
360
|
+
// in-year disposal's date/proceeds (U, V) on that same row; a new purchase's
|
|
361
|
+
// date/supplier/cost (B, C, E) on a New Plant & Machinery row. See
|
|
362
|
+
// app/products/se.js's cellWrites() Schedule writer.
|
|
359
363
|
const EXISTING_SCHEDULE_ROWS = new Set([...EXISTING_ASSET_ROWS.motor, ...EXISTING_ASSET_ROWS.computer]);
|
|
360
364
|
const NEW_SCHEDULE_ROWS = new Set(NEW_PLANT_ROWS);
|
|
361
|
-
const EXISTING_SCHEDULE_COLUMNS = ["C", "E", "F", "O", "U", "V"];
|
|
365
|
+
const EXISTING_SCHEDULE_COLUMNS = ["C", "E", "F", "O", "U", "V", SPECIAL_RATE_POOL_MARKER_COLUMN];
|
|
362
366
|
const NEW_SCHEDULE_COLUMNS = ["B", "C", "E"];
|
|
363
367
|
const HP_ROWS = new Set(HP_AGREEMENT_ROWS);
|
|
364
368
|
const HP_COLUMNS = ["B", "C", "D", "E", "F", "G", "H", "L"];
|
|
@@ -389,11 +393,15 @@ function isVatInputCell(sheet, cellRef) {
|
|
|
389
393
|
return false;
|
|
390
394
|
}
|
|
391
395
|
|
|
392
|
-
// The hub's own business name and
|
|
396
|
+
// The hub's own business name, description and goods for own use, the two
|
|
397
|
+
// stock counts, the disallowable percentages and the SA103F boxes the book
|
|
398
|
+
// states by hand.
|
|
399
|
+
const SE_FULL_STATED_CELLS = new Set([...Object.values(ANNUAL_ALLOWANCE_CELLS), ...Object.values(ANNUAL_ADJUSTMENT_CELLS)]);
|
|
393
400
|
function isHubInputCell(sheet, cellRef) {
|
|
394
|
-
if (sheet === "Business Details") return cellRef === "C5" || cellRef === BUSINESS_DESCRIPTION_CELL;
|
|
401
|
+
if (sheet === "Business Details") return cellRef === "C5" || cellRef === BUSINESS_DESCRIPTION_CELL || cellRef === GOODS_FOR_OWN_USE_CELL;
|
|
395
402
|
if (sheet === "StockControl") return cellRef === STOCK_OPENING_COUNT_CELL || cellRef === STOCK_CLOSING_COUNT_CELL;
|
|
396
403
|
if (sheet === "VitalTax") return Object.values(DISALLOWABLE_PERCENT_CELLS).includes(cellRef);
|
|
404
|
+
if (sheet === "SE Full") return SE_FULL_STATED_CELLS.has(cellRef);
|
|
397
405
|
return false;
|
|
398
406
|
}
|
|
399
407
|
|
|
@@ -60,7 +60,10 @@ const VAT_RATE = 0.2;
|
|
|
60
60
|
// Sales and Purchases analysis columns, code letter by column letter, in the
|
|
61
61
|
// order row 5 of each month tab tests them. Column AK of a Purchases month
|
|
62
62
|
// tab is the CIS certificates column, which the writer fills from a
|
|
63
|
-
// sub-contractor purchase rather than from a code letter.
|
|
63
|
+
// sub-contractor purchase rather than from a code letter. Column AJ, business
|
|
64
|
+
// entertainment, sits after the fixed asset column: it is the one expense the
|
|
65
|
+
// working sheet adds back in full, so it has to be analysed apart from the
|
|
66
|
+
// advertising it shares a profit and loss line with.
|
|
64
67
|
export const SALES_ANALYSIS_COLUMNS = { a: "O", b: "P", c: "Q", d: "R", g: "S", o: "T", fs: "U" };
|
|
65
68
|
export const PURCHASE_ANALYSIS_COLUMNS = {
|
|
66
69
|
s: "O",
|
|
@@ -84,6 +87,7 @@ export const PURCHASE_ANALYSIS_COLUMNS = {
|
|
|
84
87
|
y: "AG",
|
|
85
88
|
z: "AH",
|
|
86
89
|
fa: "AI",
|
|
90
|
+
e: "AJ",
|
|
87
91
|
};
|
|
88
92
|
const SALES_CIS_COLUMN = "V";
|
|
89
93
|
const PURCHASES_CIS_COLUMN = "AK";
|
|
@@ -106,7 +110,15 @@ const SALES_ROW_OFFSET = 49;
|
|
|
106
110
|
const SALES_BAD_DEBT_ROW = 34;
|
|
107
111
|
|
|
108
112
|
// The management P&L's expense rows and the trial balance row each reads.
|
|
113
|
+
// Row 27 also reads the entertainment row, TRIAL_BALANCE_ENTERTAINMENT_ROW.
|
|
109
114
|
const EXPENSE_PL_ROWS = { 21: 68, 22: 69, 23: 70, 24: 71, 25: 72, 26: 73, 27: 74, 28: 75, 29: 76, 30: 77, 31: 78, 32: 79, 33: 80 };
|
|
115
|
+
const ADVERTISING_PL_ROW = 27;
|
|
116
|
+
|
|
117
|
+
// The trial balance row business entertainment lands on. The profit and loss
|
|
118
|
+
// account shows it with advertising; the Corporation Tax working sheet reads
|
|
119
|
+
// it back out of the trial balance as a third add-back beside goodwill and
|
|
120
|
+
// depreciation, the way HMRC disallows client entertaining in full.
|
|
121
|
+
const TRIAL_BALANCE_ENTERTAINMENT_ROW = 90;
|
|
110
122
|
|
|
111
123
|
// WagesInterface holds a month a row in two blocks, employees above
|
|
112
124
|
// directors, twelve rows each.
|
|
@@ -275,6 +287,7 @@ const TRIAL_BALANCE_READS = [
|
|
|
275
287
|
"EJ40",
|
|
276
288
|
"EJ48",
|
|
277
289
|
"EJ66",
|
|
290
|
+
"EJ90",
|
|
278
291
|
"EJ91",
|
|
279
292
|
"L34",
|
|
280
293
|
];
|
|
@@ -284,7 +297,7 @@ const TRIAL_BALANCE_READS = [
|
|
|
284
297
|
const AUDIT_ROWS = [
|
|
285
298
|
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 40, 42, 43, 44, 47, 48,
|
|
286
299
|
49, 53, 54, 55, 56, 57, 58, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
|
|
287
|
-
88, 89,
|
|
300
|
+
88, 89, 90,
|
|
288
301
|
];
|
|
289
302
|
|
|
290
303
|
// ── Small helpers ──────────────────────────────────────────────────────────
|
|
@@ -1541,6 +1554,7 @@ function buildTrialBalance(input) {
|
|
|
1541
1554
|
monthly[87] = zeroMonths().map(() => blocks.whole.I / 12);
|
|
1542
1555
|
monthly[88] = negated(receipts("X"));
|
|
1543
1556
|
monthly[89] = payments("X");
|
|
1557
|
+
monthly[TRIAL_BALANCE_ENTERTAINMENT_ROW] = purchasesMonthly(PURCHASE_ANALYSIS_COLUMNS.e);
|
|
1544
1558
|
monthly[19] = stock.movements;
|
|
1545
1559
|
for (const [row, values] of Object.entries(monthly)) tb[`EJ${row}`] = sum(values) + (tb[`D${row}`] || 0);
|
|
1546
1560
|
|
|
@@ -1651,7 +1665,13 @@ function buildMonthlyProfitAndLoss(tb, tabs) {
|
|
|
1651
1665
|
pl.B18 = tb.EJ64 + tb.EJ65;
|
|
1652
1666
|
pl.B19 = tb.EJ66;
|
|
1653
1667
|
pl.B20 = tb.EJ67;
|
|
1654
|
-
for (const [row, source] of Object.entries(EXPENSE_PL_ROWS))
|
|
1668
|
+
for (const [row, source] of Object.entries(EXPENSE_PL_ROWS)) {
|
|
1669
|
+
const values =
|
|
1670
|
+
Number(row) === ADVERTISING_PL_ROW
|
|
1671
|
+
? tb.monthly[source].map((value, index) => value + tb.monthly[TRIAL_BALANCE_ENTERTAINMENT_ROW][index])
|
|
1672
|
+
: tb.monthly[source];
|
|
1673
|
+
setRow(row, values);
|
|
1674
|
+
}
|
|
1655
1675
|
setRow(SALES_BAD_DEBT_ROW, tb.monthly[81]);
|
|
1656
1676
|
pl.B35 = tb.EJ82;
|
|
1657
1677
|
pl.B36 = tb.EJ83 + tb.EJ88 + tb.EJ89;
|
|
@@ -1781,6 +1801,7 @@ function buildDirectorsReport(publishedPl, publishedBalanceSheet, companySecreta
|
|
|
1781
1801
|
function buildCorporationTax({ admin, trialBalance, blocks, publishedPl, openAccounts }) {
|
|
1782
1802
|
const goodwill = trialBalance.EJ85 > 0 ? trialBalance.EJ85 : 0;
|
|
1783
1803
|
const depreciation = trialBalance.EJ87 > 0 ? trialBalance.EJ87 : 0;
|
|
1804
|
+
const entertainment = trialBalance[`EJ${TRIAL_BALANCE_ENTERTAINMENT_ROW}`] > 0 ? trialBalance[`EJ${TRIAL_BALANCE_ENTERTAINMENT_ROW}`] : 0;
|
|
1784
1805
|
const netBalancingCharge = blocks.whole.W > 0 ? blocks.whole.Z - blocks.whole.Y : 0;
|
|
1785
1806
|
|
|
1786
1807
|
const sheet = {
|
|
@@ -1794,7 +1815,8 @@ function buildCorporationTax({ admin, trialBalance, blocks, publishedPl, openAcc
|
|
|
1794
1815
|
};
|
|
1795
1816
|
if (goodwill > 0) sheet.I7 = goodwill;
|
|
1796
1817
|
if (depreciation > 0) sheet.I8 = depreciation;
|
|
1797
|
-
sheet.
|
|
1818
|
+
if (entertainment > 0) sheet.I9 = entertainment;
|
|
1819
|
+
sheet.K10 = goodwill + depreciation + entertainment;
|
|
1798
1820
|
sheet.K12 = sheet.K5 + sheet.K10;
|
|
1799
1821
|
sheet.K20 = sheet.I15 + sheet.I16 + sheet.I17 + sheet.I18;
|
|
1800
1822
|
sheet.K22 = sheet.K12 - sheet.K20;
|
|
@@ -63,17 +63,24 @@ function primaryField(boxes, boxNumber) {
|
|
|
63
63
|
// rather than the object this call meant to build. Every path here comes from
|
|
64
64
|
// sa103-mtd-mapping.json's own "field" column, so a segment like this is a
|
|
65
65
|
// defect in that mapping file and must fail loudly rather than land quietly
|
|
66
|
-
// on some other object.
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
// on some other object. The check sits inside the walk, as literal
|
|
67
|
+
// comparisons, and each intermediate node is only reused when it is the
|
|
68
|
+
// object's own property: the shape static analysis recognises as guarded.
|
|
69
69
|
export function setPath(target, path, value) {
|
|
70
70
|
const parts = path.split(".");
|
|
71
|
-
for (const part of parts) {
|
|
72
|
-
if (UNSAFE_PATH_SEGMENTS.has(part)) throw new Error(`setPath: "${part}" is not a field name, in path "${path}"`);
|
|
73
|
-
}
|
|
74
71
|
let node = target;
|
|
75
|
-
for (let i = 0; i < parts.length
|
|
76
|
-
|
|
72
|
+
for (let i = 0; i < parts.length; i++) {
|
|
73
|
+
const part = parts[i];
|
|
74
|
+
if (part === "__proto__" || part === "constructor" || part === "prototype") {
|
|
75
|
+
throw new Error(`setPath: "${part}" is not a field name, in path "${path}"`);
|
|
76
|
+
}
|
|
77
|
+
if (i === parts.length - 1) {
|
|
78
|
+
node[part] = value;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!Object.hasOwn(node, part) || node[part] === null || typeof node[part] !== "object") node[part] = {};
|
|
82
|
+
node = node[part];
|
|
83
|
+
}
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
function round2(value) {
|
|
@@ -452,27 +459,32 @@ export function buildSelfEmploymentQuarterlyUpdates(book, lines, taxData, option
|
|
|
452
459
|
};
|
|
453
460
|
}
|
|
454
461
|
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
462
|
+
// The annual boxes with no formula behind them that the book can state
|
|
463
|
+
// (tax.selfEmployment.allowances / .adjustments, keyed by the API field
|
|
464
|
+
// name): the writer puts each figure on its SE Full cell and the derivation
|
|
465
|
+
// reads it back from there, so the payload carries what the return prints.
|
|
466
|
+
// A box the book does not state stays blank and warns.
|
|
467
|
+
const BOOK_STATED_ANNUAL_BOXES = [
|
|
468
|
+
{ box: "52", cell: "D152", table: "allowances" }, // zeroEmissionsGoodsVehicleAllowance -- gone from 2025-26
|
|
469
|
+
{ box: "52.1", cell: "D156", table: "allowances" }, // zeroEmissionsCarAllowance
|
|
470
|
+
{ box: "53", cell: "D160", table: "allowances" }, // structuredBuildingAllowance
|
|
471
|
+
{ box: "62", cell: "D179", table: "adjustments" }, // includedNonTaxableProfits
|
|
472
|
+
{ box: "71", cell: "D210", table: "adjustments" }, // accountingAdjustment
|
|
473
|
+
];
|
|
474
|
+
|
|
475
|
+
// Every annual field no cell and no book field can source, and a warning
|
|
476
|
+
// naming the box for each -- section 8 of the design records why each is
|
|
477
|
+
// out of reach today: no cell in the template, or a figure the book has no
|
|
478
|
+
// record for. Box 69's field also moves out of HMRC's own schema by tax
|
|
460
479
|
// year -- annualFieldsUnavailableForYear() below is what keeps a year that
|
|
461
480
|
// no longer accepts a field from warning about it as if it still did.
|
|
462
481
|
const NO_SOURCE_ANNUAL_BOXES = [
|
|
463
|
-
{ box: "51", pick: 0 }, // allowances.capitalAllowanceSpecialRatePool
|
|
464
482
|
{ box: "51", pick: 1 }, // allowances.capitalAllowanceSingleAssetPool (shared with box 50)
|
|
465
|
-
{ box: "52", pick: 0 }, // allowances.zeroEmissionsGoodsVehicleAllowance -- gone from 2025-26
|
|
466
|
-
{ box: "52.1", pick: 0 }, // allowances.zeroEmissionsCarAllowance
|
|
467
|
-
{ box: "53", pick: 0 }, // allowances.structuredBuildingAllowance
|
|
468
483
|
{ box: "53.1", pick: 0 }, // allowances.enhancedStructuredBuildingAllowance
|
|
469
|
-
{ box: "54", pick: 1 }, // allowances.electricChargePointAllowance -- gone from 2025-26 (shared with box 55's enhancedCapitalAllowance)
|
|
470
484
|
{ box: "55", pick: 1 }, // allowances.businessPremisesRenovationAllowance (shared with enhancedCapitalAllowance)
|
|
471
485
|
{ box: "59", pick: 1 }, // adjustments.balancingChargeBpra (shared with balancingChargeOther)
|
|
472
|
-
{ box: "62", pick: 0 }, // adjustments.includedNonTaxableProfits
|
|
473
486
|
{ box: "68", pick: 0 }, // adjustments.basisAdjustment
|
|
474
487
|
{ box: "69", pick: 0 }, // adjustments.overlapReliefUsed -- gone from 2026-27
|
|
475
|
-
{ box: "71", pick: 0 }, // adjustments.accountingAdjustment
|
|
476
488
|
{ box: "73.3", pick: 0 }, // adjustments.transitionProfitAmount -- added in 2024-25
|
|
477
489
|
{ box: "73.3", pick: 1 }, // adjustments.transitionProfitAccelerationAmount -- added in 2024-25
|
|
478
490
|
];
|
|
@@ -527,9 +539,16 @@ export function buildSelfEmploymentAnnualSubmission(book, lines, taxData, option
|
|
|
527
539
|
// ...), so both branches land on one shared root and fall out of it rather
|
|
528
540
|
// than being built separately and then walked into again.
|
|
529
541
|
const root = {};
|
|
542
|
+
const cellNumber = (cell) => (typeof seFull[cell] === "number" ? seFull[cell] : 0);
|
|
530
543
|
setPath(root, primaryField(boxes, "49"), round2(seFull.D139));
|
|
531
544
|
setPath(root, primaryField(boxes, "50"), round2(seFull.D144));
|
|
532
|
-
|
|
545
|
+
// Box 51 is the schedule's special rate pool (column AC, the rows marked S).
|
|
546
|
+
setPath(root, primaryField(boxes, "51"), round2(seFull.D147));
|
|
547
|
+
// HMRC's mapping files box 54 and box 55 under one field: the small pools
|
|
548
|
+
// write-off the sheet computes at O144 and the charge-point figure the book
|
|
549
|
+
// states at O139 both reach it.
|
|
550
|
+
const enhancedCapitalAllowanceField = primaryField(boxes, "55");
|
|
551
|
+
setPath(root, enhancedCapitalAllowanceField, round2(seFull.O144 + cellNumber("O139")));
|
|
533
552
|
setPath(root, primaryField(boxes, "56"), round2(seFull.O149));
|
|
534
553
|
|
|
535
554
|
setPath(root, primaryField(boxes, "59"), round2(seFull.O160));
|
|
@@ -537,21 +556,63 @@ export function buildSelfEmploymentAnnualSubmission(book, lines, taxData, option
|
|
|
537
556
|
setPath(root, ownUseField, round2(seFull.D169));
|
|
538
557
|
setPath(root, primaryField(boxes, "75"), round2(seFull.O204));
|
|
539
558
|
|
|
540
|
-
const allowances = root.allowances || {};
|
|
541
|
-
const adjustments = root.adjustments || {};
|
|
542
|
-
|
|
543
|
-
const enhancedCapitalAllowanceField = primaryField(boxes, "55");
|
|
544
559
|
const warnings = [
|
|
545
|
-
{
|
|
546
|
-
field: ownUseField,
|
|
547
|
-
reason: "Business Details!O50 is a customer input cell the book schema has no field for; the template always carries nil here.",
|
|
548
|
-
},
|
|
549
560
|
{
|
|
550
561
|
field: enhancedCapitalAllowanceField,
|
|
551
562
|
reason:
|
|
552
563
|
"SA103F box 55 is the small pools write-off (Schedule S1, once the pool balance and the writing down allowance together are under £1,000), not a genuine 100% or enhanced capital allowance; HMRC's own field mapping still files it here.",
|
|
553
564
|
},
|
|
554
565
|
];
|
|
566
|
+
if (typeof rawResults["Business Details"]?.O50 !== "number") {
|
|
567
|
+
warnings.push({
|
|
568
|
+
field: ownUseField,
|
|
569
|
+
reason: "the book states no tax.selfEmployment.adjustments.goodsAndServicesOwnUse, so Business Details!O50 and box 60 carry nil.",
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
for (const { box: boxNumber, cell, table } of BOOK_STATED_ANNUAL_BOXES) {
|
|
573
|
+
const field = primaryField(boxes, boxNumber);
|
|
574
|
+
const leaf = field.split(".").pop();
|
|
575
|
+
if (unavailableFields.has(field)) {
|
|
576
|
+
if (cellNumber(cell) !== 0) {
|
|
577
|
+
warnings.push({
|
|
578
|
+
field,
|
|
579
|
+
reason: `the book states ${cellNumber(cell)} for SA103F box ${boxNumber}, which the sheet totals, but HMRC's schema for ${taxYear} no longer accepts the field; nothing is filed for it.`,
|
|
580
|
+
handComputed: round2(cellNumber(cell)),
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
if (typeof seFull[cell] === "number") {
|
|
586
|
+
setPath(root, field, round2(seFull[cell]));
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
warnings.push({
|
|
590
|
+
field,
|
|
591
|
+
reason: `SA103F box ${boxNumber} has no formula behind it and the book states no tax.selfEmployment.${table}.${leaf}; the box stays blank.`,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
const electricChargePointField = fieldsOf(boxEntry(boxes, "54"))[1];
|
|
595
|
+
if (cellNumber("O139") !== 0) {
|
|
596
|
+
warnings.push({
|
|
597
|
+
field: electricChargePointField,
|
|
598
|
+
reason: `SA103F box 54 (SE Full!O139) is filed under ${enhancedCapitalAllowanceField}, HMRC's primary field for the box, together with box 55.`,
|
|
599
|
+
handComputed: round2(cellNumber("O139")),
|
|
600
|
+
});
|
|
601
|
+
} else if (!unavailableFields.has(electricChargePointField)) {
|
|
602
|
+
warnings.push({
|
|
603
|
+
field: electricChargePointField,
|
|
604
|
+
reason:
|
|
605
|
+
"SA103F box 54 has no formula behind it and the book states no tax.selfEmployment.allowances.electricChargePointAllowance; the box stays blank.",
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
if (cellNumber("D210") !== 0) {
|
|
609
|
+
warnings.push({
|
|
610
|
+
field: primaryField(boxes, "71"),
|
|
611
|
+
reason:
|
|
612
|
+
"SE Full carries box 71 into box 77 (D219 = O179+E197+D210+P190) and not into box 73 (O194 = O174); HMRC computes the adjusted profit from the filed field, so the sheet's printed box 73 and its Income Tax sheet leave it out.",
|
|
613
|
+
handComputed: round2(seFull.O174 + cellNumber("D210")),
|
|
614
|
+
});
|
|
615
|
+
}
|
|
555
616
|
for (const { box: boxNumber, pick } of NO_SOURCE_ANNUAL_BOXES) {
|
|
556
617
|
const entry = boxEntry(boxes, boxNumber);
|
|
557
618
|
const field = fieldsOf(entry)[pick];
|
|
@@ -562,6 +623,9 @@ export function buildSelfEmploymentAnnualSubmission(book, lines, taxData, option
|
|
|
562
623
|
});
|
|
563
624
|
}
|
|
564
625
|
|
|
626
|
+
const allowances = root.allowances || {};
|
|
627
|
+
const adjustments = root.adjustments || {};
|
|
628
|
+
|
|
565
629
|
const result = { taxYear };
|
|
566
630
|
if (Object.keys(allowances).length > 0) result.allowances = allowances;
|
|
567
631
|
if (Object.keys(adjustments).length > 0) result.adjustments = adjustments;
|