@diy-accounting-uk/diya-gl 1.2.4 → 1.2.6
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/hmrc/form-layouts/ltd.json +1 -1
- package/dist/app/data/ltd-2020.toml +7 -2
- package/dist/app/data/ltd-2021.toml +7 -2
- package/dist/app/data/ltd-2022.toml +7 -2
- package/dist/app/data/ltd-2023.toml +7 -2
- package/dist/app/data/ltd-2024.toml +7 -2
- package/dist/app/data/ltd-2025.toml +7 -2
- package/dist/app/data/ltd-2026.toml +7 -2
- package/dist/app/data/ltd-2027.toml +7 -2
- package/dist/app/data/releases.json +52 -0
- package/dist/app/lib/anchors/se.js +2 -0
- package/dist/app/lib/calculators/ltd.js +25 -21
- package/dist/app/lib/calculators/se-derivations.js +3 -1
- package/dist/app/lib/calculators/se.js +159 -68
- package/dist/app/lib/generator.js +18 -24
- package/dist/app/lib/provenance-data.js +8 -8
- package/dist/app/lib/scenario-extractor.js +12 -1
- package/dist/app/lib/tax/corporation-tax.js +63 -35
- package/dist/app/products/ltd.js +82 -51
- package/dist/app/products/se.js +78 -13
- package/package.json +1 -1
|
@@ -485,7 +485,7 @@
|
|
|
485
485
|
{
|
|
486
486
|
"cell": "Financialaccounts.xlsx!CT600!AA126",
|
|
487
487
|
"test": "lt",
|
|
488
|
-
"against": "Financialaccounts.xlsx!Admin!
|
|
488
|
+
"against": "Financialaccounts.xlsx!Admin!R6"
|
|
489
489
|
}
|
|
490
490
|
],
|
|
491
491
|
"why": "the form ticks 329 for marginal relief or the small profits rate; the sheet prints neither tick"
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 0
|
|
|
17
17
|
marginal_relief_fraction = 0
|
|
18
18
|
|
|
19
19
|
# Financial year 2019, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.19
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 0
|
|
27
|
+
main_rate_limit = 0
|
|
28
|
+
marginal_relief_fraction = 0
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 0
|
|
|
17
17
|
marginal_relief_fraction = 0
|
|
18
18
|
|
|
19
19
|
# Financial year 2020, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.19
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 0
|
|
27
|
+
main_rate_limit = 0
|
|
28
|
+
marginal_relief_fraction = 0
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 0
|
|
|
17
17
|
marginal_relief_fraction = 0
|
|
18
18
|
|
|
19
19
|
# Financial year 2021, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.19
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 0
|
|
27
|
+
main_rate_limit = 0
|
|
28
|
+
marginal_relief_fraction = 0
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 250000
|
|
|
17
17
|
marginal_relief_fraction = 0.015
|
|
18
18
|
|
|
19
19
|
# Financial year 2022, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.19
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 0
|
|
27
|
+
main_rate_limit = 0
|
|
28
|
+
marginal_relief_fraction = 0
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 250000
|
|
|
17
17
|
marginal_relief_fraction = 0.015
|
|
18
18
|
|
|
19
19
|
# Financial year 2023, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.25
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 50000
|
|
27
|
+
main_rate_limit = 250000
|
|
28
|
+
marginal_relief_fraction = 0.015
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -17,10 +17,15 @@ main_rate_limit = 250000
|
|
|
17
17
|
marginal_relief_fraction = 0.015
|
|
18
18
|
|
|
19
19
|
# Financial year 2024, the year before this one. An accounting period that
|
|
20
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
21
|
-
#
|
|
20
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
21
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
22
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
22
23
|
[corporation_tax_previous_financial_year]
|
|
24
|
+
main_rate = 0.25
|
|
23
25
|
small_profits_rate = 0.19
|
|
26
|
+
small_profits_limit = 50000
|
|
27
|
+
main_rate_limit = 250000
|
|
28
|
+
marginal_relief_fraction = 0.015
|
|
24
29
|
|
|
25
30
|
[capital_allowances]
|
|
26
31
|
annual_investment_allowance = 1.00
|
|
@@ -21,10 +21,15 @@ main_rate_limit = 250000
|
|
|
21
21
|
marginal_relief_fraction = 0.015 # 3/200
|
|
22
22
|
|
|
23
23
|
# Financial year 2025, the year before this one. An accounting period that
|
|
24
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
25
|
-
#
|
|
24
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
25
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
26
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
26
27
|
[corporation_tax_previous_financial_year]
|
|
28
|
+
main_rate = 0.25
|
|
27
29
|
small_profits_rate = 0.19
|
|
30
|
+
small_profits_limit = 50000
|
|
31
|
+
main_rate_limit = 250000
|
|
32
|
+
marginal_relief_fraction = 0.015
|
|
28
33
|
|
|
29
34
|
[capital_allowances]
|
|
30
35
|
annual_investment_allowance = 1.00
|
|
@@ -19,10 +19,15 @@ main_rate_limit = 250000
|
|
|
19
19
|
marginal_relief_fraction = 0.015 # 3/200
|
|
20
20
|
|
|
21
21
|
# Financial year 2026, the year before this one. An accounting period that
|
|
22
|
-
# reaches back over 1 April charges its earlier days at that year's
|
|
23
|
-
#
|
|
22
|
+
# reaches back over 1 April charges its earlier days at that year's own rates,
|
|
23
|
+
# limits and relief fraction, which the working sheet's first tax row carries
|
|
24
|
+
# in Admin P6, R6, S6, T6 and U6.
|
|
24
25
|
[corporation_tax_previous_financial_year]
|
|
26
|
+
main_rate = 0.25
|
|
25
27
|
small_profits_rate = 0.19
|
|
28
|
+
small_profits_limit = 50000
|
|
29
|
+
main_rate_limit = 250000
|
|
30
|
+
marginal_relief_fraction = 0.015
|
|
26
31
|
|
|
27
32
|
[capital_allowances]
|
|
28
33
|
annual_investment_allowance = 1.00
|
|
@@ -337,6 +337,58 @@
|
|
|
337
337
|
"scorecard": "101279 passed, 0 warnings, 0 failed"
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"tag": "diya-gl-v1.2.4",
|
|
343
|
+
"date": "2026-09-11",
|
|
344
|
+
"formatVersion": "diya-gl/1",
|
|
345
|
+
"engineVersion": "1.2.4+f5483192",
|
|
346
|
+
"taxDataHash": "9b8294b452fb",
|
|
347
|
+
"reconciledCommit": "320b4b8a1585e16fa170c2d312c14df38d0bcf5b",
|
|
348
|
+
"templates": {
|
|
349
|
+
"bst": {
|
|
350
|
+
"hash": "0d003e10e00c",
|
|
351
|
+
"scorecard": "810 passed, 0 warnings, 0 failed"
|
|
352
|
+
},
|
|
353
|
+
"taxi": {
|
|
354
|
+
"hash": "1ff69654d64d",
|
|
355
|
+
"scorecard": "686 passed, 0 warnings, 0 failed"
|
|
356
|
+
},
|
|
357
|
+
"se": {
|
|
358
|
+
"hash": "e78c4aab0660",
|
|
359
|
+
"scorecard": "7679 passed, 0 warnings, 0 failed"
|
|
360
|
+
},
|
|
361
|
+
"ltd": {
|
|
362
|
+
"hash": "4dc8932fa566",
|
|
363
|
+
"scorecard": "101279 passed, 0 warnings, 0 failed"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"tag": "diya-gl-v1.2.5",
|
|
369
|
+
"date": "2026-09-11",
|
|
370
|
+
"formatVersion": "diya-gl/1",
|
|
371
|
+
"engineVersion": "1.2.5+adcbb86f",
|
|
372
|
+
"taxDataHash": "84585062cad8",
|
|
373
|
+
"reconciledCommit": "320b4b8a1585e16fa170c2d312c14df38d0bcf5b",
|
|
374
|
+
"templates": {
|
|
375
|
+
"bst": {
|
|
376
|
+
"hash": "0d003e10e00c",
|
|
377
|
+
"scorecard": "810 passed, 0 warnings, 0 failed"
|
|
378
|
+
},
|
|
379
|
+
"taxi": {
|
|
380
|
+
"hash": "1ff69654d64d",
|
|
381
|
+
"scorecard": "686 passed, 0 warnings, 0 failed"
|
|
382
|
+
},
|
|
383
|
+
"se": {
|
|
384
|
+
"hash": "e78c4aab0660",
|
|
385
|
+
"scorecard": "7679 passed, 0 warnings, 0 failed"
|
|
386
|
+
},
|
|
387
|
+
"ltd": {
|
|
388
|
+
"hash": "81ffdc956e5a",
|
|
389
|
+
"scorecard": "101279 passed, 0 warnings, 0 failed"
|
|
390
|
+
}
|
|
391
|
+
}
|
|
340
392
|
}
|
|
341
393
|
]
|
|
342
394
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
STOCK_OPENING_COUNT_CELL,
|
|
27
27
|
STOCK_CLOSING_COUNT_CELL,
|
|
28
28
|
BUSINESS_DESCRIPTION_CELL,
|
|
29
|
+
DISALLOWABLE_PERCENT_CELLS,
|
|
29
30
|
SALESINVOICE_VAT_REG_CELL,
|
|
30
31
|
SALESINVOICE_TELEPHONE_CELL,
|
|
31
32
|
SALESINVOICE_PRODUCT_DETAILS_COLUMNS,
|
|
@@ -392,6 +393,7 @@ function isVatInputCell(sheet, cellRef) {
|
|
|
392
393
|
function isHubInputCell(sheet, cellRef) {
|
|
393
394
|
if (sheet === "Business Details") return cellRef === "C5" || cellRef === BUSINESS_DESCRIPTION_CELL;
|
|
394
395
|
if (sheet === "StockControl") return cellRef === STOCK_OPENING_COUNT_CELL || cellRef === STOCK_CLOSING_COUNT_CELL;
|
|
396
|
+
if (sheet === "VitalTax") return Object.values(DISALLOWABLE_PERCENT_CELLS).includes(cellRef);
|
|
395
397
|
return false;
|
|
396
398
|
}
|
|
397
399
|
|
|
@@ -25,12 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { toExcelSerial } from "../spreadsheet-runner.js";
|
|
27
27
|
import { BANK_ACCOUNT_FILES, BANK_LAYOUTS, OPENING_FIXED_ASSET_COLUMNS, isLtdOpeningBankLine } from "../ltd-layout.js";
|
|
28
|
-
import {
|
|
29
|
-
apportionCorporationTax,
|
|
30
|
-
financialYearsInPeriod,
|
|
31
|
-
financialYearNumber,
|
|
32
|
-
smallProfitsRatePercentFor,
|
|
33
|
-
} from "../tax/corporation-tax.js";
|
|
28
|
+
import { apportionCorporationTax, financialYearsInPeriod, financialYearNumber, financialYearRatesFor } from "../tax/corporation-tax.js";
|
|
34
29
|
import { calculateCapitalAllowances } from "../tax/capital-allowances.js";
|
|
35
30
|
import {
|
|
36
31
|
monthlyPayrollBlockRow,
|
|
@@ -878,7 +873,6 @@ export function calculateLtdCells(book, lines, taxData, scenario) {
|
|
|
878
873
|
// at 31 March: L6 is the period start, N6 the earlier of the first financial
|
|
879
874
|
// year's end and the period end, L7 the day after and N7 the period end.
|
|
880
875
|
function buildAdmin(taxData, period, associatedCompanies) {
|
|
881
|
-
const corporationTax = taxData.corporation_tax || {};
|
|
882
876
|
const capitalAllowances = taxData.capital_allowances || {};
|
|
883
877
|
const depreciation = taxData.depreciation || {};
|
|
884
878
|
const mileage = taxData.mileage || {};
|
|
@@ -889,19 +883,27 @@ function buildAdmin(taxData, period, associatedCompanies) {
|
|
|
889
883
|
const financialYears = financialYearsInPeriod(period.start, period.yearEnd);
|
|
890
884
|
const fileFinancialYear = financialYearNumber(period.yearEnd);
|
|
891
885
|
|
|
886
|
+
// What each tax row's own financial year charges by. The two rows differ
|
|
887
|
+
// whenever the period reaches back over 1 April into a year that charged
|
|
888
|
+
// differently.
|
|
889
|
+
const rowRates = financialYears.years.map((financialYear) =>
|
|
890
|
+
financialYearRatesFor(taxData, financialYear.year, fileFinancialYear, financialYear.days),
|
|
891
|
+
);
|
|
892
|
+
|
|
892
893
|
return {
|
|
893
894
|
B9: periodStartSerial,
|
|
894
895
|
B32: yearEndSerial,
|
|
895
896
|
F21: yearEndSerial,
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
897
|
+
P6: rowRates[0].smallProfitsRatePercent,
|
|
898
|
+
R6: rowRates[0].mainRatePercent,
|
|
899
|
+
S6: rowRates[0].marginalReliefFraction,
|
|
900
|
+
T6: rowRates[0].lowerLimit,
|
|
901
|
+
U6: rowRates[0].upperLimit,
|
|
902
|
+
P7: rowRates[1].smallProfitsRatePercent,
|
|
903
|
+
R7: rowRates[1].mainRatePercent,
|
|
904
|
+
S7: rowRates[1].marginalReliefFraction,
|
|
905
|
+
T7: rowRates[1].lowerLimit,
|
|
906
|
+
U7: rowRates[1].upperLimit,
|
|
905
907
|
P14: associatedCompanies,
|
|
906
908
|
G5: Math.round(capitalAllowances.annual_investment_allowance * 100),
|
|
907
909
|
G6: Math.round(capitalAllowances.writing_down_allowance_main * 100),
|
|
@@ -1804,11 +1806,13 @@ function buildCorporationTax({ admin, trialBalance, blocks, publishedPl, openAcc
|
|
|
1804
1806
|
|
|
1805
1807
|
const financialYears = financialYearsInPeriod(fromSerial(admin.L6), fromSerial(admin.N7));
|
|
1806
1808
|
const charge = apportionCorporationTax(sheet.K28, financialYears.years, financialYears.totalDays, {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1809
|
+
perYear: [6, 7].map((adminRow) => ({
|
|
1810
|
+
smallProfitsRatePercent: admin[`P${adminRow}`],
|
|
1811
|
+
mainRatePercent: admin[`R${adminRow}`],
|
|
1812
|
+
marginalReliefFraction: admin[`S${adminRow}`],
|
|
1813
|
+
lowerLimit: admin[`T${adminRow}`],
|
|
1814
|
+
upperLimit: admin[`U${adminRow}`],
|
|
1815
|
+
})),
|
|
1812
1816
|
associatedCompanies: admin.P14,
|
|
1813
1817
|
frankedInvestmentIncome: sheet.K29,
|
|
1814
1818
|
});
|
|
@@ -205,7 +205,9 @@ function quarterlyFromMonthlyGrid(pl) {
|
|
|
205
205
|
// adjustment mixed in, read off app/lib/scenario-extractor.js's own account
|
|
206
206
|
// maps by way of the code letters scenario transactions already carry.
|
|
207
207
|
const SALES_QUARTERLY_ROW = { a: 5, b: 6, c: 7, d: 8 };
|
|
208
|
-
|
|
208
|
+
// Codes a (advertising) and e (business entertainment) both feed row 27 --
|
|
209
|
+
// box 24's own combined figure -- so both land on it here too.
|
|
210
|
+
const PURCHASES_QUARTERLY_ROW = { s: 14, c: 15, o: 16, w: 21, p: 22, m: 23, g: 24, v: 25, h: 26, a: 27, e: 27, l: 28, y: 32 };
|
|
209
211
|
|
|
210
212
|
const MONTH_KEYS = ["apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", "jan", "feb", "mar"];
|
|
211
213
|
const MONTH_SHEET_NAMES = {
|
|
@@ -83,6 +83,7 @@ export const PURCHASES_ANALYSIS_COLUMNS = {
|
|
|
83
83
|
l: "Z",
|
|
84
84
|
y: "AA",
|
|
85
85
|
fa: "AB",
|
|
86
|
+
e: "AC",
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
// Bank.xlsx and Cash.xlsx analyse a receipt and a payment under different code
|
|
@@ -803,7 +804,9 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
803
804
|
pl[`${col}24`] = purchasesCode(index, "g");
|
|
804
805
|
pl[`${col}25`] = purchasesCode(index, "v");
|
|
805
806
|
pl[`${col}26`] = purchasesCode(index, "h");
|
|
806
|
-
|
|
807
|
+
// Advertising and business entertainment share one Purchases analysis
|
|
808
|
+
// column each, but the same Purchases row: box 24 is the combined figure.
|
|
809
|
+
pl[`${col}27`] = purchasesCode(index, "a") + purchasesCode(index, "e");
|
|
807
810
|
pl[`${col}28`] = purchasesCode(index, "l");
|
|
808
811
|
pl[`${col}29`] = -salesCode(index, "o");
|
|
809
812
|
pl[`${col}30`] = bankPayments(bank, index, "J");
|
|
@@ -815,9 +818,12 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
815
818
|
pl[`${col}37`] = pl[`${col}19`] - pl[`${col}35`];
|
|
816
819
|
pl[`${col}38`] = bankReceipts(bank, index, "K");
|
|
817
820
|
pl[`${col}39`] = pl[`${col}37`] + pl[`${col}38`];
|
|
821
|
+
// Row 49: a memo of the entertainment share of row 27, outside the B35
|
|
822
|
+
// administrative-expenses total the way the template's own row 49 is.
|
|
823
|
+
pl[`${col}49`] = purchasesCode(index, "e");
|
|
818
824
|
});
|
|
819
825
|
const yearTotal = (row) => sheetSum(MONTH_COLS.map((col) => pl[`${col}${row}`]));
|
|
820
|
-
for (const row of [5, 6, 7, 8, 11, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 38]) {
|
|
826
|
+
for (const row of [5, 6, 7, 8, 11, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 38, 49]) {
|
|
821
827
|
pl[`B${row}`] = yearTotal(row);
|
|
822
828
|
}
|
|
823
829
|
pl.B9 = pl.B5 + pl.B6 + pl.B7 + pl.B8;
|
|
@@ -827,6 +833,119 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
827
833
|
pl.B37 = pl.B19 - pl.B35;
|
|
828
834
|
pl.B39 = pl.B37 + pl.B38;
|
|
829
835
|
|
|
836
|
+
// ── VitalTax ──
|
|
837
|
+
// The summary quotes the three product sales rows and the two direct cost
|
|
838
|
+
// rows, then a quarterly grid of fifteen expense categories: an allowable
|
|
839
|
+
// row read straight off the P&L beside a disallowable row that is a
|
|
840
|
+
// customer-set percentage of that same allowable row -- the source boxes
|
|
841
|
+
// 32 to 45 (SE Full's O66 to O118) read. Built ahead of the two self
|
|
842
|
+
// assessment returns because both read it.
|
|
843
|
+
const quarterSum = (row, quarter) => sheetSum(MONTH_COLS.slice(quarter * 3, quarter * 3 + 3).map((col) => pl[`${col}${row}`]));
|
|
844
|
+
const quarterSumRows = (rows, quarter) => sheetSum(rows.map((row) => quarterSum(row, quarter)));
|
|
845
|
+
const productSales = (quarter) => quarterSum(5, quarter) + quarterSum(6, quarter) + quarterSum(7, quarter);
|
|
846
|
+
const directCosts = (quarter) => quarterSum(14, quarter) + quarterSum(16, quarter);
|
|
847
|
+
const vitalTax = {
|
|
848
|
+
C5: productSales(0),
|
|
849
|
+
D5: productSales(1),
|
|
850
|
+
E5: productSales(2),
|
|
851
|
+
F5: productSales(3),
|
|
852
|
+
C7: directCosts(0),
|
|
853
|
+
D7: directCosts(1),
|
|
854
|
+
E7: directCosts(2),
|
|
855
|
+
F7: directCosts(3),
|
|
856
|
+
};
|
|
857
|
+
vitalTax.G5 = vitalTax.C5 + vitalTax.D5 + vitalTax.E5 + vitalTax.F5;
|
|
858
|
+
vitalTax.G7 = vitalTax.C7 + vitalTax.D7 + vitalTax.E7 + vitalTax.F7;
|
|
859
|
+
|
|
860
|
+
const QUARTER_COLS = [
|
|
861
|
+
["C", 0],
|
|
862
|
+
["D", 1],
|
|
863
|
+
["E", 2],
|
|
864
|
+
["F", 3],
|
|
865
|
+
];
|
|
866
|
+
|
|
867
|
+
// Every allowable row the disallowable percentages multiply, keyed by its
|
|
868
|
+
// own row number, reading the P&L row (or rows) its caption names.
|
|
869
|
+
const ALLOWABLE_PL_ROWS = {
|
|
870
|
+
12: [21], // Wages and staff costs
|
|
871
|
+
13: [22], // Premises running costs
|
|
872
|
+
14: [23], // Repairs and renewals
|
|
873
|
+
15: [24], // General administrative expenses
|
|
874
|
+
16: [31], // Finance charges (HP interest, lease, bank charges)
|
|
875
|
+
17: [25, 26], // Car, van and travel expenses
|
|
876
|
+
18: [27], // Advertising (now the combined advertising + entertainment row)
|
|
877
|
+
19: [28], // Professional fees
|
|
878
|
+
20: [29], // Bad debts
|
|
879
|
+
21: [30], // Interest on bank and other loans
|
|
880
|
+
22: [32], // Other expenses
|
|
881
|
+
24: [15], // Payments to subcontractors
|
|
882
|
+
25: [49], // Business entertainment (the memo row)
|
|
883
|
+
26: [33, 34], // Loss on disposal + depreciation
|
|
884
|
+
};
|
|
885
|
+
for (const [row, plRows] of Object.entries(ALLOWABLE_PL_ROWS)) {
|
|
886
|
+
for (const [col, quarter] of QUARTER_COLS) vitalTax[`${col}${row}`] = quarterSumRows(plRows, quarter);
|
|
887
|
+
vitalTax[`G${row}`] = vitalTax[`C${row}`] + vitalTax[`D${row}`] + vitalTax[`E${row}`] + vitalTax[`F${row}`];
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// The fifteen disallowable rows behind boxes 32 to 45. Thirteen are a
|
|
891
|
+
// customer-set percentage of the allowable row beside them; business
|
|
892
|
+
// entertainment (row 44) is wholly disallowable, so it echoes row 25
|
|
893
|
+
// outright, and depreciation (row 49) already has its own source on SE
|
|
894
|
+
// Full (O114 = P&L row 34), so it echoes that P&L row rather than taking a
|
|
895
|
+
// percentage of anything.
|
|
896
|
+
const DISALLOWABLE_ROWS = {
|
|
897
|
+
36: { allowableRow: 7, key: "costOfGoods" },
|
|
898
|
+
37: { allowableRow: 24, key: "paymentsToSubcontractors" },
|
|
899
|
+
38: { allowableRow: 12, key: "wagesAndStaffCosts" },
|
|
900
|
+
39: { allowableRow: 17, key: "carVanTravelExpenses" },
|
|
901
|
+
40: { allowableRow: 13, key: "premisesRunningCosts" },
|
|
902
|
+
41: { allowableRow: 14, key: "maintenanceCosts" },
|
|
903
|
+
42: { allowableRow: 15, key: "adminCosts" },
|
|
904
|
+
43: { allowableRow: 18, key: "advertisingCosts" },
|
|
905
|
+
45: { allowableRow: 21, key: "interestOnBankOtherLoans" },
|
|
906
|
+
46: { allowableRow: 16, key: "financeCharges" },
|
|
907
|
+
47: { allowableRow: 20, key: "irrecoverableDebts" },
|
|
908
|
+
48: { allowableRow: 19, key: "professionalFees" },
|
|
909
|
+
50: { allowableRow: 22, key: "otherExpenses" },
|
|
910
|
+
};
|
|
911
|
+
const disallowablePercent = scenario.disallowable || {};
|
|
912
|
+
for (const [row, { allowableRow, key }] of Object.entries(DISALLOWABLE_ROWS)) {
|
|
913
|
+
const percent = disallowablePercent[key] || 0;
|
|
914
|
+
for (const [col] of QUARTER_COLS) vitalTax[`${col}${row}`] = vitalTax[`${col}${allowableRow}`] * percent;
|
|
915
|
+
vitalTax[`G${row}`] = vitalTax[`C${row}`] + vitalTax[`D${row}`] + vitalTax[`E${row}`] + vitalTax[`F${row}`];
|
|
916
|
+
}
|
|
917
|
+
for (const [col] of QUARTER_COLS) vitalTax[`${col}44`] = vitalTax[`${col}25`];
|
|
918
|
+
vitalTax.G44 = vitalTax.C44 + vitalTax.D44 + vitalTax.E44 + vitalTax.F44;
|
|
919
|
+
for (const [col, quarter] of QUARTER_COLS) vitalTax[`${col}49`] = quarterSum(34, quarter);
|
|
920
|
+
vitalTax.G49 = vitalTax.C49 + vitalTax.D49 + vitalTax.E49 + vitalTax.F49;
|
|
921
|
+
|
|
922
|
+
// Row 29: the grand total of allowable expenses. Entertainment (row 25) is
|
|
923
|
+
// left out -- it is already inside row 18's P&L!C27:E27 read, which now
|
|
924
|
+
// carries the combined advertising-and-entertainment figure.
|
|
925
|
+
const ROW29_ROWS = [7, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26];
|
|
926
|
+
for (const [col] of QUARTER_COLS) vitalTax[`${col}29`] = sheetSum(ROW29_ROWS.map((row) => vitalTax[`${col}${row}`]));
|
|
927
|
+
vitalTax.G29 = sheetSum(ROW29_ROWS.map((row) => vitalTax[`G${row}`]));
|
|
928
|
+
|
|
929
|
+
// The thirteen SE Full disallowable boxes (32 to 45 minus 44, which shares
|
|
930
|
+
// box 39 with advertising) read straight off VitalTax's annual column.
|
|
931
|
+
const seFullDisallowable = {
|
|
932
|
+
O66: vitalTax.G36,
|
|
933
|
+
O70: vitalTax.G37,
|
|
934
|
+
O74: vitalTax.G38,
|
|
935
|
+
O78: vitalTax.G39,
|
|
936
|
+
O82: vitalTax.G40,
|
|
937
|
+
O86: vitalTax.G41,
|
|
938
|
+
O90: vitalTax.G42,
|
|
939
|
+
O94: vitalTax.G43 + vitalTax.G44,
|
|
940
|
+
O98: vitalTax.G45,
|
|
941
|
+
O102: vitalTax.G46,
|
|
942
|
+
O106: vitalTax.G47,
|
|
943
|
+
O110: vitalTax.G48,
|
|
944
|
+
O118: vitalTax.G50,
|
|
945
|
+
};
|
|
946
|
+
// Box 46, boxes 32 to 45 summed. seShort.O64 (box 20) reads the same total.
|
|
947
|
+
const boxes32to45Total = sheetSum(Object.values(seFullDisallowable)) + pl.B34;
|
|
948
|
+
|
|
830
949
|
// ── The fixed asset workbook ──
|
|
831
950
|
const scheduleCells = { E57: schedule.existing.E, E110: schedule.additions.E };
|
|
832
951
|
for (const column of SCHEDULE_TOTAL_COLUMNS) scheduleCells[`${column}1`] = schedule.totals[column];
|
|
@@ -907,16 +1026,27 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
907
1026
|
seShort.D38 > admin.F26
|
|
908
1027
|
? `SELF-EMPLOYMENT FULL RETURN REQUIRED AS TURNOVER EXCEEDS £${admin.F26} VAT threshold`
|
|
909
1028
|
: `Business income - if your annual turnover was below £${admin.F26} VAT threshold`;
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
seShort.
|
|
916
|
-
seShort.
|
|
917
|
-
seShort.
|
|
918
|
-
seShort.
|
|
919
|
-
seShort.
|
|
1029
|
+
// Boxes 11 to 19 go net of their own share of boxes 32 to 45: SA103S has no
|
|
1030
|
+
// disallowable column of its own, so each allowable figure drops the
|
|
1031
|
+
// matching SE Full box before it reaches the short return. Box 33
|
|
1032
|
+
// (subcontractor payments) has no short-return box of its own -- SA103S
|
|
1033
|
+
// folds it into box 11 -- so D46 drops both box 32's and box 33's shares.
|
|
1034
|
+
seShort.D46 = analysed(pl.B17 - seFullDisallowable.O66 - seFullDisallowable.O70);
|
|
1035
|
+
seShort.O46 = analysed(pl.B28 - seFullDisallowable.O110);
|
|
1036
|
+
seShort.D51 = analysed(pl.B25 + pl.B26 - seFullDisallowable.O78);
|
|
1037
|
+
seShort.O51 = analysed(pl.B30 + pl.B31 - seFullDisallowable.O98 - seFullDisallowable.O102);
|
|
1038
|
+
seShort.D55 = analysed(pl.B21 - seFullDisallowable.O74);
|
|
1039
|
+
seShort.O55 = analysed(pl.B24 - seFullDisallowable.O90);
|
|
1040
|
+
seShort.D60 = analysed(pl.B22 - seFullDisallowable.O82);
|
|
1041
|
+
// Box 19's base (B27+B29+B32+B33) never includes depreciation (P&L row 34
|
|
1042
|
+
// sits outside it, in row 33's disposal loss instead), so nothing here
|
|
1043
|
+
// reads box 44's O114 -- box 20 (O64) is where depreciation's disallowable
|
|
1044
|
+
// share leaves the total. O94 already carries the whole of box 39's
|
|
1045
|
+
// entertainment share (VitalTax row 44 echoes row 25, the entertainment
|
|
1046
|
+
// memo's own annual total), so box 19 does not also subtract P&L!B49.
|
|
1047
|
+
seShort.O60 = analysed(pl.B27 + pl.B29 + pl.B32 + pl.B33 - seFullDisallowable.O94 - seFullDisallowable.O106 - seFullDisallowable.O118);
|
|
1048
|
+
seShort.D64 = analysed(pl.B23 - seFullDisallowable.O86);
|
|
1049
|
+
seShort.O64 = pl.B17 + pl.B35 - boxes32to45Total;
|
|
920
1050
|
const shortNetProfit = seShort.D38 + seShort.O38 - seShort.O64;
|
|
921
1051
|
seShort.D71 = shortNetProfit >= 0 ? shortNetProfit : 0;
|
|
922
1052
|
seShort.O71 = shortNetProfit < 0 ? -shortNetProfit : 0;
|
|
@@ -969,43 +1099,23 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
969
1099
|
seFull.D118 = pl.B32;
|
|
970
1100
|
seFull.D122 = pl.B17 + pl.B35;
|
|
971
1101
|
seFull.O114 = pl.B34;
|
|
972
|
-
// Boxes 32 to 45, the disallowable side of each expense category
|
|
973
|
-
//
|
|
974
|
-
|
|
975
|
-
seFull.
|
|
976
|
-
seFull.
|
|
977
|
-
seFull.
|
|
978
|
-
seFull.
|
|
979
|
-
seFull.
|
|
980
|
-
seFull.
|
|
981
|
-
seFull.
|
|
982
|
-
seFull.
|
|
983
|
-
seFull.
|
|
984
|
-
seFull.
|
|
985
|
-
seFull.
|
|
986
|
-
seFull.
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
// read of the depreciation figure box 44 already carries. The sum stays
|
|
990
|
-
// equal to box 44 alone until the boxes above gain a source.
|
|
991
|
-
seFull.O122 = carry([seFull.O114], () =>
|
|
992
|
-
sheetSum([
|
|
993
|
-
seFull.O66,
|
|
994
|
-
seFull.O70,
|
|
995
|
-
seFull.O74,
|
|
996
|
-
seFull.O78,
|
|
997
|
-
seFull.O82,
|
|
998
|
-
seFull.O86,
|
|
999
|
-
seFull.O90,
|
|
1000
|
-
seFull.O94,
|
|
1001
|
-
seFull.O98,
|
|
1002
|
-
seFull.O102,
|
|
1003
|
-
seFull.O106,
|
|
1004
|
-
seFull.O110,
|
|
1005
|
-
seFull.O114,
|
|
1006
|
-
seFull.O118,
|
|
1007
|
-
]),
|
|
1008
|
-
);
|
|
1102
|
+
// Boxes 32 to 45, the disallowable side of each expense category, sourced
|
|
1103
|
+
// from the VitalTax percentages built earlier.
|
|
1104
|
+
seFull.O66 = seFullDisallowable.O66;
|
|
1105
|
+
seFull.O70 = seFullDisallowable.O70;
|
|
1106
|
+
seFull.O74 = seFullDisallowable.O74;
|
|
1107
|
+
seFull.O78 = seFullDisallowable.O78;
|
|
1108
|
+
seFull.O82 = seFullDisallowable.O82;
|
|
1109
|
+
seFull.O86 = seFullDisallowable.O86;
|
|
1110
|
+
seFull.O90 = seFullDisallowable.O90;
|
|
1111
|
+
seFull.O94 = seFullDisallowable.O94;
|
|
1112
|
+
seFull.O98 = seFullDisallowable.O98;
|
|
1113
|
+
seFull.O102 = seFullDisallowable.O102;
|
|
1114
|
+
seFull.O106 = seFullDisallowable.O106;
|
|
1115
|
+
seFull.O110 = seFullDisallowable.O110;
|
|
1116
|
+
seFull.O118 = seFullDisallowable.O118;
|
|
1117
|
+
// Box 46's own caption calls it the total of boxes 32 to 45.
|
|
1118
|
+
seFull.O122 = carry([seFull.O114], () => boxes32to45Total);
|
|
1009
1119
|
const fullNetProfit = seFull.D55 + seFull.O55 - seFull.D122;
|
|
1010
1120
|
seFull.D129 = fullNetProfit >= 0 ? fullNetProfit : 0;
|
|
1011
1121
|
seFull.O129 = fullNetProfit < 0 ? -fullNetProfit : 0;
|
|
@@ -1127,25 +1237,6 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
|
|
|
1127
1237
|
sheetSum([forecast.C42, forecast.C43, forecast.C44, forecast.C45]),
|
|
1128
1238
|
);
|
|
1129
1239
|
|
|
1130
|
-
// ── VitalTax ──
|
|
1131
|
-
// The summary quotes the three product sales rows and the two direct cost
|
|
1132
|
-
// rows, nothing else.
|
|
1133
|
-
const quarterSum = (row, quarter) => sheetSum(MONTH_COLS.slice(quarter * 3, quarter * 3 + 3).map((col) => pl[`${col}${row}`]));
|
|
1134
|
-
const productSales = (quarter) => quarterSum(5, quarter) + quarterSum(6, quarter) + quarterSum(7, quarter);
|
|
1135
|
-
const directCosts = (quarter) => quarterSum(14, quarter) + quarterSum(16, quarter);
|
|
1136
|
-
const vitalTax = {
|
|
1137
|
-
C5: productSales(0),
|
|
1138
|
-
D5: productSales(1),
|
|
1139
|
-
E5: productSales(2),
|
|
1140
|
-
F5: productSales(3),
|
|
1141
|
-
C7: directCosts(0),
|
|
1142
|
-
D7: directCosts(1),
|
|
1143
|
-
E7: directCosts(2),
|
|
1144
|
-
F7: directCosts(3),
|
|
1145
|
-
};
|
|
1146
|
-
vitalTax.G5 = vitalTax.C5 + vitalTax.D5 + vitalTax.E5 + vitalTax.F5;
|
|
1147
|
-
vitalTax.G7 = vitalTax.C7 + vitalTax.D7 + vitalTax.E7 + vitalTax.F7;
|
|
1148
|
-
|
|
1149
1240
|
// ── The results, keyed the way the reconciliation reads them ──
|
|
1150
1241
|
const results = {
|
|
1151
1242
|
"Business Details": { C5: businessName(scenario) },
|