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