@diy-accounting-uk/diya-gl 1.2.13 → 1.2.15

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.
@@ -571,6 +571,58 @@
571
571
  "scorecard": "101605 passed, 0 warnings, 0 failed"
572
572
  }
573
573
  }
574
+ },
575
+ {
576
+ "tag": "diya-gl-v1.2.13",
577
+ "date": "2026-09-14",
578
+ "formatVersion": "diya-gl/1",
579
+ "engineVersion": "1.2.13+f5b44cdf",
580
+ "taxDataHash": "4ecdbe985d22",
581
+ "reconciledCommit": "c910fdeefbe4f2f71ae9ec5f5d4a713783a1c0c7",
582
+ "templates": {
583
+ "bst": {
584
+ "hash": "0d003e10e00c",
585
+ "scorecard": "812 passed, 0 warnings, 0 failed"
586
+ },
587
+ "taxi": {
588
+ "hash": "1ff69654d64d",
589
+ "scorecard": "686 passed, 0 warnings, 0 failed"
590
+ },
591
+ "se": {
592
+ "hash": "15d3b753f7a4",
593
+ "scorecard": "7956 passed, 0 warnings, 0 failed"
594
+ },
595
+ "ltd": {
596
+ "hash": "caa14632083b",
597
+ "scorecard": "101605 passed, 0 warnings, 0 failed"
598
+ }
599
+ }
600
+ },
601
+ {
602
+ "tag": "diya-gl-v1.2.14",
603
+ "date": "2026-09-14",
604
+ "formatVersion": "diya-gl/1",
605
+ "engineVersion": "1.2.14+959e4a1d",
606
+ "taxDataHash": "4ecdbe985d22",
607
+ "reconciledCommit": "c910fdeefbe4f2f71ae9ec5f5d4a713783a1c0c7",
608
+ "templates": {
609
+ "bst": {
610
+ "hash": "0d003e10e00c",
611
+ "scorecard": "812 passed, 0 warnings, 0 failed"
612
+ },
613
+ "taxi": {
614
+ "hash": "1ff69654d64d",
615
+ "scorecard": "686 passed, 0 warnings, 0 failed"
616
+ },
617
+ "se": {
618
+ "hash": "94754ec2c44b",
619
+ "scorecard": "7962 passed, 0 warnings, 0 failed"
620
+ },
621
+ "ltd": {
622
+ "hash": "caa14632083b",
623
+ "scorecard": "101605 passed, 0 warnings, 0 failed"
624
+ }
625
+ }
574
626
  }
575
627
  ]
576
628
  }
@@ -462,5 +462,9 @@
462
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.",
463
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.",
464
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.",
465
+ "cell/Fixedassets.xlsx!Schedule!M40": "The single asset pool car's own Schedule row (the third motor asset brought forward): its private use share, its P marker, its allowance and its written-down value. The Fixed assets view lists the brought-forward register by asset, without a cell key a row, and the AE1, AF1 and AG1 totals those rows sum into are on the same view under their own cell keys.",
466
+ "cell/Fixedassets.xlsx!Schedule!AD40": "The single asset pool car's own Schedule row (the third motor asset brought forward): its private use share, its P marker, its allowance and its written-down value. The Fixed assets view lists the brought-forward register by asset, without a cell key a row, and the AE1, AF1 and AG1 totals those rows sum into are on the same view under their own cell keys.",
467
+ "cell/Fixedassets.xlsx!Schedule!AE40": "The single asset pool car's own Schedule row (the third motor asset brought forward): its private use share, its P marker, its allowance and its written-down value. The Fixed assets view lists the brought-forward register by asset, without a cell key a row, and the AE1, AF1 and AG1 totals those rows sum into are on the same view under their own cell keys.",
468
+ "cell/Fixedassets.xlsx!Schedule!AG40": "The single asset pool car's own Schedule row (the third motor asset brought forward): its private use share, its P marker, its allowance and its written-down value. The Fixed assets view lists the brought-forward register by asset, without a cell key a row, and the AE1, AF1 and AG1 totals those rows sum into are on the same view under their own cell keys.",
465
469
  "section/accounting-profit-to-tax-profit-bridge/add-the-full-return-s-own-box-71-adjustment-for-change-of-accounting-practice": "The page has no profit-bridge view; the bridge from accounting profit to tax profit is not printed anywhere on the books page."
466
470
  }
@@ -31,6 +31,8 @@ import {
31
31
  ANNUAL_ADJUSTMENT_CELLS,
32
32
  GOODS_FOR_OWN_USE_CELL,
33
33
  SPECIAL_RATE_POOL_MARKER_COLUMN,
34
+ SINGLE_ASSET_POOL_MARKER_COLUMN,
35
+ PRIVATE_USE_COLUMN,
34
36
  SALESINVOICE_VAT_REG_CELL,
35
37
  SALESINVOICE_TELEPHONE_CELL,
36
38
  SALESINVOICE_PRODUCT_DETAILS_COLUMNS,
@@ -355,14 +357,25 @@ function isPayslipsInputCell(sheet, cellRef) {
355
357
  return isPayslipsMonthInputCell(sheet, cellRef);
356
358
  }
357
359
 
358
- // Schedule: an opening asset's description/cost/acc-dep/tax-wdv (C, E, F, O)
359
- // and its special rate pool marker (AB) on its existing-asset row, or an
360
+ // Schedule: an opening asset's description/cost/acc-dep/tax-wdv (C, E, F, O),
361
+ // its private use share (M), its special rate pool marker (AB) and its single
362
+ // asset pool marker (AD) on its existing-asset row, or an
360
363
  // in-year disposal's date/proceeds (U, V) on that same row; a new purchase's
361
364
  // date/supplier/cost (B, C, E) on a New Plant & Machinery row. See
362
365
  // app/products/se.js's cellWrites() Schedule writer.
363
366
  const EXISTING_SCHEDULE_ROWS = new Set([...EXISTING_ASSET_ROWS.motor, ...EXISTING_ASSET_ROWS.computer]);
364
367
  const NEW_SCHEDULE_ROWS = new Set(NEW_PLANT_ROWS);
365
- const EXISTING_SCHEDULE_COLUMNS = ["C", "E", "F", "O", "U", "V", SPECIAL_RATE_POOL_MARKER_COLUMN];
368
+ const EXISTING_SCHEDULE_COLUMNS = [
369
+ "C",
370
+ "E",
371
+ "F",
372
+ PRIVATE_USE_COLUMN,
373
+ "O",
374
+ "U",
375
+ "V",
376
+ SPECIAL_RATE_POOL_MARKER_COLUMN,
377
+ SINGLE_ASSET_POOL_MARKER_COLUMN,
378
+ ];
366
379
  const NEW_SCHEDULE_COLUMNS = ["B", "C", "E"];
367
380
  const HP_ROWS = new Set(HP_AGREEMENT_ROWS);
368
381
  const HP_COLUMNS = ["B", "C", "D", "E", "F", "G", "H", "L"];
@@ -479,7 +479,6 @@ const BOOK_STATED_ANNUAL_BOXES = [
479
479
  // year -- annualFieldsUnavailableForYear() below is what keeps a year that
480
480
  // no longer accepts a field from warning about it as if it still did.
481
481
  const NO_SOURCE_ANNUAL_BOXES = [
482
- { box: "51", pick: 1 }, // allowances.capitalAllowanceSingleAssetPool (shared with box 50)
483
482
  { box: "53.1", pick: 0 }, // allowances.enhancedStructuredBuildingAllowance
484
483
  { box: "55", pick: 1 }, // allowances.businessPremisesRenovationAllowance (shared with enhancedCapitalAllowance)
485
484
  { box: "59", pick: 1 }, // adjustments.balancingChargeBpra (shared with balancingChargeOther)
@@ -541,9 +540,19 @@ export function buildSelfEmploymentAnnualSubmission(book, lines, taxData, option
541
540
  const root = {};
542
541
  const cellNumber = (cell) => (typeof seFull[cell] === "number" ? seFull[cell] : 0);
543
542
  setPath(root, primaryField(boxes, "49"), round2(seFull.D139));
544
- setPath(root, primaryField(boxes, "50"), round2(seFull.D144));
543
+ // The printed boxes 50 and 51 hold every writing down allowance by rate,
544
+ // single asset pools included; the API files the single asset pools
545
+ // (Schedule AE and AF, the rows marked P) under their own field and the
546
+ // pooled balance under each rate's field.
547
+ const schedule = rawResults["Fixedassets.xlsx!Schedule"];
548
+ const singleAssetMain = typeof schedule?.AE1 === "number" ? schedule.AE1 : 0;
549
+ const singleAssetSpecial = typeof schedule?.AF1 === "number" ? schedule.AF1 : 0;
550
+ setPath(root, primaryField(boxes, "50"), round2(seFull.D144 - singleAssetMain));
545
551
  // Box 51 is the schedule's special rate pool (column AC, the rows marked S).
546
- setPath(root, primaryField(boxes, "51"), round2(seFull.D147));
552
+ setPath(root, primaryField(boxes, "51"), round2(seFull.D147 - singleAssetSpecial));
553
+ if (singleAssetMain + singleAssetSpecial !== 0) {
554
+ setPath(root, fieldsOf(boxEntry(boxes, "50"))[1], round2(singleAssetMain + singleAssetSpecial));
555
+ }
547
556
  // HMRC's mapping files box 54 and box 55 under one field: the small pools
548
557
  // write-off the sheet computes at O144 and the charge-point figure the book
549
558
  // states at O139 both reach it.
@@ -440,9 +440,24 @@ function payrollMonths(payroll) {
440
440
  * That is what the sheet reports for a disposal with no tax value entered, so
441
441
  * it is what this reports too.
442
442
  */
443
- function scheduleRow({ cost, accDep = 0, taxWdv, depRate, aiaRate, wdaRate, specialRate = 0, pool, disposal }) {
443
+ function scheduleRow({
444
+ cost,
445
+ accDep = 0,
446
+ taxWdv,
447
+ depRate,
448
+ aiaRate,
449
+ wdaRate,
450
+ specialRate = 0,
451
+ pool,
452
+ singleAssetPool = false,
453
+ privateUse = 0,
454
+ disposal,
455
+ }) {
444
456
  const written = cost > 0;
445
457
  const isNewAsset = aiaRate !== undefined;
458
+ // The car rows multiply the allowances by (1-M), the business share; M is
459
+ // nil on every other row, so the factor is 1 there.
460
+ const businessShare = 1 - privateUse;
446
461
  const row = { E: cost, F: accDep, H: depRate };
447
462
  // A New asset block has no opening net book value column at all, so the row
448
463
  // shows nothing there and the depreciation charge below is not capped.
@@ -459,8 +474,8 @@ function scheduleRow({ cost, accDep = 0, taxWdv, depRate, aiaRate, wdaRate, spec
459
474
  row.O = taxWdv === undefined ? SHEET_BLANK : taxWdv;
460
475
  row.Q = SHEET_BLANK;
461
476
  const claimsWritingDown = typeof row.O === "number" && row.O > 0;
462
- row.R = claimsWritingDown && !specialPool ? row.O * wdaRate : SHEET_BLANK;
463
- row.AC = claimsWritingDown && specialPool ? row.O * specialRate : SHEET_BLANK;
477
+ row.R = claimsWritingDown && !specialPool ? row.O * wdaRate * businessShare : SHEET_BLANK;
478
+ row.AC = claimsWritingDown && specialPool ? row.O * specialRate * businessShare : SHEET_BLANK;
464
479
  row.S = claimsWritingDown ? row.O - sheetNumber(row.R) - sheetNumber(row.AC) : SHEET_BLANK;
465
480
  } else {
466
481
  row.O = SHEET_BLANK;
@@ -470,6 +485,16 @@ function scheduleRow({ cost, accDep = 0, taxWdv, depRate, aiaRate, wdaRate, spec
470
485
  row.AC = SHEET_BLANK;
471
486
  row.S = written ? cost - sheetNumber(row.Q) : SHEET_BLANK;
472
487
  }
488
+ // Column AD marks a row "P" for a single asset pool: AE and AF repeat its
489
+ // allowance by rate and AG its written-down value, so the small pools
490
+ // test can leave the row out. The row's own R, AC and S do not change.
491
+ row.AE = singleAssetPool && !specialPool ? sheetNumber(row.R) : SHEET_BLANK;
492
+ row.AF = singleAssetPool && specialPool ? sheetNumber(row.AC) : SHEET_BLANK;
493
+ row.AG = singleAssetPool ? sheetNumber(row.S) : SHEET_BLANK;
494
+ // AH and AI split the pooled written-down value by rate pool, single asset
495
+ // pool rows left out, so the small pools test can run on each pool alone.
496
+ row.AH = !singleAssetPool && !specialPool ? sheetNumber(row.S) : SHEET_BLANK;
497
+ row.AI = !singleAssetPool && specialPool ? sheetNumber(row.S) : SHEET_BLANK;
473
498
 
474
499
  row.V = disposal ? disposal.proceeds : SHEET_BLANK;
475
500
  row.W = disposal ? cost : SHEET_BLANK;
@@ -481,8 +506,8 @@ function scheduleRow({ cost, accDep = 0, taxWdv, depRate, aiaRate, wdaRate, spec
481
506
  row.Y = SHEET_ERROR;
482
507
  row.Z = SHEET_BLANK;
483
508
  } else {
484
- row.Y = disposal.proceeds < row.S ? row.S - disposal.proceeds : SHEET_BLANK;
485
- row.Z = disposal.proceeds > row.S ? disposal.proceeds - row.S : SHEET_BLANK;
509
+ row.Y = disposal.proceeds < row.S ? (row.S - disposal.proceeds) * businessShare : SHEET_BLANK;
510
+ row.Z = disposal.proceeds > row.S ? (disposal.proceeds - row.S) * businessShare : SHEET_BLANK;
486
511
  }
487
512
  return row;
488
513
  }
@@ -500,7 +525,29 @@ function carry(inputs, compute) {
500
525
  return inputs.some((value) => value === SHEET_ERROR) ? SHEET_ERROR : compute();
501
526
  }
502
527
 
503
- const SCHEDULE_TOTAL_COLUMNS = ["E", "F", "G", "I", "J", "K", "O", "Q", "R", "S", "V", "W", "X", "Y", "Z", "AC"];
528
+ const SCHEDULE_TOTAL_COLUMNS = [
529
+ "E",
530
+ "F",
531
+ "G",
532
+ "I",
533
+ "J",
534
+ "K",
535
+ "O",
536
+ "Q",
537
+ "R",
538
+ "S",
539
+ "V",
540
+ "W",
541
+ "X",
542
+ "Y",
543
+ "Z",
544
+ "AC",
545
+ "AE",
546
+ "AF",
547
+ "AG",
548
+ "AH",
549
+ "AI",
550
+ ];
504
551
 
505
552
  function scheduleTotals(rows) {
506
553
  const totals = {};
@@ -546,21 +593,34 @@ function buildSchedule(scenario, taxData, rate) {
546
593
  });
547
594
 
548
595
  const existingRows = [];
596
+ // The motor rows' own single asset pool cells, keyed by cell reference:
597
+ // the private use share the writer puts in M, the marker in AD, and the
598
+ // row's AE and AG, so a reconciliation that reads the marked row finds
599
+ // the same figures here.
600
+ const motorRowCells = {};
549
601
  for (const [category, block] of Object.entries(EXISTING_ASSET_BLOCKS)) {
550
- for (const asset of assetsByCategory[category].slice(0, block.rows.length)) {
551
- existingRows.push(
552
- scheduleRow({
553
- cost: asset.cost,
554
- accDep: asset.acc_dep || 0,
555
- taxWdv: asset.tax_wdv,
556
- depRate: depreciation[block.rateKey] ?? 0,
557
- wdaRate,
558
- specialRate,
559
- pool: asset.pool,
560
- disposal: disposalByAsset.get(asset),
561
- }),
562
- );
563
- }
602
+ assetsByCategory[category].slice(0, block.rows.length).forEach((asset, index) => {
603
+ const row = scheduleRow({
604
+ cost: asset.cost,
605
+ accDep: asset.acc_dep || 0,
606
+ taxWdv: asset.tax_wdv,
607
+ depRate: depreciation[block.rateKey] ?? 0,
608
+ wdaRate,
609
+ specialRate,
610
+ pool: asset.pool,
611
+ singleAssetPool: asset.single_asset_pool === true,
612
+ privateUse: category === "motor" ? asset.private_use || 0 : 0,
613
+ disposal: disposalByAsset.get(asset),
614
+ });
615
+ existingRows.push(row);
616
+ if (category === "motor") {
617
+ const rowNumber = block.rows[index];
618
+ motorRowCells[`M${rowNumber}`] = asset.private_use || 0;
619
+ motorRowCells[`AD${rowNumber}`] = asset.single_asset_pool ? "P" : SHEET_BLANK;
620
+ motorRowCells[`AE${rowNumber}`] = row.AE;
621
+ motorRowCells[`AG${rowNumber}`] = row.AG;
622
+ }
623
+ });
564
624
  }
565
625
 
566
626
  const newRows = capitalPurchases
@@ -569,7 +629,7 @@ function buildSchedule(scenario, taxData, rate) {
569
629
 
570
630
  const existing = scheduleTotals(existingRows);
571
631
  const additions = scheduleTotals(newRows);
572
- return { existing, additions, totals: addScheduleTotals(existing, additions) };
632
+ return { existing, additions, totals: addScheduleTotals(existing, additions), motorRowCells };
573
633
  }
574
634
 
575
635
  // ── Hire purchase ──────────────────────────────────────────────────────────
@@ -960,7 +1020,7 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
960
1020
  const boxes32to45Total = sheetSum(Object.values(seFullDisallowable)) + pl.B34;
961
1021
 
962
1022
  // ── The fixed asset workbook ──
963
- const scheduleCells = { E57: schedule.existing.E, E110: schedule.additions.E, AC4: admin.G6 };
1023
+ const scheduleCells = { E57: schedule.existing.E, E110: schedule.additions.E, AC4: admin.G6, ...schedule.motorRowCells };
964
1024
  for (const column of SCHEDULE_TOTAL_COLUMNS) scheduleCells[`${column}1`] = schedule.totals[column];
965
1025
  const faReconciliation = {
966
1026
  E11: schedule.additions.E,
@@ -1023,7 +1083,21 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
1023
1083
  const scheduleQ = schedule.totals.Q;
1024
1084
  const scheduleR = schedule.totals.R;
1025
1085
  const scheduleAC = schedule.totals.AC;
1026
- const scheduleS = schedule.totals.S;
1086
+ // The small pools write-off runs per rate pool over the pooled balance
1087
+ // before this year's allowance, the single asset pool rows (AE, AF) left
1088
+ // out: the main pool's is R1-AE1+AH1 and the special rate pool's
1089
+ // AC1-AF1+AI1, and each pool under £1,000 writes off its own written-down
1090
+ // value. The sheet reads
1091
+ // IF((R1-AE1+AH1)<1000,AH1,0)+IF((AC1-AF1+AI1)<1000,AI1,0) on
1092
+ // SE Full!O144 and SE Short!D85.
1093
+ const scheduleAE = schedule.totals.AE;
1094
+ const scheduleAF = schedule.totals.AF;
1095
+ const scheduleAH = schedule.totals.AH;
1096
+ const scheduleAI = schedule.totals.AI;
1097
+ const smallPoolsWriteOff = carry(
1098
+ [scheduleR, scheduleAC, scheduleAE, scheduleAF, scheduleAH, scheduleAI],
1099
+ () => (scheduleR - scheduleAE + scheduleAH < 1000 ? scheduleAH : 0) + (scheduleAC - scheduleAF + scheduleAI < 1000 ? scheduleAI : 0),
1100
+ );
1027
1101
  const scheduleY = schedule.totals.Y;
1028
1102
  const scheduleZ = schedule.totals.Z;
1029
1103
 
@@ -1073,7 +1147,7 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
1073
1147
  seShort.O80 = carry([scheduleR, scheduleY, scheduleAC], () =>
1074
1148
  scheduleR + scheduleY + scheduleAC > 0 ? scheduleR + scheduleY + scheduleAC : 0,
1075
1149
  );
1076
- seShort.D85 = carry([scheduleR, scheduleS], () => (scheduleR + scheduleS < 1000 ? scheduleS : 0));
1150
+ seShort.D85 = smallPoolsWriteOff;
1077
1151
  seShort.O85 = carry([scheduleZ], () => (scheduleZ > 0 ? scheduleZ : 0));
1078
1152
  seShort.D94 = goodsForOwnUse;
1079
1153
  const shortAllowances = [seShort.D71, seShort.O85, seShort.D94, seShort.O71, seShort.D80, seShort.D85, seShort.O80];
@@ -1155,7 +1229,7 @@ export function calculateSeCells(book, lines, taxData, scenario = {}) {
1155
1229
  seFull.D210 = stated(annualAdjustments.accountingAdjustment);
1156
1230
  // Box 50 carries the whole writing down allowance the schedule claims.
1157
1231
  seFull.D144 = carry([scheduleR], () => scheduleR);
1158
- seFull.O144 = carry([scheduleR, scheduleS], () => (scheduleR + scheduleS < 1000 ? scheduleS : 0));
1232
+ seFull.O144 = smallPoolsWriteOff;
1159
1233
  seFull.O149 = scheduleY;
1160
1234
  seFull.O154 = carry([seFull.D139, seFull.D144, seFull.O144, seFull.O149], () =>
1161
1235
  sheetSum([seFull.D139, seFull.D144, seFull.D147, seFull.D152, seFull.D156, seFull.D160, seFull.O139, seFull.O144, seFull.O149]),
@@ -563,6 +563,30 @@ export function diyaGlToScenario(book, lines, product) {
563
563
  const opening = { category, description: asset.description, cost: asset.cost, acc_dep: asset.accumulatedDepreciation };
564
564
  if (asset.taxWrittenDownValue !== undefined) opening.tax_wdv = asset.taxWrittenDownValue;
565
565
  if (asset.capitalAllowancePool === "special") opening.pool = "special";
566
+ // A single asset pool is a fact the book states, never one the loader
567
+ // infers: private use puts an asset in its own pool (CAA 2001 s.206),
568
+ // so a private use share without the marker is a contradiction. The
569
+ // Schedule applies the private use share on the motor rows only, and
570
+ // the Company Schedule has neither column.
571
+ if (asset.singleAssetPool || asset.privateUseProportion > 0) {
572
+ if (product !== "se") {
573
+ throw new Error(
574
+ `Fixed asset ${asset.assetID} states a single asset pool or private use, which the Company Schedule has no column for`,
575
+ );
576
+ }
577
+ if (category !== "motor") {
578
+ throw new Error(
579
+ `Fixed asset ${asset.assetID} states a single asset pool or private use on class "${asset.class}", which the Schedule applies to motor vehicles only`,
580
+ );
581
+ }
582
+ if (!asset.singleAssetPool) {
583
+ throw new Error(
584
+ `Fixed asset ${asset.assetID} states privateUseProportion without singleAssetPool; a privately used asset is its own pool`,
585
+ );
586
+ }
587
+ opening.single_asset_pool = true;
588
+ if (asset.privateUseProportion > 0) opening.private_use = asset.privateUseProportion;
589
+ }
566
590
  return opening;
567
591
  });
568
592
  if (openingFixedAssets.length > 0) scenario.opening_fixed_assets = openingFixedAssets;
@@ -12,9 +12,9 @@
12
12
 
13
13
  export const PROVENANCE_DATA = {
14
14
  formatVersion: "diya-gl/1",
15
- engineVersion: "1.2.13+f5b44cdf",
15
+ engineVersion: "1.2.15+27b706de",
16
16
  taxDataHash: "4ecdbe985d22",
17
- reconciledCommit: "c910fdeefbe4f2f71ae9ec5f5d4a713783a1c0c7",
17
+ reconciledCommit: "54e8731a91279975e61b1a232fbb67fda89515f0",
18
18
  templates: {
19
19
  bst: {
20
20
  hash: "0d003e10e00c",
@@ -25,8 +25,8 @@ export const PROVENANCE_DATA = {
25
25
  scorecard: "686 passed, 0 warnings, 0 failed",
26
26
  },
27
27
  se: {
28
- hash: "15d3b753f7a4",
29
- scorecard: "7956 passed, 0 warnings, 0 failed",
28
+ hash: "3c88a8ba129b",
29
+ scorecard: "7966 passed, 0 warnings, 0 failed",
30
30
  },
31
31
  ltd: {
32
32
  hash: "caa14632083b",
@@ -1282,6 +1282,15 @@ export function formatScenarioToml(metadata, grouped, expected) {
1282
1282
  parts.push("# The special rate (6%) pool, SA103F box 51; the Schedule marks the row S in column AB.");
1283
1283
  parts.push(`pool = "special"`);
1284
1284
  }
1285
+ if (asset.single_asset_pool) {
1286
+ parts.push("# A single asset pool (private use or a short-life asset election): the Schedule marks the row P in");
1287
+ parts.push("# column AD, and the API files its allowance as capitalAllowanceSingleAssetPool.");
1288
+ parts.push("single_asset_pool = true");
1289
+ }
1290
+ if (asset.private_use !== undefined) {
1291
+ parts.push("# The share of use that is private, which reduces the allowances (Schedule column M on the car rows).");
1292
+ parts.push(`private_use = ${asset.private_use}`);
1293
+ }
1285
1294
  parts.push("");
1286
1295
  }
1287
1296
  }
@@ -1167,6 +1167,11 @@ const SCHEDULE_DISPOSAL_COLUMNS = { disposedDate: "U", disposalProceeds: "V" };
1167
1167
  // Column AB, "S" on a row whose tax written-down value sits in the special
1168
1168
  // rate pool (the Self Employed Schedule's AC column, SA103F box 51).
1169
1169
  const SCHEDULE_SPECIAL_RATE_POOL_MARKER = { column: "AB", value: "S" };
1170
+ // The single asset pool marker beside it, and the private use share the car
1171
+ // rows carry in column M. The Company Schedule has neither column, so its
1172
+ // register never sets either field.
1173
+ const SCHEDULE_SINGLE_ASSET_POOL_MARKER = { column: "AD", value: "P" };
1174
+ const SCHEDULE_PRIVATE_USE_COLUMN = "M";
1170
1175
 
1171
1176
  // The single-file products keep one Fixed Assets sheet inside the workbook
1172
1177
  // and their writers fill only its in-year addition block: BST the Plant &
@@ -1262,6 +1267,13 @@ async function fixedAssetRegisterFrom(set, product) {
1262
1267
  if (textAt(xml, `${SCHEDULE_SPECIAL_RATE_POOL_MARKER.column}${row}`, sharedStrings) === SCHEDULE_SPECIAL_RATE_POOL_MARKER.value) {
1263
1268
  asset.capitalAllowancePool = "special";
1264
1269
  }
1270
+ if (textAt(xml, `${SCHEDULE_SINGLE_ASSET_POOL_MARKER.column}${row}`, sharedStrings) === SCHEDULE_SINGLE_ASSET_POOL_MARKER.value) {
1271
+ asset.singleAssetPool = true;
1272
+ }
1273
+ if (assetClass === "motorVehicles") {
1274
+ const privateUse = numberAt(xml, `${SCHEDULE_PRIVATE_USE_COLUMN}${row}`, sharedStrings);
1275
+ if (privateUse > 0) asset.privateUseProportion = privateUse;
1276
+ }
1265
1277
  assign(asset, "description", textAt(xml, `${SCHEDULE_ASSET_COLUMNS.description}${row}`, sharedStrings));
1266
1278
  assign(asset, "accumulatedDepreciation", numberAt(xml, `${SCHEDULE_ASSET_COLUMNS.accumulatedDepreciation}${row}`, sharedStrings));
1267
1279
  assign(asset, "taxWrittenDownValue", numberAt(xml, `${SCHEDULE_ASSET_COLUMNS.taxWrittenDownValue}${row}`, sharedStrings));
@@ -277,6 +277,17 @@ export const NEW_PLANT_ROWS = [67, 68, 69, 70, 71];
277
277
  // AB38="S" and AC$4, S38 = IF(O38>0,O38-N(R38)-N(AC38)," "), AC1 = AC57+AC110.
278
278
  export const SPECIAL_RATE_POOL_MARKER_COLUMN = "AB";
279
279
  export const SPECIAL_RATE_POOL_MARKER = "S";
280
+ // Column AD on an asset row: "P" keeps the row's tax written-down value in a
281
+ // single asset pool. R, AC and S stay as they are; AE and AF repeat the
282
+ // row's writing down allowance by rate and AG its written-down value, so the
283
+ // small pools test on SE Full!O144 can leave the marked rows out. Column M
284
+ // on the car rows (38 to 48, 91 to 101) is the private use share the R, AC,
285
+ // Y and Z formulas multiply by (1-M). Verified against the template:
286
+ // AE38 = IF(AND(AD38="P",AB38<>"S"),N(R38)," "), AF38 the same with AB38="S"
287
+ // and N(AC38), AG38 = IF(AD38="P",N(S38)," "), AE1 = AE57+AE110.
288
+ export const SINGLE_ASSET_POOL_MARKER_COLUMN = "AD";
289
+ export const SINGLE_ASSET_POOL_MARKER = "P";
290
+ export const PRIVATE_USE_COLUMN = "M";
280
291
 
281
292
  // Hire purchase agreements (Fixedassets.xlsx HPfinance sheet). Only two
282
293
  // rows are available for scenario agreements before the sheet's own
@@ -670,7 +681,7 @@ function composeWrites(scenario, targetStartYear) {
670
681
  skips.push(skipped("openingFixedAsset", assetEntry, why));
671
682
  continue;
672
683
  }
673
- // Written left-to-right (C, then E, then F). setCellValue/setCellString
684
+ // Written left-to-right (C, E, F, M, O, AB, AD). setCellValue/setCellString
674
685
  // in spreadsheet-runner.js replaces a matched cell together with every
675
686
  // self-closing sibling up to the row's next already-closed cell -- an
676
687
  // earlier write onto a later (rightward) column silently deletes any
@@ -682,6 +693,9 @@ function composeWrites(scenario, targetStartYear) {
682
693
  if (asset.description) fa[`C${row}`] = asset.description;
683
694
  fa[`E${row}`] = asset.cost;
684
695
  if (asset.acc_dep) fa[`F${row}`] = asset.acc_dep;
696
+ // The private use share reads on the car rows alone; the loader has
697
+ // already refused it on any other class.
698
+ if (asset.private_use !== undefined && asset.category === "motor") fa[`${PRIVATE_USE_COLUMN}${row}`] = asset.private_use;
685
699
  // Column O is the written down TAX value brought forward, the figure
686
700
  // the capital allowance columns work from. The schedule computes a
687
701
  // disposal's balancing allowance as that value less the sale proceeds,
@@ -689,6 +703,7 @@ function composeWrites(scenario, targetStartYear) {
689
703
  // allowance block, and every figure downstream of it, in error.
690
704
  if (asset.tax_wdv) fa[`O${row}`] = asset.tax_wdv;
691
705
  if (asset.pool === "special") fa[`${SPECIAL_RATE_POOL_MARKER_COLUMN}${row}`] = SPECIAL_RATE_POOL_MARKER;
706
+ if (asset.single_asset_pool) fa[`${SINGLE_ASSET_POOL_MARKER_COLUMN}${row}`] = SINGLE_ASSET_POOL_MARKER;
692
707
  existingAssetRowsUsed[asset.category].push(row);
693
708
  }
694
709
  }
@@ -1262,7 +1277,40 @@ export function multiFileOptions() {
1262
1277
  // E57 and E110 are the schedule's own existing-asset and new-asset
1263
1278
  // cost subtotals; row 1 adds the two. Reading both lets the report
1264
1279
  // state the year's asset movement rather than one closing total.
1265
- Schedule: ["E1", "F1", "G1", "I1", "J1", "K1", "Q1", "R1", "S1", "V1", "W1", "X1", "Y1", "Z1", "AC1", "AC4", "E57", "E110"],
1280
+ // AE1, AF1 and AG1 are the single asset pool totals; AH1 and AI1 the
1281
+ // pooled written-down value by rate pool, which the small pools test
1282
+ // now runs over per pool; M40, AD40, AE40 and AG40 are the fixture's
1283
+ // marked car row (van 38, estate car 39, hatchback 40), read so its
1284
+ // marker and private use share are proved on the row itself.
1285
+ Schedule: [
1286
+ "E1",
1287
+ "F1",
1288
+ "G1",
1289
+ "I1",
1290
+ "J1",
1291
+ "K1",
1292
+ "Q1",
1293
+ "R1",
1294
+ "S1",
1295
+ "V1",
1296
+ "W1",
1297
+ "X1",
1298
+ "Y1",
1299
+ "Z1",
1300
+ "AC1",
1301
+ "AC4",
1302
+ "AE1",
1303
+ "AF1",
1304
+ "AG1",
1305
+ "AH1",
1306
+ "AI1",
1307
+ "E57",
1308
+ "E110",
1309
+ "M40",
1310
+ "AD40",
1311
+ "AE40",
1312
+ "AG40",
1313
+ ],
1266
1314
  FAreconciliation: ["E11", "E13", "E15", "K11", "K13", "K15"],
1267
1315
  // E2 is the long-term-creditors total for the "New Hire Purchase
1268
1316
  // Agreements" block (SUM(E8:E14)); I/J/K on rows 8 and 10 are the
@@ -1653,6 +1701,17 @@ const FIXED_ASSET_CELL_LABELS = {
1653
1701
  X1: "Accumulated depreciation on the assets sold in the year",
1654
1702
  Y1: "Balancing allowance on the disposals",
1655
1703
  Z1: "Balancing charge on the disposals",
1704
+ AC1: "Total special rate writing down allowance claimed (the rows marked S in column AB)",
1705
+ AC4: "Special rate writing down allowance rate, read from Admin",
1706
+ AE1: "Single asset pool writing down allowance at the main rate (the rows marked P in column AD)",
1707
+ AF1: "Single asset pool writing down allowance at the special rate (the rows marked P and S)",
1708
+ AG1: "Single asset pool tax written down value carried forward",
1709
+ AH1: "Main pool tax written down value carried forward, single asset pool rows left out",
1710
+ AI1: "Special rate pool tax written down value carried forward (the rows marked S), single asset pool rows left out",
1711
+ M40: "Private use share of the third motor vehicle brought forward",
1712
+ AD40: "Single asset pool marker on the third motor vehicle brought forward (P)",
1713
+ AE40: "Single asset pool main rate allowance on the third motor vehicle brought forward",
1714
+ AG40: "Single asset pool tax written down value on the third motor vehicle brought forward",
1656
1715
  E57: "Cost of the assets owned at the start of the year",
1657
1716
  E110: "Cost of the assets bought during the year",
1658
1717
  },
@@ -1861,6 +1920,61 @@ export function profitBridge(results) {
1861
1920
  return buildProfitBridge(rows, `${TAX_SHEET}!E5`, num(tax.E5));
1862
1921
  }
1863
1922
 
1923
+ // ── The Schedule's capital allowance pools, from the fixture ───────────────
1924
+
1925
+ // The opening tax written-down values that claim a writing down allowance in
1926
+ // one rate pool, each reduced to its business share: a privately used car
1927
+ // claims on (1 - M), which the Schedule applies on the car rows.
1928
+ function openingPoolAllowance(expected, pool) {
1929
+ return (expected.opening_fixed_assets || [])
1930
+ .filter((asset) => (asset.pool === "special") === (pool === "special"))
1931
+ .reduce((total, asset) => total + (asset.tax_wdv || 0) * (1 - (asset.private_use || 0)), 0);
1932
+ }
1933
+
1934
+ // The small pools write-off the way SE Full!O144 and SE Short!D85 state it:
1935
+ // IF((R1-AE1+AH1)<1000,AH1,0)+IF((AC1-AF1+AI1)<1000,AI1,0). Each rate pool
1936
+ // takes the £1,000 test on its own balance before this year's allowance, the
1937
+ // single asset pool rows left out, and writes off its own written-down value
1938
+ // when it passes.
1939
+ function smallPoolsWriteOff(schedule) {
1940
+ const num = (v) => (typeof v === "number" ? v : 0);
1941
+ const mainBalance = num(schedule.R1) - num(schedule.AE1) + num(schedule.AH1);
1942
+ const specialBalance = num(schedule.AC1) - num(schedule.AF1) + num(schedule.AI1);
1943
+ return (mainBalance < 1000 ? num(schedule.AH1) : 0) + (specialBalance < 1000 ? num(schedule.AI1) : 0);
1944
+ }
1945
+
1946
+ // The same figures from the fixture's own assets, pool by pool. A pooled
1947
+ // opening asset puts its whole tax written-down value into its pool's test
1948
+ // (allowance plus written-down value is the value brought forward) and its
1949
+ // written-down balance after this year's allowance into the write-off; a
1950
+ // single asset pool row puts nothing into either; an asset bought in the
1951
+ // year puts its cost less the annual investment allowance into the main
1952
+ // pool's both.
1953
+ function fixtureSmallPools(expected, taxData, rate) {
1954
+ const wda = taxData.capital_allowances.writing_down_allowance;
1955
+ const specialWda = taxData.capital_allowances.writing_down_allowance_special || 0;
1956
+ const aia = taxData.capital_allowances.annual_investment_allowance ?? 0;
1957
+ const pools = { main: { balance: 0, writtenDown: 0 }, special: { balance: 0, writtenDown: 0 } };
1958
+ for (const asset of expected.opening_fixed_assets || []) {
1959
+ if (asset.single_asset_pool || !(asset.tax_wdv > 0)) continue;
1960
+ const pool = asset.pool === "special" ? pools.special : pools.main;
1961
+ const share = 1 - (asset.private_use || 0);
1962
+ const allowance = asset.tax_wdv * (asset.pool === "special" ? specialWda : wda) * share;
1963
+ pool.balance += asset.tax_wdv;
1964
+ pool.writtenDown += asset.tax_wdv - allowance;
1965
+ }
1966
+ for (const transactions of Object.values(expected.purchases || {})) {
1967
+ for (const tx of transactions) {
1968
+ if (tx.code !== "fa") continue;
1969
+ const cost = netOfVat(tx.amount, rate);
1970
+ pools.main.balance += cost - cost * aia;
1971
+ pools.main.writtenDown += cost - cost * aia;
1972
+ }
1973
+ }
1974
+ const writeOff = (pool) => (pool.balance < 1000 ? pool.writtenDown : 0);
1975
+ return { ...pools, writeOff: writeOff(pools.main) + writeOff(pools.special) };
1976
+ }
1977
+
1864
1978
  // ── Journal category VAT netting ───────────────────────────────────────────
1865
1979
 
1866
1980
  // A journal row takes its own VAT off its own gross and rounds nothing
@@ -2543,10 +2657,13 @@ export function checkCompliance(results, expected, taxData, calculateExpectedTax
2543
2657
  check("SA103F box 49 annual investment allowance (D139) = Schedule Q1", num(seFull.D139), Math.max(0, num(returnSchedule.Q1)));
2544
2658
  check("SA103F box 50 capital allowances at 18% (D144) = Schedule R1", num(seFull.D144), num(returnSchedule.R1));
2545
2659
  check("SA103F box 51 capital allowances at 6% (D147) = Schedule AC1", num(seFull.D147), num(returnSchedule.AC1));
2660
+ // The small pools write-off runs per rate pool over the pooled balance
2661
+ // alone, the single asset pool rows (AE1, AF1) left out:
2662
+ // O144 = IF((R1-AE1+AH1)<1000,AH1,0)+IF((AC1-AF1+AI1)<1000,AI1,0).
2546
2663
  check(
2547
- "SA103F box 55 100% and other enhanced capital allowances (O144) = Schedule S1 while the small pool balance is under £1,000",
2664
+ "SA103F box 55 100% and other enhanced capital allowances (O144) = each pool's Schedule written-down value (AH1, AI1) while that pool's balance is under £1,000",
2548
2665
  num(seFull.O144),
2549
- num(returnSchedule.R1) + num(returnSchedule.S1) < 1000 ? num(returnSchedule.S1) : 0,
2666
+ smallPoolsWriteOff(returnSchedule),
2550
2667
  );
2551
2668
  check("SA103F box 56 allowances on sale or cessation (O149) = Schedule Y1", num(seFull.O149), num(returnSchedule.Y1));
2552
2669
  check("SA103F box 59 balancing charge (O160) = Schedule Z1", num(seFull.O160), num(returnSchedule.Z1));
@@ -2561,20 +2678,27 @@ export function checkCompliance(results, expected, taxData, calculateExpectedTax
2561
2678
  // Each pool against the scenario's own assets: the main pool's boxes 50
2562
2679
  // and the special rate pool's box 51, each at its own year's rate, so an
2563
2680
  // asset marked for one pool that lands in the other moves both boxes.
2681
+ // A privately used car claims its allowance on the business share alone
2682
+ // (Schedule column M), single asset pool or not, so the anchor carries
2683
+ // that factor row by row.
2564
2684
  if (taxData?.capital_allowances && expected.opening_fixed_assets) {
2565
- const poolTaxWdv = (pool) =>
2566
- expected.opening_fixed_assets
2567
- .filter((asset) => (asset.pool === "special") === (pool === "special"))
2568
- .reduce((total, asset) => total + (asset.tax_wdv || 0), 0);
2569
2685
  check(
2570
- "SA103F box 50 capital allowances at 18% (D144) = the scenario's opening tax written-down values at the year's writing down rate",
2686
+ "SA103F box 50 capital allowances at 18% (D144) = the scenario's opening tax written-down values at the year's writing down rate, less private use",
2571
2687
  num(seFull.D144),
2572
- poolTaxWdv("main") * taxData.capital_allowances.writing_down_allowance,
2688
+ openingPoolAllowance(expected, "main") * taxData.capital_allowances.writing_down_allowance,
2573
2689
  );
2574
2690
  check(
2575
- "SA103F box 51 capital allowances at 6% (D147) = the scenario's special rate pool tax written-down values at the year's special rate",
2691
+ "SA103F box 51 capital allowances at 6% (D147) = the scenario's special rate pool tax written-down values at the year's special rate, less private use",
2576
2692
  num(seFull.D147),
2577
- poolTaxWdv("special") * (taxData.capital_allowances.writing_down_allowance_special || 0),
2693
+ openingPoolAllowance(expected, "special") * (taxData.capital_allowances.writing_down_allowance_special || 0),
2694
+ );
2695
+ // The same rule the sheet applies, run over the fixture's own pools:
2696
+ // a sheet whose R1, AC1, AE1, AF1, AH1 and AI1 agree with each other
2697
+ // but not with the assets fails here.
2698
+ check(
2699
+ "SA103F box 55 100% and other enhanced capital allowances (O144) = the small pools write-off computed per pool from the scenario's own pooled assets",
2700
+ num(seFull.O144),
2701
+ fixtureSmallPools(expected, taxData, rate).writeOff,
2578
2702
  );
2579
2703
  }
2580
2704
 
@@ -2843,14 +2967,83 @@ export function checkCompliance(results, expected, taxData, calculateExpectedTax
2843
2967
  taxData.capital_allowances.writing_down_allowance_special || 0,
2844
2968
  0.0001,
2845
2969
  );
2846
- const specialPoolWdv = expected.opening_fixed_assets
2847
- .filter((asset) => asset.pool === "special")
2848
- .reduce((t, asset) => t + (asset.tax_wdv || 0), 0);
2849
2970
  check(
2850
- "Fixed assets: Schedule special rate pool allowance (AC1) = the scenario's special rate assets at the year's special rate",
2971
+ "Fixed assets: Schedule special rate pool allowance (AC1) = the scenario's special rate assets at the year's special rate, less private use",
2851
2972
  sched.AC1 || 0,
2852
- specialPoolWdv * (taxData.capital_allowances.writing_down_allowance_special || 0),
2973
+ openingPoolAllowance(expected, "special") * (taxData.capital_allowances.writing_down_allowance_special || 0),
2974
+ );
2975
+
2976
+ // The pooled written-down values by rate pool (AH, AI), and each
2977
+ // pool's balance before this year's allowance, which is what the small
2978
+ // pools test runs over: the main pool's from the van and the year's
2979
+ // additions, the special rate pool's from the estate car's 9,000
2980
+ // alone, so a pool that counted the other's balance, or a single asset
2981
+ // pool's, moves here.
2982
+ const pools = fixtureSmallPools(expected, taxData, rate);
2983
+ check(
2984
+ "Fixed assets: Schedule main pool written down value (AH1) = the scenario's pooled main rate assets' tax written-down values less this year's allowance, plus additions less their annual investment allowance",
2985
+ sched.AH1 || 0,
2986
+ pools.main.writtenDown,
2987
+ );
2988
+ check(
2989
+ "Fixed assets: Schedule special rate pool written down value (AI1) = the scenario's pooled special rate assets' tax written-down values less this year's allowance",
2990
+ sched.AI1 || 0,
2991
+ pools.special.writtenDown,
2992
+ );
2993
+ check(
2994
+ "Fixed assets: main pool balance before this year's allowance (R1 - AE1 + AH1) = the scenario's pooled main rate assets' tax written-down values plus additions less their annual investment allowance",
2995
+ num(sched.R1) - num(sched.AE1) + num(sched.AH1),
2996
+ pools.main.balance,
2853
2997
  );
2998
+ check(
2999
+ "Fixed assets: special rate pool balance before this year's allowance (AC1 - AF1 + AI1) = the scenario's pooled special rate assets' tax written-down values",
3000
+ num(sched.AC1) - num(sched.AF1) + num(sched.AI1),
3001
+ pools.special.balance,
3002
+ );
3003
+
3004
+ // The single asset pools (column AD marked P): each marked asset's
3005
+ // allowance at its own rate on its business share, and its written-down
3006
+ // value less that allowance, anchored on the fixture's assets.
3007
+ const wda = taxData.capital_allowances.writing_down_allowance;
3008
+ const specialWda = taxData.capital_allowances.writing_down_allowance_special || 0;
3009
+ const singleAssets = expected.opening_fixed_assets.filter((asset) => asset.single_asset_pool);
3010
+ const singleAllowance = (asset) =>
3011
+ (asset.tax_wdv || 0) * (asset.pool === "special" ? specialWda : wda) * (1 - (asset.private_use || 0));
3012
+ check(
3013
+ "Fixed assets: Schedule single asset pool main rate allowance (AE1) = the scenario's single asset pool main rate assets at the year's rate, less private use",
3014
+ sched.AE1 || 0,
3015
+ singleAssets.filter((asset) => asset.pool !== "special").reduce((t, asset) => t + singleAllowance(asset), 0),
3016
+ );
3017
+ check(
3018
+ "Fixed assets: Schedule single asset pool special rate allowance (AF1) = the scenario's single asset pool special rate assets at the year's special rate, less private use",
3019
+ sched.AF1 || 0,
3020
+ singleAssets.filter((asset) => asset.pool === "special").reduce((t, asset) => t + singleAllowance(asset), 0),
3021
+ );
3022
+ check(
3023
+ "Fixed assets: Schedule single asset pool written down value (AG1) = the scenario's single asset pool assets' tax written-down values less this year's allowance",
3024
+ sched.AG1 || 0,
3025
+ singleAssets.reduce((t, asset) => t + (asset.tax_wdv || 0) - singleAllowance(asset), 0),
3026
+ );
3027
+ // The marker and the private use share on the row itself, for each
3028
+ // motor asset the fixture declares, in the order the writer fills the
3029
+ // block.
3030
+ expected.opening_fixed_assets
3031
+ .filter((asset) => asset.category === "motor")
3032
+ .forEach((asset, index) => {
3033
+ const row = EXISTING_ASSET_ROWS.motor[index];
3034
+ if (row === undefined || !(`AD${row}` in sched)) return;
3035
+ checkText(
3036
+ `Fixed assets: Schedule single asset pool marker (AD${row}) = ${asset.single_asset_pool ? "P" : "blank"} for the scenario's motor asset ${index + 1}`,
3037
+ typeof sched[`AD${row}`] === "string" ? sched[`AD${row}`].trim() : "",
3038
+ asset.single_asset_pool ? SINGLE_ASSET_POOL_MARKER : "",
3039
+ );
3040
+ check(
3041
+ `Fixed assets: Schedule private use share (M${row}) = the scenario's motor asset ${index + 1} private use`,
3042
+ num(sched[`M${row}`]),
3043
+ asset.private_use || 0,
3044
+ 0.0001,
3045
+ );
3046
+ });
2854
3047
  }
2855
3048
 
2856
3049
  // 3. P&L depreciation and disposal lines carry the Schedule's own
@@ -483,6 +483,17 @@
483
483
  "default": "main",
484
484
  "description": "Which writing down pool the asset's tax written-down value sits in. special is the 6% pool (cars with higher CO2 emissions, integral features, long-life assets), SA103F box 51; the Self Employed Schedule marks the row S in column AB and claims the allowance in column AC. Absent means the main pool, box 50."
485
485
  },
486
+ "singleAssetPool": {
487
+ "type": "boolean",
488
+ "default": false,
489
+ "description": "The asset's tax written-down value is its own pool: an asset with private use (CAA 2001 s.206) or under a short-life asset election (ss.83-86). Its writing down allowance still prints in SA103F box 50 or 51 by rate, but the API files it as capitalAllowanceSingleAssetPool and the small pools allowance never applies to it. The Self Employed Schedule marks the row P in column AD and carries the allowance and the written-down value in columns AE to AG. Self Employed only."
490
+ },
491
+ "privateUseProportion": {
492
+ "type": "number",
493
+ "minimum": 0,
494
+ "maximum": 1,
495
+ "description": "The share of the asset's use that is not business use, which reduces its writing down and balancing allowances (the Self Employed Schedule's column M on the car rows). An asset with private use is a single asset pool, so singleAssetPool must be true whenever this is above nil. Self Employed motor vehicles only."
496
+ },
486
497
  "description": {
487
498
  "type": "string"
488
499
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diy-accounting-uk/diya-gl",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
4
4
  "type": "module",
5
5
  "description": "Recalculate, read and write DIYA-GL books from the command line",
6
6
  "license": "Apache-2.0",