@classytic/ca-tax 0.0.5 → 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,9 +254,18 @@ 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",
@@ -677,38 +686,26 @@ function schedule12Values(input) {
677
686
  values
678
687
  };
679
688
  }
680
- /** Per-pool line numbers, from the live form. */
681
- const S21_POOL_LINES = {
682
- nonCapital: {
683
- opening: "031",
684
- current: "037",
685
- carryBack: "047",
686
- closing: "049"
687
- },
688
- capital: {
689
- opening: "051",
690
- current: "057",
691
- carryBack: "067",
692
- closing: "069"
693
- },
694
- farm: {
695
- opening: "071",
696
- current: "077",
697
- carryBack: "085",
698
- closing: "087"
699
- },
700
- restrictedFarm: {
701
- opening: "091",
702
- current: "097",
703
- carryBack: "105",
704
- closing: "107"
705
- },
706
- listedPersonalProperty: {
707
- opening: "111",
708
- current: "117",
709
- carryBack: "123",
710
- closing: "125"
711
- }
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"
712
709
  };
713
710
  function schedule21Values(input) {
714
711
  const values = [];
@@ -717,14 +714,97 @@ function schedule21Values(input) {
717
714
  value
718
715
  });
719
716
  if (input.currentYearNonCapitalLoss !== void 0) put("021", input.currentYearNonCapitalLoss);
720
- for (const [pool, lines] of Object.entries(S21_POOL_LINES)) {
721
- const c = input[pool];
717
+ for (const [inputKey, poolKey] of Object.entries(POOL_KEY)) {
718
+ const c = input[inputKey];
722
719
  if (!c) continue;
723
- put(lines.opening, c.openingBalance);
724
- put(lines.current, c.currentYearLoss);
725
- put(lines.carryBack, c.carriedBack);
726
- 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
+ }
727
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
+ });
728
808
  return {
729
809
  scheduleId: "021",
730
810
  values
@@ -811,150 +891,2384 @@ function schedule10Values(input) {
811
891
  ], input.capital.carrybacks)) put(f, Math.round(rate * c.amount));
812
892
  }
813
893
  return {
814
- scheduleId: "010",
815
- values
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
816
2789
  };
817
2790
  }
818
- const S20_CHARITABLE = {
819
- opening: "002",
820
- expired: "004",
821
- beginning: "006",
822
- transferred: "008",
823
- currentYear: "010",
824
- subtotal: "012",
825
- acquisitionOfControl: "013",
826
- available: "014",
827
- applied: "016",
828
- closing: "018"
829
- };
830
- const S20_GIFTS = {
831
- opening: "062",
832
- expired: "064",
833
- beginning: "066",
834
- transferred: "068",
835
- currentYear: "070",
836
- subtotal: "072",
837
- acquisitionOfControl: "073",
838
- available: "074",
839
- applied: "076",
840
- closing: "078"
841
- };
842
- function donationContinuityValues(result, f) {
843
- const put = (fieldId, value) => ({
844
- lineItemId: at1LineItemId("020", fieldId),
845
- value
846
- });
847
- return [
848
- put(f.opening, result.openingBalance),
849
- put(f.expired, result.expired),
850
- put(f.beginning, Math.max(0, result.openingBalance - result.expired)),
851
- put(f.transferred, result.transferredIn),
852
- put(f.currentYear, result.currentYearGifts),
853
- put(f.subtotal, result.transferredIn + result.currentYearGifts),
854
- put(f.acquisitionOfControl, result.acquisitionOfControlAdjustment),
855
- put(f.available, result.availableBeforeClaim),
856
- put(f.applied, result.amountApplied),
857
- put(f.closing, result.closingBalance)
858
- ];
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
+ };
859
2817
  }
860
- function schedule20Values(input) {
861
- const values = [];
862
- if (input.charitable) values.push(...donationContinuityValues(input.charitable, S20_CHARITABLE));
863
- if (input.gifts) values.push(...donationContinuityValues(input.gifts, S20_GIFTS));
864
- if (input.maximum) {
865
- const put = (fieldId, value) => values.push({
866
- lineItemId: at1LineItemId("020", fieldId),
867
- value
868
- });
869
- put("030", input.maximum.incomeComponent);
870
- put("042", input.maximum.lesserOfProceedsAndCost);
871
- put("044", input.maximum.allowableRecapture);
872
- put("046", input.maximum.gainsComponent);
873
- put("048", input.maximum.maximumDeduction);
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)`);
874
2833
  }
2834
+ const closingBalance = pool - claim;
875
2835
  return {
876
- scheduleId: "020",
877
- values
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
878
2875
  };
879
2876
  }
880
2877
  /**
881
- * The SR&ED expenditure POOL a deduction against income, not the investment tax
882
- * credit and not the innovation grant.
883
- *
884
- * Line numbers and the subtotal formula verified against the live form, which
885
- * states it exactly as transcribed:
886
- *
887
- * 016 = 002 − (004 + 006 + 008) + 010 + 012 + 014 + 015
888
- *
889
- * and closes the year-over-year chain in as many words: line 022 is *"the carry
890
- * forward amount for next year, line 012"*.
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.
891
2883
  */
892
- function schedule16Values(result) {
893
- const values = [];
894
- const put = (fieldId, value) => values.push({
895
- lineItemId: at1LineItemId("016", fieldId),
896
- value
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
+ };
897
2904
  });
898
- put("002", result.currentYearExpenditures);
899
- put("016", result.subtotal);
900
- put("018", result.deductionAvailable);
901
- put("020", result.amountClaimed);
902
- put("022", result.unclaimedPoolBalance);
2905
+ const sumForeignResourceIncome = pools.reduce((s, p) => s + p.foreignResourceIncome, 0);
903
2906
  return {
904
- scheduleId: "016",
905
- values
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
906
2936
  };
907
2937
  }
908
- //#endregion
909
- //#region src/t2/at1/schedules/at4970-ieg-projects.ts
910
- const nn$25 = (v) => Math.max(0, Math.round(v ?? 0));
911
- function computeAt4970(input) {
912
- const projects = input.projects.map((p) => ({
913
- title: p.title,
914
- ...p.projectCode !== void 0 ? { projectCode: p.projectCode } : {},
915
- albertaPortion: nn$25(p.albertaPortion),
916
- otherPortion: nn$25(p.otherPortion),
917
- salariesAndWages: nn$25(p.salariesAndWages),
918
- federalProxyAmount: nn$25(p.federalProxyAmount),
919
- albertaProxyAmount: nn$25(p.albertaProxyAmount)
920
- }));
921
- const totals = {
922
- albertaPortion: projects.reduce((s, p) => s + p.albertaPortion, 0),
923
- otherPortion: projects.reduce((s, p) => s + p.otherPortion, 0),
924
- salariesAndWages: projects.reduce((s, p) => s + p.salariesAndWages, 0),
925
- federalProxyAmount: projects.reduce((s, p) => s + p.federalProxyAmount, 0),
926
- albertaProxyAmount: projects.reduce((s, p) => s + p.albertaProxyAmount, 0)
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);
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.");
3014
+ return {
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
927
3032
  };
928
- const jurisdictions = (input.jurisdictions ?? []).map((j) => ({
929
- jurisdiction: j.jurisdiction,
930
- amountIncurred: nn$25(j.amountIncurred)
931
- }));
3033
+ }
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
+ };
3053
+ });
3054
+ const sumForeignResourceIncome = pools.reduce((s, p) => s + p.foreignResourceIncome, 0);
932
3055
  return {
933
- projects,
934
- totals,
935
- jurisdictions,
936
- jurisdictionTotal: jurisdictions.reduce((s, j) => s + j.amountIncurred, 0)
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
937
3080
  };
938
3081
  }
939
- //#endregion
940
- //#region src/t2/at1/schedules/schedule2.ts
941
- /** Round to six decimal places — the AT1 allocation-factor precision. */
942
- function round6(n) {
943
- return Math.round(n * 1e6) / 1e6;
3082
+ function schedule15LineItemId(fieldId, occurrence = 1) {
3083
+ return `015${fieldId}${String(occurrence).padStart(3, "0")}`;
944
3084
  }
945
- /** Single Alberta PE, none elsewhere → all income is Alberta income. */
946
- const SINGLE_JURISDICTION_ALBERTA_FACTOR = 1;
947
- function computeAllocationFactor(input) {
948
- const hasRevenue = input.totalGrossRevenue > 0;
949
- const hasSalaries = input.totalSalaries > 0;
950
- const revenueRatio = hasRevenue ? input.albertaGrossRevenue / input.totalGrossRevenue : 0;
951
- const salariesRatio = hasSalaries ? input.albertaSalaries / input.totalSalaries : 0;
952
- let factor;
953
- if (hasRevenue && hasSalaries) factor = (revenueRatio + salariesRatio) / 2;
954
- else if (hasRevenue) factor = revenueRatio;
955
- else if (hasSalaries) factor = salariesRatio;
956
- else factor = 0;
957
- return round6(factor);
3085
+ /**
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).
3095
+ */
3096
+ function schedule15Values(result) {
3097
+ const values = [];
3098
+ const put = (fieldId, value, occurrence = 1) => values.push({
3099
+ lineItemId: schedule15LineItemId(fieldId, occurrence),
3100
+ value
3101
+ });
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
+ }
3267
+ return {
3268
+ scheduleId: "015",
3269
+ values,
3270
+ issues: result.issues
3271
+ };
958
3272
  }
959
3273
  //#endregion
960
3274
  //#region src/t2/at1/schedules/schedule29-eligible-expenditures.ts
@@ -1481,9 +3795,18 @@ function computeAlbertaReturn(input) {
1481
3795
  const sched = input.schedules;
1482
3796
  if (sched?.smallBusinessDeduction) schedulePayloads.push(schedule1Values(sched.smallBusinessDeduction));
1483
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));
1484
3805
  if (sched?.lossCarryback) schedulePayloads.push(schedule10Values(sched.lossCarryback));
3806
+ if (sched?.manufacturingProcessing) schedulePayloads.push(schedule11Values(sched.manufacturingProcessing));
1485
3807
  if (sched?.reconciliation) schedulePayloads.push(schedule12Values(sched.reconciliation));
1486
3808
  if (sched?.cca) schedulePayloads.push(schedule13Values(sched.cca));
3809
+ if (sched?.resourceDeductions) schedulePayloads.push(schedule15Values(sched.resourceDeductions));
1487
3810
  if (sched?.scientificResearch) schedulePayloads.push(schedule16Values(sched.scientificResearch));
1488
3811
  if (sched?.reserves) schedulePayloads.push(schedule17Values(sched.reserves));
1489
3812
  if (sched?.dispositions) schedulePayloads.push(schedule18Values(sched.dispositions));
@@ -3048,6 +5371,83 @@ function applyOverride(federal, o) {
3048
5371
  opt("applyHalfYearRule", federal.applyHalfYearRule);
3049
5372
  return merged;
3050
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
+ }
3051
5451
  function computeAlbertaSchedule13(input) {
3052
5452
  const rates = input.rates ?? CCA_DECLINING_BALANCE_RATES_2024;
3053
5453
  const issues = [];
@@ -3068,6 +5468,8 @@ function computeAlbertaSchedule13(input) {
3068
5468
  ccaDifference: alberta.ccaClaimed - federal.ccaClaimed
3069
5469
  };
3070
5470
  });
5471
+ if (input.class13) classes.push(computeClass13Pair(input.class13, issues));
5472
+ if (input.class14) classes.push(computeClass14Pair(input.class14, issues));
3071
5473
  const sum = (pickFn) => classes.reduce((s, c) => s + pickFn(c), 0);
3072
5474
  const albertaTotalCca = sum((c) => c.alberta.ccaClaimed);
3073
5475
  const federalTotalCca = sum((c) => c.federal.ccaClaimed);
@@ -3662,19 +6064,58 @@ function computeDonationMaximum(input) {
3662
6064
  };
3663
6065
  }
3664
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
3665
6098
  //#region src/t2/schedules/loss-continuity.ts
3666
6099
  function computeLossContinuity(input) {
3667
6100
  const currentYearLoss = input.currentYearLoss ?? 0;
3668
6101
  const carriedBack = input.carriedBack ?? 0;
3669
6102
  const appliedCurrentYear = input.appliedCurrentYear ?? 0;
3670
6103
  const expired = input.expired ?? 0;
3671
- 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);
3672
6109
  return {
3673
6110
  openingBalance: input.openingBalance,
3674
6111
  currentYearLoss,
3675
6112
  carriedBack,
3676
6113
  appliedCurrentYear,
3677
6114
  expired,
6115
+ windUpTransfer,
6116
+ section80Adjustment,
6117
+ otherAdjustments,
6118
+ balanceAtBeginningOfYear,
3678
6119
  closingBalance
3679
6120
  };
3680
6121
  }
@@ -3706,6 +6147,76 @@ function computeLossSchedule(input) {
3706
6147
  };
3707
6148
  }
3708
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
3709
6220
  //#region src/t2/certification/fixtures.ts
3710
6221
  const T2_CERTIFICATION_FIXTURES = [
3711
6222
  {
@@ -5389,15 +7900,18 @@ function computeFederalT2(input) {
5389
7900
  const rates = input.rates ?? resolveCorpTaxRates(taxYear, book);
5390
7901
  const prorationFactor = shortYearProrationFactor(input.periodStart, input.periodEnd);
5391
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;
5392
7905
  const capitalGains = input.capitalDispositions?.length ? computeSchedule6(input.capitalDispositions, rates.CAPITAL_GAINS_INCLUSION_RATE) : void 0;
5393
7906
  const schedule1Additions = [...input.schedule1Additions ?? []];
5394
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
+ });
5395
7914
  if (cca) {
5396
- if (cca.totalCca > 0) schedule1Deductions.push({
5397
- line: "403",
5398
- label: "Capital cost allowance from Schedule 8",
5399
- amount: cca.totalCca
5400
- });
5401
7915
  if (cca.totalTerminalLoss > 0) schedule1Deductions.push({
5402
7916
  line: "404",
5403
7917
  label: "Terminal loss from Schedule 8",
@@ -5597,6 +8111,8 @@ function computeFederalT2(input) {
5597
8111
  ...donations ? { donations } : {},
5598
8112
  ...lossCarryback ? { lossCarryback } : {},
5599
8113
  ...cca ? { cca } : {},
8114
+ ...class13 ? { class13 } : {},
8115
+ ...class14 ? { class14 } : {},
5600
8116
  ...capitalGains ? { capitalGains } : {},
5601
8117
  ...provincial ? { provincial } : {},
5602
8118
  ...provincialAllocation ? { provincialAllocation } : {},
@@ -6727,4 +9243,4 @@ function computeMpDeduction(input, rates = MP_RATES_2024) {
6727
9243
  };
6728
9244
  }
6729
9245
  //#endregion
6730
- export { computeProvincialAllocation as $, at1TaxPayableDeductions as $n, CEC_DEDUCTION_RATE as $t, runConformance as A, reconcileAlbertaNetIncome as An, schedule29Values as Ar, computeCcpcActiveBusinessTax as At, computeGrip as B, formatRsiText as Bn, earliestRateYear as Br, AT1_DONATION_GAIN_RATE as Bt, computeQuebecTax as C, albertaCcaScheduleAdjustments as Cn, schedule13Values as Cr, blendProvinceRateTable as Ct, T2_LINE_META as D, albertaReserveDifference as Dn, schedule1Values as Dr, dividendsDeductibleS112 as Dt, resolveQuebecTaxRates as E, albertaRecaptureDifference as En, schedule18Values as Er, computeTaxableIncome as Et, normalizeSchedule88 as F, RSI_NEGATIVE_PREFIX as Fn, AB_GENERAL_RATE_BANDS 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, hasExactRateYear as Hr, computeDonationMaximum as Ht, computeSchedule55 as I, RSI_WORD_GAP as In, computeDayWeightedGeneralTax 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, AB_TAX_2024 as Lr, T2_CERTIFICATION_FIXTURES as Lt, computeFederalT2 as M, computeLossCarryback as Mn, schedule4970Values as Mr, computeBusinessLimit as Mt, computeSchedule101 as N, RSI_COLUMN_GAP as Nn, computeAlbertaTax as Nr, computeSBD as Nt, foldT2Lines as O, albertaTerminalLossDifference as On, schedule20Values as Or, netCapitalLossApplied as Ot, SCHEDULE_88_MAX_URLS as P, RSI_DELIMITER as Pn, computeAlbertaSbd 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, AB_TAX_RATE_BOOK as Rr, computeLossSchedule as Rt, computeQuebecAllocationFactor as S, albertaCcaDifference as Sn, schedule12Values as Sr, resolveProvinceRates as St, QC_TAX_RATE_BOOK as T, albertaDispositionAdjustments as Tn, schedule17Values as Tr, charitableDonationsDeduction as Tt, computeTaxableCapital as U, renderRsiLineItem as Un, latestRateYear as Ur, computeSchedule20 as Ut, computeSchedule43 as V, renderAt1Rsi as Vn, extendRateBook as Vr, AT1_DONATION_INCOME_RATE as Vt, computeSchedule31 as W, renderAt1NetFile as Wn, resolveRates as Wr, 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, AT1_SCHEDULES_WITHOUT_BUILDERS 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, schedule10Values 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, computeIegEligibleExpenditures as fr, findSchedule1LineDefects as ft, computeT2Settlement as g, CCA_DECLINING_BALANCE_RATES_2024 as gn, computeAt4970 as gr, terminalLossDeduction as gt, computeAdjustedTaxableIncome as h, leaseholdPeriods as hn, computeAllocationFactor as hr, recaptureAddBack as ht, computeMpDeduction as i, UnsupportedCcaClassError as in, allocateIegEvenly as ir, computePart4Rdtoh as it, runConformanceSuite as j, LossCarrybackError as jn, schedule2Values as jr, computePartITax as jt, formatConformanceReport as k, computeSchedule12 as kn, schedule21Values 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, SINGLE_JURISDICTION_ALBERTA_FACTOR 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, iegT661SourceLine 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, AT1_SCHEDULES_WITH_BUILDERS as vr, assessEifel as vt, QC_TAX_2024 as w, albertaCurrentYearLoss as wn, schedule16Values as wr, dayWeightedRate as wt, computeQuebecReturn as x, albertaCapitalGainDifference as xn, schedule12LossDeductions as xr, isSchedule5Province as xt, renderCo17DraftReturn as y, resolveCcaRates as yn, at1LineItemId as yr, PROVINCE_RATES_2024 as yt, computeSchedule54 as z, formatRsiDate as zn, resolveAlbertaTaxRates 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 };