@classytic/ca-tax 0.0.2 → 0.0.12

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/t2.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { rt as SCHEDULE_1_LINE_BY_NUMBER } from "./forms.mjs";
1
+ import { kt as AT1_SCHEDULE_21_POOLS, rt as SCHEDULE_1_LINE_BY_NUMBER } from "./forms.mjs";
2
2
  //#region src/t2/rates/rate-book.ts
3
3
  /**
4
4
  * Resolve the table in effect for `taxYear` — the entry with the greatest
@@ -254,15 +254,25 @@ function computeAlbertaTax(input, rates = AB_TAX_2024) {
254
254
  const AT1_SCHEDULES_WITH_BUILDERS = Object.freeze([
255
255
  "001",
256
256
  "002",
257
+ "003",
258
+ "004",
259
+ "005",
260
+ "006",
261
+ "007",
262
+ "008",
263
+ "009",
257
264
  "010",
265
+ "011",
258
266
  "012",
259
267
  "013",
268
+ "015",
260
269
  "016",
261
270
  "017",
262
271
  "018",
263
272
  "020",
264
273
  "021",
265
- "029"
274
+ "029",
275
+ "4970"
266
276
  ]);
267
277
  /**
268
278
  * Schedules the engine computes but cannot yet file.
@@ -435,6 +445,15 @@ function schedule18Values(result) {
435
445
  * Field ids read off the live, TRA-certified form
436
446
  * (`research/sources/tra-forms/pdf/AT1SCH29-*.pdf`, Rev. 2026-06):
437
447
  *
448
+ * Page 1 — Eligible Expenditures, filed only when the caller supplies
449
+ * `eligible` (from `computeIegEligibleExpenditures` in
450
+ * `schedule29-eligible-expenditures.ts`):
451
+ * 003 federal amount of qualified/current SR&ED expenditures (T661 line
452
+ * 559 or 557 — see `iegT661SourceLine`)
453
+ * 005/007/009/011/025 the five adjustments; 031 = 005 − 007 + 009 + 011 + 025
454
+ * 040 primary field of science or technology (1–4), when the caller
455
+ * supplies `primaryFieldCode`
456
+ *
438
457
  * Page 1/2 — the grant itself:
439
458
  * 110 8% of the capped expenditures
440
459
  * 112 12% of the increment above base — the non-associated path. Filed
@@ -477,12 +496,22 @@ function schedule18Values(result) {
477
496
  * band. See `schedule29-ieg.ts` for why 128 exists and the one unresolved
478
497
  * discrepancy in how 130 is meant to be computed.
479
498
  */
480
- function schedule29Values(result, agreement) {
499
+ function schedule29Values(result, agreement, eligible, primaryFieldCode) {
481
500
  const values = [];
482
501
  const put = (fieldId, value, occurrence = 1) => values.push({
483
502
  lineItemId: at1LineItemId("029", fieldId, occurrence),
484
503
  value
485
504
  });
505
+ if (eligible) {
506
+ put("003", eligible.federalAmount);
507
+ put("005", eligible.albertaPortion);
508
+ if (eligible.federalProxyAmount > 0) put("007", eligible.federalProxyAmount);
509
+ if (eligible.albertaProxyAmount > 0) put("009", eligible.albertaProxyAmount);
510
+ if (eligible.iegReducingFederalExpenditure > 0) put("011", eligible.iegReducingFederalExpenditure);
511
+ if (eligible.repaymentOrContractPayment > 0) put("025", eligible.repaymentOrContractPayment);
512
+ put("031", eligible.totalEligibleExpenditures);
513
+ }
514
+ if (primaryFieldCode !== void 0) put("040", primaryFieldCode);
486
515
  put("110", result.creditAtBaseRate);
487
516
  put(result.enhancedRateBasis === "associated" ? "125" : "112", result.creditAtEnhancedRate);
488
517
  put("126", result.taxableCapital);
@@ -524,6 +553,70 @@ function schedule29Values(result, agreement) {
524
553
  };
525
554
  }
526
555
  /**
556
+ * A separate attachment from Schedule 29 itself, one occurrence per Alberta
557
+ * SR&ED project (101/103/105/107/109/111/113), a TOTAL row (repeats the same
558
+ * field ids at a fixed trailing occurrence — see below), and the jurisdiction
559
+ * table (135–170, occurrence 1, no per-jurisdiction row structure since each
560
+ * jurisdiction has its own field id already).
561
+ *
562
+ * ⚠ The schedule id (`'4970'`) and line-item id scheme used here are a
563
+ * PLACEHOLDER — TRA's own published Guide confirms the field NUMBERS
564
+ * (verified twice, independently) but not the NetFile wire-format encoding
565
+ * for this attachment. See `at4970-ieg-projects.ts` and `forms/at4970.ts`
566
+ * for the same caveat. Verify before relying on this for live filing.
567
+ *
568
+ * The TOTAL row is filed at occurrence `projects.length + 1` — one past the
569
+ * last project row — so a real project can never collide with it.
570
+ */
571
+ function schedule4970Values(result) {
572
+ const values = [];
573
+ const put = (fieldId, value, occurrence = 1) => values.push({
574
+ lineItemId: `4970${fieldId}${String(occurrence).padStart(3, "0")}`,
575
+ value
576
+ });
577
+ result.projects.forEach((p, i) => {
578
+ const n = i + 1;
579
+ put("101", p.title, n);
580
+ if (p.projectCode !== void 0) put("103", p.projectCode, n);
581
+ put("105", p.albertaPortion, n);
582
+ put("107", p.otherPortion, n);
583
+ put("109", p.salariesAndWages, n);
584
+ if (p.federalProxyAmount > 0) put("111", p.federalProxyAmount, n);
585
+ if (p.albertaProxyAmount > 0) put("113", p.albertaProxyAmount, n);
586
+ });
587
+ const totalRow = result.projects.length + 1;
588
+ put("105", result.totals.albertaPortion, totalRow);
589
+ put("107", result.totals.otherPortion, totalRow);
590
+ put("109", result.totals.salariesAndWages, totalRow);
591
+ put("111", result.totals.federalProxyAmount, totalRow);
592
+ put("113", result.totals.albertaProxyAmount, totalRow);
593
+ const jurisdictionFields = {
594
+ alberta: "135",
595
+ britishColumbia: "137",
596
+ manitoba: "139",
597
+ newBrunswick: "141",
598
+ newfoundlandAndLabrador: "143",
599
+ northwestTerritories: "145",
600
+ novaScotia: "147",
601
+ nunavut: "149",
602
+ ontario: "151",
603
+ princeEdwardIsland: "153",
604
+ quebec: "155",
605
+ saskatchewan: "157",
606
+ yukon: "159",
607
+ other: "161"
608
+ };
609
+ for (const j of result.jurisdictions) {
610
+ const fieldId = jurisdictionFields[j.jurisdiction];
611
+ if (fieldId && j.amountIncurred > 0) put(fieldId, j.amountIncurred);
612
+ }
613
+ if (result.jurisdictionTotal > 0) put("170", result.jurisdictionTotal);
614
+ return {
615
+ scheduleId: "4970",
616
+ values
617
+ };
618
+ }
619
+ /**
527
620
  * The Schedule 21 → Schedule 12 carry-forwards, as the form states them beside
528
621
  * each line:
529
622
  *
@@ -593,38 +686,26 @@ function schedule12Values(input) {
593
686
  values
594
687
  };
595
688
  }
596
- /** Per-pool line numbers, from the live form. */
597
- const S21_POOL_LINES = {
598
- nonCapital: {
599
- opening: "031",
600
- current: "037",
601
- carryBack: "047",
602
- closing: "049"
603
- },
604
- capital: {
605
- opening: "051",
606
- current: "057",
607
- carryBack: "067",
608
- closing: "069"
609
- },
610
- farm: {
611
- opening: "071",
612
- current: "077",
613
- carryBack: "085",
614
- closing: "087"
615
- },
616
- restrictedFarm: {
617
- opening: "091",
618
- current: "097",
619
- carryBack: "105",
620
- closing: "107"
621
- },
622
- listedPersonalProperty: {
623
- opening: "111",
624
- current: "117",
625
- carryBack: "123",
626
- closing: "125"
627
- }
689
+ /** `Schedule21FilingInput` key `AT1_SCHEDULE_21_POOLS` key. */
690
+ const POOL_KEY = {
691
+ nonCapital: "non-capital",
692
+ capital: "capital",
693
+ farm: "farm",
694
+ restrictedFarm: "restricted-farm",
695
+ listedPersonalProperty: "listed-personal"
696
+ };
697
+ /** `AlbertaLossPool` field → the `LossContinuityResult` field it reports. */
698
+ const POOL_FIELD_TO_RESULT_KEY = {
699
+ carriedForward: "openingBalance",
700
+ expired: "expired",
701
+ opening: "balanceAtBeginningOfYear",
702
+ windUpTransfer: "windUpTransfer",
703
+ currentYearLoss: "currentYearLoss",
704
+ appliedAgainstIncome: "appliedCurrentYear",
705
+ section80Adjustment: "section80Adjustment",
706
+ otherAdjustments: "otherAdjustments",
707
+ carryBack: "carriedBack",
708
+ closing: "closingBalance"
628
709
  };
629
710
  function schedule21Values(input) {
630
711
  const values = [];
@@ -633,14 +714,97 @@ function schedule21Values(input) {
633
714
  value
634
715
  });
635
716
  if (input.currentYearNonCapitalLoss !== void 0) put("021", input.currentYearNonCapitalLoss);
636
- for (const [pool, lines] of Object.entries(S21_POOL_LINES)) {
637
- const c = input[pool];
717
+ for (const [inputKey, poolKey] of Object.entries(POOL_KEY)) {
718
+ const c = input[inputKey];
638
719
  if (!c) continue;
639
- put(lines.opening, c.openingBalance);
640
- put(lines.current, c.currentYearLoss);
641
- put(lines.carryBack, c.carriedBack);
642
- put(lines.closing, c.closingBalance);
720
+ const pool = AT1_SCHEDULE_21_POOLS.find((p) => p.key === poolKey);
721
+ for (const [field, resultKey] of Object.entries(POOL_FIELD_TO_RESULT_KEY)) {
722
+ const line = pool[field];
723
+ if (!line) continue;
724
+ put(line, c[resultKey]);
725
+ }
643
726
  }
727
+ input.limitedPartnershipLosses?.rows.forEach((r, i) => {
728
+ const n = i + 1;
729
+ if (r.identifier !== void 0) values.push({
730
+ lineItemId: at1LineItemId("021", "131", n),
731
+ value: r.identifier
732
+ });
733
+ values.push({
734
+ lineItemId: at1LineItemId("021", "133", n),
735
+ value: r.precedingYearBalance
736
+ });
737
+ values.push({
738
+ lineItemId: at1LineItemId("021", "135", n),
739
+ value: r.transferredOnWindUp
740
+ });
741
+ values.push({
742
+ lineItemId: at1LineItemId("021", "137", n),
743
+ value: r.currentYearLoss
744
+ });
745
+ values.push({
746
+ lineItemId: at1LineItemId("021", "139", n),
747
+ value: r.applied
748
+ });
749
+ values.push({
750
+ lineItemId: at1LineItemId("021", "141", n),
751
+ value: r.closingBalance
752
+ });
753
+ });
754
+ input.nonCapitalByYearOfOrigin?.rows.forEach((r, i) => {
755
+ const n = i + 1;
756
+ values.push({
757
+ lineItemId: at1LineItemId("021", "151", n),
758
+ value: r.yearIndex
759
+ });
760
+ if (r.taxYearEnd !== void 0) values.push({
761
+ lineItemId: at1LineItemId("021", "153", n),
762
+ value: r.taxYearEnd
763
+ });
764
+ values.push({
765
+ lineItemId: at1LineItemId("021", "155", n),
766
+ value: r.balanceAtBeginning
767
+ });
768
+ values.push({
769
+ lineItemId: at1LineItemId("021", "157", n),
770
+ value: r.lossIncurred
771
+ });
772
+ values.push({
773
+ lineItemId: at1LineItemId("021", "159", n),
774
+ value: r.adjustments
775
+ });
776
+ values.push({
777
+ lineItemId: at1LineItemId("021", "165", n),
778
+ value: r.carriedBack
779
+ });
780
+ values.push({
781
+ lineItemId: at1LineItemId("021", "167", n),
782
+ value: r.applied
783
+ });
784
+ values.push({
785
+ lineItemId: at1LineItemId("021", "169", n),
786
+ value: r.balanceAtEnd
787
+ });
788
+ });
789
+ input.otherLossesByYearOfOrigin?.rows.forEach((r, i) => {
790
+ const n = i + 1;
791
+ values.push({
792
+ lineItemId: at1LineItemId("021", "181", n),
793
+ value: r.yearIndex
794
+ });
795
+ values.push({
796
+ lineItemId: at1LineItemId("021", "183", n),
797
+ value: r.farmLosses
798
+ });
799
+ values.push({
800
+ lineItemId: at1LineItemId("021", "185", n),
801
+ value: r.restrictedFarmLosses
802
+ });
803
+ if (r.yearIndex <= 7) values.push({
804
+ lineItemId: at1LineItemId("021", "187", n),
805
+ value: r.listedPersonalPropertyLosses
806
+ });
807
+ });
644
808
  return {
645
809
  scheduleId: "021",
646
810
  values
@@ -717,129 +881,2488 @@ function schedule10Values(input) {
717
881
  ], input.nonCapital.carrybacks)) put(f, c.amount);
718
882
  put("010", input.nonCapital.remainingLoss);
719
883
  }
720
- if (input.capital) {
721
- const rate = input.inclusionRate ?? .5;
722
- put("042", input.capital.currentYearLoss);
723
- for (const [f, c] of zip([
724
- "044",
725
- "046",
726
- "048"
727
- ], input.capital.carrybacks)) put(f, Math.round(rate * c.amount));
884
+ if (input.capital) {
885
+ const rate = input.inclusionRate ?? .5;
886
+ put("042", input.capital.currentYearLoss);
887
+ for (const [f, c] of zip([
888
+ "044",
889
+ "046",
890
+ "048"
891
+ ], input.capital.carrybacks)) put(f, Math.round(rate * c.amount));
892
+ }
893
+ return {
894
+ scheduleId: "010",
895
+ values
896
+ };
897
+ }
898
+ const S20_CHARITABLE = {
899
+ opening: "002",
900
+ expired: "004",
901
+ beginning: "006",
902
+ transferred: "008",
903
+ currentYear: "010",
904
+ subtotal: "012",
905
+ acquisitionOfControl: "013",
906
+ available: "014",
907
+ applied: "016",
908
+ closing: "018"
909
+ };
910
+ const S20_GIFTS = {
911
+ opening: "062",
912
+ expired: "064",
913
+ beginning: "066",
914
+ transferred: "068",
915
+ currentYear: "070",
916
+ subtotal: "072",
917
+ acquisitionOfControl: "073",
918
+ available: "074",
919
+ applied: "076",
920
+ closing: "078"
921
+ };
922
+ function donationContinuityValues(result, f) {
923
+ const put = (fieldId, value) => ({
924
+ lineItemId: at1LineItemId("020", fieldId),
925
+ value
926
+ });
927
+ return [
928
+ put(f.opening, result.openingBalance),
929
+ put(f.expired, result.expired),
930
+ put(f.beginning, Math.max(0, result.openingBalance - result.expired)),
931
+ put(f.transferred, result.transferredIn),
932
+ put(f.currentYear, result.currentYearGifts),
933
+ put(f.subtotal, result.transferredIn + result.currentYearGifts),
934
+ put(f.acquisitionOfControl, result.acquisitionOfControlAdjustment),
935
+ put(f.available, result.availableBeforeClaim),
936
+ put(f.applied, result.amountApplied),
937
+ put(f.closing, result.closingBalance)
938
+ ];
939
+ }
940
+ function schedule20Values(input) {
941
+ const values = [];
942
+ if (input.charitable) values.push(...donationContinuityValues(input.charitable, S20_CHARITABLE));
943
+ if (input.gifts) values.push(...donationContinuityValues(input.gifts, S20_GIFTS));
944
+ if (input.maximum) {
945
+ const put = (fieldId, value) => values.push({
946
+ lineItemId: at1LineItemId("020", fieldId),
947
+ value
948
+ });
949
+ put("030", input.maximum.incomeComponent);
950
+ put("042", input.maximum.lesserOfProceedsAndCost);
951
+ put("044", input.maximum.allowableRecapture);
952
+ put("046", input.maximum.gainsComponent);
953
+ put("048", input.maximum.maximumDeduction);
954
+ }
955
+ return {
956
+ scheduleId: "020",
957
+ values
958
+ };
959
+ }
960
+ /**
961
+ * The SR&ED expenditure POOL — a deduction against income, not the investment tax
962
+ * credit and not the innovation grant.
963
+ *
964
+ * Line numbers and the subtotal formula verified against the live form, which
965
+ * states it exactly as transcribed:
966
+ *
967
+ * 016 = 002 − (004 + 006 + 008) + 010 + 012 + 014 + 015
968
+ *
969
+ * and closes the year-over-year chain in as many words: line 022 is *"the carry
970
+ * forward amount for next year, line 012"*.
971
+ */
972
+ function schedule16Values(result) {
973
+ const values = [];
974
+ const put = (fieldId, value) => values.push({
975
+ lineItemId: at1LineItemId("016", fieldId),
976
+ value
977
+ });
978
+ put("002", result.currentYearExpenditures);
979
+ put("016", result.subtotal);
980
+ put("018", result.deductionAvailable);
981
+ put("020", result.amountClaimed);
982
+ put("022", result.unclaimedPoolBalance);
983
+ return {
984
+ scheduleId: "016",
985
+ values
986
+ };
987
+ }
988
+ //#endregion
989
+ //#region src/t2/at1/schedules/at4970-ieg-projects.ts
990
+ const nn$34 = (v) => Math.max(0, Math.round(v ?? 0));
991
+ function computeAt4970(input) {
992
+ const projects = input.projects.map((p) => ({
993
+ title: p.title,
994
+ ...p.projectCode !== void 0 ? { projectCode: p.projectCode } : {},
995
+ albertaPortion: nn$34(p.albertaPortion),
996
+ otherPortion: nn$34(p.otherPortion),
997
+ salariesAndWages: nn$34(p.salariesAndWages),
998
+ federalProxyAmount: nn$34(p.federalProxyAmount),
999
+ albertaProxyAmount: nn$34(p.albertaProxyAmount)
1000
+ }));
1001
+ const totals = {
1002
+ albertaPortion: projects.reduce((s, p) => s + p.albertaPortion, 0),
1003
+ otherPortion: projects.reduce((s, p) => s + p.otherPortion, 0),
1004
+ salariesAndWages: projects.reduce((s, p) => s + p.salariesAndWages, 0),
1005
+ federalProxyAmount: projects.reduce((s, p) => s + p.federalProxyAmount, 0),
1006
+ albertaProxyAmount: projects.reduce((s, p) => s + p.albertaProxyAmount, 0)
1007
+ };
1008
+ const jurisdictions = (input.jurisdictions ?? []).map((j) => ({
1009
+ jurisdiction: j.jurisdiction,
1010
+ amountIncurred: nn$34(j.amountIncurred)
1011
+ }));
1012
+ return {
1013
+ projects,
1014
+ totals,
1015
+ jurisdictions,
1016
+ jurisdictionTotal: jurisdictions.reduce((s, j) => s + j.amountIncurred, 0)
1017
+ };
1018
+ }
1019
+ //#endregion
1020
+ //#region src/t2/at1/schedules/schedule2.ts
1021
+ /** Round to six decimal places — the AT1 allocation-factor precision. */
1022
+ function round6(n) {
1023
+ return Math.round(n * 1e6) / 1e6;
1024
+ }
1025
+ /** Single Alberta PE, none elsewhere → all income is Alberta income. */
1026
+ const SINGLE_JURISDICTION_ALBERTA_FACTOR = 1;
1027
+ function computeAllocationFactor(input) {
1028
+ const hasRevenue = input.totalGrossRevenue > 0;
1029
+ const hasSalaries = input.totalSalaries > 0;
1030
+ const revenueRatio = hasRevenue ? input.albertaGrossRevenue / input.totalGrossRevenue : 0;
1031
+ const salariesRatio = hasSalaries ? input.albertaSalaries / input.totalSalaries : 0;
1032
+ let factor;
1033
+ if (hasRevenue && hasSalaries) factor = (revenueRatio + salariesRatio) / 2;
1034
+ else if (hasRevenue) factor = revenueRatio;
1035
+ else if (hasSalaries) factor = salariesRatio;
1036
+ else factor = 0;
1037
+ return round6(factor);
1038
+ }
1039
+ //#endregion
1040
+ //#region src/t2/at1/schedules/schedule3-other-deductions-credits.ts
1041
+ /**
1042
+ * Alberta AT1 Schedule 3 — Alberta Other Tax Deductions and Credits.
1043
+ *
1044
+ * NOT one calculation. The spec (TRA spec §3.2.3.4, "3B3B3.2.3.4 Schedule 3 -
1045
+ * Alberta Other Tax Deductions and Credits", `AT1-Chapter3-2025.2-full.txt`
1046
+ * lines 4127-4898) groups THREE independent non-refundable investment tax
1047
+ * credit continuities under one shared ceiling:
1048
+ *
1049
+ * ITC Investor Tax Credit (lines 100-108, 120-130)
1050
+ * CITC Capital Investment Tax Credit (lines 200-208, 220-230)
1051
+ * APITC Agri-Processing Investment Tax Credit (lines 300-316, 330-340)
1052
+ * MAD Maximum Allowable Deduction — the shared ceiling (lines 600-604)
1053
+ *
1054
+ * No matching form PDF exists under `research/sources/tra-forms/pdf/` (searched
1055
+ * for `AT1SCH03*` — nothing found, unlike every other schedule this package
1056
+ * models). The specification TEXT is therefore the only source for this
1057
+ * schedule's shape; there is no live-form layout to cross-check it against.
1058
+ *
1059
+ * ── The shared ceiling (lines 600-604) ───────────────────────────────────────
1060
+ *
1061
+ * 600 = 003104 + 003204 + 003312 (ITC + CITC + APITC applied)
1062
+ * 602 = 000068 − (000070+000071+000072+000074) (AT1 page 2 room)
1063
+ * 604 = lesser of 600 and 602 ("Total Deduction")
1064
+ *
1065
+ * `000068`/`000070`/`000071`/`000072`/`000074` are AT1 page-2 jacket lines this
1066
+ * engine does not compute here (out of this module's scope per the task's scope
1067
+ * rule) — they are plain numeric inputs (`MaximumAllowableDeductionInput`).
1068
+ *
1069
+ * ── Three pools, one room, in a STATED precedence ────────────────────────────
1070
+ *
1071
+ * ITC is applied first, capped only by 602 itself:
1072
+ * 104 ≤ 000068 − (000070+000071+000072+000074)
1073
+ *
1074
+ * CITC is gated behind ITC: "If 003108 > 0 [ITC still has an unused carryforward
1075
+ * balance after this year's application], then [204] must equal zero." Only once
1076
+ * the ITC pool is fully drawn down may CITC be claimed, capped at what room ITC
1077
+ * left behind:
1078
+ * 204 ≤ 602 − 104 (when 108 = 0; otherwise 204 = 0)
1079
+ *
1080
+ * APITC draws on what both leave behind, but against a DIFFERENT room formula —
1081
+ * the spec's four "cannot exceed" clauses for 304/306/308/310 all subtract only
1082
+ * `(000070+000072)`, NOT `000071`/`000074` the way 104/204/602 do. That asymmetry
1083
+ * is transcribed exactly as written, not corrected, because it repeats
1084
+ * identically across all five APITC business-rule cells (304, 306, 308, 310, 312)
1085
+ * — consistent enough to be deliberate rather than a transcription slip:
1086
+ * 312 ≤ 000068 − (000070+000072) − (104+204)
1087
+ *
1088
+ * ── APITC: per-vintage percentage caps ───────────────────────────────────────
1089
+ *
1090
+ * The four "cannot exceed" clauses for 304/306/308/310, read together, are
1091
+ * algebraically just ONE combined-total constraint stated four times from four
1092
+ * different partial-sum vantage points (each says "this line ≤ R − the lines
1093
+ * listed", and the lines listed are exactly the OTHER three) — they collapse to
1094
+ * `304+306+308+310 ≤ R`, which is exactly what 312's own business rule states
1095
+ * directly. So the four clauses add nothing beyond that one shared-room ceiling;
1096
+ * what makes each vintage different is its OWN percentage cap from AAPITC
1097
+ * (330-340):
1098
+ *
1099
+ * occurrence 0 (current year, 334/336 occ 0) ≤ 20% of that vintage's receipt
1100
+ * occurrence 1 (1st preceding, occ 1) ≤ 30% of that vintage's receipt
1101
+ * occurrence 2 (2nd preceding, occ 2) ≤ 50% of that vintage's receipt
1102
+ * occurrences 3-10 (3rd-10th preceding) no percentage cap, own balance only
1103
+ *
1104
+ * `computeSchedule3` claims each vintage's OWN cap first, then allocates the
1105
+ * shared room OLDEST-VINTAGE-FIRST when the total requested exceeds it — APITC
1106
+ * is a 10-year-preceding, use-it-or-lose-it credit and the spec states no
1107
+ * application order, so this engine flags that choice in `issues` rather than
1108
+ * silently guessing at NetFile's actual tie-break. Supply `amountApplied` on
1109
+ * each vintage directly for exact filing parity.
1110
+ *
1111
+ * ── What is deliberately NOT modelled ────────────────────────────────────────
1112
+ *
1113
+ * The AITC/ACITC year-of-origin analysis tables (120-130, 220-230) are
1114
+ * supplementary detail TRA requires when a corporation carries ITC or CITC.
1115
+ * Unlike AAPITC, the spec gives them no percentage cap or application order of
1116
+ * their own — every business rule on those lines is a reconciliation back to the
1117
+ * aggregate 104/106/204/206 this module already computes (e.g. "126 … Value must
1118
+ * be less than or equal to 124+125", "130 … calculate 124+125-126-128"). Adding a
1119
+ * per-vintage array for ITC/CITC would only re-derive numbers this module
1120
+ * already produces without a spec-given rule to allocate them across years, so
1121
+ * it is left out; a caller filing the AITC/ACITC detail pages supplies that
1122
+ * per-vintage split itself.
1123
+ *
1124
+ * Whole dollars, pure.
1125
+ */
1126
+ const nn$33 = (v) => Math.max(0, Math.round(v ?? 0));
1127
+ const raw = (v) => Math.round(v ?? 0);
1128
+ function computeItc(input, roomCap, issues) {
1129
+ const certificatesIssued = nn$33(input.certificatesIssued);
1130
+ const carryforwardFromPriorYear = nn$33(input.carryforwardFromPriorYear);
1131
+ const expired = nn$33(input.expired);
1132
+ const availableBeforeClaim = certificatesIssued + carryforwardFromPriorYear;
1133
+ const cap = Math.min(availableBeforeClaim, roomCap);
1134
+ const wanted = input.amountApplied != null ? nn$33(input.amountApplied) : cap;
1135
+ const amountApplied = Math.max(0, Math.min(wanted, cap));
1136
+ const carryforwardToNextYear = availableBeforeClaim - amountApplied - expired;
1137
+ if (carryforwardToNextYear < 0) issues.push(`Alberta Schedule 3 (ITC): expired (${expired}) plus the amount applied (${amountApplied}) exceed the available pool (${availableBeforeClaim}) by ${-carryforwardToNextYear}. Check the 100/102/104/106 inputs.`);
1138
+ return {
1139
+ certificatesIssued,
1140
+ carryforwardFromPriorYear,
1141
+ availableBeforeClaim,
1142
+ amountApplied,
1143
+ expired,
1144
+ carryforwardToNextYear
1145
+ };
1146
+ }
1147
+ function computeCitc(input, roomCap, itcApplied, itcCarryforwardRemaining, issues) {
1148
+ const certificatesIssued = nn$33(input.certificatesIssued);
1149
+ const carryforwardFromPriorYear = nn$33(input.carryforwardFromPriorYear);
1150
+ const expired = nn$33(input.expired);
1151
+ const availableBeforeClaim = certificatesIssued + carryforwardFromPriorYear;
1152
+ let amountApplied;
1153
+ if (itcCarryforwardRemaining > 0) {
1154
+ amountApplied = 0;
1155
+ if (input.amountApplied != null && nn$33(input.amountApplied) > 0) issues.push(`Alberta Schedule 3 (CITC): a claim of ${nn$33(input.amountApplied)} was requested, but the Investor Tax Credit still has an unused carryforward balance (${itcCarryforwardRemaining}) after this year's application, so line 204 must be nil until ITC is fully drawn down.`);
1156
+ } else {
1157
+ const cap = Math.min(availableBeforeClaim, Math.max(0, roomCap - itcApplied));
1158
+ const wanted = input.amountApplied != null ? nn$33(input.amountApplied) : cap;
1159
+ amountApplied = Math.max(0, Math.min(wanted, cap));
1160
+ }
1161
+ const carryforwardToNextYear = availableBeforeClaim - amountApplied - expired;
1162
+ if (carryforwardToNextYear < 0) issues.push(`Alberta Schedule 3 (CITC): expired (${expired}) plus the amount applied (${amountApplied}) exceed the available pool (${availableBeforeClaim}) by ${-carryforwardToNextYear}. Check the 200/202/204/206 inputs.`);
1163
+ return {
1164
+ certificatesIssued,
1165
+ carryforwardFromPriorYear,
1166
+ availableBeforeClaim,
1167
+ amountApplied,
1168
+ expired,
1169
+ carryforwardToNextYear
1170
+ };
1171
+ }
1172
+ function computeApitc(input, roomCap, issues) {
1173
+ const current = input.current ?? {};
1174
+ const first = input.firstPreceding ?? {};
1175
+ const second = input.secondPreceding ?? {};
1176
+ const thirdToTenth = input.thirdToTenthPreceding ?? {};
1177
+ const currentAvailable = nn$33(current.received);
1178
+ const firstAvailable = nn$33(first.availableAtBeginning);
1179
+ const secondAvailable = nn$33(second.availableAtBeginning);
1180
+ const thirdToTenthAvailable = nn$33(thirdToTenth.availableAtBeginning);
1181
+ const currentCap = Math.round(currentAvailable * .2);
1182
+ const firstCap = Math.round(firstAvailable * .3);
1183
+ const secondCap = Math.round(secondAvailable * .5);
1184
+ const thirdToTenthCap = thirdToTenthAvailable;
1185
+ const currentAsk = Math.min(current.amountApplied != null ? nn$33(current.amountApplied) : currentCap, currentCap);
1186
+ const firstAsk = Math.min(first.amountApplied != null ? nn$33(first.amountApplied) : firstCap, firstCap);
1187
+ const secondAsk = Math.min(second.amountApplied != null ? nn$33(second.amountApplied) : secondCap, secondCap);
1188
+ const thirdToTenthAsk = Math.min(thirdToTenth.amountApplied != null ? nn$33(thirdToTenth.amountApplied) : thirdToTenthCap, thirdToTenthCap);
1189
+ const totalRequested = currentAsk + firstAsk + secondAsk + thirdToTenthAsk;
1190
+ let room = Math.max(0, roomCap);
1191
+ const allocate = (ask) => {
1192
+ const got = Math.min(ask, room);
1193
+ room -= got;
1194
+ return got;
1195
+ };
1196
+ const thirdToTenthApplied = allocate(thirdToTenthAsk);
1197
+ const secondApplied = allocate(secondAsk);
1198
+ const firstApplied = allocate(firstAsk);
1199
+ const currentApplied = allocate(currentAsk);
1200
+ const totalApplied = currentApplied + firstApplied + secondApplied + thirdToTenthApplied;
1201
+ if (totalApplied < totalRequested) issues.push(`Alberta Schedule 3 (APITC): the shared Maximum Allowable Deduction room (${roomCap}) was insufficient to cover ${totalRequested} requested across vintages; ${totalRequested - totalApplied} went unapplied. Allocation prioritized the oldest vintage first (closest to the 10-year expiry) — the spec states only the combined ceiling on 312, not an application order across 304/306/308/310. Supply amountApplied on each vintage directly for exact filing parity.`);
1202
+ const totalReceived = currentAvailable;
1203
+ const carryforwardFromPriorYear = firstAvailable + secondAvailable + thirdToTenthAvailable;
1204
+ const expired = nn$33(input.expiredThisYear);
1205
+ const availableForCarryforward = totalReceived + carryforwardFromPriorYear - totalApplied - expired;
1206
+ if (availableForCarryforward < 0) issues.push(`Alberta Schedule 3 (APITC): applied (${totalApplied}) plus expired (${expired}) exceed the available pool (${totalReceived + carryforwardFromPriorYear}) by ${-availableForCarryforward}. Check the 300/302/312/314 inputs.`);
1207
+ return {
1208
+ current: {
1209
+ available: currentAvailable,
1210
+ ownCap: currentCap,
1211
+ amountApplied: currentApplied
1212
+ },
1213
+ firstPreceding: {
1214
+ available: firstAvailable,
1215
+ ownCap: firstCap,
1216
+ amountApplied: firstApplied
1217
+ },
1218
+ secondPreceding: {
1219
+ available: secondAvailable,
1220
+ ownCap: secondCap,
1221
+ amountApplied: secondApplied
1222
+ },
1223
+ thirdToTenthPreceding: {
1224
+ available: thirdToTenthAvailable,
1225
+ amountApplied: thirdToTenthApplied
1226
+ },
1227
+ totalReceived,
1228
+ carryforwardFromPriorYear,
1229
+ totalRequested,
1230
+ totalApplied,
1231
+ expired,
1232
+ availableForCarryforward
1233
+ };
1234
+ }
1235
+ function computeSchedule3(input) {
1236
+ const issues = [];
1237
+ const mad = input.mad ?? {};
1238
+ const line068 = nn$33(mad.taxPayableBeforeDeduction);
1239
+ const line070 = nn$33(mad.line070);
1240
+ const line071 = nn$33(mad.line071);
1241
+ const line072 = nn$33(mad.line072);
1242
+ const line074 = nn$33(mad.line074);
1243
+ const room602 = raw(line068) - (line070 + line071 + line072 + line074);
1244
+ const itcCitcCap = Math.max(0, room602);
1245
+ const itc = computeItc(input.itc ?? {}, itcCitcCap, issues);
1246
+ const citc = computeCitc(input.citc ?? {}, itcCitcCap, itc.amountApplied, itc.carryforwardToNextYear, issues);
1247
+ const apitcRoom = Math.max(0, line068 - (line070 + line072) - (itc.amountApplied + citc.amountApplied));
1248
+ const apitc = computeApitc(input.apitc ?? {}, apitcRoom, issues);
1249
+ const creditsApplied = itc.amountApplied + citc.amountApplied + apitc.totalApplied;
1250
+ return {
1251
+ mad: {
1252
+ creditsApplied,
1253
+ room: room602
1254
+ },
1255
+ itc,
1256
+ citc,
1257
+ apitc,
1258
+ totalDeduction: Math.max(0, Math.min(creditsApplied, Math.max(0, room602))),
1259
+ issues
1260
+ };
1261
+ }
1262
+ function schedule3LineItemId(fieldId, occurrence = 1) {
1263
+ return `003${fieldId}${String(occurrence).padStart(3, "0")}`;
1264
+ }
1265
+ /**
1266
+ * Field ids per the spec transcription above: 100-108 (ITC), 200-208 (CITC),
1267
+ * 300-316 (APITC), 600-604 (MAD). The by-year-of-origin detail pages
1268
+ * (120-130/220-230/330-340) are NOT emitted — this module does not compute a
1269
+ * per-vintage split for ITC/CITC (see the module docstring), and the APITC
1270
+ * per-vintage figures this DOES compute (304/306/308/310) are filed on the
1271
+ * 300-series rollup, not re-emitted as an AAPITC occurrence table.
1272
+ */
1273
+ function schedule3Values(result) {
1274
+ const values = [];
1275
+ const put = (fieldId, value) => values.push({
1276
+ lineItemId: schedule3LineItemId(fieldId),
1277
+ value
1278
+ });
1279
+ put("100", result.itc.certificatesIssued);
1280
+ put("102", result.itc.carryforwardFromPriorYear);
1281
+ put("104", result.itc.amountApplied);
1282
+ put("106", result.itc.expired);
1283
+ put("108", result.itc.carryforwardToNextYear);
1284
+ put("200", result.citc.certificatesIssued);
1285
+ put("202", result.citc.carryforwardFromPriorYear);
1286
+ put("204", result.citc.amountApplied);
1287
+ put("206", result.citc.expired);
1288
+ put("208", result.citc.carryforwardToNextYear);
1289
+ put("300", result.apitc.totalReceived);
1290
+ put("302", result.apitc.carryforwardFromPriorYear);
1291
+ put("304", result.apitc.current.amountApplied);
1292
+ put("306", result.apitc.firstPreceding.amountApplied);
1293
+ put("308", result.apitc.secondPreceding.amountApplied);
1294
+ put("310", result.apitc.thirdToTenthPreceding.amountApplied);
1295
+ put("312", result.apitc.totalApplied);
1296
+ put("314", result.apitc.expired);
1297
+ put("316", result.apitc.availableForCarryforward);
1298
+ put("600", result.mad.creditsApplied);
1299
+ put("602", result.mad.room);
1300
+ put("604", result.totalDeduction);
1301
+ return {
1302
+ scheduleId: "003",
1303
+ values
1304
+ };
1305
+ }
1306
+ //#endregion
1307
+ //#region src/t2/at1/schedules/schedule4-foreign-investment-tax-credit.ts
1308
+ const nn$32 = (v) => Math.max(0, v ?? 0);
1309
+ /** Round to 3 decimal places, half-up — the precision the spec directs for D and G. */
1310
+ function round3(v) {
1311
+ return Math.round((v + Number.EPSILON) * 1e3) / 1e3;
1312
+ }
1313
+ function computeSchedule4(input) {
1314
+ const issues = [];
1315
+ const albertaTaxableIncome = nn$32(input.albertaTaxableIncome);
1316
+ const royaltyTaxDeduction = nn$32(input.royaltyTaxDeduction);
1317
+ const allocationFactor = input.allocationFactor ?? 0;
1318
+ const basicAlbertaTax = nn$32(input.basicAlbertaTax);
1319
+ const denominator = (albertaTaxableIncome - royaltyTaxDeduction) * allocationFactor;
1320
+ const countries = input.countries.map((c) => {
1321
+ if (!c.country) issues.push("Alberta Schedule 4: a country code is required for each occurrence (004002).");
1322
+ const netForeignInvestmentIncome = nn$32(c.netForeignInvestmentIncome);
1323
+ const fedForeignTaxPaid = nn$32(c.fedForeignTaxPaid);
1324
+ const fedIta2012Deduction = nn$32(c.fedIta2012Deduction);
1325
+ const albertaDeduction = Math.max(c.albertaActa82Deduction ?? fedIta2012Deduction, fedIta2012Deduction);
1326
+ const taxPaidNetOfDeduction = Math.max(0, fedForeignTaxPaid - albertaDeduction);
1327
+ const federalNonBusinessForeignTaxCredit = nn$32(c.fedNonBusinessForeignTaxCredit);
1328
+ let incomeProrationAmount;
1329
+ if (denominator === 0) {
1330
+ incomeProrationAmount = 0;
1331
+ if (netForeignInvestmentIncome > 0) issues.push(`Alberta Schedule 4 (${c.country || "unspecified country"}): (albertaTaxableIncome − royaltyTaxDeduction) × allocationFactor is nil, so the D proration (000068 / that product) cannot be computed. Treated as nil rather than dividing by zero.`);
1332
+ } else incomeProrationAmount = round3(netForeignInvestmentIncome * allocationFactor * (basicAlbertaTax / denominator));
1333
+ const taxPaidLessFederalCredit = round3((taxPaidNetOfDeduction - federalNonBusinessForeignTaxCredit) * allocationFactor);
1334
+ const allowableCredit = Math.max(0, Math.round(Math.min(incomeProrationAmount, taxPaidLessFederalCredit)));
1335
+ return {
1336
+ country: c.country,
1337
+ netForeignInvestmentIncome,
1338
+ taxPaidNetOfDeduction,
1339
+ federalNonBusinessForeignTaxCredit,
1340
+ incomeProrationAmount,
1341
+ taxPaidLessFederalCredit,
1342
+ allowableCredit
1343
+ };
1344
+ });
1345
+ const totalAllowableCredit = countries.reduce((sum, c) => sum + c.allowableCredit, 0);
1346
+ if (input.countries.length > 0 && totalAllowableCredit === 0) issues.push("Alberta Schedule 4: total allowable credit is nil across all occurrences — the spec directs that this form not be printed/filed in that case.");
1347
+ return {
1348
+ countries,
1349
+ totalAllowableCredit,
1350
+ issues
1351
+ };
1352
+ }
1353
+ /**
1354
+ * Net File line items for AT1 Schedule 4 — one FIC occurrence per country.
1355
+ * Field ids from the spec (§3.2.3.5): 002 country, 004 net foreign investment
1356
+ * income, 006 foreign tax paid net of deduction, 008 federal non-business
1357
+ * foreign tax credit, 012 allowable credit.
1358
+ *
1359
+ * Does NOT emit jacket line 000072 (the sum-vs-remaining-tax comparison) —
1360
+ * that is a jacket line, not a Schedule 4 line, and belongs to whichever
1361
+ * module builds the jacket. Use `result.totalAllowableCredit` for that.
1362
+ */
1363
+ function schedule4Values(result) {
1364
+ const values = [];
1365
+ result.countries.forEach((c, i) => {
1366
+ const n = i + 1;
1367
+ const put = (fieldId, value) => values.push({
1368
+ lineItemId: at1LineItemId("004", fieldId, n),
1369
+ value
1370
+ });
1371
+ put("002", c.country);
1372
+ put("004", c.netForeignInvestmentIncome);
1373
+ put("006", c.taxPaidNetOfDeduction);
1374
+ put("008", c.federalNonBusinessForeignTaxCredit);
1375
+ put("012", c.allowableCredit);
1376
+ });
1377
+ return {
1378
+ scheduleId: "004",
1379
+ values
1380
+ };
1381
+ }
1382
+ //#endregion
1383
+ //#region src/t2/at1/schedules/schedule5-royalty-tax-deduction.ts
1384
+ const nn$31 = (v) => Math.max(0, Math.round(v ?? 0));
1385
+ const num$2 = (v) => Math.round(v ?? 0);
1386
+ function processSuccessoredPool(entries, label, issues) {
1387
+ return (entries ?? []).map((e, i) => {
1388
+ const hasBrought = e.poolBroughtForward !== void 0;
1389
+ const hasAcquired = e.acquisitionAmount !== void 0;
1390
+ if (hasBrought && hasAcquired) issues.push(`Alberta Schedule 5: ${label} occurrence ${i + 1} (${e.vendorName}) has both a pool-brought-forward and an acquisition amount; the spec allows only one per occurrence. Using poolBroughtForward and ignoring acquisitionAmount.`);
1391
+ else if (!hasBrought && !hasAcquired) issues.push(`Alberta Schedule 5: ${label} occurrence ${i + 1} (${e.vendorName}) has neither a pool-brought-forward nor an acquisition amount; treating the pool base as zero.`);
1392
+ const base = nn$31(hasBrought ? e.poolBroughtForward : e.acquisitionAmount);
1393
+ const baseKind = hasBrought ? "broughtForward" : hasAcquired ? "acquired" : "unspecified";
1394
+ const propertyIncome = nn$31(e.propertyIncome);
1395
+ const claim = Math.min(base, propertyIncome);
1396
+ return {
1397
+ vendorName: e.vendorName,
1398
+ dateOfEvent: e.dateOfEvent,
1399
+ base,
1400
+ baseKind,
1401
+ propertyIncome,
1402
+ claim,
1403
+ carryForwardBeforeTransfer: base - claim
1404
+ };
1405
+ });
1406
+ }
1407
+ function computeAlbertaSchedule5(input) {
1408
+ const issues = [];
1409
+ const crownCharges = nn$31(input.crownChargesNetOfReimbursements);
1410
+ const resourceAllowance = nn$31(input.albertaResourceAllowance ?? input.federalResourceAllowance);
1411
+ const reimbursements = nn$31(input.reimbursementsForCrownCharges);
1412
+ const predecessorTransfersTotal = (input.predecessorTransfers ?? []).reduce((s, t) => s + nn$31(t.amountTransferred), 0);
1413
+ const attributedRoyaltyIncomeCarryForwardIn = nn$31(input.openingUnsuccessoredPoolBalance) + predecessorTransfersTotal;
1414
+ const unsuccessoredPoolAvailable = crownCharges - resourceAllowance - reimbursements + attributedRoyaltyIncomeCarryForwardIn;
1415
+ const hasSuccessoredPools = input.hasSuccessoredPools ?? false;
1416
+ if (!hasSuccessoredPools && ((input.secondSuccessoredPools?.length ?? 0) > 0 || (input.firstSuccessoredPools?.length ?? 0) > 0)) issues.push("Alberta Schedule 5: hasSuccessoredPools (005200) is false, but successored pool occurrences were supplied. Per the spec, 005101-005140 must not exist when 005200 = 2 (No); the supplied occurrences were ignored.");
1417
+ const secondSuccessoredPools = hasSuccessoredPools ? processSuccessoredPool(input.secondSuccessoredPools, "second successored (SSPI)", issues) : [];
1418
+ const firstSuccessoredPools = hasSuccessoredPools ? processSuccessoredPool(input.firstSuccessoredPools, "first successored (FSPI)", issues) : [];
1419
+ const secondSuccessoredSubtotal = secondSuccessoredPools.reduce((s, e) => s + e.carryForwardBeforeTransfer, 0);
1420
+ const firstSuccessoredSubtotal = firstSuccessoredPools.reduce((s, e) => s + e.carryForwardBeforeTransfer, 0);
1421
+ const successoredTotal = secondSuccessoredPools.reduce((s, e) => s + e.claim, 0) + firstSuccessoredPools.reduce((s, e) => s + e.claim, 0);
1422
+ const albertaTaxableIncome = num$2(input.albertaTaxableIncomeBeforeDeduction);
1423
+ const crtdMaxClaimable = Math.max(0, Math.min(unsuccessoredPoolAvailable, albertaTaxableIncome - successoredTotal));
1424
+ const crtdClaim = input.crtdAmountClaimed != null ? Math.max(0, Math.min(Math.round(input.crtdAmountClaimed), crtdMaxClaimable)) : crtdMaxClaimable;
1425
+ const poolAvailableCarryForward = unsuccessoredPoolAvailable - crtdClaim;
1426
+ const transferredOnDisposal = nn$31(input.transferredOnDisposal);
1427
+ const uncappedTotal = crtdClaim + successoredTotal;
1428
+ const totalRoyaltyTaxDeduction = Math.max(0, Math.min(uncappedTotal, albertaTaxableIncome));
1429
+ if (totalRoyaltyTaxDeduction < uncappedTotal && uncappedTotal > albertaTaxableIncome) if (albertaTaxableIncome < 0) issues.push(`Alberta Schedule 5: Alberta taxable income before the deduction (AT1 line 062) is ${albertaTaxableIncome}, which is negative. AT1 line 064 is specified as "005016 + 005140, cannot exceed 000062" with no stated floor; this engine floors the combined deduction at zero rather than reporting a negative Royalty Tax Deduction. The successored-pool claims (005140 = ${successoredTotal}) are computed independently of line 062 and are NOT reduced by this cap — review manually if this scenario occurs.`);
1430
+ else issues.push(`Alberta Schedule 5: the combined Royalty Tax Deduction (005016 + 005140 = ${uncappedTotal}) exceeds Alberta taxable income before the deduction (AT1 line 062 = ${albertaTaxableIncome}); AT1 line 064 is capped at ${totalRoyaltyTaxDeduction} per the spec.`);
1431
+ const netOfResourceAllowance = crownCharges - resourceAllowance;
1432
+ const carryForwardRaw = netOfResourceAllowance >= 0 ? netOfResourceAllowance + attributedRoyaltyIncomeCarryForwardIn - totalRoyaltyTaxDeduction - transferredOnDisposal : attributedRoyaltyIncomeCarryForwardIn - totalRoyaltyTaxDeduction - transferredOnDisposal;
1433
+ const attributedRoyaltyIncomeCarryForwardOut = Math.max(0, carryForwardRaw);
1434
+ if (input.poolTransfer) {
1435
+ const { type, acquirerName } = input.poolTransfer;
1436
+ if ((type === 1 || type === 2) && !acquirerName) issues.push("Alberta Schedule 5: pool transfer type (005026) is 1 or 2, but the acquiring corporation's legal name (005027) is missing.");
1437
+ if (type === 3 && acquirerName) issues.push("Alberta Schedule 5: pool transfer type (005026) is 3 (no transfer), but an acquiring corporation name (005027) was supplied. Per the spec, 005027 must be blank when 005026 = 3.");
1438
+ }
1439
+ if (input.changeInControlEndedPrecedingYear !== void 0 && input.at1TaxYearEndChanged !== void 0 && input.at1TaxYearEndChangeReason !== void 0) {
1440
+ const cicChangedYearEnd = input.at1TaxYearEndChanged && input.at1TaxYearEndChangeReason === 2;
1441
+ if (input.changeInControlEndedPrecedingYear !== cicChangedYearEnd) issues.push(`Alberta Schedule 5: 005100 (change in control ended the preceding taxation year) is ${input.changeInControlEndedPrecedingYear}, which is inconsistent with AT1 core 000038/000039 (tax year end changed due to change in control = ${cicChangedYearEnd}). Per the spec, these must agree in both directions.`);
1442
+ }
1443
+ const formRequired = crownCharges > 0 || attributedRoyaltyIncomeCarryForwardIn > 0 || hasSuccessoredPools;
1444
+ return {
1445
+ crownCharges,
1446
+ resourceAllowance,
1447
+ reimbursements,
1448
+ predecessorTransfersTotal,
1449
+ attributedRoyaltyIncomeCarryForwardIn,
1450
+ unsuccessoredPoolAvailable,
1451
+ crtdMaxClaimable,
1452
+ crtdClaim,
1453
+ poolAvailableCarryForward,
1454
+ transferredOnDisposal,
1455
+ secondSuccessoredPools,
1456
+ firstSuccessoredPools,
1457
+ secondSuccessoredSubtotal,
1458
+ firstSuccessoredSubtotal,
1459
+ successoredTotal,
1460
+ totalRoyaltyTaxDeduction,
1461
+ attributedRoyaltyIncomeCarryForwardOut,
1462
+ ...input.poolTransfer !== void 0 ? { poolTransfer: input.poolTransfer } : {},
1463
+ ...input.changeInControlEndedPrecedingYear !== void 0 ? { changeInControlEndedPrecedingYear: input.changeInControlEndedPrecedingYear } : {},
1464
+ formRequired,
1465
+ issues
1466
+ };
1467
+ }
1468
+ /** `005` + field id + 3-digit occurrence — the nine-digit AT1 line item id. */
1469
+ function schedule5LineItemId(fieldId, occurrence = 1) {
1470
+ return `005${fieldId}${String(occurrence).padStart(3, "0")}`;
1471
+ }
1472
+ function putSuccessoredPool(values, entries, fields) {
1473
+ entries.forEach((e, i) => {
1474
+ const n = i + 1;
1475
+ const put = (fieldId, value) => values.push({
1476
+ lineItemId: schedule5LineItemId(fieldId, n),
1477
+ value
1478
+ });
1479
+ put(fields.vendorName, e.vendorName);
1480
+ put(fields.dateOfEvent, e.dateOfEvent);
1481
+ if (e.baseKind === "broughtForward") put(fields.broughtForward, e.base);
1482
+ else if (e.baseKind === "acquired") put(fields.acquired, e.base);
1483
+ put(fields.propertyIncome, e.propertyIncome);
1484
+ put(fields.claim, e.claim);
1485
+ put(fields.carryForward, e.carryForwardBeforeTransfer);
1486
+ });
1487
+ }
1488
+ function schedule5Values(result) {
1489
+ const values = [];
1490
+ const put = (fieldId, value) => values.push({
1491
+ lineItemId: schedule5LineItemId(fieldId),
1492
+ value
1493
+ });
1494
+ put("001", result.crownCharges);
1495
+ put("005", result.resourceAllowance);
1496
+ put("007", result.reimbursements);
1497
+ put("011", result.attributedRoyaltyIncomeCarryForwardIn);
1498
+ put("016", result.crtdClaim);
1499
+ put("017", result.poolAvailableCarryForward);
1500
+ put("023", result.transferredOnDisposal);
1501
+ put("025", result.attributedRoyaltyIncomeCarryForwardOut);
1502
+ if (result.poolTransfer) {
1503
+ put("026", result.poolTransfer.type);
1504
+ if (result.poolTransfer.acquirerName) put("027", result.poolTransfer.acquirerName);
1505
+ }
1506
+ if (result.changeInControlEndedPrecedingYear !== void 0) put("100", result.changeInControlEndedPrecedingYear ? 1 : 2);
1507
+ putSuccessoredPool(values, result.secondSuccessoredPools, {
1508
+ vendorName: "101",
1509
+ dateOfEvent: "103",
1510
+ broughtForward: "105",
1511
+ acquired: "107",
1512
+ propertyIncome: "109",
1513
+ claim: "111",
1514
+ carryForward: "113"
1515
+ });
1516
+ put("115", result.secondSuccessoredSubtotal);
1517
+ putSuccessoredPool(values, result.firstSuccessoredPools, {
1518
+ vendorName: "121",
1519
+ dateOfEvent: "123",
1520
+ broughtForward: "125",
1521
+ acquired: "127",
1522
+ propertyIncome: "129",
1523
+ claim: "131",
1524
+ carryForward: "133"
1525
+ });
1526
+ put("135", result.firstSuccessoredSubtotal);
1527
+ put("140", result.successoredTotal);
1528
+ return {
1529
+ scheduleId: "005",
1530
+ values
1531
+ };
1532
+ }
1533
+ //#endregion
1534
+ //#region src/t2/at1/schedules/schedule6-royalty-tax-credit.ts
1535
+ const rd$1 = (v) => Math.round(v ?? 0);
1536
+ const nn$30 = (v) => Math.max(0, Math.round(v ?? 0));
1537
+ const round4$1 = (v) => Math.round(v * 1e4) / 1e4;
1538
+ function computeWeightedAverageRate(quarters, issues) {
1539
+ if (quarters.length === 0) {
1540
+ issues.push("Alberta Schedule 6: no quarters were supplied for the weighted average rate (006008 is mandatory). Defaulted to 0 — supply the RTC quarterly rate(s) from Alberta Treasury Board and Finance's published rate table.");
1541
+ return 0;
1542
+ }
1543
+ const totalDays = quarters.reduce((s, q) => s + Math.max(0, q.days), 0);
1544
+ if (totalDays <= 0) {
1545
+ issues.push("Alberta Schedule 6: the quarters supplied for 006008 total zero days.");
1546
+ return 0;
1547
+ }
1548
+ const weighted = quarters.reduce((s, q) => s + Math.max(0, q.days) / totalDays * q.rate, 0);
1549
+ return round4$1(weighted);
1550
+ }
1551
+ function resolveAllocations(allocations, pool, issues) {
1552
+ const totalRequested = allocations.reduce((s, a) => s + nn$30(a.allocatedAmount), 0);
1553
+ if (totalRequested > pool) issues.push(`Alberta Schedule 6: the associated group allocated ${totalRequested} of a Crown Royalty Shelter pool of ${pool} (006034 occurrences exceed the $2,000,000 × (006028/365) limit). Allocations were capped in the order given — the group must agree a valid split.`);
1554
+ let remaining = pool;
1555
+ return allocations.map((a) => {
1556
+ const requestedAmount = nn$30(a.allocatedAmount);
1557
+ const allocatedAmount = Math.min(requestedAmount, remaining);
1558
+ remaining -= allocatedAmount;
1559
+ return {
1560
+ name: a.name,
1561
+ ...a.albertaCan !== void 0 ? { albertaCan: a.albertaCan } : {},
1562
+ allocatedAmount,
1563
+ requestedAmount
1564
+ };
1565
+ });
1566
+ }
1567
+ function schedule6LineItemId(fieldId, occurrence = 1) {
1568
+ return `006${fieldId}${String(occurrence).padStart(3, "0")}`;
1569
+ }
1570
+ /**
1571
+ * Emits every field this module's own MAPPINGS transcription actually defines
1572
+ * a number for: 006002/004/006/008, and — only when the corporation is
1573
+ * associated — the ACRS section (006022-028) and the AACRS allocation table
1574
+ * (006030-034, one occurrence per row).
1575
+ *
1576
+ * ── No credit amount is filed here, because none exists on this schedule ────
1577
+ *
1578
+ * See the module docstring's "CONFIRMED: there is no 'credit' dollar amount
1579
+ * to compute here" section — the Alberta Royalty Tax Credit is an instalment
1580
+ * program (AT1 jacket line 000082, the shared "Payments & Instalments"
1581
+ * schedule), not a value Schedule 6 computes and files. This builder emits
1582
+ * exactly the three components the schedule DOES define (006004, 006006,
1583
+ * 006008) plus the ACRS/AACRS detail when associated — matching
1584
+ * `schedule3Values`'s `At1ScheduleDataLike` shape exactly: `{ scheduleId,
1585
+ * values }`.
1586
+ */
1587
+ function schedule6Values(result) {
1588
+ const values = [];
1589
+ const put = (fieldId, value, occurrence = 1) => values.push({
1590
+ lineItemId: schedule6LineItemId(fieldId, occurrence),
1591
+ value
1592
+ });
1593
+ put("002", result.associatedWithCrownRoyaltyCorporations ? 1 : 2);
1594
+ put("004", result.albertaCrownRoyaltyIncurred);
1595
+ put("006", result.crownRoyaltyShelter);
1596
+ put("008", result.weightedAverageRate);
1597
+ if (result.associatedWithCrownRoyaltyCorporations && result.longestAssociatedYear) {
1598
+ if (result.longestAssociatedYear.albertaCan !== void 0) values.push({
1599
+ lineItemId: schedule6LineItemId("022"),
1600
+ value: result.longestAssociatedYear.albertaCan
1601
+ });
1602
+ if (result.longestAssociatedYear.taxationYearBeginning !== void 0) values.push({
1603
+ lineItemId: schedule6LineItemId("024"),
1604
+ value: result.longestAssociatedYear.taxationYearBeginning
1605
+ });
1606
+ if (result.longestAssociatedYear.taxationYearEnding !== void 0) values.push({
1607
+ lineItemId: schedule6LineItemId("026"),
1608
+ value: result.longestAssociatedYear.taxationYearEnding
1609
+ });
1610
+ put("028", result.longestAssociatedYear.days);
1611
+ result.allocations.forEach((a, i) => {
1612
+ const occurrence = i + 1;
1613
+ values.push({
1614
+ lineItemId: schedule6LineItemId("030", occurrence),
1615
+ value: a.name
1616
+ });
1617
+ if (a.albertaCan !== void 0) values.push({
1618
+ lineItemId: schedule6LineItemId("032", occurrence),
1619
+ value: a.albertaCan
1620
+ });
1621
+ put("034", a.allocatedAmount, occurrence);
1622
+ });
1623
+ }
1624
+ return {
1625
+ scheduleId: "006",
1626
+ values
1627
+ };
1628
+ }
1629
+ function computeAlbertaSchedule6(input) {
1630
+ const issues = [];
1631
+ const associatedWithCrownRoyaltyCorporations = input.associatedWithCrownRoyaltyCorporations ?? false;
1632
+ const albertaCrownRoyaltyIncurred = rd$1(input.albertaCrownRoyaltyIncurred);
1633
+ const weightedAverageRate = computeWeightedAverageRate(input.quarters ?? [], issues);
1634
+ const formRequired = albertaCrownRoyaltyIncurred > 0;
1635
+ if (!associatedWithCrownRoyaltyCorporations) {
1636
+ const days = Math.max(0, Math.min(input.taxationYearDays ?? 365, 365));
1637
+ return {
1638
+ associatedWithCrownRoyaltyCorporations,
1639
+ albertaCrownRoyaltyIncurred,
1640
+ crownRoyaltyShelter: Math.round(2e6 * (days / 365)),
1641
+ weightedAverageRate,
1642
+ aggregateShelterPool: 0,
1643
+ allocations: [],
1644
+ totalAllocated: 0,
1645
+ formRequired,
1646
+ issues
1647
+ };
1648
+ }
1649
+ if (!input.longestAssociatedYear) issues.push("Alberta Schedule 6: the corporation is associated with one or more corporations that incurred Alberta Crown Royalty (006002 = 1), but no ACRS data (006022-006028, the associated corporation with the longest taxation year) was supplied. The Crown Royalty Shelter pool defaulted to zero.");
1650
+ const days = Math.max(0, Math.min(input.longestAssociatedYear?.days ?? 0, 365));
1651
+ const aggregateShelterPool = Math.round(2e6 * (days / 365));
1652
+ const rawAllocations = input.allocations ?? [];
1653
+ if (rawAllocations.length === 0) issues.push("Alberta Schedule 6: the corporation is associated (006002 = 1), but no AACRS allocation rows (006030-006034) were supplied. This filer's own Crown Royalty Shelter (006006) defaulted to zero.");
1654
+ const allocations = resolveAllocations(rawAllocations, aggregateShelterPool, issues);
1655
+ const totalAllocated = allocations.reduce((s, a) => s + a.allocatedAmount, 0);
1656
+ return {
1657
+ associatedWithCrownRoyaltyCorporations,
1658
+ albertaCrownRoyaltyIncurred,
1659
+ crownRoyaltyShelter: allocations[0]?.allocatedAmount ?? 0,
1660
+ weightedAverageRate,
1661
+ longestAssociatedYear: {
1662
+ ...input.longestAssociatedYear?.albertaCan !== void 0 ? { albertaCan: input.longestAssociatedYear.albertaCan } : {},
1663
+ ...input.longestAssociatedYear?.taxationYearBeginning !== void 0 ? { taxationYearBeginning: input.longestAssociatedYear.taxationYearBeginning } : {},
1664
+ ...input.longestAssociatedYear?.taxationYearEnding !== void 0 ? { taxationYearEnding: input.longestAssociatedYear.taxationYearEnding } : {},
1665
+ days
1666
+ },
1667
+ aggregateShelterPool,
1668
+ allocations,
1669
+ totalAllocated,
1670
+ formRequired,
1671
+ issues
1672
+ };
1673
+ }
1674
+ //#endregion
1675
+ //#region src/t2/at1/schedules/schedule7-royalty-supplemental.ts
1676
+ /** Signed whole-dollar rounding — no floor, several of these lines are marked "+/-". */
1677
+ const rd = (v) => Math.round(v ?? 0);
1678
+ /** Non-negative whole-dollar rounding, for the "+"-only lines. */
1679
+ const nn$29 = (v) => Math.max(0, Math.round(v ?? 0));
1680
+ const round4 = (v) => Math.round(v * 1e4) / 1e4;
1681
+ function resolvePartnership(p, index, issues) {
1682
+ if (!p.name) issues.push(`Alberta Schedule 7: partnership row ${index + 1} has no name (007071 is mandatory for every PITI occurrence).`);
1683
+ if (p.interestPercent < 0 || p.interestPercent > 1) issues.push(`Alberta Schedule 7: ${p.name || `partnership row ${index + 1}`} has an interest of ${p.interestPercent}, expected a decimal between 0 and 1 (007073, e.g. .7500 for 75%).`);
1684
+ return {
1685
+ name: p.name,
1686
+ interestPercent: round4(p.interestPercent),
1687
+ ...p.fiscalPeriodEnd !== void 0 ? { fiscalPeriodEnd: p.fiscalPeriodEnd } : {},
1688
+ shareEligibleForCredit: nn$29(p.shareEligibleForCredit),
1689
+ shareOtherRoyaltiesNotEligible: nn$29(p.shareOtherRoyaltiesNotEligible),
1690
+ shareOtherCrownChargesEligibleForDeduction: nn$29(p.shareOtherCrownChargesEligibleForDeduction)
1691
+ };
1692
+ }
1693
+ function resolveAdjustment(a, index, issues) {
1694
+ if (!a.priorProductionPeriodEnd) issues.push(`Alberta Schedule 7: prior-year adjustment row ${index + 1} has no prior production period (007083 is mandatory for every ACRA occurrence).`);
1695
+ if (a.sourceOfAdjustment !== 1 && a.sourceOfAdjustment !== 2) issues.push(`Alberta Schedule 7: prior-year adjustment row ${index + 1} has no valid source code (007085 must be 1 = Dept. of Resource Development or 2 = Operator).`);
1696
+ return {
1697
+ ...a.priorProductionPeriodEnd !== void 0 ? { priorProductionPeriodEnd: a.priorProductionPeriodEnd } : {},
1698
+ ...a.sourceOfAdjustment !== void 0 ? { sourceOfAdjustment: a.sourceOfAdjustment } : {},
1699
+ increase: nn$29(a.increase),
1700
+ decrease: nn$29(a.decrease),
1701
+ adjustmentNotEligibleForCredit: rd(a.adjustmentNotEligibleForCredit)
1702
+ };
1703
+ }
1704
+ function schedule7LineItemId(fieldId, occurrence = 1) {
1705
+ return `007${fieldId}${String(occurrence).padStart(3, "0")}`;
1706
+ }
1707
+ /**
1708
+ * Emits CPI (007003-029), the computed totals 007051 and 007061, and the two
1709
+ * repeating sections — PITI (007071-081, one occurrence per partnership) and
1710
+ * ACRA (007083-091, one occurrence per prior-year adjustment).
1711
+ *
1712
+ * 007061 is filed even though it has no defining row of its own anywhere in
1713
+ * this schedule's MAPPINGS block (spec lines 6654-7251) — see the module
1714
+ * docstring's "Two totals with NO defining row of their own in this MAPPINGS
1715
+ * table" section. It is a real Schedule 7 output line: AT1 Schedule 5's own
1716
+ * field 005001 definition (spec lines 5237-5248) names it explicitly as
1717
+ * "Schedule 7, line 061" and gives its formula in full, so it is filed here
1718
+ * under that citation rather than omitted for lack of a home row.
1719
+ */
1720
+ function schedule7Values(result) {
1721
+ const values = [];
1722
+ const put = (fieldId, value, occurrence = 1) => values.push({
1723
+ lineItemId: schedule7LineItemId(fieldId, occurrence),
1724
+ value
1725
+ });
1726
+ put("003", result.eligibleCrownRoyalty);
1727
+ put("005", result.otherRoyaltiesNotEligible);
1728
+ put("007", result.royaltyPaidToOtherJurisdictions);
1729
+ put("009", result.nonDeductibleCrownLeaseRentals);
1730
+ put("011", result.mineralTaxes);
1731
+ put("013", result.saskatchewanResourcesSurcharge);
1732
+ result.otherNonDeductibleCrownChargeTypes.forEach((name, i) => {
1733
+ values.push({
1734
+ lineItemId: schedule7LineItemId(String(14 + i).padStart(3, "0")),
1735
+ value: name
1736
+ });
1737
+ });
1738
+ put("017", result.otherNonDeductibleCrownCharges);
1739
+ put("025", result.crownLeaseRentalsCapitalized);
1740
+ if (result.otherBalanceSheetDeductionName !== void 0) values.push({
1741
+ lineItemId: schedule7LineItemId("027"),
1742
+ value: result.otherBalanceSheetDeductionName
1743
+ });
1744
+ put("029", result.otherBalanceSheetDeduction);
1745
+ result.partnerships.forEach((p, i) => {
1746
+ const occurrence = i + 1;
1747
+ values.push({
1748
+ lineItemId: schedule7LineItemId("071", occurrence),
1749
+ value: p.name
1750
+ });
1751
+ put("073", p.interestPercent, occurrence);
1752
+ if (p.fiscalPeriodEnd !== void 0) values.push({
1753
+ lineItemId: schedule7LineItemId("075", occurrence),
1754
+ value: p.fiscalPeriodEnd
1755
+ });
1756
+ put("077", p.shareEligibleForCredit, occurrence);
1757
+ put("079", p.shareOtherRoyaltiesNotEligible, occurrence);
1758
+ put("081", p.shareOtherCrownChargesEligibleForDeduction, occurrence);
1759
+ });
1760
+ result.priorYearAdjustments.forEach((a, i) => {
1761
+ const occurrence = i + 1;
1762
+ if (a.priorProductionPeriodEnd !== void 0) values.push({
1763
+ lineItemId: schedule7LineItemId("083", occurrence),
1764
+ value: a.priorProductionPeriodEnd
1765
+ });
1766
+ if (a.sourceOfAdjustment !== void 0) put("085", a.sourceOfAdjustment, occurrence);
1767
+ put("087", a.increase, occurrence);
1768
+ put("089", a.decrease, occurrence);
1769
+ put("091", a.adjustmentNotEligibleForCredit, occurrence);
1770
+ });
1771
+ put("051", result.totalAdjustments);
1772
+ put("061", result.crownChargesNetOfReimbursements);
1773
+ return {
1774
+ scheduleId: "007",
1775
+ values
1776
+ };
1777
+ }
1778
+ function computeAlbertaSchedule7(input) {
1779
+ const issues = [];
1780
+ const eligibleCrownRoyalty = rd(input.eligibleCrownRoyalty);
1781
+ const otherRoyaltiesNotEligible = rd(input.otherRoyaltiesNotEligible);
1782
+ const royaltyPaidToOtherJurisdictions = rd(input.royaltyPaidToOtherJurisdictions);
1783
+ const nonDeductibleCrownLeaseRentals = rd(input.nonDeductibleCrownLeaseRentals);
1784
+ const mineralTaxes = rd(input.mineralTaxes);
1785
+ const saskatchewanResourcesSurcharge = rd(input.saskatchewanResourcesSurcharge);
1786
+ const otherNonDeductibleCrownChargeTypes = (input.otherNonDeductibleCrownChargeTypes ?? []).slice(0, 3);
1787
+ if ((input.otherNonDeductibleCrownChargeTypes?.length ?? 0) > 3) issues.push("Alberta Schedule 7: more than three other non-deductible crown charge types were supplied (007014/015/016 allow three); only the first three were kept.");
1788
+ let otherNonDeductibleCrownCharges = rd(input.otherNonDeductibleCrownCharges);
1789
+ if (otherNonDeductibleCrownChargeTypes.length === 0 && otherNonDeductibleCrownCharges !== 0) {
1790
+ issues.push("Alberta Schedule 7: an amount was given for other non-deductible crown charges (007017) but no charge type was named (007014/015/016). The spec defaults 007017 to zero when no type is named; the amount was zeroed out.");
1791
+ otherNonDeductibleCrownCharges = 0;
1792
+ }
1793
+ const crownLeaseRentalsCapitalized = rd(input.crownLeaseRentalsCapitalized);
1794
+ let otherBalanceSheetDeduction = nn$29(input.otherBalanceSheetDeduction);
1795
+ if (!input.otherBalanceSheetDeductionName && otherBalanceSheetDeduction !== 0) {
1796
+ issues.push("Alberta Schedule 7: an amount was given for another balance sheet eligible deduction (007029) but no deduction was named (007027). The spec defaults 007029 to zero when no name is given; the amount was zeroed out.");
1797
+ otherBalanceSheetDeduction = 0;
1798
+ }
1799
+ const partnerships = (input.partnerships ?? []).map((p, i) => resolvePartnership(p, i, issues));
1800
+ const totalPartnershipShareEligibleForCredit = partnerships.reduce((s, p) => s + p.shareEligibleForCredit, 0);
1801
+ const totalPartnershipShareOtherRoyaltiesNotEligible = partnerships.reduce((s, p) => s + p.shareOtherRoyaltiesNotEligible, 0);
1802
+ const totalPartnershipShareOtherCrownCharges = partnerships.reduce((s, p) => s + p.shareOtherCrownChargesEligibleForDeduction, 0);
1803
+ const priorYearAdjustments = (input.priorYearAdjustments ?? []).map((a, i) => resolveAdjustment(a, i, issues));
1804
+ const totalIncrease = priorYearAdjustments.reduce((s, a) => s + a.increase, 0);
1805
+ const totalDecrease = priorYearAdjustments.reduce((s, a) => s + a.decrease, 0);
1806
+ const totalAdjustmentNotEligible = priorYearAdjustments.reduce((s, a) => s + a.adjustmentNotEligibleForCredit, 0);
1807
+ const totalAdjustments = priorYearAdjustments.length === 0 ? 0 : totalIncrease - totalDecrease + totalAdjustmentNotEligible;
1808
+ const crownChargesNetOfReimbursements = eligibleCrownRoyalty + otherRoyaltiesNotEligible + royaltyPaidToOtherJurisdictions + nonDeductibleCrownLeaseRentals + mineralTaxes + saskatchewanResourcesSurcharge + otherNonDeductibleCrownCharges + crownLeaseRentalsCapitalized + otherBalanceSheetDeduction + totalPartnershipShareEligibleForCredit + totalPartnershipShareOtherRoyaltiesNotEligible + totalPartnershipShareOtherCrownCharges - totalAdjustments;
1809
+ const albertaCrownRoyaltyForSchedule6 = eligibleCrownRoyalty + totalPartnershipShareEligibleForCredit - totalIncrease + totalDecrease;
1810
+ return {
1811
+ eligibleCrownRoyalty,
1812
+ otherRoyaltiesNotEligible,
1813
+ royaltyPaidToOtherJurisdictions,
1814
+ nonDeductibleCrownLeaseRentals,
1815
+ mineralTaxes,
1816
+ saskatchewanResourcesSurcharge,
1817
+ otherNonDeductibleCrownChargeTypes,
1818
+ otherNonDeductibleCrownCharges,
1819
+ crownLeaseRentalsCapitalized,
1820
+ ...input.otherBalanceSheetDeductionName !== void 0 ? { otherBalanceSheetDeductionName: input.otherBalanceSheetDeductionName } : {},
1821
+ otherBalanceSheetDeduction,
1822
+ partnerships,
1823
+ totalPartnershipShareEligibleForCredit,
1824
+ totalPartnershipShareOtherRoyaltiesNotEligible,
1825
+ totalPartnershipShareOtherCrownCharges,
1826
+ priorYearAdjustments,
1827
+ totalAdjustments,
1828
+ crownChargesNetOfReimbursements,
1829
+ albertaCrownRoyaltyForSchedule6,
1830
+ issues
1831
+ };
1832
+ }
1833
+ //#endregion
1834
+ //#region src/t2/at1/schedules/schedule8-political-contributions.ts
1835
+ const nn$28 = (v) => Math.max(0, Math.round(v ?? 0));
1836
+ function yearOf(dateIso) {
1837
+ if (!dateIso || dateIso.length < 4) return void 0;
1838
+ const y = Number(dateIso.slice(0, 4));
1839
+ return Number.isFinite(y) ? y : void 0;
1840
+ }
1841
+ /** Tiered rate for the "all made in 2003 or earlier" branch. */
1842
+ function tierTo2003(a) {
1843
+ if (a <= 150) return a * .75;
1844
+ if (a <= 825) return 112.5 + (a - 150) * .5;
1845
+ return 450 + (a - 825) * .333;
1846
+ }
1847
+ /** Tiered rate for the "all made in 2004 or later" branch. */
1848
+ function tierFrom2004(a) {
1849
+ if (a <= 200) return a * .75;
1850
+ if (a <= 900) return 150 + (a - 200) * .5;
1851
+ return 600 + (a - 900) * .333;
1852
+ }
1853
+ function computeSchedule8$1(input) {
1854
+ const issues = [];
1855
+ const contributions = input.contributions ?? [];
1856
+ contributions.forEach((c, i) => {
1857
+ if (!c.dateOfDonation) issues.push(`Alberta Schedule 8: contribution #${i + 1} (${c.name || "unnamed"}) has no date of donation (008006) — the spec requires one for every receipted contribution.`);
1858
+ if (!c.receiptNumber) issues.push(`Alberta Schedule 8: contribution #${i + 1} (${c.name || "unnamed"}) has no official receipt number (008004).`);
1859
+ });
1860
+ const partnershipContributionsTo2003 = nn$28(input.partnershipContributionsTo2003);
1861
+ const partnershipContributionsFrom2004 = nn$28(input.partnershipContributionsFrom2004);
1862
+ const totalDirect = contributions.reduce((sum, c) => sum + nn$28(c.amount), 0);
1863
+ const directTo2003 = contributions.filter((c) => (yearOf(c.dateOfDonation) ?? 0) <= 2003).reduce((sum, c) => sum + nn$28(c.amount), 0);
1864
+ const directFrom2004 = contributions.filter((c) => (yearOf(c.dateOfDonation) ?? 9999) >= 2004).reduce((sum, c) => sum + nn$28(c.amount), 0);
1865
+ const directIn2004Only = contributions.filter((c) => yearOf(c.dateOfDonation) === 2004).reduce((sum, c) => sum + nn$28(c.amount), 0);
1866
+ const hasTo2003 = directTo2003 > 0 || partnershipContributionsTo2003 > 0;
1867
+ const hasFrom2004 = directFrom2004 > 0 || partnershipContributionsFrom2004 > 0;
1868
+ if (!hasTo2003 && !hasFrom2004) return {
1869
+ contributions,
1870
+ partnershipContributionsTo2003,
1871
+ partnershipContributionsFrom2004,
1872
+ period: "none",
1873
+ creditBeforeCeiling: 0,
1874
+ credit: 0,
1875
+ issues
1876
+ };
1877
+ const taxYearBeginYear = yearOf(input.taxYearBegin);
1878
+ const taxYearEndYear = yearOf(input.taxYearEnd);
1879
+ const taxYearStraddles2003to2004 = taxYearBeginYear === 2003 && taxYearEndYear === 2004;
1880
+ const ceiling = input.remainingBasicTax;
1881
+ let period;
1882
+ let rawCredit;
1883
+ let credit;
1884
+ if (hasTo2003 && !hasFrom2004) {
1885
+ period = "to-2003";
1886
+ rawCredit = tierTo2003(totalDirect + partnershipContributionsTo2003);
1887
+ if (ceiling == null) {
1888
+ issues.push("Alberta Schedule 8: remainingBasicTax (000068 − (000070+000071+000072)) was not supplied, so no political contributions credit can be claimed for the 2003-or-earlier rate period.");
1889
+ credit = 0;
1890
+ } else credit = Math.max(0, Math.round(Math.min(rawCredit, 750, ceiling)));
1891
+ } else if (hasFrom2004 && !hasTo2003) {
1892
+ period = "from-2004";
1893
+ rawCredit = tierFrom2004(totalDirect + partnershipContributionsFrom2004);
1894
+ if (ceiling == null) {
1895
+ issues.push("Alberta Schedule 8: remainingBasicTax (000068 − (000070+000071+000072)) was not supplied, so no political contributions credit can be claimed for the 2004-or-later rate period.");
1896
+ credit = 0;
1897
+ } else credit = Math.max(0, Math.round(Math.min(rawCredit, 1e3, ceiling)));
1898
+ } else if (taxYearStraddles2003to2004) {
1899
+ period = "straddle-2003-2004";
1900
+ const x = directIn2004Only + partnershipContributionsFrom2004;
1901
+ const y = totalDirect + partnershipContributionsTo2003 + partnershipContributionsFrom2004;
1902
+ const a = Math.min(y, 150);
1903
+ const b = Math.min(Math.max(0, x - a), 50);
1904
+ const c = Math.min(Math.max(0, y - (a + b)), 675);
1905
+ const d = Math.min(Math.max(0, x - (a + b + c)), 225);
1906
+ const e = Math.min(Math.max(0, y - (a + b + c + d)), 900);
1907
+ const f = Math.min(Math.max(0, x - (a + b + c + d + e)), 300);
1908
+ rawCredit = .75 * a + .75 * b + .5 * c + .5 * d + e / 3 + f / 3;
1909
+ credit = Math.max(0, Math.round(rawCredit));
1910
+ } else {
1911
+ period = "none";
1912
+ rawCredit = 0;
1913
+ credit = 0;
1914
+ issues.push("Alberta Schedule 8: contributions span both the 2003-or-earlier and 2004-or-later rate periods, but the tax year does not begin in 2003 and end in 2004 — the spec only defines a mixed-period formula for that straddling tax year. No credit computed.");
1915
+ }
1916
+ return {
1917
+ contributions,
1918
+ partnershipContributionsTo2003,
1919
+ partnershipContributionsFrom2004,
1920
+ period,
1921
+ creditBeforeCeiling: Math.round(rawCredit),
1922
+ credit,
1923
+ issues
1924
+ };
1925
+ }
1926
+ /**
1927
+ * Net File line items for AT1 Schedule 8: one PCD occurrence per contribution
1928
+ * (002 name, 004 receipt number, 006 date, 008 amount), plus the two APC
1929
+ * partnership totals (012, 013).
1930
+ *
1931
+ * Does NOT emit jacket line 000074 (the actual tax credit) — that is a
1932
+ * jacket line, not a Schedule 8 line. Use `result.credit` for that.
1933
+ */
1934
+ function schedule8Values(result) {
1935
+ const values = [];
1936
+ result.contributions.forEach((c, i) => {
1937
+ const n = i + 1;
1938
+ const put = (fieldId, value) => values.push({
1939
+ lineItemId: at1LineItemId("008", fieldId, n),
1940
+ value
1941
+ });
1942
+ put("002", c.name);
1943
+ put("004", c.receiptNumber);
1944
+ put("006", c.dateOfDonation);
1945
+ put("008", Math.max(0, Math.round(c.amount)));
1946
+ });
1947
+ values.push({
1948
+ lineItemId: at1LineItemId("008", "012"),
1949
+ value: result.partnershipContributionsTo2003
1950
+ });
1951
+ values.push({
1952
+ lineItemId: at1LineItemId("008", "013"),
1953
+ value: result.partnershipContributionsFrom2004
1954
+ });
1955
+ return {
1956
+ scheduleId: "008",
1957
+ values
1958
+ };
1959
+ }
1960
+ //#endregion
1961
+ //#region src/t2/at1/schedules/schedule9-sred-tax-credit.ts
1962
+ const nn$27 = (v) => Math.max(0, Math.round(v ?? 0));
1963
+ /** Signed whole-dollar rounding, for fields the spec marks "+/-". */
1964
+ const signed = (v) => Math.round(v);
1965
+ /** 009120's flat rate — "lesser of line 009031 and 009108 X 10%". */
1966
+ const ALBERTA_SRED_TAX_CREDIT_RATE = .1;
1967
+ /** Alberta's SR&ED program did not exist before this date (line 009104's note). */
1968
+ const ALBERTA_SRED_PROGRAM_START = "2009-01-01";
1969
+ /** Alberta SR&ED expenditures carried out after this date are not eligible (module docstring). */
1970
+ const ALBERTA_SRED_EXPENDITURE_CUTOFF = "2019-12-31";
1971
+ /**
1972
+ * 009104 / 009206's day-prorated $4,000,000 expenditure limit. Days are clamped
1973
+ * to [0, 366] — 366 only for a year genuinely spanning a February 29, per the
1974
+ * spec's own note.
1975
+ */
1976
+ function computeSchedule9MaximumExpenditureLimit(daysInTaxYear = 365) {
1977
+ return Math.round(4e6 * (Math.max(0, Math.min(daysInTaxYear, 366)) / 365));
1978
+ }
1979
+ function computeAlbertaSchedule9(input) {
1980
+ const issues = [];
1981
+ const federalQualifiedExpenditures = nn$27(input.federalQualifiedExpenditures);
1982
+ const albertaPortionOfExpenditures = nn$27(input.albertaPortionOfExpenditures);
1983
+ const federalProxyAmountInAlbertaPortion = nn$27(input.federalProxyAmountInAlbertaPortion);
1984
+ const albertaProxyAmount = nn$27(input.albertaProxyAmount);
1985
+ const albertaCreditReducingFederalExpense = nn$27(input.albertaCreditReducingFederalExpense);
1986
+ const priorYearFederalItcReceived = nn$27(input.priorYearFederalItcReceived);
1987
+ const totalAlbertaExpendituresAllYears = nn$27(input.totalAlbertaExpendituresAllYears);
1988
+ const totalFederalExpendituresAllYears = nn$27(input.totalFederalExpendituresAllYears);
1989
+ const albertaPortionOfRepayments = nn$27(input.albertaPortionOfRepayments);
1990
+ const disposalRecapture = nn$27(input.disposalRecapture);
1991
+ const priorYearFederalItcAdjustment = nn$27(input.priorYearFederalItcAdjustment);
1992
+ if (albertaPortionOfExpenditures > federalQualifiedExpenditures) issues.push(`Alberta Schedule 9: the Alberta portion of expenditures (009005 = ${albertaPortionOfExpenditures}) exceeds the federal total (009003 = ${federalQualifiedExpenditures}). 009005 must not exceed 009003.`);
1993
+ const priorYearItcAlbertaPortion = totalFederalExpendituresAllYears > 0 ? Math.round(priorYearFederalItcReceived * totalAlbertaExpendituresAllYears / totalFederalExpendituresAllYears) : 0;
1994
+ if (totalFederalExpendituresAllYears === 0 && priorYearFederalItcReceived > 0) issues.push("Alberta Schedule 9: a prior-year federal ITC (009015) was supplied but total federal expenditures for those years (009019) is nil, so no Alberta portion (009023) could be allocated. Supply 009019 if a prior-year ITC allocation is expected.");
1995
+ const derivedEligibleExpenditures = albertaPortionOfExpenditures - federalProxyAmountInAlbertaPortion + albertaProxyAmount + albertaCreditReducingFederalExpense - priorYearItcAlbertaPortion + albertaPortionOfRepayments;
1996
+ const eligibleExpenditures = input.eligibleExpenditures !== void 0 ? signed(input.eligibleExpenditures) : derivedEligibleExpenditures;
1997
+ if (input.eligibleExpenditures === void 0) issues.push(`Alberta Schedule 9: line 009031 ("Total eligible expenditures for Alberta purposes") has no formula in the TRA spec's mapping tables (the text jumps from 009025 to 009040). Derived here as 005 − 007 + 009 + 011 − 023 + 025 = ${derivedEligibleExpenditures}, from the lines the spec captions unambiguously "Deduct"/"Add". Supply eligibleExpenditures directly to override with the authoritative figure — see the Guide to Claiming the Alberta SR&ED Tax Credit.`);
1998
+ if (input.fieldOfScience === void 0) issues.push("Alberta Schedule 9: line 009040 (primary field of science or technology) is mandatory on the live form and was not supplied.");
1999
+ const isAssociated = input.isAssociated ?? false;
2000
+ const nonAssociatedMaximumExpenditureLimit = computeSchedule9MaximumExpenditureLimit(input.daysInTaxYear ?? 365);
2001
+ let maximumExpenditureLimit;
2002
+ if (isAssociated) if (input.allocatedExpenditureLimit === void 0) {
2003
+ issues.push("Alberta Schedule 9: the corporation is associated (009100 = 1) but no allocated expenditure limit (009102) was supplied. Complete the Allocation of the Maximum Expenditure Limit (page 3) and pass its result as allocatedExpenditureLimit — see allocateSchedule9ExpenditureLimit.");
2004
+ maximumExpenditureLimit = 0;
2005
+ } else maximumExpenditureLimit = nn$27(input.allocatedExpenditureLimit);
2006
+ else maximumExpenditureLimit = nonAssociatedMaximumExpenditureLimit;
2007
+ if (input.taxationYearEnd !== void 0 && input.taxationYearEnd > "2019-12-31") issues.push(`Alberta Schedule 9: the taxation year end (${input.taxationYearEnd}) falls after ${ALBERTA_SRED_EXPENDITURE_CUTOFF}. The Alberta SR&ED Tax Credit may not be claimed for expenditures carried out in Alberta after that date — confirm albertaPortionOfExpenditures (009005) excludes any post-cutoff spending.`);
2008
+ const netCredit = Math.round(Math.min(eligibleExpenditures, maximumExpenditureLimit) * ALBERTA_SRED_TAX_CREDIT_RATE) - disposalRecapture - priorYearFederalItcAdjustment;
2009
+ return {
2010
+ federalQualifiedExpenditures,
2011
+ albertaPortionOfExpenditures,
2012
+ federalProxyAmountInAlbertaPortion,
2013
+ albertaProxyAmount,
2014
+ albertaCreditReducingFederalExpense,
2015
+ priorYearFederalItcReceived,
2016
+ totalAlbertaExpendituresAllYears,
2017
+ totalFederalExpendituresAllYears,
2018
+ priorYearItcAlbertaPortion,
2019
+ albertaPortionOfRepayments,
2020
+ eligibleExpenditures,
2021
+ derivedEligibleExpenditures,
2022
+ fieldOfScience: input.fieldOfScience,
2023
+ isAssociated,
2024
+ nonAssociatedMaximumExpenditureLimit,
2025
+ maximumExpenditureLimit,
2026
+ disposalRecapture,
2027
+ priorYearFederalItcAdjustment,
2028
+ netCredit,
2029
+ issues
2030
+ };
2031
+ }
2032
+ /**
2033
+ * Allocate the day-prorated $4,000,000 maximum expenditure limit among an
2034
+ * associated group (page 3). Per the spec, EACH occurrence of 009240 and the
2035
+ * SUM of all occurrences are independently capped at the limit — unlike a
2036
+ * running-remainder split, one member requesting more than the limit does not
2037
+ * consume another member's room; it is simply capped and flagged.
2038
+ */
2039
+ function allocateSchedule9ExpenditureLimit(daysInLongestYear, requested) {
2040
+ const issues = [];
2041
+ const days = Math.max(0, Math.min(daysInLongestYear, 366));
2042
+ const maximumExpenditureLimit = computeSchedule9MaximumExpenditureLimit(days);
2043
+ const members = requested.map((m) => {
2044
+ const requestedAmount = nn$27(m.allocated);
2045
+ if (requestedAmount > maximumExpenditureLimit) issues.push(`Alberta Schedule 9: ${m.name} was allocated ${requestedAmount}, exceeding the maximum expenditure limit of ${maximumExpenditureLimit} (009240 cannot exceed $4,000,000 x 009206/365). Capped at ${maximumExpenditureLimit}.`);
2046
+ return {
2047
+ name: m.name,
2048
+ ...m.albertaCan !== void 0 ? { albertaCan: m.albertaCan } : {},
2049
+ allocated: Math.min(requestedAmount, maximumExpenditureLimit)
2050
+ };
2051
+ });
2052
+ const totalAllocated = members.reduce((s, m) => s + m.allocated, 0);
2053
+ if (totalAllocated > maximumExpenditureLimit) issues.push(`Alberta Schedule 9: the associated group allocated a total of ${totalAllocated}, exceeding the maximum expenditure limit of ${maximumExpenditureLimit} (the sum of all 009240 occurrences cannot exceed $4,000,000 x 009206/365). The group must agree a valid split.`);
2054
+ return {
2055
+ daysInLongestYear: days,
2056
+ maximumExpenditureLimit,
2057
+ members,
2058
+ totalAllocated,
2059
+ claimantAllocatedAmount: members[0]?.allocated ?? 0,
2060
+ unallocated: Math.max(0, maximumExpenditureLimit - totalAllocated),
2061
+ issues
2062
+ };
2063
+ }
2064
+ function schedule9LineItemId(fieldId, occurrence = 1) {
2065
+ return `009${fieldId}${String(occurrence).padStart(3, "0")}`;
2066
+ }
2067
+ /**
2068
+ * Field ids per the spec transcription in the module docstring: 003-025 (the
2069
+ * expenditure buildup), 040 (field of science), 100-120 (the credit
2070
+ * calculation), and — when `group` is supplied — 200-240 (page 3's
2071
+ * allocation). Line 031 has no confirmed transmitted status of its own (see
2072
+ * the module docstring's "line 009031 has no formula in the spec text"), but
2073
+ * is filed anyway alongside 106 since both carry the identical "Total
2074
+ * eligible expenditures for Alberta purposes" figure per the spec's own
2075
+ * cross-reference ("106 ... Value must equal 009031").
2076
+ */
2077
+ function schedule9Values(result, group) {
2078
+ const values = [];
2079
+ const put = (fieldId, value) => values.push({
2080
+ lineItemId: schedule9LineItemId(fieldId),
2081
+ value
2082
+ });
2083
+ put("003", result.federalQualifiedExpenditures);
2084
+ put("005", result.albertaPortionOfExpenditures);
2085
+ put("007", result.federalProxyAmountInAlbertaPortion);
2086
+ put("009", result.albertaProxyAmount);
2087
+ put("011", result.albertaCreditReducingFederalExpense);
2088
+ put("015", result.priorYearFederalItcReceived);
2089
+ put("017", result.totalAlbertaExpendituresAllYears);
2090
+ put("019", result.totalFederalExpendituresAllYears);
2091
+ put("023", result.priorYearItcAlbertaPortion);
2092
+ put("025", result.albertaPortionOfRepayments);
2093
+ put("031", result.eligibleExpenditures);
2094
+ if (result.fieldOfScience !== void 0) put("040", result.fieldOfScience);
2095
+ put("100", result.isAssociated ? 1 : 2);
2096
+ if (result.isAssociated) put("102", result.maximumExpenditureLimit);
2097
+ else put("104", result.nonAssociatedMaximumExpenditureLimit);
2098
+ put("106", result.eligibleExpenditures);
2099
+ put("108", result.maximumExpenditureLimit);
2100
+ put("112", result.disposalRecapture);
2101
+ put("116", result.priorYearFederalItcAdjustment);
2102
+ put("120", result.netCredit);
2103
+ if (group) {
2104
+ if (group.longestYearCan !== void 0) values.push({
2105
+ lineItemId: schedule9LineItemId("200"),
2106
+ value: group.longestYearCan
2107
+ });
2108
+ if (group.longestYearBegin !== void 0) values.push({
2109
+ lineItemId: schedule9LineItemId("202"),
2110
+ value: group.longestYearBegin
2111
+ });
2112
+ if (group.longestYearEnd !== void 0) values.push({
2113
+ lineItemId: schedule9LineItemId("204"),
2114
+ value: group.longestYearEnd
2115
+ });
2116
+ values.push({
2117
+ lineItemId: schedule9LineItemId("206"),
2118
+ value: group.allocation.daysInLongestYear
2119
+ });
2120
+ group.allocation.members.forEach((m, i) => {
2121
+ const occurrence = i + 1;
2122
+ values.push({
2123
+ lineItemId: schedule9LineItemId("220", occurrence),
2124
+ value: m.name
2125
+ });
2126
+ if (m.albertaCan !== void 0) values.push({
2127
+ lineItemId: schedule9LineItemId("230", occurrence),
2128
+ value: m.albertaCan
2129
+ });
2130
+ values.push({
2131
+ lineItemId: schedule9LineItemId("240", occurrence),
2132
+ value: m.allocated
2133
+ });
2134
+ });
2135
+ }
2136
+ return {
2137
+ scheduleId: "009",
2138
+ values
2139
+ };
2140
+ }
2141
+ //#endregion
2142
+ //#region src/t2/at1/schedules/schedule11-manufacturing-processing.ts
2143
+ const nn$26 = (v) => Math.max(0, Math.round(v ?? 0));
2144
+ function computeSchedule11(input) {
2145
+ const issues = [];
2146
+ const dateEligible = input.taxYearStart < "2001-04-01";
2147
+ if (!dateEligible) issues.push(`Schedule 11: the Alberta Manufacturing and Processing Profits Deduction applies only where the tax year begins before 2001-04-01 (TRA-spec lines 9135-9146). This tax year begins ${input.taxYearStart}, so no deduction applies; line 011042 is reported as nil regardless of the workings below.`);
2148
+ let grossRevenueRatio;
2149
+ let revenueEligible = true;
2150
+ if (dateEligible) if (input.manufacturingGrossRevenue == null || input.totalGrossRevenue == null) {
2151
+ revenueEligible = false;
2152
+ issues.push("Schedule 11: the 10% manufacturing gross-revenue test (TRA-spec lines 9138-9142) could not be run because gross revenue figures were not supplied, so form 011 is treated as not applicable.");
2153
+ } else {
2154
+ const total = nn$26(input.totalGrossRevenue);
2155
+ grossRevenueRatio = total > 0 ? nn$26(input.manufacturingGrossRevenue) / total : 0;
2156
+ if (grossRevenueRatio < .1) {
2157
+ revenueEligible = false;
2158
+ issues.push(`Schedule 11: manufacturing gross revenue is ${(grossRevenueRatio * 100).toFixed(1)}% of total gross revenue, below the 10% threshold in the AT1 spec, so form 011 is not applicable.`);
2159
+ }
2160
+ }
2161
+ const eligible = dateEligible && revenueEligible;
2162
+ let albertaAdjubi;
2163
+ if (input.albertaAdjubiFromSchedule12) albertaAdjubi = Math.max(0, Math.round(input.albertaAdjubiFromSchedule12.line112 + input.albertaAdjubiFromSchedule12.line114));
2164
+ else {
2165
+ albertaAdjubi = nn$26(input.federalAdjubi);
2166
+ if (input.federalAdjubi == null && eligible) issues.push("Schedule 11: neither `federalAdjubi` (fed 027130) nor `albertaAdjubiFromSchedule12` was supplied; line 011001 defaults to nil.");
2167
+ }
2168
+ let aggregateInvestmentIncome;
2169
+ if (input.isCcpc) if (input.schedule12Exists) aggregateInvestmentIncome = nn$26(input.albertaAggregateInvestmentIncome);
2170
+ else {
2171
+ aggregateInvestmentIncome = nn$26(input.federalAggregateInvestmentIncome);
2172
+ if (input.federalAggregateInvestmentIncome == null) issues.push("Schedule 11: the corp is a CCPC and no Alberta Schedule 12 exists, so line 011013 must equal fed 200440, but `federalAggregateInvestmentIncome` was not supplied; line 011013 defaults to nil.");
2173
+ }
2174
+ const isSmallManufacturingCorp = input.isSmallManufacturingCorp ?? false;
2175
+ let costOfCapital = 0;
2176
+ let albertaCostOfCapital = 0;
2177
+ let costOfLabour = 0;
2178
+ let albertaCostOfLabour = 0;
2179
+ let albertaManufacturingProcessingProfits = 0;
2180
+ if (isSmallManufacturingCorp) {
2181
+ if (input.costOfCapital != null || input.albertaCostOfCapital != null || input.costOfLabour != null || input.albertaCostOfLabour != null) issues.push("Schedule 11: lines 011031-011039 must not exist for a small manufacturing corp, but capital/labour figures were supplied; they are ignored for line 011042.");
2182
+ if (input.smallManufacturerAmpp != null) albertaManufacturingProcessingProfits = nn$26(input.smallManufacturerAmpp);
2183
+ else if (eligible) issues.push("Schedule 11: the small-manufacturing-corp formula for line 011042 is not stated in the transcribed spec range (TRA-spec lines 9082-9397); supply `smallManufacturerAmpp` directly per the AT1 Guide criteria. Line 011042 defaults to nil.");
2184
+ } else {
2185
+ costOfCapital = nn$26(input.costOfCapital);
2186
+ albertaCostOfCapital = nn$26(input.albertaCostOfCapital);
2187
+ costOfLabour = nn$26(input.costOfLabour);
2188
+ albertaCostOfLabour = nn$26(input.albertaCostOfLabour);
2189
+ if (albertaCostOfCapital > costOfCapital) {
2190
+ issues.push(`Schedule 11: line 011033 (Alberta Cost of Manufacturing and Processing Capital, ${albertaCostOfCapital}) exceeds line 011031 (Cost of Capital, ${costOfCapital}); the AT1 spec requires 011033 ≤ 011031. Clamped to ${costOfCapital}.`);
2191
+ albertaCostOfCapital = costOfCapital;
2192
+ }
2193
+ if (albertaCostOfLabour > costOfLabour) {
2194
+ issues.push(`Schedule 11: line 011039 (Alberta Cost of Manufacturing and Processing Labour, ${albertaCostOfLabour}) exceeds line 011037 (Cost of Labour, ${costOfLabour}); the AT1 spec requires 011039 ≤ 011037. Clamped to ${costOfLabour}.`);
2195
+ albertaCostOfLabour = costOfLabour;
2196
+ }
2197
+ const denominator = costOfCapital + costOfLabour;
2198
+ if (denominator === 0) {
2199
+ if (albertaAdjubi > 0 && eligible) issues.push("Schedule 11: Cost of Capital (011031) and Cost of Labour (011037) are both nil, so the line 011042 proration cannot be computed; supply them. Line 011042 defaults to nil.");
2200
+ } else {
2201
+ const factor = (Math.min(albertaCostOfCapital * 100 / 85, costOfCapital) + Math.min(albertaCostOfLabour * 100 / 75, costOfLabour)) / denominator;
2202
+ albertaManufacturingProcessingProfits = Math.round(albertaAdjubi * factor);
2203
+ }
2204
+ }
2205
+ if (!eligible && albertaManufacturingProcessingProfits > 0) issues.push(`Schedule 11: the workings above yield ${albertaManufacturingProcessingProfits}, but the deduction is not applicable this year (see the eligibility issue above); line 011042 is reported as nil.`);
2206
+ if (!eligible) albertaManufacturingProcessingProfits = 0;
2207
+ return {
2208
+ eligible,
2209
+ isSmallManufacturingCorp,
2210
+ albertaAdjubi,
2211
+ ...aggregateInvestmentIncome !== void 0 ? { aggregateInvestmentIncome } : {},
2212
+ costOfCapital,
2213
+ albertaCostOfCapital,
2214
+ costOfLabour,
2215
+ albertaCostOfLabour,
2216
+ albertaManufacturingProcessingProfits,
2217
+ ...grossRevenueRatio !== void 0 ? { grossRevenueRatio } : {},
2218
+ issues
2219
+ };
2220
+ }
2221
+ function schedule11LineItemId(fieldId, occurrence = 1) {
2222
+ return `011${fieldId}${String(occurrence).padStart(3, "0")}`;
2223
+ }
2224
+ /**
2225
+ * Field ids per the spec transcription above: 001 (AMPPD/ADJUBI), 013 (CCPC
2226
+ * aggregate investment income), 031/033/037/039 (cost of capital/labour, both
2227
+ * jurisdictions) and 042 (Alberta M&P Profits).
2228
+ *
2229
+ * Line 042 is ALWAYS emitted, including when the historical-eligibility gate
2230
+ * (pre-2001-04-01 tax year AND the 10% gross-revenue test) has forced it to
2231
+ * nil — an ineligible year still has a line 011042 on the form, and it reads
2232
+ * nil, so this files nil rather than omitting the line entirely. The REASON
2233
+ * it is nil (a `Schedule 11: … applies only where the tax year begins before
2234
+ * 2001-04-01 …` / `… below the 10% threshold …` entry) lives on
2235
+ * `result.issues`, which this builder does not carry onto the wire itself —
2236
+ * the caller already has `result` (this function's own input) and so already
2237
+ * has `result.issues` sitting beside whatever this returns; duplicating it
2238
+ * onto `At1ScheduleDataLike`, which has no field for prose, would only be
2239
+ * losing information conversion by not adding any.
2240
+ *
2241
+ * Line 013 (CCPC aggregate investment income) is the one line legitimately
2242
+ * OMITTED rather than filed as nil: it is undefined, not zero, for a non-CCPC
2243
+ * corporation — the spec's own business rule gates it on `000029 = 1 or 2`,
2244
+ * so a non-CCPC has no box to fill here at all, unlike line 042's "nil is a
2245
+ * real answer" case above.
2246
+ */
2247
+ function schedule11Values(result) {
2248
+ const values = [];
2249
+ const put = (fieldId, value) => values.push({
2250
+ lineItemId: schedule11LineItemId(fieldId),
2251
+ value
2252
+ });
2253
+ put("001", result.albertaAdjubi);
2254
+ if (result.aggregateInvestmentIncome !== void 0) put("013", result.aggregateInvestmentIncome);
2255
+ put("031", result.costOfCapital);
2256
+ put("033", result.albertaCostOfCapital);
2257
+ put("037", result.costOfLabour);
2258
+ put("039", result.albertaCostOfLabour);
2259
+ put("042", result.albertaManufacturingProcessingProfits);
2260
+ return {
2261
+ scheduleId: "011",
2262
+ values
2263
+ };
2264
+ }
2265
+ //#endregion
2266
+ //#region src/t2/at1/schedules/schedule15-resource-related-deductions.ts
2267
+ /**
2268
+ * Alberta AT1 Schedule 15 — Alberta Resource Related Deductions.
2269
+ *
2270
+ * Source: `research/sources/tra-spec/AT1-Chapter3-2025.2-full.txt`, lines
2271
+ * 12394-15937 (§3.2.3.16 "Schedule 15 - Alberta Resource Related Deductions").
2272
+ * There is NO standalone AT1SCH15 PDF under `research/sources/tra-forms/pdf/`
2273
+ * (unlike schedules 1, 2, 10, 12, 13, 16, 17, 18, 20, 21, 29, which all have
2274
+ * one) — the NetFile mapping spec above is the ONLY source available for this
2275
+ * schedule, so every field below is cited to that text file alone and cannot
2276
+ * be cross-checked against a rendered form layout.
2277
+ *
2278
+ * Like AT1 Schedule 18 (dispositions) and Schedule 13 (CCA), this is a
2279
+ * RECONCILIATION overlay, not a second engine: for most lines, the Alberta
2280
+ * figure defaults to the corresponding FEDERAL pool figure, and only the
2281
+ * lines where Alberta actually diverges are entered. The form is forbidden
2282
+ * when the return declares no Alberta/federal divergence (000060 AND 000061
2283
+ * both 2) and required when the opening balance or the claim for Alberta
2284
+ * purposes differs from federal (line 015 gating text, source lines
2285
+ * 12444-12456).
2286
+ *
2287
+ * Despite the task brief's expectation of a "resource allowance" pool, THERE
2288
+ * IS NO resource-allowance computation on this schedule — the federal
2289
+ * resource allowance deduction was repealed for taxation years after 1989
2290
+ * (phased out through the mid-1990s) and this schedule is entirely about the
2291
+ * EIGHT resource-expense CONTINUITY pools that survive it:
2292
+ *
2293
+ * EDA — Continuity of Earned Depletion Base (grandfathered; regular +
2294
+ * successor expenses), lines 001-021.
2295
+ * CMEDB — Continuity of Mining Exploration Depletion Base, lines 023-033.
2296
+ * CEE — Cumulative Canadian Exploration Expenses (regular + successor),
2297
+ * lines 041-083.
2298
+ * CDE — Cumulative Canadian Development Expenses (regular + successor),
2299
+ * lines 091-143.
2300
+ * CCOGPE — Cumulative Canadian Oil and Gas Property Expenses (regular +
2301
+ * successor), lines 151-191.
2302
+ * FEDE — Foreign Exploration and Development Expenses (regular +
2303
+ * successor), lines 201-233.
2304
+ * SFEDE — Specified Foreign Exploration and Development Expenses, PER
2305
+ * COUNTRY (regular + successor), lines 241-277.
2306
+ * CFRE — Cumulative Foreign Resource Expenses, PER COUNTRY (regular +
2307
+ * successor), lines 281-317.
2308
+ *
2309
+ * Each pool is modelled as its own `Input`/`Result` pair and its own pure
2310
+ * `computeXxx` function, per this directory's convention (see
2311
+ * `schedule18-dispositions.ts`, `schedule21-year-of-origin.ts`). A single
2312
+ * `computeAlbertaSchedule15` at the bottom composes all eight (accepting the
2313
+ * already-computed CCOGPE results where CDE needs them — see "CCOGPE ↔ CDE
2314
+ * cross-linkage" below) and applies the schedule-level 000060/000061 gate.
2315
+ *
2316
+ * ── Two proration conventions, NOT interchangeable ──────────────────────────
2317
+ *
2318
+ * The current-year-claim caps use TWO different short-tax-year formulas, and
2319
+ * the spec is explicit that they differ:
2320
+ *
2321
+ * - CDE (lines 115, 141) and CCOGPE (lines 169, 189) use a STEP function:
2322
+ * "if days in tax year ≥ 357, cap = rate × pool; if < 357, cap = rate ×
2323
+ * (days/365) × pool" — i.e. the proration is skipped entirely for a
2324
+ * near-full year.
2325
+ * - FEDE (line 209), SFEDE (line 253) and CFRE (lines 293, 313) use a
2326
+ * PLAIN `days/365` multiplier with no 357-day step.
2327
+ *
2328
+ * `stepYearFactor` implements the first; `linearYearFactor` the second. CEE
2329
+ * (lines 061, 081) and EDA/CMEDB have no day-proration at all — CEE is fully
2330
+ * claimable up to the pool balance in one year, no percentage rate applies.
2331
+ *
2332
+ * ── CCOGPE ↔ CDE cross-linkage (FLAGGED, not fully auto-wired) ──────────────
2333
+ *
2334
+ * When a CCOGPE pool's pre-claim subtotal is NEGATIVE, three things in the
2335
+ * spec text interact in a way this module resolves only PARTIALLY:
2336
+ *
2337
+ * 1. CDE line 105 ("Deduct: credit balance in the cumulative Canadian oil
2338
+ * and gas property expense pool", source lines 13604-13622) has its OWN
2339
+ * self-contained formula: `A = 015151+015153+015155+015157-015159-
2340
+ * 015161-015165-015167; if A < 0, value = A; otherwise enter fed
2341
+ * 012330`. This is unconditional — it does not mention the designation
2342
+ * election. This module implements 105 EXACTLY this way, computed from
2343
+ * the already-computed CCOGPE-regular pool subtotal (see
2344
+ * `computeCdeRegular`'s `ccogpeRegular` parameter).
2345
+ * 2. CCOGPE line 169 ("Deduct: current year claim...", source lines
2346
+ * 14281-14370) separately says that when that SAME subtotal A is
2347
+ * negative, it "must be carried forward to 015105" ONLY if the
2348
+ * corporation "has made a designation pursuant to subparagraph
2349
+ * 66.7(4)(a)(iii)" — and to 015133 (CDE SUCCESSOR, not 105) if it has
2350
+ * NOT. This CONTRADICTS (1)'s unconditional reading. Line 189 (source
2351
+ * lines 14646-14661) makes the analogous claim for the CCOGPE-successor
2352
+ * pool: negative → 015133 if designated, or "included in 015167"
2353
+ * (CCOGPE's OWN regular-pool deduction line, not a CDE line) if not.
2354
+ *
2355
+ * This module resolves (1) literally (105 is auto-computed, unconditionally,
2356
+ * from the CCOGPE-regular subtotal) because that is the more specific,
2357
+ * self-contained rule attached directly to line 105 itself. It does NOT
2358
+ * auto-route a negative CCOGPE-successor subtotal into CDE line 133 or back
2359
+ * into CCOGPE-regular line 167, because (a) line 133 has no unconditional
2360
+ * formula of its own — only the ambiguous "value may not exceed amount A"
2361
+ * (source lines 13945-13962), and (b) whether it should land on 133 or 167
2362
+ * hinges on the 66.7(4)(a)(iii) designation, which this schedule has no
2363
+ * source for and no sibling schedule to pull it from. Whenever the CCOGPE-
2364
+ * successor subtotal goes negative, `computeCcogpeSuccessor` raises an issue
2365
+ * naming lines 133/167/189 so a preparer resolves the routing by hand; the
2366
+ * pool's own claim and closing balance are zeroed per the schedule's
2367
+ * unconditional instruction ("enter zero at 015189 and 015191").
2368
+ *
2369
+ * A second, independent anomaly: CCOGPE-successor's own closing balance
2370
+ * formula (line 191, source lines 14731-14740) reads literally as "value =
2371
+ * 015173+015175+015177-015181-015185-015187-015189 **-015167-015169**" when
2372
+ * the pre-subtraction total is positive — i.e. it appears to subtract the
2373
+ * CCOGPE-REGULAR pool's OWN deduction (167) and claim (169) from the
2374
+ * SUCCESSOR pool's closing balance. No other continuity balance on this
2375
+ * schedule mixes fields across the regular/successor split this way, and
2376
+ * nothing else in the 000-series explains why a successor balance would
2377
+ * absorb the regular pool's claim. This module computes 191 with the clean,
2378
+ * schedule-consistent formula (additions − deductions − claim, floored at
2379
+ * zero) and raises an issue quoting the literal spec text whenever 167 or
2380
+ * 169 is non-zero (the only case where the two readings diverge), so a
2381
+ * reviewer can check it against the real form — which, again, has no PDF in
2382
+ * this engine's sources to check against.
2383
+ *
2384
+ * ── Other flagged items ─────────────────────────────────────────────────────
2385
+ *
2386
+ * - CDE line 107 ("Deduct: other deductions or transfers", source lines
2387
+ * 13681-13691) carries a parenthetical "(Note: If 015139 is negative,
2388
+ * include the amount at 015107 as a positive value.)" — there is NO line
2389
+ * 139 anywhere else in this schedule's field list (CDE regular runs
2390
+ * 091-117 with no 108/109/113/114/116/139 gaps that resolve to it, and
2391
+ * no other pool numbers into the 130s except CDE-successor's own 133-137,
2392
+ * which are a different pool entirely). This looks like either an OCR
2393
+ * artifact or a reference to a paper-form-only field outside the NetFile
2394
+ * schema (the same category as Schedule 21's RIFE section). Not modelled;
2395
+ * flagged verbatim whenever the schedule is computed.
2396
+ * - Negative-pool amounts on FEDE, SFEDE and CFRE (claim lines 209, 221,
2397
+ * 253, 273, 293, 313) are, per the spec, "include[d] in 012040" — a
2398
+ * FEDERAL T2 line, entirely outside this schedule and this engine's
2399
+ * stated scope for Schedule 15. This module zeroes the claim/closing per
2400
+ * the schedule's own instruction and raises an issue noting the federal
2401
+ * inclusion is the caller's responsibility elsewhere.
2402
+ * - CFRE line 293's "B" component is capped at "the global foreign
2403
+ * resource limit for the year designated for that country" (source lines
2404
+ * 15703-15711) — a quantity with no definition or source anywhere in
2405
+ * this spec excerpt. Modelled as an optional plain-number INPUT per
2406
+ * country (`globalForeignResourceLimit`); omitting it makes B = 0 (the
2407
+ * conservative, under-claim direction) and raises an issue rather than
2408
+ * inventing a limit.
2409
+ *
2410
+ * Whole dollars, pure functions, no I/O.
2411
+ */
2412
+ const nn$25 = (v) => Math.max(0, v ?? 0);
2413
+ const num$1 = (v) => v ?? 0;
2414
+ const round = (v) => Math.round(v);
2415
+ /** `override ?? federal`, tracking whether an Alberta-specific figure was actually entered. */
2416
+ function reconcile(federal, override) {
2417
+ return override !== void 0 ? {
2418
+ value: override,
2419
+ differs: true
2420
+ } : {
2421
+ value: num$1(federal),
2422
+ differs: false
2423
+ };
2424
+ }
2425
+ /**
2426
+ * CDE / CCOGPE style short-tax-year proration: the full rate applies with NO
2427
+ * proration once the tax year reaches 357 days; below that, rate × days/365.
2428
+ */
2429
+ function stepYearFactor(daysInTaxYear) {
2430
+ const days = daysInTaxYear ?? 365;
2431
+ return days >= 357 ? 1 : Math.max(0, days) / 365;
2432
+ }
2433
+ /** FEDE / SFEDE / CFRE style proration: plain days/365, no 357-day step. */
2434
+ function linearYearFactor(daysInTaxYear) {
2435
+ return Math.max(0, daysInTaxYear ?? 365) / 365;
2436
+ }
2437
+ /** A discretionary claim capped at `cap` (≥ 0), defaulting to the maximum (`cap`) when omitted. */
2438
+ function claimUpToCap(requested, cap, issues, label) {
2439
+ const c = Math.max(0, round(cap));
2440
+ if (requested === void 0) return c;
2441
+ if (requested < 0) {
2442
+ issues.push(`${label}: a requested claim of ${requested} is negative; treated as 0.`);
2443
+ return 0;
2444
+ }
2445
+ if (round(requested) > c) {
2446
+ issues.push(`${label}: the requested claim of ${requested} exceeds the maximum allowable ${c} for the year; capped at ${c}.`);
2447
+ return c;
2448
+ }
2449
+ return round(requested);
2450
+ }
2451
+ /**
2452
+ * CEE-style claim: when the pool subtotal is ≤ 0 the claim MUST equal that
2453
+ * (negative or zero) subtotal — an income inclusion, not a deduction — and
2454
+ * any requested figure is overridden. When positive, it is a discretionary
2455
+ * claim up to the subtotal (100% claimable, no percentage rate), defaulting
2456
+ * to the maximum.
2457
+ */
2458
+ function forcedOrCappedClaim(requested, subtotal, issues, label) {
2459
+ if (subtotal <= 0) {
2460
+ if (requested !== void 0 && round(requested) !== round(subtotal)) issues.push(`${label}: the pool subtotal is ${subtotal} (≤ 0), so the claim must equal the subtotal (an income inclusion) — overriding the requested ${requested}.`);
2461
+ return round(subtotal);
2462
+ }
2463
+ return claimUpToCap(requested, subtotal, issues, label);
2464
+ }
2465
+ function computeEdaRegular(input) {
2466
+ const issues = [];
2467
+ const f = input.federal;
2468
+ const o = input.albertaOverride ?? {};
2469
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2470
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2471
+ const sale = reconcile(f.saleTransfer, o.saleTransfer);
2472
+ const poolBeforeClaim = opening.value + amalg.value - sale.value;
2473
+ const claim = pool_cappedClaim(reconcile(f.regulation1201Claim, o.regulation1201Claim), poolBeforeClaim, issues, "Schedule 15 EDA regular (015007)");
2474
+ const closingBalance = poolBeforeClaim - claim.value;
2475
+ return {
2476
+ openingBalance: opening.value,
2477
+ amalgamationTransfer: amalg.value,
2478
+ saleTransfer: sale.value,
2479
+ poolBeforeClaim,
2480
+ claim: claim.value,
2481
+ closingBalance,
2482
+ differsFromFederal: opening.differs || amalg.differs || sale.differs || claim.differs,
2483
+ issues
2484
+ };
2485
+ }
2486
+ /** A reconciled figure that is ALSO capped by a pool — the EDA/CMEDB claim shape (015007, 015019). */
2487
+ function pool_cappedClaim(reconciled, pool, issues, label) {
2488
+ const cap = Math.max(0, round(pool));
2489
+ if (pool <= 0) return {
2490
+ value: 0,
2491
+ differs: reconciled.differs
2492
+ };
2493
+ if (round(reconciled.value) > cap) {
2494
+ issues.push(`${label}: the entered claim ${reconciled.value} exceeds the pool ${cap}; capped.`);
2495
+ return {
2496
+ value: cap,
2497
+ differs: reconciled.differs
2498
+ };
2499
+ }
2500
+ return {
2501
+ value: Math.max(0, round(reconciled.value)),
2502
+ differs: reconciled.differs
2503
+ };
2504
+ }
2505
+ function computeEdaSuccessor(input) {
2506
+ const issues = [];
2507
+ const f = input.federal;
2508
+ const o = input.albertaOverride ?? {};
2509
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2510
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2511
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2512
+ const sale = reconcile(f.saleTransfer, o.saleTransfer);
2513
+ const poolBeforeClaim = opening.value + amalg.value + other.value - sale.value;
2514
+ const claim = pool_cappedClaim(reconcile(f.regulation1202Claim, o.regulation1202Claim), poolBeforeClaim, issues, "Schedule 15 EDA successor (015019)");
2515
+ const closingBalance = poolBeforeClaim - claim.value;
2516
+ return {
2517
+ openingBalance: opening.value,
2518
+ amalgamationTransfer: amalg.value,
2519
+ otherTransfer: other.value,
2520
+ saleTransfer: sale.value,
2521
+ poolBeforeClaim,
2522
+ claim: claim.value,
2523
+ closingBalance,
2524
+ differsFromFederal: opening.differs || amalg.differs || other.differs || sale.differs || claim.differs,
2525
+ issues
2526
+ };
2527
+ }
2528
+ function computeCmedb(input) {
2529
+ const issues = [];
2530
+ const f = input.federal;
2531
+ const o = input.albertaOverride ?? {};
2532
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2533
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2534
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2535
+ const disposal = reconcile(f.disposalTransfer, o.disposalTransfer);
2536
+ const poolBeforeClaim = opening.value + amalg.value + other.value - disposal.value;
2537
+ const claim = poolBeforeClaim <= 0 ? 0 : claimUpToCap(input.claimed, poolBeforeClaim, issues, "Schedule 15 CMEDB (015031)");
2538
+ const closingBalance = poolBeforeClaim - claim;
2539
+ return {
2540
+ openingBalance: opening.value,
2541
+ amalgamationTransfer: amalg.value,
2542
+ otherTransfer: other.value,
2543
+ disposalTransfer: disposal.value,
2544
+ poolBeforeClaim,
2545
+ claim,
2546
+ closingBalance,
2547
+ differsFromFederal: opening.differs || amalg.differs || other.differs || disposal.differs,
2548
+ issues
2549
+ };
2550
+ }
2551
+ function computeCeeRegular(input) {
2552
+ const issues = [];
2553
+ const f = input.federal;
2554
+ const o = input.albertaOverride ?? {};
2555
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2556
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2557
+ const otherAdd = reconcile(f.otherAdditions, o.otherAdditions);
2558
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2559
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2560
+ const currentYearExpenses = num$1(f.currentYearExpenses);
2561
+ const lookBackExpenses = num$1(f.lookBackExpenses);
2562
+ const reclassifiedFromCde = num$1(f.reclassifiedFromCde);
2563
+ const renewableConservationExpenses = num$1(f.renewableConservationExpenses);
2564
+ const governmentAssistance = num$1(f.governmentAssistance);
2565
+ const renouncedFlowThrough = num$1(f.renouncedFlowThrough);
2566
+ const renouncedLookBack = num$1(f.renouncedLookBack);
2567
+ const subtotal = opening.value + currentYearExpenses + lookBackExpenses + reclassifiedFromCde + amalg.value + renewableConservationExpenses + otherAdd.value - governmentAssistance - otherDed.value - renouncedFlowThrough - toSuccessor.value - renouncedLookBack;
2568
+ const claim = forcedOrCappedClaim(input.claimed, subtotal, issues, "Schedule 15 CEE regular (015061)");
2569
+ const closingBalance = subtotal > 0 ? subtotal - claim : 0;
2570
+ return {
2571
+ openingBalance: opening.value,
2572
+ currentYearExpenses,
2573
+ lookBackExpenses,
2574
+ reclassifiedFromCde,
2575
+ amalgamationTransfer: amalg.value,
2576
+ renewableConservationExpenses,
2577
+ otherAdditions: otherAdd.value,
2578
+ governmentAssistance,
2579
+ otherDeductions: otherDed.value,
2580
+ renouncedFlowThrough,
2581
+ transferredToSuccessor: toSuccessor.value,
2582
+ renouncedLookBack,
2583
+ subtotal,
2584
+ claim,
2585
+ closingBalance,
2586
+ differsFromFederal: opening.differs || amalg.differs || otherAdd.differs || otherDed.differs || toSuccessor.differs,
2587
+ issues
2588
+ };
2589
+ }
2590
+ function computeCeeSuccessor(input) {
2591
+ const issues = [];
2592
+ const f = input.federal;
2593
+ const o = input.albertaOverride ?? {};
2594
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2595
+ const reclassified = num$1(f.reclassifiedFromCde);
2596
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2597
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2598
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2599
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2600
+ const subtotal = opening.value + reclassified + amalg.value + other.value - otherDed.value - toSuccessor.value;
2601
+ const claim = forcedOrCappedClaim(input.claimed, subtotal, issues, "Schedule 15 CEE successor (015081)");
2602
+ const closingBalance = subtotal - claim;
2603
+ return {
2604
+ openingBalance: opening.value,
2605
+ reclassifiedFromCde: reclassified,
2606
+ amalgamationTransfer: amalg.value,
2607
+ otherTransfer: other.value,
2608
+ otherDeductions: otherDed.value,
2609
+ transferredToSuccessor: toSuccessor.value,
2610
+ subtotal,
2611
+ claim,
2612
+ closingBalance,
2613
+ differsFromFederal: opening.differs || amalg.differs || other.differs || otherDed.differs || toSuccessor.differs,
2614
+ issues
2615
+ };
2616
+ }
2617
+ /** CDE / CCOGPE claim rate — 30% for CDE (ITA s.66.2(2)), applied via `stepYearFactor`. */
2618
+ const CDE_CLAIM_RATE = .3;
2619
+ /** CCOGPE claim rate — 10% (ITA s.66.4(2)/66.7(5)), applied via `stepYearFactor`. */
2620
+ const CCOGPE_CLAIM_RATE = .1;
2621
+ function computeCdeRegular(input, ccogpeRegular) {
2622
+ const issues = [];
2623
+ const f = input.federal;
2624
+ const o = input.albertaOverride ?? {};
2625
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2626
+ const currentYearExpenses = num$1(f.currentYearExpenses);
2627
+ const lookBackExpenses = num$1(f.lookBackExpenses);
2628
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2629
+ const otherAdd = reconcile(f.otherAdditions, o.otherAdditions);
2630
+ const reclassified = num$1(f.reclassifiedFromCee);
2631
+ const governmentAssistance = num$1(f.governmentAssistance);
2632
+ const receivable = reconcile(f.receivableOnDisposition, o.receivableOnDisposition);
2633
+ const creditBalanceReconciled = reconcile(f.creditBalanceInCogpePool, o.creditBalanceInCogpePool);
2634
+ const creditBalance = ccogpeRegular.subtotal < 0 ? {
2635
+ value: round(ccogpeRegular.subtotal),
2636
+ differs: true
2637
+ } : creditBalanceReconciled;
2638
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2639
+ const renouncedFlowThrough = num$1(f.renouncedFlowThrough);
2640
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2641
+ const renouncedLookBack = num$1(f.renouncedLookBack);
2642
+ const subtotal = opening.value + currentYearExpenses + lookBackExpenses + amalg.value + otherAdd.value - reclassified - governmentAssistance - receivable.value - creditBalance.value - otherDed.value - toSuccessor.value - renouncedFlowThrough - renouncedLookBack;
2643
+ if (f.otherDeductions !== void 0 || o.otherDeductions !== void 0) issues.push("Schedule 15 CDE regular (015107): the spec's own business rule for this line adds \"If 015139 is negative, include the amount at 015107 as a positive value\" — line 015139 does not exist anywhere else in Schedule 15 as extracted from the source text (source lines 13681-13691). Not modelled; verify against the live TRA form, which has no PDF in this engine's sources for Schedule 15.");
2644
+ const claim = subtotal > 0 ? claimUpToCap(input.claimed, CDE_CLAIM_RATE * stepYearFactor(input.daysInTaxYear) * subtotal, issues, "Schedule 15 CDE regular (015115)") : 0;
2645
+ const closingBalance = Math.max(0, subtotal - claim);
2646
+ return {
2647
+ openingBalance: opening.value,
2648
+ currentYearExpenses,
2649
+ lookBackExpenses,
2650
+ amalgamationTransfer: amalg.value,
2651
+ otherAdditions: otherAdd.value,
2652
+ reclassifiedFromCee: reclassified,
2653
+ governmentAssistance,
2654
+ receivableOnDisposition: receivable.value,
2655
+ creditBalanceInCogpePool: creditBalance.value,
2656
+ otherDeductions: otherDed.value,
2657
+ renouncedFlowThrough,
2658
+ transferredToSuccessor: toSuccessor.value,
2659
+ renouncedLookBack,
2660
+ subtotal,
2661
+ claim,
2662
+ closingBalance,
2663
+ differsFromFederal: opening.differs || amalg.differs || otherAdd.differs || receivable.differs || creditBalance.differs || otherDed.differs || toSuccessor.differs,
2664
+ issues
2665
+ };
2666
+ }
2667
+ function computeCdeSuccessor(input, ccogpeSuccessor) {
2668
+ const issues = [];
2669
+ const f = input.federal;
2670
+ const o = input.albertaOverride ?? {};
2671
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2672
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2673
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2674
+ const reclassified = num$1(f.reclassifiedFromCee);
2675
+ const creditBalanceReconciled = reconcile(f.creditBalanceInCogpePool, o.creditBalanceInCogpePool);
2676
+ const creditBalance = ccogpeSuccessor.subtotal < 0 ? {
2677
+ value: round(ccogpeSuccessor.subtotal),
2678
+ differs: true
2679
+ } : creditBalanceReconciled;
2680
+ if (creditBalance.value !== 0) issues.push("Schedule 15 CDE successor (015133): the \"value may not exceed amount A\" wording (source lines 13945-13962) is ambiguous — this module treats it as \"value = A\" (matching line 015105's unconditional wording) whenever the CCOGPE-successor subtotal is negative. Verify against the live TRA form.");
2681
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2682
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2683
+ const subtotal = opening.value + amalg.value + other.value - reclassified - creditBalance.value - otherDed.value - toSuccessor.value;
2684
+ const claim = subtotal > 0 ? claimUpToCap(input.claimed, CDE_CLAIM_RATE * stepYearFactor(input.daysInTaxYear) * subtotal, issues, "Schedule 15 CDE successor (015141)") : 0;
2685
+ const closingBalance = Math.max(0, subtotal - claim);
2686
+ return {
2687
+ openingBalance: opening.value,
2688
+ amalgamationTransfer: amalg.value,
2689
+ otherTransfer: other.value,
2690
+ reclassifiedFromCee: reclassified,
2691
+ creditBalanceInCogpePool: creditBalance.value,
2692
+ otherDeductions: otherDed.value,
2693
+ transferredToSuccessor: toSuccessor.value,
2694
+ subtotal,
2695
+ claim,
2696
+ closingBalance,
2697
+ differsFromFederal: opening.differs || amalg.differs || other.differs || creditBalance.differs || otherDed.differs || toSuccessor.differs,
2698
+ issues
2699
+ };
2700
+ }
2701
+ function computeCcogpeRegular(input) {
2702
+ const issues = [];
2703
+ const f = input.federal;
2704
+ const o = input.albertaOverride ?? {};
2705
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2706
+ const currentYearExpenses = num$1(f.currentYearExpenses);
2707
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2708
+ const otherAdd = reconcile(f.otherAdditions, o.otherAdditions);
2709
+ const receivable = reconcile(f.receivableOnDisposition, o.receivableOnDisposition);
2710
+ const governmentAssistance = num$1(f.governmentAssistance);
2711
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2712
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2713
+ const subtotal = opening.value + currentYearExpenses + amalg.value + otherAdd.value - receivable.value - governmentAssistance - toSuccessor.value - otherDed.value;
2714
+ let claim = 0;
2715
+ if (subtotal < 0) issues.push("Schedule 15 CCOGPE regular (015169): the pool subtotal is negative. Per source lines 14346-14359, this must be carried forward to line 015105 (CDE regular) if the corporation has made a designation under subparagraph 66.7(4)(a)(iii), or to line 015133 (CDE successor) if it has not. computeCdeRegular auto-applies the 015105 route unconditionally (see that function's doc comment) — confirm the designation status before relying on that routing when it should instead land on 015133.");
2716
+ else if (subtotal > 0) claim = claimUpToCap(input.claimed, CCOGPE_CLAIM_RATE * stepYearFactor(input.daysInTaxYear) * subtotal, issues, "Schedule 15 CCOGPE regular (015169)");
2717
+ const closingBalance = subtotal > 0 ? subtotal - claim : 0;
2718
+ return {
2719
+ openingBalance: opening.value,
2720
+ currentYearExpenses,
2721
+ amalgamationTransfer: amalg.value,
2722
+ otherAdditions: otherAdd.value,
2723
+ receivableOnDisposition: receivable.value,
2724
+ governmentAssistance,
2725
+ transferredToSuccessor: toSuccessor.value,
2726
+ otherDeductions: otherDed.value,
2727
+ subtotal,
2728
+ claim,
2729
+ closingBalance,
2730
+ differsFromFederal: opening.differs || amalg.differs || otherAdd.differs || receivable.differs || toSuccessor.differs || otherDed.differs,
2731
+ issues
2732
+ };
2733
+ }
2734
+ function computeCcogpeSuccessor(input) {
2735
+ const issues = [];
2736
+ const f = input.federal;
2737
+ const o = input.albertaOverride ?? {};
2738
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2739
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2740
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2741
+ const receivable = reconcile(f.receivableOnDisposition, o.receivableOnDisposition);
2742
+ const toSuccessor = reconcile(f.transferredToSuccessor, o.transferredToSuccessor);
2743
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2744
+ const subtotal = opening.value + amalg.value + other.value - receivable.value - toSuccessor.value - otherDed.value;
2745
+ let claim = 0;
2746
+ if (subtotal < 0) issues.push("Schedule 15 CCOGPE successor (015189): the pool subtotal is negative. Per source lines 14646-14660, this must be carried forward to line 015133 (CDE successor) if the corporation has made a designation under subparagraph 66.7(4)(a)(iii), or included in line 015167 (this SAME pool's own regular-side deduction line) if it has not. Neither route is auto-applied by this function; supply the appropriate override manually. See module doc comment.");
2747
+ else if (subtotal > 0) claim = claimUpToCap(input.claimed, CCOGPE_CLAIM_RATE * stepYearFactor(input.daysInTaxYear) * subtotal, issues, "Schedule 15 CCOGPE successor (015189)");
2748
+ const closingBalance = Math.max(0, subtotal - claim);
2749
+ return {
2750
+ openingBalance: opening.value,
2751
+ amalgamationTransfer: amalg.value,
2752
+ otherTransfer: other.value,
2753
+ receivableOnDisposition: receivable.value,
2754
+ transferredToSuccessor: toSuccessor.value,
2755
+ otherDeductions: otherDed.value,
2756
+ subtotal,
2757
+ claim,
2758
+ closingBalance,
2759
+ differsFromFederal: opening.differs || amalg.differs || other.differs || receivable.differs || toSuccessor.differs || otherDed.differs,
2760
+ issues
2761
+ };
2762
+ }
2763
+ function computeFedeRegular(input) {
2764
+ const issues = [];
2765
+ const f = input.federal;
2766
+ const o = input.albertaOverride ?? {};
2767
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2768
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2769
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2770
+ const foreignResourceIncome = num$1(f.foreignResourceIncome);
2771
+ const pool = opening.value + amalg.value - otherDed.value;
2772
+ let claim = 0;
2773
+ if (pool < 0) issues.push("Schedule 15 FEDE regular (015209): the pool balance is negative. Per source lines 14855-14858 this is included in federal T2 line 012040 — outside AT1 Schedule 15 and this engine's scope. Claim and closing balance are zeroed per the schedule's own instruction.");
2774
+ else if (pool > 0) {
2775
+ const cap = Math.max(CCOGPE_CLAIM_RATE * linearYearFactor(input.daysInTaxYear) * pool, foreignResourceIncome);
2776
+ claim = claimUpToCap(input.claimed, Math.min(pool, cap), issues, "Schedule 15 FEDE regular (015209)");
2777
+ }
2778
+ const closingBalance = pool - claim;
2779
+ return {
2780
+ openingBalance: opening.value,
2781
+ amalgamationTransfer: amalg.value,
2782
+ otherDeductions: otherDed.value,
2783
+ foreignResourceIncome,
2784
+ pool,
2785
+ claim,
2786
+ closingBalance,
2787
+ differsFromFederal: opening.differs || amalg.differs || otherDed.differs,
2788
+ issues
2789
+ };
2790
+ }
2791
+ function computeFedeSuccessor(input) {
2792
+ const issues = [];
2793
+ const f = input.federal;
2794
+ const o = input.albertaOverride ?? {};
2795
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2796
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2797
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2798
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2799
+ const foreignResourceIncome = num$1(f.foreignResourceIncome);
2800
+ const pool = opening.value + amalg.value + other.value - otherDed.value;
2801
+ let claim = 0;
2802
+ if (pool < 0) issues.push("Schedule 15 FEDE successor (015221): the pool balance is negative. Per source lines 14855-14858-style wording (successor variant, source lines ~15045-15048) this is included in federal T2 line 012040 — outside AT1 Schedule 15 and this engine's scope.");
2803
+ else if (pool > 0) claim = claimUpToCap(input.claimed, Math.min(pool, foreignResourceIncome), issues, "Schedule 15 FEDE successor (015221)");
2804
+ const closingBalance = pool - claim;
2805
+ return {
2806
+ openingBalance: opening.value,
2807
+ amalgamationTransfer: amalg.value,
2808
+ otherTransfer: other.value,
2809
+ otherDeductions: otherDed.value,
2810
+ foreignResourceIncome,
2811
+ pool,
2812
+ claim,
2813
+ closingBalance,
2814
+ differsFromFederal: opening.differs || amalg.differs || other.differs || otherDed.differs,
2815
+ issues
2816
+ };
2817
+ }
2818
+ function computeSfedeCountryRegular(input) {
2819
+ const issues = [];
2820
+ const f = input.federal;
2821
+ const o = input.albertaOverride ?? {};
2822
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2823
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2824
+ const otherAdd = reconcile(f.otherAdditions, o.otherAdditions);
2825
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2826
+ const foreignResourceIncome = num$1(f.foreignResourceIncome);
2827
+ const pool = opening.value + amalg.value + otherAdd.value - otherDed.value;
2828
+ let claim = 0;
2829
+ if (pool < 0) issues.push(`Schedule 15 SFEDE regular, country ${f.countryCode} (015253): the pool balance is negative. Per the schedule's pattern for foreign pools (source lines 15262-15265), this is included in federal T2 line 012040 — outside this engine's scope.`);
2830
+ else if (pool > 0) {
2831
+ const cap = Math.max(CCOGPE_CLAIM_RATE * linearYearFactor(input.daysInTaxYear) * pool, foreignResourceIncome);
2832
+ claim = claimUpToCap(input.claimed, Math.min(pool, cap), issues, `Schedule 15 SFEDE regular, country ${f.countryCode} (015253)`);
2833
+ }
2834
+ const closingBalance = pool - claim;
2835
+ return {
2836
+ countryCode: f.countryCode,
2837
+ openingBalance: opening.value,
2838
+ amalgamationTransfer: amalg.value,
2839
+ otherAdditions: otherAdd.value,
2840
+ otherDeductions: otherDed.value,
2841
+ foreignResourceIncome,
2842
+ pool,
2843
+ claim,
2844
+ closingBalance,
2845
+ differsFromFederal: opening.differs || amalg.differs || otherAdd.differs || otherDed.differs,
2846
+ issues
2847
+ };
2848
+ }
2849
+ function computeSfedeCountrySuccessor(input) {
2850
+ const issues = [];
2851
+ const f = input.federal;
2852
+ const o = input.albertaOverride ?? {};
2853
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2854
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2855
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
2856
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2857
+ const foreignResourceIncome = num$1(f.foreignResourceIncome);
2858
+ const pool = opening.value + amalg.value + other.value - otherDed.value;
2859
+ let claim = 0;
2860
+ if (pool < 0) issues.push(`Schedule 15 SFEDE successor, country ${f.countryCode} (015273): the pool balance is negative. Per the schedule's pattern for foreign pools this is included in federal T2 line 012040 — outside this engine's scope.`);
2861
+ else if (pool > 0) claim = claimUpToCap(input.claimed, Math.min(pool, foreignResourceIncome), issues, `Schedule 15 SFEDE successor, country ${f.countryCode} (015273)`);
2862
+ const closingBalance = pool - claim;
2863
+ return {
2864
+ countryCode: f.countryCode,
2865
+ openingBalance: opening.value,
2866
+ amalgamationTransfer: amalg.value,
2867
+ otherTransfer: other.value,
2868
+ otherDeductions: otherDed.value,
2869
+ foreignResourceIncome,
2870
+ pool,
2871
+ claim,
2872
+ closingBalance,
2873
+ differsFromFederal: opening.differs || amalg.differs || other.differs || otherDed.differs,
2874
+ issues
2875
+ };
2876
+ }
2877
+ /**
2878
+ * CFRE regular claims need a second pass across all countries (line 015293's
2879
+ * "A" component caps at "total of all occurrences of 015297"), so this takes
2880
+ * the whole array and the pre-summed `sumForeignResourceIncome` across every
2881
+ * country's own 015297 rather than being called per-entry like the other
2882
+ * per-country pools.
2883
+ */
2884
+ function computeCfreRegular(entries) {
2885
+ const issues = [];
2886
+ const pools = entries.map((e) => {
2887
+ const f = e.federal;
2888
+ const o = e.albertaOverride ?? {};
2889
+ const opening = reconcile(f.openingBalance, o.openingBalance);
2890
+ const currentYearExpenses = num$1(f.currentYearExpenses);
2891
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
2892
+ const otherAdd = reconcile(f.otherAdditions, o.otherAdditions);
2893
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
2894
+ return {
2895
+ e,
2896
+ opening,
2897
+ currentYearExpenses,
2898
+ amalg,
2899
+ otherAdd,
2900
+ otherDed,
2901
+ foreignResourceIncome: num$1(f.foreignResourceIncome),
2902
+ pool: opening.value + currentYearExpenses + amalg.value + otherAdd.value - otherDed.value
2903
+ };
2904
+ });
2905
+ const sumForeignResourceIncome = pools.reduce((s, p) => s + p.foreignResourceIncome, 0);
2906
+ return {
2907
+ entries: pools.map((p) => {
2908
+ const { e, opening, currentYearExpenses, amalg, otherAdd, otherDed, foreignResourceIncome, pool } = p;
2909
+ let claim = 0;
2910
+ if (pool < 0) issues.push(`Schedule 15 CFRE regular, country ${e.federal.countryCode} (015293): the pool balance is negative. Per the schedule's pattern for foreign pools this is included in federal T2 line 012040 — outside this engine's scope.`);
2911
+ else if (pool > 0) {
2912
+ const linear = linearYearFactor(e.daysInTaxYear);
2913
+ const partA = Math.max(CCOGPE_CLAIM_RATE * linear * pool, Math.min(CDE_CLAIM_RATE * linear * pool, foreignResourceIncome, sumForeignResourceIncome));
2914
+ const remainder = Math.max(0, pool - partA);
2915
+ if (e.globalForeignResourceLimit === void 0) issues.push(`Schedule 15 CFRE regular, country ${e.federal.countryCode} (015293): the "global foreign resource limit for the year designated for that country" (source lines 15703-15711) was not supplied — this quantity has no source anywhere in this engine, so the B-component of the claim is treated as 0 (conservative — the pool may be under-claimed relative to what the corporation is actually entitled to).`);
2916
+ const partB = Math.min(remainder, nn$25(e.globalForeignResourceLimit));
2917
+ claim = claimUpToCap(e.claimed, partA + partB, issues, `Schedule 15 CFRE regular, country ${e.federal.countryCode} (015293)`);
2918
+ }
2919
+ const closingBalance = pool - claim;
2920
+ return {
2921
+ countryCode: e.federal.countryCode,
2922
+ openingBalance: opening.value,
2923
+ currentYearExpenses,
2924
+ amalgamationTransfer: amalg.value,
2925
+ otherAdditions: otherAdd.value,
2926
+ otherDeductions: otherDed.value,
2927
+ foreignResourceIncome,
2928
+ pool,
2929
+ claim,
2930
+ closingBalance,
2931
+ differsFromFederal: opening.differs || amalg.differs || otherAdd.differs || otherDed.differs,
2932
+ issues
2933
+ };
2934
+ }),
2935
+ issues
2936
+ };
2937
+ }
2938
+ function computeAlbertaSchedule15(input) {
2939
+ const issues = [];
2940
+ const differsFlags = [];
2941
+ const eda = input.eda ? {
2942
+ regular: computeEdaRegular(input.eda.regular),
2943
+ successor: computeEdaSuccessor(input.eda.successor)
2944
+ } : void 0;
2945
+ if (eda) {
2946
+ issues.push(...eda.regular.issues, ...eda.successor.issues);
2947
+ differsFlags.push(eda.regular.differsFromFederal, eda.successor.differsFromFederal);
2948
+ }
2949
+ const cmedb = input.cmedb ? computeCmedb(input.cmedb) : void 0;
2950
+ if (cmedb) {
2951
+ issues.push(...cmedb.issues);
2952
+ differsFlags.push(cmedb.differsFromFederal);
2953
+ }
2954
+ const cee = input.cee ? {
2955
+ regular: computeCeeRegular(input.cee.regular),
2956
+ successor: computeCeeSuccessor(input.cee.successor)
2957
+ } : void 0;
2958
+ if (cee) {
2959
+ issues.push(...cee.regular.issues, ...cee.successor.issues);
2960
+ differsFlags.push(cee.regular.differsFromFederal, cee.successor.differsFromFederal);
2961
+ }
2962
+ const ccogpe = input.ccogpe ? {
2963
+ regular: computeCcogpeRegular(input.ccogpe.regular),
2964
+ successor: computeCcogpeSuccessor(input.ccogpe.successor)
2965
+ } : void 0;
2966
+ if (ccogpe) {
2967
+ issues.push(...ccogpe.regular.issues, ...ccogpe.successor.issues);
2968
+ differsFlags.push(ccogpe.regular.differsFromFederal, ccogpe.successor.differsFromFederal);
2969
+ }
2970
+ const cde = input.cde ? {
2971
+ regular: computeCdeRegular(input.cde.regular, { subtotal: ccogpe?.regular.subtotal ?? 0 }),
2972
+ successor: computeCdeSuccessor(input.cde.successor, { subtotal: ccogpe?.successor.subtotal ?? 0 })
2973
+ } : void 0;
2974
+ if (cde) {
2975
+ issues.push(...cde.regular.issues, ...cde.successor.issues);
2976
+ differsFlags.push(cde.regular.differsFromFederal, cde.successor.differsFromFederal);
2977
+ if (ccogpe && (ccogpe.regular.otherDeductions !== 0 || ccogpe.regular.claim !== 0)) issues.push("Schedule 15 CCOGPE successor (015191): source lines 14731-14740 read literally as subtracting the CCOGPE-REGULAR pool's own 015167 and 015169 from the SUCCESSOR pool's closing balance, and 015167/015169 are non-zero this year. This module used the clean additions−deductions−claim formula instead (see module doc comment) — confirm against the live TRA form before relying on 015191.");
2978
+ }
2979
+ const fede = input.fede ? {
2980
+ regular: computeFedeRegular(input.fede.regular),
2981
+ successor: computeFedeSuccessor(input.fede.successor)
2982
+ } : void 0;
2983
+ if (fede) {
2984
+ issues.push(...fede.regular.issues, ...fede.successor.issues);
2985
+ differsFlags.push(fede.regular.differsFromFederal, fede.successor.differsFromFederal);
728
2986
  }
2987
+ const sfede = input.sfede ? {
2988
+ regular: input.sfede.regular.map(computeSfedeCountryRegular),
2989
+ successor: input.sfede.successor.map(computeSfedeCountrySuccessor)
2990
+ } : void 0;
2991
+ if (sfede) {
2992
+ for (const r of sfede.regular) {
2993
+ issues.push(...r.issues);
2994
+ differsFlags.push(r.differsFromFederal);
2995
+ }
2996
+ for (const s of sfede.successor) {
2997
+ issues.push(...s.issues);
2998
+ differsFlags.push(s.differsFromFederal);
2999
+ }
3000
+ }
3001
+ const cfre = input.cfre ? {
3002
+ regular: computeCfreRegular(input.cfre.regular),
3003
+ successor: computeCfreSuccessor(input.cfre.successor)
3004
+ } : void 0;
3005
+ if (cfre) {
3006
+ issues.push(...cfre.regular.issues, ...cfre.successor.issues);
3007
+ for (const r of cfre.regular.entries) differsFlags.push(r.differsFromFederal);
3008
+ for (const s of cfre.successor.entries) differsFlags.push(s.differsFromFederal);
3009
+ }
3010
+ if (input.cde) issues.push("Schedule 15 CDE regular (015107): the field's own business rule references line 015139, which does not exist elsewhere in this schedule. See module doc comment.");
3011
+ const formRequired = differsFlags.some(Boolean);
3012
+ const formPermitted = (input.reportsDifferentAlbertaIncome ?? false) || (input.electsDifferentDiscretionaryAmounts ?? false);
3013
+ if (formRequired && !formPermitted) issues.push("Alberta Schedule 15: an opening balance or claim differs from federal, so the form is required, but neither line 000060 nor 000061 is set to 1. Set line 000061 and file a Schedule 12.");
729
3014
  return {
730
- scheduleId: "010",
731
- values
3015
+ ...eda ? { eda } : {},
3016
+ ...cmedb ? { cmedb } : {},
3017
+ ...cee ? { cee } : {},
3018
+ ...cde ? { cde } : {},
3019
+ ...ccogpe ? { ccogpe } : {},
3020
+ ...fede ? { fede } : {},
3021
+ ...sfede ? { sfede: {
3022
+ regular: sfede.regular,
3023
+ successor: sfede.successor
3024
+ } } : {},
3025
+ ...cfre ? { cfre: {
3026
+ regular: cfre.regular.entries,
3027
+ successor: cfre.successor.entries
3028
+ } } : {},
3029
+ formRequired,
3030
+ formPermitted,
3031
+ issues
732
3032
  };
733
3033
  }
734
- const S20_CHARITABLE = {
735
- opening: "002",
736
- expired: "004",
737
- beginning: "006",
738
- transferred: "008",
739
- currentYear: "010",
740
- subtotal: "012",
741
- acquisitionOfControl: "013",
742
- available: "014",
743
- applied: "016",
744
- closing: "018"
745
- };
746
- const S20_GIFTS = {
747
- opening: "062",
748
- expired: "064",
749
- beginning: "066",
750
- transferred: "068",
751
- currentYear: "070",
752
- subtotal: "072",
753
- acquisitionOfControl: "073",
754
- available: "074",
755
- applied: "076",
756
- closing: "078"
757
- };
758
- function donationContinuityValues(result, f) {
759
- const put = (fieldId, value) => ({
760
- lineItemId: at1LineItemId("020", fieldId),
761
- value
3034
+ /** Line 015313 caps at "total of all occurrence of 015317", so this also processes the whole array. */
3035
+ function computeCfreSuccessor(entries) {
3036
+ const issues = [];
3037
+ const pools = entries.map((e) => {
3038
+ const f = e.federal;
3039
+ const o = e.albertaOverride ?? {};
3040
+ const opening = reconcile(f.openingBalance, o.openingBalance);
3041
+ const amalg = reconcile(f.amalgamationTransfer, o.amalgamationTransfer);
3042
+ const other = reconcile(f.otherTransfer, o.otherTransfer);
3043
+ const otherDed = reconcile(f.otherDeductions, o.otherDeductions);
3044
+ return {
3045
+ e,
3046
+ opening,
3047
+ amalg,
3048
+ other,
3049
+ otherDed,
3050
+ foreignResourceIncome: num$1(f.foreignResourceIncome),
3051
+ pool: opening.value + amalg.value + other.value - otherDed.value
3052
+ };
762
3053
  });
763
- return [
764
- put(f.opening, result.openingBalance),
765
- put(f.expired, result.expired),
766
- put(f.beginning, Math.max(0, result.openingBalance - result.expired)),
767
- put(f.transferred, result.transferredIn),
768
- put(f.currentYear, result.currentYearGifts),
769
- put(f.subtotal, result.transferredIn + result.currentYearGifts),
770
- put(f.acquisitionOfControl, result.acquisitionOfControlAdjustment),
771
- put(f.available, result.availableBeforeClaim),
772
- put(f.applied, result.amountApplied),
773
- put(f.closing, result.closingBalance)
774
- ];
775
- }
776
- function schedule20Values(input) {
777
- const values = [];
778
- if (input.charitable) values.push(...donationContinuityValues(input.charitable, S20_CHARITABLE));
779
- if (input.gifts) values.push(...donationContinuityValues(input.gifts, S20_GIFTS));
780
- if (input.maximum) {
781
- const put = (fieldId, value) => values.push({
782
- lineItemId: at1LineItemId("020", fieldId),
783
- value
784
- });
785
- put("030", input.maximum.incomeComponent);
786
- put("042", input.maximum.lesserOfProceedsAndCost);
787
- put("044", input.maximum.allowableRecapture);
788
- put("046", input.maximum.gainsComponent);
789
- put("048", input.maximum.maximumDeduction);
790
- }
3054
+ const sumForeignResourceIncome = pools.reduce((s, p) => s + p.foreignResourceIncome, 0);
791
3055
  return {
792
- scheduleId: "020",
793
- values
3056
+ entries: pools.map((p) => {
3057
+ const { e, opening, amalg, other, otherDed, foreignResourceIncome, pool } = p;
3058
+ let claim = 0;
3059
+ if (pool < 0) issues.push(`Schedule 15 CFRE successor, country ${e.federal.countryCode} (015313): the pool balance is negative. Per the schedule's pattern for foreign pools this is included in federal T2 line 012040 — outside this engine's scope.`);
3060
+ else if (pool > 0) {
3061
+ const cap = Math.min(CDE_CLAIM_RATE * linearYearFactor(e.daysInTaxYear) * pool, sumForeignResourceIncome);
3062
+ claim = claimUpToCap(e.claimed, cap, issues, `Schedule 15 CFRE successor, country ${e.federal.countryCode} (015313)`);
3063
+ }
3064
+ const closingBalance = pool - claim;
3065
+ return {
3066
+ countryCode: e.federal.countryCode,
3067
+ openingBalance: opening.value,
3068
+ amalgamationTransfer: amalg.value,
3069
+ otherTransfer: other.value,
3070
+ otherDeductions: otherDed.value,
3071
+ foreignResourceIncome,
3072
+ pool,
3073
+ claim,
3074
+ closingBalance,
3075
+ differsFromFederal: opening.differs || amalg.differs || other.differs || otherDed.differs,
3076
+ issues
3077
+ };
3078
+ }),
3079
+ issues
794
3080
  };
795
3081
  }
3082
+ function schedule15LineItemId(fieldId, occurrence = 1) {
3083
+ return `015${fieldId}${String(occurrence).padStart(3, "0")}`;
3084
+ }
796
3085
  /**
797
- * The SR&ED expenditure POOL a deduction against income, not the investment tax
798
- * credit and not the innovation grant.
799
- *
800
- * Line numbers and the subtotal formula verified against the live form, which
801
- * states it exactly as transcribed:
802
- *
803
- * 016 = 002 (004 + 006 + 008) + 010 + 012 + 014 + 015
804
- *
805
- * and closes the year-over-year chain in as many words: line 022 is *"the carry
806
- * forward amount for next year, line 012"*.
3086
+ * Field ids per the spec transcription in the module doc comment:
3087
+ * EDA 001-021 CMEDB 023-033 CEE 041-083 CDE 091-143
3088
+ * CCOGPE 151-191 FEDE 201-233 SFEDE 241-277 (per country)
3089
+ * CFRE 281-317 (per country)
3090
+ * A pool absent from `result` (never supplied to `computeAlbertaSchedule15`)
3091
+ * emits NOTHING — there is no zero-filled line for a pool the corporation
3092
+ * does not carry, matching every other reconciliation-style AT1 schedule
3093
+ * builder in this package (e.g. `schedule18Values` only emits categories that
3094
+ * were actually computed).
807
3095
  */
808
- function schedule16Values(result) {
3096
+ function schedule15Values(result) {
809
3097
  const values = [];
810
- const put = (fieldId, value) => values.push({
811
- lineItemId: at1LineItemId("016", fieldId),
3098
+ const put = (fieldId, value, occurrence = 1) => values.push({
3099
+ lineItemId: schedule15LineItemId(fieldId, occurrence),
812
3100
  value
813
3101
  });
814
- put("002", result.currentYearExpenditures);
815
- put("016", result.subtotal);
816
- put("018", result.deductionAvailable);
817
- put("020", result.amountClaimed);
818
- put("022", result.unclaimedPoolBalance);
3102
+ const putStr = (fieldId, value, occurrence = 1) => values.push({
3103
+ lineItemId: schedule15LineItemId(fieldId, occurrence),
3104
+ value
3105
+ });
3106
+ if (result.eda) {
3107
+ const { regular, successor } = result.eda;
3108
+ put("001", regular.openingBalance);
3109
+ put("003", regular.amalgamationTransfer);
3110
+ put("005", regular.saleTransfer);
3111
+ put("007", regular.claim);
3112
+ put("009", regular.closingBalance);
3113
+ put("011", successor.openingBalance);
3114
+ put("013", successor.amalgamationTransfer);
3115
+ put("015", successor.otherTransfer);
3116
+ put("017", successor.saleTransfer);
3117
+ put("019", successor.claim);
3118
+ put("021", successor.closingBalance);
3119
+ }
3120
+ if (result.cmedb) {
3121
+ const c = result.cmedb;
3122
+ put("023", c.openingBalance);
3123
+ put("025", c.amalgamationTransfer);
3124
+ put("027", c.otherTransfer);
3125
+ put("029", c.disposalTransfer);
3126
+ put("031", c.claim);
3127
+ put("033", c.closingBalance);
3128
+ }
3129
+ if (result.cee) {
3130
+ const { regular, successor } = result.cee;
3131
+ put("041", regular.openingBalance);
3132
+ put("043", regular.currentYearExpenses);
3133
+ put("044", regular.lookBackExpenses);
3134
+ put("045", regular.reclassifiedFromCde);
3135
+ put("047", regular.amalgamationTransfer);
3136
+ put("049", regular.renewableConservationExpenses);
3137
+ put("051", regular.otherAdditions);
3138
+ put("053", regular.governmentAssistance);
3139
+ put("055", regular.otherDeductions);
3140
+ put("058", regular.renouncedFlowThrough);
3141
+ put("059", regular.transferredToSuccessor);
3142
+ put("060", regular.renouncedLookBack);
3143
+ put("061", regular.claim);
3144
+ put("063", regular.closingBalance);
3145
+ put("064", successor.openingBalance);
3146
+ put("065", successor.reclassifiedFromCde);
3147
+ put("067", successor.amalgamationTransfer);
3148
+ put("069", successor.otherTransfer);
3149
+ put("077", successor.otherDeductions);
3150
+ put("079", successor.transferredToSuccessor);
3151
+ put("081", successor.claim);
3152
+ put("083", successor.closingBalance);
3153
+ }
3154
+ if (result.cde) {
3155
+ const { regular, successor } = result.cde;
3156
+ put("091", regular.openingBalance);
3157
+ put("093", regular.currentYearExpenses);
3158
+ put("094", regular.lookBackExpenses);
3159
+ put("095", regular.amalgamationTransfer);
3160
+ put("097", regular.otherAdditions);
3161
+ put("099", regular.reclassifiedFromCee);
3162
+ put("101", regular.governmentAssistance);
3163
+ put("103", regular.receivableOnDisposition);
3164
+ put("105", regular.creditBalanceInCogpePool);
3165
+ put("107", regular.otherDeductions);
3166
+ put("110", regular.renouncedFlowThrough);
3167
+ put("111", regular.transferredToSuccessor);
3168
+ put("112", regular.renouncedLookBack);
3169
+ put("115", regular.claim);
3170
+ put("117", regular.closingBalance);
3171
+ put("119", successor.openingBalance);
3172
+ put("121", successor.amalgamationTransfer);
3173
+ put("123", successor.otherTransfer);
3174
+ put("127", successor.reclassifiedFromCee);
3175
+ put("133", successor.creditBalanceInCogpePool);
3176
+ put("135", successor.otherDeductions);
3177
+ put("137", successor.transferredToSuccessor);
3178
+ put("141", successor.claim);
3179
+ put("143", successor.closingBalance);
3180
+ }
3181
+ if (result.ccogpe) {
3182
+ const { regular, successor } = result.ccogpe;
3183
+ put("151", regular.openingBalance);
3184
+ put("153", regular.currentYearExpenses);
3185
+ put("155", regular.amalgamationTransfer);
3186
+ put("157", regular.otherAdditions);
3187
+ put("159", regular.receivableOnDisposition);
3188
+ put("161", regular.governmentAssistance);
3189
+ put("165", regular.transferredToSuccessor);
3190
+ put("167", regular.otherDeductions);
3191
+ put("169", regular.claim);
3192
+ put("171", regular.closingBalance);
3193
+ put("173", successor.openingBalance);
3194
+ put("175", successor.amalgamationTransfer);
3195
+ put("177", successor.otherTransfer);
3196
+ put("181", successor.receivableOnDisposition);
3197
+ put("185", successor.transferredToSuccessor);
3198
+ put("187", successor.otherDeductions);
3199
+ put("189", successor.claim);
3200
+ put("191", successor.closingBalance);
3201
+ }
3202
+ if (result.fede) {
3203
+ const { regular, successor } = result.fede;
3204
+ put("201", regular.openingBalance);
3205
+ put("205", regular.amalgamationTransfer);
3206
+ put("207", regular.otherDeductions);
3207
+ put("209", regular.claim);
3208
+ put("211", regular.closingBalance);
3209
+ put("231", regular.foreignResourceIncome);
3210
+ put("213", successor.openingBalance);
3211
+ put("215", successor.amalgamationTransfer);
3212
+ put("217", successor.otherTransfer);
3213
+ put("219", successor.otherDeductions);
3214
+ put("221", successor.claim);
3215
+ put("223", successor.closingBalance);
3216
+ put("233", successor.foreignResourceIncome);
3217
+ }
3218
+ if (result.sfede) {
3219
+ result.sfede.regular.forEach((r, i) => {
3220
+ const occ = i + 1;
3221
+ putStr("241", r.countryCode, occ);
3222
+ put("243", r.openingBalance, occ);
3223
+ put("247", r.amalgamationTransfer, occ);
3224
+ put("249", r.otherAdditions, occ);
3225
+ put("251", r.otherDeductions, occ);
3226
+ put("253", r.claim, occ);
3227
+ put("255", r.closingBalance, occ);
3228
+ put("257", r.foreignResourceIncome, occ);
3229
+ });
3230
+ result.sfede.successor.forEach((s, i) => {
3231
+ const occ = i + 1;
3232
+ putStr("261", s.countryCode, occ);
3233
+ put("263", s.openingBalance, occ);
3234
+ put("265", s.amalgamationTransfer, occ);
3235
+ put("267", s.otherTransfer, occ);
3236
+ put("269", s.otherDeductions, occ);
3237
+ put("273", s.claim, occ);
3238
+ put("275", s.closingBalance, occ);
3239
+ put("277", s.foreignResourceIncome, occ);
3240
+ });
3241
+ }
3242
+ if (result.cfre) {
3243
+ result.cfre.regular.forEach((r, i) => {
3244
+ const occ = i + 1;
3245
+ putStr("281", r.countryCode, occ);
3246
+ put("283", r.openingBalance, occ);
3247
+ put("285", r.currentYearExpenses, occ);
3248
+ put("287", r.amalgamationTransfer, occ);
3249
+ put("289", r.otherAdditions, occ);
3250
+ put("291", r.otherDeductions, occ);
3251
+ put("293", r.claim, occ);
3252
+ put("295", r.closingBalance, occ);
3253
+ put("297", r.foreignResourceIncome, occ);
3254
+ });
3255
+ result.cfre.successor.forEach((s, i) => {
3256
+ const occ = i + 1;
3257
+ putStr("301", s.countryCode, occ);
3258
+ put("303", s.openingBalance, occ);
3259
+ put("305", s.amalgamationTransfer, occ);
3260
+ put("307", s.otherTransfer, occ);
3261
+ put("309", s.otherDeductions, occ);
3262
+ put("313", s.claim, occ);
3263
+ put("315", s.closingBalance, occ);
3264
+ put("317", s.foreignResourceIncome, occ);
3265
+ });
3266
+ }
819
3267
  return {
820
- scheduleId: "016",
821
- values
3268
+ scheduleId: "015",
3269
+ values,
3270
+ issues: result.issues
822
3271
  };
823
3272
  }
824
3273
  //#endregion
825
- //#region src/t2/at1/schedules/schedule2.ts
826
- /** Round to six decimal places — the AT1 allocation-factor precision. */
827
- function round6(n) {
828
- return Math.round(n * 1e6) / 1e6;
829
- }
830
- /** Single Alberta PE, none elsewhere all income is Alberta income. */
831
- const SINGLE_JURISDICTION_ALBERTA_FACTOR = 1;
832
- function computeAllocationFactor(input) {
833
- const hasRevenue = input.totalGrossRevenue > 0;
834
- const hasSalaries = input.totalSalaries > 0;
835
- const revenueRatio = hasRevenue ? input.albertaGrossRevenue / input.totalGrossRevenue : 0;
836
- const salariesRatio = hasSalaries ? input.albertaSalaries / input.totalSalaries : 0;
837
- let factor;
838
- if (hasRevenue && hasSalaries) factor = (revenueRatio + salariesRatio) / 2;
839
- else if (hasRevenue) factor = revenueRatio;
840
- else if (hasSalaries) factor = salariesRatio;
841
- else factor = 0;
842
- return round6(factor);
3274
+ //#region src/t2/at1/schedules/schedule29-eligible-expenditures.ts
3275
+ /**
3276
+ * AT1 Schedule 29 page 1 — Eligible Expenditures for IEG Purposes (lines
3277
+ * 001–040). AT29's own worksheet for deriving the "eligible expenditures"
3278
+ * figure (line 031) that everything else on this schedule — the base amount,
3279
+ * the increment, the Agreement's per-member 245/250/260 is built from.
3280
+ *
3281
+ * ── Why this module exists separately from `schedule29-ieg.ts` ─────────────
3282
+ *
3283
+ * `computeIeg` took `eligibleExpenditures` as a bare number input. It is not
3284
+ * one it is DERIVED, by a six-line formula off federal T661, and a prior
3285
+ * version of this package never modelled that derivation at all: it skipped
3286
+ * page 1 of the live form entirely (only pages 2 and 3 were ever rendered),
3287
+ * so every caller had to compute line 031 by hand outside the engine, with
3288
+ * nothing here to check the arithmetic. Confirmed missing by rendering the
3289
+ * live, TRA-certified form (`research/sources/tra-forms/pdf/AT1SCH29-*.pdf`,
3290
+ * page 1) and independently re-verified against two of TRA's own published
3291
+ * worked examples (`research/sources/tra-guides/tra-guide-claiming-the-
3292
+ * innovation-employment-grant.pdf`, Examples 3 and 4) — see
3293
+ * `research/knowledge-base/at1-schedule-29-ieg-mechanics.md`.
3294
+ *
3295
+ * ── The formula ──────────────────────────────────────────────────────────
3296
+ *
3297
+ * 003 federal amount of qualified/current SR&ED expenditures, T661 line
3298
+ * 559 or 557 (see below)
3299
+ * 005 portion of 003 carried out in Alberta — normally the sum of every
3300
+ * AT4970 project row's own 105 column (`at4970-ieg-projects.ts`)
3301
+ * 007 deduct: federal prescribed proxy amount included in the Alberta
3302
+ * portion of 003 — normally Σ AT4970's 111 column
3303
+ * 009 add: Alberta proxy amount — normally Σ AT4970's 113 column
3304
+ * 011 add: IEG that reduced the federal expenditure IN THE TAXATION YEAR
3305
+ * — zero for a first-time current-year claim reported on the
3306
+ * pre-deduction federal figures (see the Step 1 / Step 2 note below)
3307
+ * 025 add: the Alberta portion of a repayment of government assistance
3308
+ * (other than an IEG) OR A CONTRACT PAYMENT, relating to amounts in
3309
+ * 005 from the current year or ANY PRECEDING taxation year — two
3310
+ * independent triggers, not one
3311
+ * 031 TOTAL = 005 − 007 + 009 + 011 + 025
3312
+ *
3313
+ * 031 is what `computeIeg` calls `eligibleExpenditures` and what an
3314
+ * Agreement member's own `currentYearExpenditures` (line 245) is — the same
3315
+ * formula, computed once per corporation per year, whether or not that
3316
+ * corporation is associated.
3317
+ *
3318
+ * ── The federal T661 line 557 vs 559 split (Fall 2026 re-certification) ────
3319
+ *
3320
+ * TRA's own certification correspondence and the live form agree: for a
3321
+ * taxation year ending ON OR BEFORE 2024-12-15, line 003 comes from federal
3322
+ * T661 line 559 (qualified SR&ED expenditures); for a taxation year ending
3323
+ * ON OR AFTER 2024-12-16, it comes from T661 line 557 (CURRENT SR&ED
3324
+ * expenditures) instead — a different federal figure, not a renumbering.
3325
+ * `iegT661SourceLine` below is purely informational (which federal line a
3326
+ * preparer should be told to copy from); it does not change the Alberta
3327
+ * arithmetic itself, which is identical either way.
3328
+ *
3329
+ * ── The "Step 1 / Step 2" columns in TRA's Guide are not two form fields ────
3330
+ *
3331
+ * The Guide illustrates every page-1 line twice — once using the federal
3332
+ * figures BEFORE the current year's own IEG is netted against them as
3333
+ * government assistance (T661 line 513), once AFTER, with line 011 adding
3334
+ * the IEG back. Both worked examples land on the IDENTICAL line 031 either
3335
+ * way (011 exists specifically to undo the 513 netting for ALBERTA's own
3336
+ * base, so it cannot shrink itself). Only ONE set of figures is filed. Every
3337
+ * TRA test case gives the pre-deduction ("Step 1") federal figures — use
3338
+ * those directly, with 011 = 0 for a first-time current-year claim. Do not
3339
+ * build a literal two-pass loop; there is nothing for it to converge on that
3340
+ * isn't already true of the Step 1 figures alone.
3341
+ *
3342
+ * Whole dollars, pure.
3343
+ */
3344
+ /** Which federal T661 line line 003 sources from, given a taxation year end. */
3345
+ function iegT661SourceLine(taxationYearEnd) {
3346
+ return taxationYearEnd >= "2024-12-16" ? "557" : "559";
3347
+ }
3348
+ const nn$24 = (v) => Math.round(v ?? 0);
3349
+ /** Line 031 = 005 − 007 + 009 + 011 + 025. Every term but 005 defaults to nil. */
3350
+ function computeIegEligibleExpenditures(input) {
3351
+ const federalAmount = nn$24(input.federalAmount);
3352
+ const albertaPortion = nn$24(input.albertaPortion);
3353
+ const federalProxyAmount = nn$24(input.federalProxyAmount);
3354
+ const albertaProxyAmount = nn$24(input.albertaProxyAmount);
3355
+ const iegReducingFederalExpenditure = nn$24(input.iegReducingFederalExpenditure);
3356
+ const repaymentOrContractPayment = nn$24(input.repaymentOrContractPayment);
3357
+ return {
3358
+ federalAmount,
3359
+ albertaPortion,
3360
+ federalProxyAmount,
3361
+ albertaProxyAmount,
3362
+ iegReducingFederalExpenditure,
3363
+ repaymentOrContractPayment,
3364
+ totalEligibleExpenditures: albertaPortion - federalProxyAmount + albertaProxyAmount + iegReducingFederalExpenditure + repaymentOrContractPayment
3365
+ };
843
3366
  }
844
3367
  //#endregion
845
3368
  //#region src/t2/at1/schedules/schedule29-ieg.ts
@@ -1014,6 +3537,37 @@ function computeIeg(input) {
1014
3537
  * contributes its taxable capital to the grind. Both cases appear in the Alberta
1015
3538
  * certification material and both are modelled.
1016
3539
  *
3540
+ * ── ⚠ This module's "group aggregated base amount" has no line on the real form ──
3541
+ *
3542
+ * Rendering Schedule 29 page 2 in full (a prior version of this package only
3543
+ * ever rendered pages 2 and 3's CREDIT section, never page 1) shows the base
3544
+ * amount (lines 114/116/118) captioned "*If the corporation is NOT
3545
+ * associated with one or more corporations in the taxation year, the Base
3546
+ * Amount is the amount that is the average of the eligible expenditures OF
3547
+ * THE CORPORATION for the two immediately preceding taxation years" — the
3548
+ * CLAIMANT's own figures alone, no group aggregation, and used ONLY for the
3549
+ * non-associated path (line 112). Confirmed blank in both of TRA's own
3550
+ * published worked examples whenever the corporation IS associated — the
3551
+ * associated path (line 125) uses no base amount at all, replacing the
3552
+ * whole mechanism with the Agreement's Allowed Amount instead (see
3553
+ * `computeIegAgreement` below). The live form also states an associated
3554
+ * corporation MUST complete the Agreement ("If 'Yes', complete page 3") —
3555
+ * so there is no scenario on the real form where a group is associated but
3556
+ * has not filed one. `computeIegGroupFigures`'s "aggregate every member's
3557
+ * prior-two-years spending into one shared base amount", used regardless of
3558
+ * associated/non-associated status by `computeAlbertaReturn`, therefore
3559
+ * does not correspond to anything a preparer could actually file — it is
3560
+ * harmless ONLY because `computeIeg`'s associated branch ignores
3561
+ * `baseAmount` entirely. Left in place rather than removed: not exercised by
3562
+ * any of TRA's Fall 2026 certification test cases (all either non-associated
3563
+ * with a genuine single-corporation base, or associated with a filed
3564
+ * Agreement whose own figures are what actually reach the credit), so a full
3565
+ * rearchitecture is deferred rather than rushed. `taxableCapital` for the
3566
+ * associated path is already sourced from the Agreement, not from here — see
3567
+ * `alberta-return.ts`. See `research/knowledge-base/
3568
+ * at1-schedule-29-ieg-mechanics.md` for the full comparison against the live
3569
+ * form and both of TRA's published worked examples.
3570
+ *
1017
3571
  * Whole dollars, pure.
1018
3572
  */
1019
3573
  const nn$23 = (v) => Math.max(0, Math.round(v ?? 0));
@@ -1095,17 +3649,20 @@ function allocateIegEvenly(groupExpenditureLimit, names) {
1095
3649
  allocated: i === 0 ? each + remainder : each
1096
3650
  })));
1097
3651
  }
1098
- function computeAgreementMember(m) {
3652
+ function computeAgreementMember(m, index, issues) {
1099
3653
  const allocatedExpenditureLimit = nn$23(m.allocatedExpenditureLimit);
1100
3654
  const currentYearExpenditures = nn$23(m.currentYearExpenditures);
1101
3655
  const priorYear1 = nn$23(m.priorYear1);
1102
3656
  const priorYear2 = nn$23(m.priorYear2);
1103
3657
  const base = Math.round((priorYear1 + priorYear2) / 2);
1104
- const individualMaximumAllowedAmount = Math.max(0, currentYearExpenditures - base);
3658
+ const individualMaximumAllowedAmount = currentYearExpenditures - base;
1105
3659
  const days = Math.max(0, Math.min(m.daysInTaxYear ?? 365, 366));
1106
3660
  const dayProratedLimit = Math.round(4e6 * (days / 365));
1107
- const dayProratedComponent = Math.max(0, Math.min(dayProratedLimit, currentYearExpenditures) - base);
1108
- const allocatedAllowedAmount = Math.min(allocatedExpenditureLimit, individualMaximumAllowedAmount, dayProratedComponent);
3661
+ const dayProratedComponent = Math.min(dayProratedLimit, currentYearExpenditures) - base;
3662
+ const isClaimant = index === 0;
3663
+ if (m.hasAlbertaPermanentEstablishment === void 0 && !isClaimant) issues.push(`Alberta Schedule 29 Agreement: ${m.name} did not state whether it has a permanent establishment in Alberta. Treated as no permanent establishment (line 268 = nil) — a member without one is not eligible for the IEG even when its own formula would allow an amount. Confirm and re-enter if this member does have an Alberta PE.`);
3664
+ const hasAlbertaPermanentEstablishment = m.hasAlbertaPermanentEstablishment ?? isClaimant;
3665
+ const allocatedAllowedAmount = hasAlbertaPermanentEstablishment ? Math.max(0, Math.min(allocatedExpenditureLimit, individualMaximumAllowedAmount, dayProratedComponent)) : 0;
1109
3666
  return {
1110
3667
  name: m.name,
1111
3668
  ...m.federalBusinessNumber !== void 0 ? { federalBusinessNumber: m.federalBusinessNumber } : {},
@@ -1116,6 +3673,7 @@ function computeAgreementMember(m) {
1116
3673
  priorYear1,
1117
3674
  priorYear2,
1118
3675
  taxableCapitalPriorYear: nn$23(m.taxableCapitalPriorYear),
3676
+ hasAlbertaPermanentEstablishment,
1119
3677
  individualMaximumAllowedAmount,
1120
3678
  allocatedAllowedAmount
1121
3679
  };
@@ -1130,7 +3688,7 @@ function computeIegAgreement(input) {
1130
3688
  if (input.members.length === 0) issues.push("Alberta Schedule 29 Agreement: no members were supplied. The claiming corporation must appear as the first row even in a two-corporation group.");
1131
3689
  const days = Math.max(0, Math.min(input.daysInLongestYear, 366));
1132
3690
  const maximumExpenditureLimit = Math.round(4e6 * (days / 365));
1133
- const members = input.members.map(computeAgreementMember);
3691
+ const members = input.members.map((m, i) => computeAgreementMember(m, i, issues));
1134
3692
  const sum = (get) => members.reduce((s, m) => s + get(m), 0);
1135
3693
  const totalAllocatedLimit = sum((m) => m.allocatedExpenditureLimit);
1136
3694
  const totalCurrentYearExpenditures = sum((m) => m.currentYearExpenditures);
@@ -1190,42 +3748,72 @@ function computeAlbertaReturn(input) {
1190
3748
  const issues = [];
1191
3749
  let iegGroup;
1192
3750
  let iegAgreement;
3751
+ let iegEligibleExpenditures;
1193
3752
  let ieg;
1194
- if (input.ieg) if (input.ieg.group.length === 0) issues.push("AT1 Schedule 29: no associated-group members were supplied, so no grant was claimed. Pass the claimant itself even when it has no associated corporations — an empty group would otherwise imply no taxable-capital grind and a nil base, producing the maximum possible grant on absent data.");
1195
- else {
1196
- iegGroup = computeIegGroupFigures(input.ieg.group);
1197
- issues.push(...iegGroup.issues);
1198
- const allocation = allocateIegExpenditureLimit(iegGroup.groupExpenditureLimit, [{
1199
- name: "claimant",
1200
- allocated: input.ieg.allocatedLimit ?? iegGroup.groupExpenditureLimit
1201
- }]);
1202
- issues.push(...allocation.issues);
1203
- if (input.ieg.agreement) {
1204
- iegAgreement = computeIegAgreement(input.ieg.agreement);
1205
- issues.push(...iegAgreement.issues);
3753
+ let at4970;
3754
+ if (input.ieg) {
3755
+ if (input.ieg.at4970) at4970 = computeAt4970(input.ieg.at4970);
3756
+ let resolvedEligibleExpenditures = input.ieg.eligibleExpenditures ?? 0;
3757
+ if (input.ieg.eligible) {
3758
+ const e = input.ieg.eligible;
3759
+ const federalProxyAmount = e.federalProxyAmount ?? at4970?.totals.federalProxyAmount;
3760
+ const albertaProxyAmount = e.albertaProxyAmount ?? at4970?.totals.albertaProxyAmount;
3761
+ iegEligibleExpenditures = computeIegEligibleExpenditures({
3762
+ federalAmount: e.federalAmount,
3763
+ albertaPortion: e.albertaPortion ?? at4970?.totals.albertaPortion ?? 0,
3764
+ ...federalProxyAmount !== void 0 ? { federalProxyAmount } : {},
3765
+ ...albertaProxyAmount !== void 0 ? { albertaProxyAmount } : {},
3766
+ ...e.iegReducingFederalExpenditure !== void 0 ? { iegReducingFederalExpenditure: e.iegReducingFederalExpenditure } : {},
3767
+ ...e.repaymentOrContractPayment !== void 0 ? { repaymentOrContractPayment: e.repaymentOrContractPayment } : {}
3768
+ });
3769
+ resolvedEligibleExpenditures = iegEligibleExpenditures.totalEligibleExpenditures;
3770
+ }
3771
+ if (input.ieg.group.length === 0) issues.push("AT1 Schedule 29: no associated-group members were supplied, so no grant was claimed. Pass the claimant itself even when it has no associated corporations — an empty group would otherwise imply no taxable-capital grind and a nil base, producing the maximum possible grant on absent data.");
3772
+ else {
3773
+ iegGroup = computeIegGroupFigures(input.ieg.group);
3774
+ issues.push(...iegGroup.issues);
3775
+ const allocation = allocateIegExpenditureLimit(iegGroup.groupExpenditureLimit, [{
3776
+ name: "claimant",
3777
+ allocated: input.ieg.allocatedLimit ?? iegGroup.groupExpenditureLimit
3778
+ }]);
3779
+ issues.push(...allocation.issues);
3780
+ if (input.ieg.agreement) {
3781
+ iegAgreement = computeIegAgreement(input.ieg.agreement);
3782
+ issues.push(...iegAgreement.issues);
3783
+ }
3784
+ ieg = computeIeg({
3785
+ eligibleExpenditures: resolvedEligibleExpenditures,
3786
+ baseAmount: iegGroup.groupBaseAmount,
3787
+ expenditureLimit: allocation.allocations[0]?.allocated ?? 0,
3788
+ taxableCapital: iegAgreement ? iegAgreement.totalTaxableCapitalPriorYear : iegGroup.groupTaxableCapital,
3789
+ ...input.ieg.recapture !== void 0 ? { recapture: input.ieg.recapture } : {},
3790
+ ...iegAgreement ? { associatedAllowedAmount: iegAgreement.claimantAllocatedAllowedAmount } : {}
3791
+ });
1206
3792
  }
1207
- ieg = computeIeg({
1208
- eligibleExpenditures: input.ieg.eligibleExpenditures,
1209
- baseAmount: iegGroup.groupBaseAmount,
1210
- expenditureLimit: allocation.allocations[0]?.allocated ?? 0,
1211
- taxableCapital: iegGroup.groupTaxableCapital,
1212
- ...input.ieg.recapture !== void 0 ? { recapture: input.ieg.recapture } : {},
1213
- ...iegAgreement ? { associatedAllowedAmount: iegAgreement.claimantAllocatedAllowedAmount } : {}
1214
- });
1215
3793
  }
1216
3794
  const schedulePayloads = [];
1217
3795
  const sched = input.schedules;
1218
3796
  if (sched?.smallBusinessDeduction) schedulePayloads.push(schedule1Values(sched.smallBusinessDeduction));
1219
3797
  if (sched?.allocation) schedulePayloads.push(schedule2Values(sched.allocation));
3798
+ if (sched?.otherDeductionsCredits) schedulePayloads.push(schedule3Values(sched.otherDeductionsCredits));
3799
+ if (sched?.foreignInvestmentTaxCredit) schedulePayloads.push(schedule4Values(sched.foreignInvestmentTaxCredit));
3800
+ if (sched?.royaltyTaxDeduction) schedulePayloads.push(schedule5Values(sched.royaltyTaxDeduction));
3801
+ if (sched?.royaltyTaxCredit) schedulePayloads.push(schedule6Values(sched.royaltyTaxCredit));
3802
+ if (sched?.royaltySupplemental) schedulePayloads.push(schedule7Values(sched.royaltySupplemental));
3803
+ if (sched?.politicalContributions) schedulePayloads.push(schedule8Values(sched.politicalContributions));
3804
+ if (sched?.sredTaxCredit) schedulePayloads.push(schedule9Values(sched.sredTaxCredit.result, sched.sredTaxCredit.group));
1220
3805
  if (sched?.lossCarryback) schedulePayloads.push(schedule10Values(sched.lossCarryback));
3806
+ if (sched?.manufacturingProcessing) schedulePayloads.push(schedule11Values(sched.manufacturingProcessing));
1221
3807
  if (sched?.reconciliation) schedulePayloads.push(schedule12Values(sched.reconciliation));
1222
3808
  if (sched?.cca) schedulePayloads.push(schedule13Values(sched.cca));
3809
+ if (sched?.resourceDeductions) schedulePayloads.push(schedule15Values(sched.resourceDeductions));
1223
3810
  if (sched?.scientificResearch) schedulePayloads.push(schedule16Values(sched.scientificResearch));
1224
3811
  if (sched?.reserves) schedulePayloads.push(schedule17Values(sched.reserves));
1225
3812
  if (sched?.dispositions) schedulePayloads.push(schedule18Values(sched.dispositions));
1226
3813
  if (sched?.donations) schedulePayloads.push(schedule20Values(sched.donations));
1227
3814
  if (sched?.losses) schedulePayloads.push(schedule21Values(sched.losses));
1228
- if (ieg) schedulePayloads.push(schedule29Values(ieg, iegAgreement));
3815
+ if (ieg) schedulePayloads.push(schedule29Values(ieg, iegAgreement, iegEligibleExpenditures, input.ieg?.primaryFieldCode));
3816
+ if (at4970) schedulePayloads.push(schedule4970Values(at4970));
1229
3817
  return {
1230
3818
  allocationFactor,
1231
3819
  albertaTax,
@@ -1233,6 +3821,7 @@ function computeAlbertaReturn(input) {
1233
3821
  schedulePayloads,
1234
3822
  ...iegGroup ? { iegGroup } : {},
1235
3823
  ...iegAgreement ? { iegAgreement } : {},
3824
+ ...iegEligibleExpenditures ? { iegEligibleExpenditures } : {},
1236
3825
  ...ieg ? { ieg } : {},
1237
3826
  innovationEmploymentGrant: ieg?.ieg ?? 0,
1238
3827
  issues
@@ -1382,6 +3971,7 @@ const AT1_MANDATORY_WITHOUT_DEFAULT = Object.freeze([
1382
3971
  function assertAt1MandatoryComplete(d) {
1383
3972
  const blank = (v) => v === void 0 || v === null || String(v).trim() === "";
1384
3973
  const missing = AT1_MANDATORY_WITHOUT_DEFAULT.filter(([key]) => blank(d[key])).map(([, label]) => label);
3974
+ if (d.transmitter?.isAmended && blank(d.transmitter.amendmentDescription)) missing.push("EDI073 description of changes (required when the amended return indicator is set)");
1385
3975
  if (missing.length > 0) throw new At1MandatoryFieldMissingError(missing);
1386
3976
  }
1387
3977
  /** Refuse to render when any critical mandatory field is absent. */
@@ -1741,6 +4331,16 @@ const AT1_EDI_LINE_ITEMS = [
1741
4331
  id: "EDI041001",
1742
4332
  get: (t) => t.contact.email,
1743
4333
  fmt: "text"
4334
+ },
4335
+ {
4336
+ id: "EDI071001",
4337
+ get: (t) => t.isAmended ? "1" : void 0,
4338
+ fmt: "text"
4339
+ },
4340
+ {
4341
+ id: "EDI073001",
4342
+ get: (t) => t.isAmended ? t.amendmentDescription : void 0,
4343
+ fmt: "text"
1744
4344
  }
1745
4345
  ];
1746
4346
  //#endregion
@@ -2232,8 +4832,9 @@ function albertaCurrentYearLoss(result) {
2232
4832
  *
2233
4833
  * The STRAIGHT-LINE classes (13 leasehold interests, 14 limited-life intangibles)
2234
4834
  * are deliberately NOT here — they use their own arithmetic in Reg 1100(1)(b) and
2235
- * 1100(1)(c), so `computeCcaClass` throws for them rather than silently applying
2236
- * a declining-balance rate. See `schedules/cca-straight-line.ts`.
4835
+ * 1100(1)(c), so `computeCcaClass` never looks them up in this table. It handles
4836
+ * an EXISTING opening-UCC pool for them directly (see `schedule8.ts`); a NEW
4837
+ * addition needs the full layer/property mechanics in `schedules/cca-straight-line.ts`.
2237
4838
  *
2238
4839
  * Class 14.1 IS a declining-balance class (5%) and belongs here. Its pre-2027
2239
4840
  * transitional additional allowance — which keeps pre-2017 expenditures moving at
@@ -2519,19 +5120,79 @@ function computeClass141AdditionalAllowance(input) {
2519
5120
  * Terminal loss: if the class is EMPTIED (no assets left) with positive UCC, the
2520
5121
  * remaining UCC is deducted as a terminal loss, no CCA, closing UCC nil.
2521
5122
  *
2522
- * NOT modelled (caller / other schedules): class 13/14/14.1 straight-line, the
2523
- * shared $1.5M immediate-expensing limit across an associated group (caller passes
2524
- * the already-capped eligible amount), and the 2024+ AIIP phase-out factor.
5123
+ * Class 13/14 (straight-line, Reg 1100(1)(b)/(c)): this function accepts them
5124
+ * too, but ONLY as an existing opening-UCC pool being drawn down no rate, no
5125
+ * half-year rule, no AIIP; the claim is capped at the UCC before CCA and
5126
+ * nothing else. A current-year ADDITION on one of these classes is refused
5127
+ * (see `UnsupportedCcaClassError`) rather than silently claimed without the
5128
+ * 5-year floor / remaining-life proration Schedule III and Reg 1100(1)(c)
5129
+ * require for a NEW layer or property — that full mechanic is
5130
+ * `computeClass13` / `computeClass14` in `cca-straight-line.ts`, which this
5131
+ * entry point does not have layer/remaining-life data to drive.
5132
+ *
5133
+ * NOT modelled (caller / other schedules): a NEW class 13/14 addition (see
5134
+ * above), class 14.1's transitional additional allowance, the shared $1.5M
5135
+ * immediate-expensing limit across an associated group (caller passes the
5136
+ * already-capped eligible amount), and the 2024+ AIIP phase-out factor.
2525
5137
  */
2526
5138
  var UnsupportedCcaClassError = class extends Error {
2527
5139
  ccaClass;
2528
- constructor(ccaClass) {
2529
- super(`CCA class ${ccaClass} is not a declining-balance class this engine computes yet (straight-line / special classes like 13, 14, 14.1 use different mechanics).`);
5140
+ constructor(ccaClass, reason) {
5141
+ super(reason ?? `CCA class ${ccaClass} is not a declining-balance class this engine computes yet (straight-line / special classes like 13, 14, 14.1 use different mechanics).`);
2530
5142
  this.name = "UnsupportedCcaClassError";
2531
5143
  this.ccaClass = ccaClass;
2532
5144
  }
2533
5145
  };
5146
+ /** Class 13/14 accept only an opening-UCC drawdown here — see the file docstring. */
5147
+ const STRAIGHT_LINE_OPENING_BALANCE_ONLY = /* @__PURE__ */ new Set(["13", "14"]);
5148
+ /**
5149
+ * Class 13/14, opening-balance drawdown only: no rate, no half-year rule, no
5150
+ * AIIP, no immediate expensing — none of those concepts apply to a
5151
+ * straight-line class. The claim is simply capped at the UCC before CCA.
5152
+ * Recapture and terminal loss reuse the same general rules as every other
5153
+ * class (s.13(1) / s.20(16) are not declining-balance-specific).
5154
+ */
5155
+ function computeStraightLineOpeningBalanceClass(input) {
5156
+ if (Math.max(0, input.additions ?? 0) > 0) throw new UnsupportedCcaClassError(input.ccaClass, `CCA class ${input.ccaClass}: a current-year addition needs the detailed leasehold/limited-life schedule (computeClass13 / computeClass14 in cca-straight-line.ts), which this entry point has no lease-term / remaining-life data to drive. Drawing down an EXISTING opening balance (no addition this year) is supported directly.`);
5157
+ const dispositions = Math.max(0, input.dispositions ?? 0);
5158
+ const netAdjustments = input.netAdjustments ?? 0;
5159
+ const uccBeforeCca = input.openingUCC + netAdjustments - dispositions;
5160
+ const base = {
5161
+ ccaClass: input.ccaClass,
5162
+ rate: 0,
5163
+ uccBeforeCca,
5164
+ immediateExpensingClaim: 0,
5165
+ halfYearAdjustment: 0,
5166
+ aiipEnhancement: 0,
5167
+ ccaBase: 0,
5168
+ maxCca: 0,
5169
+ ccaClaimed: 0,
5170
+ closingUCC: 0,
5171
+ recapture: 0,
5172
+ terminalLoss: 0
5173
+ };
5174
+ if (uccBeforeCca < 0) return {
5175
+ ...base,
5176
+ closingUCC: 0,
5177
+ recapture: -uccBeforeCca
5178
+ };
5179
+ if (input.classEmptied && uccBeforeCca > 0) return {
5180
+ ...base,
5181
+ closingUCC: 0,
5182
+ terminalLoss: uccBeforeCca
5183
+ };
5184
+ const maxCca = uccBeforeCca;
5185
+ const ccaClaimed = input.claim != null ? Math.min(Math.max(0, input.claim), maxCca) : maxCca;
5186
+ return {
5187
+ ...base,
5188
+ ccaBase: uccBeforeCca,
5189
+ maxCca,
5190
+ ccaClaimed,
5191
+ closingUCC: uccBeforeCca - ccaClaimed
5192
+ };
5193
+ }
2534
5194
  function computeCcaClass(input, rates = CCA_DECLINING_BALANCE_RATES_2024, prorationFactor = 1) {
5195
+ if (STRAIGHT_LINE_OPENING_BALANCE_ONLY.has(input.ccaClass)) return computeStraightLineOpeningBalanceClass(input);
2535
5196
  const rate = rates[input.ccaClass];
2536
5197
  if (rate === void 0) throw new UnsupportedCcaClassError(input.ccaClass);
2537
5198
  const additions = Math.max(0, input.additions ?? 0);
@@ -2710,6 +5371,83 @@ function applyOverride(federal, o) {
2710
5371
  opt("applyHalfYearRule", federal.applyHalfYearRule);
2711
5372
  return merged;
2712
5373
  }
5374
+ /**
5375
+ * Adapts a straight-line {@link Class13Result}/{@link Class14Result} into the
5376
+ * SAME shape every declining-balance class uses, so `classes` stays one
5377
+ * homogeneous array and the filing layer (`schedule13Values`, which reads
5378
+ * only the shared `CcaClassResult` fields) needs no changes at all. Rate/
5379
+ * half-year/AIIP/immediate-expensing are inapplicable to a straight-line
5380
+ * class and reported as 0; neither mechanic produces recapture here.
5381
+ */
5382
+ function straightLineAsCcaClassResult(ccaClass, r) {
5383
+ return {
5384
+ ccaClass,
5385
+ rate: 0,
5386
+ uccBeforeCca: r.uccBeforeCca,
5387
+ immediateExpensingClaim: 0,
5388
+ halfYearAdjustment: 0,
5389
+ aiipEnhancement: 0,
5390
+ ccaBase: 0,
5391
+ maxCca: r.maxCca,
5392
+ ccaClaimed: r.ccaClaimed,
5393
+ closingUCC: r.closingUCC,
5394
+ recapture: 0,
5395
+ terminalLoss: 0
5396
+ };
5397
+ }
5398
+ /** Federal claims `federalClaim` (max if omitted); Alberta defaults to federal's ACTUAL claim, not federal's max. */
5399
+ function computeClass13Pair(input, issues) {
5400
+ const shared = {
5401
+ layers: input.layers,
5402
+ openingUCC: input.openingUCC
5403
+ };
5404
+ const applyHalfYearRule = input.applyHalfYearRule !== void 0 ? { applyHalfYearRule: input.applyHalfYearRule } : {};
5405
+ const federal = computeClass13({
5406
+ ...shared,
5407
+ ...applyHalfYearRule,
5408
+ ...input.federalClaim !== void 0 ? { claim: input.federalClaim } : {}
5409
+ });
5410
+ const albertaClaim = input.albertaClaim !== void 0 ? input.albertaClaim : federal.ccaClaimed;
5411
+ const alberta = computeClass13({
5412
+ ...shared,
5413
+ ...applyHalfYearRule,
5414
+ claim: albertaClaim
5415
+ });
5416
+ issues.push(...federal.issues, ...alberta.issues);
5417
+ return {
5418
+ ccaClass: "13",
5419
+ alberta: straightLineAsCcaClassResult("13", alberta),
5420
+ federal: straightLineAsCcaClassResult("13", federal),
5421
+ openingUccDiffers: false,
5422
+ claimDiffers: alberta.ccaClaimed !== federal.ccaClaimed,
5423
+ ccaDifference: alberta.ccaClaimed - federal.ccaClaimed
5424
+ };
5425
+ }
5426
+ function computeClass14Pair(input, issues) {
5427
+ const shared = {
5428
+ properties: input.properties,
5429
+ openingUCC: input.openingUCC,
5430
+ ...input.daysInTaxYear !== void 0 ? { daysInTaxYear: input.daysInTaxYear } : {}
5431
+ };
5432
+ const federal = computeClass14({
5433
+ ...shared,
5434
+ ...input.federalClaim !== void 0 ? { claim: input.federalClaim } : {}
5435
+ });
5436
+ const albertaClaim = input.albertaClaim !== void 0 ? input.albertaClaim : federal.ccaClaimed;
5437
+ const alberta = computeClass14({
5438
+ ...shared,
5439
+ claim: albertaClaim
5440
+ });
5441
+ issues.push(...federal.issues, ...alberta.issues);
5442
+ return {
5443
+ ccaClass: "14",
5444
+ alberta: straightLineAsCcaClassResult("14", alberta),
5445
+ federal: straightLineAsCcaClassResult("14", federal),
5446
+ openingUccDiffers: false,
5447
+ claimDiffers: alberta.ccaClaimed !== federal.ccaClaimed,
5448
+ ccaDifference: alberta.ccaClaimed - federal.ccaClaimed
5449
+ };
5450
+ }
2713
5451
  function computeAlbertaSchedule13(input) {
2714
5452
  const rates = input.rates ?? CCA_DECLINING_BALANCE_RATES_2024;
2715
5453
  const issues = [];
@@ -2730,6 +5468,8 @@ function computeAlbertaSchedule13(input) {
2730
5468
  ccaDifference: alberta.ccaClaimed - federal.ccaClaimed
2731
5469
  };
2732
5470
  });
5471
+ if (input.class13) classes.push(computeClass13Pair(input.class13, issues));
5472
+ if (input.class14) classes.push(computeClass14Pair(input.class14, issues));
2733
5473
  const sum = (pickFn) => classes.reduce((s, c) => s + pickFn(c), 0);
2734
5474
  const albertaTotalCca = sum((c) => c.alberta.ccaClaimed);
2735
5475
  const federalTotalCca = sum((c) => c.federal.ccaClaimed);
@@ -3324,19 +6064,58 @@ function computeDonationMaximum(input) {
3324
6064
  };
3325
6065
  }
3326
6066
  //#endregion
6067
+ //#region src/t2/at1/schedules/schedule21-limited-partnership.ts
6068
+ function computeLimitedPartnershipLossRow(row) {
6069
+ const issues = [];
6070
+ const precedingYearBalance = Math.max(0, row.precedingYearBalance);
6071
+ const transferredOnWindUp = Math.max(0, row.transferredOnWindUp ?? 0);
6072
+ const currentYearLoss = Math.max(0, row.currentYearLoss ?? 0);
6073
+ const maxApplied = precedingYearBalance + transferredOnWindUp;
6074
+ const requestedApplied = Math.max(0, row.applied ?? 0);
6075
+ if (requestedApplied > maxApplied) issues.push(`Limited partnership loss${row.identifier ? ` (${row.identifier})` : ""}: the amount applied (${requestedApplied}) cannot exceed the preceding-year balance plus any wind-up transfer (${maxApplied}). Capped at ${maxApplied}.`);
6076
+ const applied = Math.min(requestedApplied, maxApplied);
6077
+ const closingBalance = precedingYearBalance + transferredOnWindUp + currentYearLoss - applied;
6078
+ return {
6079
+ ...row.identifier !== void 0 ? { identifier: row.identifier } : {},
6080
+ precedingYearBalance,
6081
+ transferredOnWindUp,
6082
+ currentYearLoss,
6083
+ applied,
6084
+ closingBalance,
6085
+ issues
6086
+ };
6087
+ }
6088
+ function computeLimitedPartnershipLosses(rows) {
6089
+ const computedRows = rows.map(computeLimitedPartnershipLossRow);
6090
+ return {
6091
+ rows: computedRows,
6092
+ totalApplied: computedRows.reduce((s, r) => s + r.applied, 0),
6093
+ totalClosingBalance: computedRows.reduce((s, r) => s + r.closingBalance, 0),
6094
+ issues: computedRows.flatMap((r) => r.issues)
6095
+ };
6096
+ }
6097
+ //#endregion
3327
6098
  //#region src/t2/schedules/loss-continuity.ts
3328
6099
  function computeLossContinuity(input) {
3329
6100
  const currentYearLoss = input.currentYearLoss ?? 0;
3330
6101
  const carriedBack = input.carriedBack ?? 0;
3331
6102
  const appliedCurrentYear = input.appliedCurrentYear ?? 0;
3332
6103
  const expired = input.expired ?? 0;
3333
- const closingBalance = Math.max(0, input.openingBalance + currentYearLoss - carriedBack - appliedCurrentYear - expired);
6104
+ const windUpTransfer = input.windUpTransfer ?? 0;
6105
+ const section80Adjustment = input.section80Adjustment ?? 0;
6106
+ const otherAdjustments = input.otherAdjustments ?? 0;
6107
+ const balanceAtBeginningOfYear = input.openingBalance - expired;
6108
+ const closingBalance = Math.max(0, balanceAtBeginningOfYear + windUpTransfer + currentYearLoss - appliedCurrentYear - section80Adjustment - otherAdjustments - carriedBack);
3334
6109
  return {
3335
6110
  openingBalance: input.openingBalance,
3336
6111
  currentYearLoss,
3337
6112
  carriedBack,
3338
6113
  appliedCurrentYear,
3339
6114
  expired,
6115
+ windUpTransfer,
6116
+ section80Adjustment,
6117
+ otherAdjustments,
6118
+ balanceAtBeginningOfYear,
3340
6119
  closingBalance
3341
6120
  };
3342
6121
  }
@@ -3368,6 +6147,76 @@ function computeLossSchedule(input) {
3368
6147
  };
3369
6148
  }
3370
6149
  //#endregion
6150
+ //#region src/t2/at1/schedules/schedule21-year-of-origin.ts
6151
+ function sumRows(rows) {
6152
+ const sum = (f) => rows.reduce((s, r) => s + f(r), 0);
6153
+ return {
6154
+ balanceAtBeginning: sum((r) => r.balanceAtBeginning),
6155
+ lossIncurred: sum((r) => r.lossIncurred),
6156
+ adjustments: sum((r) => r.adjustments),
6157
+ carriedBack: sum((r) => r.carriedBack),
6158
+ applied: sum((r) => r.applied),
6159
+ balanceAtEnd: sum((r) => r.balanceAtEnd)
6160
+ };
6161
+ }
6162
+ function computeNonCapitalLossByYearOfOrigin(input) {
6163
+ const issues = [];
6164
+ const lossIncurred = Math.max(0, input.currentYearLoss);
6165
+ const carriedBack = Math.max(0, input.currentYearCarriedBack);
6166
+ const rows = [{
6167
+ yearIndex: 0,
6168
+ balanceAtBeginning: 0,
6169
+ lossIncurred,
6170
+ adjustments: 0,
6171
+ carriedBack,
6172
+ applied: 0,
6173
+ balanceAtEnd: Math.max(0, lossIncurred - carriedBack)
6174
+ }, ...(input.priorVintages ?? []).map((v) => {
6175
+ if (v.yearsAgo < 1 || v.yearsAgo > 20) issues.push(`Non-capital loss year of origin: yearsAgo (${v.yearsAgo}) must be 1-20 — a non-capital loss expires after 20 years.`);
6176
+ const balanceAtBeginning = Math.max(0, v.balanceAtBeginning ?? 0);
6177
+ const adjustments = v.adjustments ?? 0;
6178
+ const applied = Math.max(0, v.applied ?? 0);
6179
+ return {
6180
+ yearIndex: v.yearsAgo,
6181
+ ...v.taxYearEnd !== void 0 ? { taxYearEnd: v.taxYearEnd } : {},
6182
+ balanceAtBeginning,
6183
+ lossIncurred: 0,
6184
+ adjustments,
6185
+ carriedBack: 0,
6186
+ applied,
6187
+ balanceAtEnd: Math.max(0, balanceAtBeginning + adjustments - applied)
6188
+ };
6189
+ })];
6190
+ return {
6191
+ rows,
6192
+ totals: sumRows(rows),
6193
+ issues
6194
+ };
6195
+ }
6196
+ function computeOtherLossByYearOfOrigin(rows) {
6197
+ const issues = [];
6198
+ const computed = rows.map((r) => {
6199
+ if (r.yearIndex < 0 || r.yearIndex > 20) issues.push(`Other-loss year of origin: yearIndex (${r.yearIndex}) must be 0-20 — a farm/restricted-farm loss expires after 20 years.`);
6200
+ const listedPersonalPropertyLosses = Math.max(0, r.listedPersonalPropertyLosses ?? 0);
6201
+ if (r.yearIndex > 7 && listedPersonalPropertyLosses > 0) issues.push(`Other-loss year of origin: listed personal property losses cannot exist for occurrence ${r.yearIndex} — that loss type expires after 7 taxation years, not 20.`);
6202
+ return {
6203
+ yearIndex: r.yearIndex,
6204
+ farmLosses: Math.max(0, r.farmLosses ?? 0),
6205
+ restrictedFarmLosses: Math.max(0, r.restrictedFarmLosses ?? 0),
6206
+ listedPersonalPropertyLosses: r.yearIndex > 7 ? 0 : listedPersonalPropertyLosses
6207
+ };
6208
+ });
6209
+ return {
6210
+ rows: computed,
6211
+ totals: {
6212
+ farmLosses: computed.reduce((s, r) => s + r.farmLosses, 0),
6213
+ restrictedFarmLosses: computed.reduce((s, r) => s + r.restrictedFarmLosses, 0),
6214
+ listedPersonalPropertyLosses: computed.reduce((s, r) => s + r.listedPersonalPropertyLosses, 0)
6215
+ },
6216
+ issues
6217
+ };
6218
+ }
6219
+ //#endregion
3371
6220
  //#region src/t2/certification/fixtures.ts
3372
6221
  const T2_CERTIFICATION_FIXTURES = [
3373
6222
  {
@@ -5051,15 +7900,18 @@ function computeFederalT2(input) {
5051
7900
  const rates = input.rates ?? resolveCorpTaxRates(taxYear, book);
5052
7901
  const prorationFactor = shortYearProrationFactor(input.periodStart, input.periodEnd);
5053
7902
  const cca = input.ccaClasses?.length ? computeCcaSchedule(input.ccaClasses, resolveCcaRates(taxYear), prorationFactor) : void 0;
7903
+ const class13 = input.class13 ? computeClass13(input.class13) : void 0;
7904
+ const class14 = input.class14 ? computeClass14(input.class14) : void 0;
5054
7905
  const capitalGains = input.capitalDispositions?.length ? computeSchedule6(input.capitalDispositions, rates.CAPITAL_GAINS_INCLUSION_RATE) : void 0;
5055
7906
  const schedule1Additions = [...input.schedule1Additions ?? []];
5056
7907
  const schedule1Deductions = [...input.schedule1Deductions ?? []];
7908
+ const totalCcaDeduction = (cca?.totalCca ?? 0) + (class13?.ccaClaimed ?? 0) + (class14?.ccaClaimed ?? 0);
7909
+ if (totalCcaDeduction > 0) schedule1Deductions.push({
7910
+ line: "403",
7911
+ label: "Capital cost allowance from Schedule 8",
7912
+ amount: totalCcaDeduction
7913
+ });
5057
7914
  if (cca) {
5058
- if (cca.totalCca > 0) schedule1Deductions.push({
5059
- line: "403",
5060
- label: "Capital cost allowance from Schedule 8",
5061
- amount: cca.totalCca
5062
- });
5063
7915
  if (cca.totalTerminalLoss > 0) schedule1Deductions.push({
5064
7916
  line: "404",
5065
7917
  label: "Terminal loss from Schedule 8",
@@ -5259,6 +8111,8 @@ function computeFederalT2(input) {
5259
8111
  ...donations ? { donations } : {},
5260
8112
  ...lossCarryback ? { lossCarryback } : {},
5261
8113
  ...cca ? { cca } : {},
8114
+ ...class13 ? { class13 } : {},
8115
+ ...class14 ? { class14 } : {},
5262
8116
  ...capitalGains ? { capitalGains } : {},
5263
8117
  ...provincial ? { provincial } : {},
5264
8118
  ...provincialAllocation ? { provincialAllocation } : {},
@@ -6389,4 +9243,4 @@ function computeMpDeduction(input, rates = MP_RATES_2024) {
6389
9243
  };
6390
9244
  }
6391
9245
  //#endregion
6392
- export { computeProvincialAllocation as $, at1TaxPayableDeductions as $n, CEC_DEDUCTION_RATE as $t, runConformance as A, reconcileAlbertaNetIncome as An, computeAlbertaSbd as Ar, computeCcpcActiveBusinessTax as At, computeGrip as B, formatRsiText as Bn, resolveRates as Br, AT1_DONATION_GAIN_RATE as Bt, computeQuebecTax as C, albertaCcaScheduleAdjustments as Cn, schedule18Values as Cr, blendProvinceRateTable as Ct, T2_LINE_META as D, albertaReserveDifference as Dn, schedule29Values as Dr, dividendsDeductibleS112 as Dt, resolveQuebecTaxRates as E, albertaRecaptureDifference as En, schedule21Values as Er, computeTaxableIncome as Et, normalizeSchedule88 as F, RSI_NEGATIVE_PREFIX as Fn, resolveAlbertaTaxRates as Fr, CORP_TAX_RATE_BOOK as Ft, ITC_RECAPTURE_PERIOD_YEARS as G, AT1_CRITICAL_MANDATORY_FIELDS as Gn, SECTION_34_2_GROSS_UP as Gt, LARGE_CORPORATION_THRESHOLD as H, renderRsiHeader as Hn, computeDonationMaximum as Ht, computeSchedule55 as I, RSI_WORD_GAP as In, earliestRateYear as Ir, resolveCorpTaxRates as It, allocateEvenly as J, At1TaxPayableMismatchError as Jn, AT1_RESERVE_LINES as Jt, computeItcRecapture as K, At1CriticalFieldMissingError as Kn, computeAlbertaSchedule18 as Kt, LRIP_INVESTMENT_CORPORATION_MULTIPLE as L, RsiLineItemError as Ln, extendRateBook as Lr, T2_CERTIFICATION_FIXTURES as Lt, computeFederalT2 as M, computeLossCarryback as Mn, computeDayWeightedGeneralTax as Mr, computeBusinessLimit as Mt, computeSchedule101 as N, RSI_COLUMN_GAP as Nn, AB_TAX_2024 as Nr, computeSBD as Nt, foldT2Lines as O, albertaTerminalLossDifference as On, schedule2Values as Or, netCapitalLossApplied as Ot, SCHEDULE_88_MAX_URLS as P, RSI_DELIMITER as Pn, AB_TAX_RATE_BOOK as Pr, CORP_TAX_2024 as Pt, computeSchedule6 as Q, assertCriticalFields as Qn, computeAlbertaSchedule16 as Qt, LRIP_INVESTMENT_INCOME_FACTOR as R, formatRsiAmount as Rn, hasExactRateYear as Rr, computeLossSchedule as Rt, computeQuebecAllocationFactor as S, albertaCcaDifference as Sn, schedule17Values as Sr, resolveProvinceRates as St, QC_TAX_RATE_BOOK as T, albertaDispositionAdjustments as Tn, schedule20Values as Tr, charitableDonationsDeduction as Tt, computeTaxableCapital as U, renderRsiLineItem as Un, computeSchedule20 as Ut, computeSchedule43 as V, renderAt1Rsi as Vn, AT1_DONATION_INCOME_RATE as Vt, computeSchedule31 as W, renderAt1NetFile as Wn, AT1_DISPOSITION_CATEGORIES as Wt, computeSchedule21 as X, assertAt1MandatoryComplete as Xn, computeAlbertaSchedule17 as Xt, computeBusinessLimitAllocation as Y, albertaBalanceUnpaid as Yn, AT1_RESERVE_TOTAL_LINES as Yt, computeSchedule13 as Z, assertAt1TaxPayableReconciles as Zn, assistanceFrom as Zt, renderT2DraftReturn as _, CCA_RATE_BOOK as _n, schedule10Values as _r, EIFEL_EFFECTIVE_FROM as _t, PART_VI_1_DEDUCTION_BANDS as a, computeCcaClass as an, allocateIegExpenditureLimit as ar, computeSchedule2 as at, co17Engine as b, albertaAbilDifference as bn, schedule13Values as br, PROVINCE_RATE_BOOK as bt, EIFEL_FIRST_YEAR_START as c, CLASS_14_1_MINIMUM_DEDUCTION as cn, IEG_2024 as cr, assertSchedule1Fileable as ct, EIFEL_TRANSITIONAL_RATIO as d, MIN_LEASEHOLD_PERIODS as dn, computeIegReductionFactor as dr, deferredIncomeTaxProvisionAddBack as dt, CEC_INCLUSION_RATE as en, at1YesNo as er, computeSchedule5 as et, computeEifelLimitation as f, computeClass13 as fn, SINGLE_JURISDICTION_ALBERTA_FACTOR as fr, findSchedule1LineDefects as ft, computeT2Settlement as g, CCA_DECLINING_BALANCE_RATES_2024 as gn, at1LineItemId as gr, terminalLossDeduction as gt, computeAdjustedTaxableIncome as h, leaseholdPeriods as hn, AT1_SCHEDULES_WITH_BUILDERS as hr, recaptureAddBack as ht, computeMpDeduction as i, UnsupportedCcaClassError as in, allocateIegEvenly as ir, computePart4Rdtoh as it, runConformanceSuite as j, LossCarrybackError as jn, AB_GENERAL_RATE_BANDS as jr, computePartITax as jt, formatConformanceReport as k, computeSchedule12 as kn, computeAlbertaTax as kr, nonCapitalLossApplied as kt, EIFEL_STANDARD_RATIO as l, CLASS_14_1_TRANSITIONAL_RATE as ln, computeIeg as lr, ccaDeduction as lt, FOREIGN_TAX_CREDIT_GROSS_UP as m, computeClass141AdditionalAllowance as mn, AT1_SCHEDULES_WITHOUT_BUILDERS as mr, mealsAndEntertainmentAddBack as mt, MP_GROSS_REVENUE_THRESHOLD as n, computeAlbertaSchedule14 as nn, at1Engine as nr, PART_IV_RATE as nt, computePartVI1Deduction as o, computeCcaSchedule as on, computeIegAgreement as or, Schedule1NotFileableError as ot, ratioOfPermissibleExpenses as p, computeClass14 as pn, computeAllocationFactor as pr, incomeTaxProvisionAddBack as pt, computeZetm as q, At1MandatoryFieldMissingError as qn, AT1_RESERVE_KINDS as qt, MP_RATES_2024 as r, computeAlbertaSchedule13 as rn, computeAlbertaReturn as rr, REFUNDABLE_PART_I_RATE as rt, partVI1DeductionMultiple as s, computeSchedule8 as sn, computeIegGroupFigures as sr, amortizationAddBack as st, MP_EXCLUDED_ACTIVITIES as t, cecScheduleAppliesToTaxYear as tn, xmlEscape as tr, computeSchedule4Losses as tt, EIFEL_STANDARD_RATIO_FROM as u, MAX_LEASEHOLD_PERIODS as un, computeIegBaseAmount as ur, computeSchedule1 as ut, t2Engine as v, isDecliningBalanceClass as vn, schedule12LossDeductions as vr, assessEifel as vt, QC_TAX_2024 as w, albertaCurrentYearLoss as wn, schedule1Values as wr, dayWeightedRate as wt, computeQuebecReturn as x, albertaCapitalGainDifference as xn, schedule16Values as xr, isSchedule5Province as xt, renderCo17DraftReturn as y, resolveCcaRates as yn, schedule12Values as yr, PROVINCE_RATES_2024 as yt, computeSchedule54 as z, formatRsiDate as zn, latestRateYear as zr, computeLossContinuity as zt };
9246
+ export { computeProvincialAllocation as $, albertaBalanceUnpaid as $n, schedule4Values as $r, AT1_RESERVE_TOTAL_LINES as $t, runConformance as A, hasExactRateYear as Ai, albertaRecaptureDifference as An, computeFedeRegular as Ar, computeCcpcActiveBusinessTax as At, computeGrip as B, RSI_WORD_GAP as Bn, allocateSchedule9ExpenditureLimit as Br, computeLossSchedule as Bt, computeQuebecTax as C, AB_GENERAL_RATE_BANDS as Ci, resolveCcaRates as Cn, computeCeeRegular as Cr, blendProvinceRateTable as Ct, T2_LINE_META as D, resolveAlbertaTaxRates as Di, albertaCcaScheduleAdjustments as Dn, computeCmedb as Dr, dividendsDeductibleS112 as Dt, resolveQuebecTaxRates as E, AB_TAX_RATE_BOOK as Ei, albertaCcaDifference as En, computeCfreSuccessor as Er, computeTaxableIncome as Et, normalizeSchedule88 as F, LossCarrybackError as Fn, computeSchedule11 as Fr, CORP_TAX_RATE_BOOK as Ft, ITC_RECAPTURE_PERIOD_YEARS as G, renderAt1Rsi as Gn, schedule8Values as Gr, AT1_DONATION_INCOME_RATE as Gt, LARGE_CORPORATION_THRESHOLD as H, formatRsiAmount as Hn, computeSchedule9MaximumExpenditureLimit as Hr, computeLimitedPartnershipLossRow as Ht, computeSchedule55 as I, computeLossCarryback as In, schedule11Values as Ir, resolveCorpTaxRates as It, allocateEvenly as J, renderAt1NetFile as Jn, computeAlbertaSchedule6 as Jr, AT1_DISPOSITION_CATEGORIES as Jt, computeItcRecapture as K, renderRsiHeader as Kn, computeAlbertaSchedule7 as Kr, computeDonationMaximum as Kt, LRIP_INVESTMENT_CORPORATION_MULTIPLE as L, RSI_COLUMN_GAP as Ln, ALBERTA_SRED_EXPENDITURE_CUTOFF as Lr, T2_CERTIFICATION_FIXTURES as Lt, computeFederalT2 as M, resolveRates as Mi, albertaTerminalLossDifference as Mn, computeSfedeCountryRegular as Mr, computeBusinessLimit as Mt, computeSchedule101 as N, computeSchedule12 as Nn, computeSfedeCountrySuccessor as Nr, computeSBD as Nt, foldT2Lines as O, earliestRateYear as Oi, albertaCurrentYearLoss as On, computeEdaRegular as Or, netCapitalLossApplied as Ot, SCHEDULE_88_MAX_URLS as P, reconcileAlbertaNetIncome as Pn, schedule15Values as Pr, CORP_TAX_2024 as Pt, computeSchedule6 as Q, At1TaxPayableMismatchError as Qn, computeSchedule4 as Qr, AT1_RESERVE_LINES as Qt, LRIP_INVESTMENT_INCOME_FACTOR as R, RSI_DELIMITER as Rn, ALBERTA_SRED_PROGRAM_START as Rr, computeNonCapitalLossByYearOfOrigin as Rt, computeQuebecAllocationFactor as S, computeAlbertaSbd as Si, isDecliningBalanceClass as Sn, computeCdeSuccessor as Sr, resolveProvinceRates as St, QC_TAX_RATE_BOOK as T, AB_TAX_2024 as Ti, albertaCapitalGainDifference as Tn, computeCfreRegular as Tr, charitableDonationsDeduction as Tt, computeTaxableCapital as U, formatRsiDate as Un, schedule9Values as Ur, computeLimitedPartnershipLosses as Ut, computeSchedule43 as V, RsiLineItemError as Vn, computeAlbertaSchedule9 as Vr, computeLossContinuity as Vt, computeSchedule31 as W, formatRsiText as Wn, computeSchedule8$1 as Wr, AT1_DONATION_GAIN_RATE as Wt, computeSchedule21 as X, At1CriticalFieldMissingError as Xn, computeAlbertaSchedule5 as Xr, computeAlbertaSchedule18 as Xt, computeBusinessLimitAllocation as Y, AT1_CRITICAL_MANDATORY_FIELDS as Yn, schedule6Values as Yr, SECTION_34_2_GROSS_UP as Yt, computeSchedule13 as Z, At1MandatoryFieldMissingError as Zn, schedule5Values as Zr, AT1_RESERVE_KINDS as Zt, renderT2DraftReturn as _, schedule21Values as _i, computeClass14 as _n, iegT661SourceLine as _r, EIFEL_EFFECTIVE_FROM as _t, PART_VI_1_DEDUCTION_BANDS as a, AT1_SCHEDULES_WITHOUT_BUILDERS as ai, cecScheduleAppliesToTaxYear as an, xmlEscape as ar, computeSchedule2 as at, co17Engine as b, schedule4970Values as bi, CCA_DECLINING_BALANCE_RATES_2024 as bn, computeCcogpeSuccessor as br, PROVINCE_RATE_BOOK as bt, EIFEL_FIRST_YEAR_START as c, schedule10Values as ci, UnsupportedCcaClassError as cn, allocateIegEvenly as cr, assertSchedule1Fileable as ct, EIFEL_TRANSITIONAL_RATIO as d, schedule13Values as di, computeSchedule8 as dn, computeIegGroupFigures as dr, deferredIncomeTaxProvisionAddBack as dt, computeSchedule3 as ei, computeAlbertaSchedule17 as en, assertAt1MandatoryComplete as er, computeSchedule5 as et, computeEifelLimitation as f, schedule16Values as fi, CLASS_14_1_MINIMUM_DEDUCTION as fn, IEG_2024 as fr, findSchedule1LineDefects as ft, computeT2Settlement as g, schedule20Values as gi, computeClass13 as gn, computeIegEligibleExpenditures as gr, terminalLossDeduction as gt, computeAdjustedTaxableIncome as h, schedule1Values as hi, MIN_LEASEHOLD_PERIODS as hn, computeIegReductionFactor as hr, recaptureAddBack as ht, computeMpDeduction as i, computeAt4970 as ii, CEC_INCLUSION_RATE as in, at1YesNo as ir, computePart4Rdtoh as it, runConformanceSuite as j, latestRateYear as ji, albertaReserveDifference as jn, computeFedeSuccessor as jr, computePartITax as jt, formatConformanceReport as k, extendRateBook as ki, albertaDispositionAdjustments as kn, computeEdaSuccessor as kr, nonCapitalLossApplied as kt, EIFEL_STANDARD_RATIO as l, schedule12LossDeductions as li, computeCcaClass as ln, allocateIegExpenditureLimit as lr, ccaDeduction as lt, FOREIGN_TAX_CREDIT_GROSS_UP as m, schedule18Values as mi, MAX_LEASEHOLD_PERIODS as mn, computeIegBaseAmount as mr, mealsAndEntertainmentAddBack as mt, MP_GROSS_REVENUE_THRESHOLD as n, SINGLE_JURISDICTION_ALBERTA_FACTOR as ni, computeAlbertaSchedule16 as nn, assertCriticalFields as nr, PART_IV_RATE as nt, computePartVI1Deduction as o, AT1_SCHEDULES_WITH_BUILDERS as oi, computeAlbertaSchedule14 as on, at1Engine as or, Schedule1NotFileableError as ot, ratioOfPermissibleExpenses as p, schedule17Values as pi, CLASS_14_1_TRANSITIONAL_RATE as pn, computeIeg as pr, incomeTaxProvisionAddBack as pt, computeZetm as q, renderRsiLineItem as qn, schedule7Values as qr, computeSchedule20 as qt, MP_RATES_2024 as r, computeAllocationFactor as ri, CEC_DEDUCTION_RATE as rn, at1TaxPayableDeductions as rr, REFUNDABLE_PART_I_RATE as rt, partVI1DeductionMultiple as s, at1LineItemId as si, computeAlbertaSchedule13 as sn, computeAlbertaReturn as sr, amortizationAddBack as st, MP_EXCLUDED_ACTIVITIES as t, schedule3Values as ti, assistanceFrom as tn, assertAt1TaxPayableReconciles as tr, computeSchedule4Losses as tt, EIFEL_STANDARD_RATIO_FROM as u, schedule12Values as ui, computeCcaSchedule as un, computeIegAgreement as ur, computeSchedule1 as ut, t2Engine as v, schedule29Values as vi, computeClass141AdditionalAllowance as vn, computeAlbertaSchedule15 as vr, assessEifel as vt, QC_TAX_2024 as w, computeDayWeightedGeneralTax as wi, albertaAbilDifference as wn, computeCeeSuccessor as wr, dayWeightedRate as wt, computeQuebecReturn as x, computeAlbertaTax as xi, CCA_RATE_BOOK as xn, computeCdeRegular as xr, isSchedule5Province as xt, renderCo17DraftReturn as y, schedule2Values as yi, leaseholdPeriods as yn, computeCcogpeRegular as yr, PROVINCE_RATES_2024 as yt, computeSchedule54 as z, RSI_NEGATIVE_PREFIX as zn, ALBERTA_SRED_TAX_CREDIT_RATE as zr, computeOtherLossByYearOfOrigin as zt };