@classytic/ca-tax 0.0.15 → 0.0.18
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 +344 -0
- package/dist/forms/index.d.mts +2 -2
- package/dist/forms/index.mjs +2 -2
- package/dist/forms.mjs +2400 -1572
- package/dist/index.d.mts +158 -20
- package/dist/index.mjs +3 -3
- package/dist/index3.d.mts +1359 -1490
- 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 +1406 -1315
- package/package.json +3 -3
package/dist/t2.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mt as AT1_SCHEDULE_21_POOLS, ot 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
|
|
@@ -242,11 +242,6 @@ function computeAlbertaTax(input, rates = AB_TAX_2024) {
|
|
|
242
242
|
//#endregion
|
|
243
243
|
//#region src/t2/at1/filing/at1-schedule-line-items.ts
|
|
244
244
|
/**
|
|
245
|
-
* Schedules the engine computes but cannot yet file, because their line numbers
|
|
246
|
-
* have not been transcribed from the specification. Named so the difference
|
|
247
|
-
* between "no data" and "not implemented" is visible rather than silent.
|
|
248
|
-
*/
|
|
249
|
-
/**
|
|
250
245
|
* Schedules this module can put into a filing payload. Exported so callers and
|
|
251
246
|
* tests share one source of truth — a hard-coded copy in a test drifts the moment
|
|
252
247
|
* a builder is added, and then quietly stops guarding anything.
|
|
@@ -697,6 +692,7 @@ function schedule12Values(input) {
|
|
|
697
692
|
alwaysPair("072", "073", d?.limitedPartnership);
|
|
698
693
|
alwaysPair("056", "057", input.donations?.charitable);
|
|
699
694
|
alwaysPair("058", "059", input.donations?.gifts);
|
|
695
|
+
alwaysPair("130", "131", input.restrictedInterestAndFinancing);
|
|
700
696
|
put("054", input.albertaNetIncomeForTax);
|
|
701
697
|
return {
|
|
702
698
|
scheduleId: "012",
|
|
@@ -724,7 +720,7 @@ const POOL_FIELD_TO_RESULT_KEY = {
|
|
|
724
720
|
carryBack: "carriedBack",
|
|
725
721
|
closing: "closingBalance"
|
|
726
722
|
};
|
|
727
|
-
function schedule21Values(input) {
|
|
723
|
+
function schedule21Values$1(input) {
|
|
728
724
|
const values = [];
|
|
729
725
|
const put = (fieldId, value) => values.push({
|
|
730
726
|
lineItemId: at1LineItemId("021", fieldId),
|
|
@@ -827,7 +823,7 @@ function schedule21Values(input) {
|
|
|
827
823
|
values
|
|
828
824
|
};
|
|
829
825
|
}
|
|
830
|
-
function schedule1Values(input) {
|
|
826
|
+
function schedule1Values$1(input) {
|
|
831
827
|
const values = [];
|
|
832
828
|
const put = (fieldId, value, occurrence = 1) => values.push({
|
|
833
829
|
lineItemId: at1LineItemId("001", fieldId, occurrence),
|
|
@@ -853,7 +849,7 @@ function schedule1Values(input) {
|
|
|
853
849
|
values
|
|
854
850
|
};
|
|
855
851
|
}
|
|
856
|
-
function schedule2Values(input) {
|
|
852
|
+
function schedule2Values$1(input) {
|
|
857
853
|
const values = [];
|
|
858
854
|
const put = (fieldId, value) => {
|
|
859
855
|
if (value === void 0) return;
|
|
@@ -928,13 +924,20 @@ function schedule10Values(input) {
|
|
|
928
924
|
put("040", input.otherLoss.result.remainingLoss);
|
|
929
925
|
}
|
|
930
926
|
if (input.capital) {
|
|
931
|
-
const rate = input.inclusionRate ?? .5;
|
|
932
927
|
put("042", input.capital.currentYearLoss);
|
|
933
|
-
|
|
928
|
+
const rateFields = [
|
|
929
|
+
"043",
|
|
930
|
+
"045",
|
|
931
|
+
"047"
|
|
932
|
+
];
|
|
933
|
+
const grossFields = [
|
|
934
934
|
"044",
|
|
935
935
|
"046",
|
|
936
936
|
"048"
|
|
937
|
-
]
|
|
937
|
+
];
|
|
938
|
+
for (const [f, c] of zip(rateFields, input.capital.carrybacks)) put(f, (c.inclusionRate ?? input.inclusionRate ?? .5).toFixed(6));
|
|
939
|
+
for (const [f, c] of zip(grossFields, input.capital.carrybacks)) put(f, c.amount);
|
|
940
|
+
put("050", input.capital.remainingLoss);
|
|
938
941
|
}
|
|
939
942
|
return {
|
|
940
943
|
scheduleId: "010",
|
|
@@ -1060,16 +1063,16 @@ function schedule16Values(result) {
|
|
|
1060
1063
|
}
|
|
1061
1064
|
//#endregion
|
|
1062
1065
|
//#region src/t2/at1/schedules/at4970-ieg-projects.ts
|
|
1063
|
-
const nn$
|
|
1066
|
+
const nn$31 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1064
1067
|
function computeAt4970(input) {
|
|
1065
1068
|
const projects = input.projects.map((p) => ({
|
|
1066
1069
|
title: p.title,
|
|
1067
1070
|
...p.projectCode !== void 0 ? { projectCode: p.projectCode } : {},
|
|
1068
|
-
albertaPortion: nn$
|
|
1069
|
-
otherPortion: nn$
|
|
1070
|
-
salariesAndWages: nn$
|
|
1071
|
-
federalProxyAmount: nn$
|
|
1072
|
-
albertaProxyAmount: nn$
|
|
1071
|
+
albertaPortion: nn$31(p.albertaPortion),
|
|
1072
|
+
otherPortion: nn$31(p.otherPortion),
|
|
1073
|
+
salariesAndWages: nn$31(p.salariesAndWages),
|
|
1074
|
+
federalProxyAmount: nn$31(p.federalProxyAmount),
|
|
1075
|
+
albertaProxyAmount: nn$31(p.albertaProxyAmount)
|
|
1073
1076
|
}));
|
|
1074
1077
|
const totals = {
|
|
1075
1078
|
albertaPortion: projects.reduce((s, p) => s + p.albertaPortion, 0),
|
|
@@ -1080,7 +1083,7 @@ function computeAt4970(input) {
|
|
|
1080
1083
|
};
|
|
1081
1084
|
const jurisdictions = (input.jurisdictions ?? []).map((j) => ({
|
|
1082
1085
|
jurisdiction: j.jurisdiction,
|
|
1083
|
-
amountIncurred: nn$
|
|
1086
|
+
amountIncurred: nn$31(j.amountIncurred)
|
|
1084
1087
|
}));
|
|
1085
1088
|
return {
|
|
1086
1089
|
projects,
|
|
@@ -1196,15 +1199,15 @@ function computeAllocationFactor(input) {
|
|
|
1196
1199
|
*
|
|
1197
1200
|
* Whole dollars, pure.
|
|
1198
1201
|
*/
|
|
1199
|
-
const nn$
|
|
1202
|
+
const nn$30 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1200
1203
|
const raw = (v) => Math.round(v ?? 0);
|
|
1201
1204
|
function computeItc(input, roomCap, issues) {
|
|
1202
|
-
const certificatesIssued = nn$
|
|
1203
|
-
const carryforwardFromPriorYear = nn$
|
|
1204
|
-
const expired = nn$
|
|
1205
|
+
const certificatesIssued = nn$30(input.certificatesIssued);
|
|
1206
|
+
const carryforwardFromPriorYear = nn$30(input.carryforwardFromPriorYear);
|
|
1207
|
+
const expired = nn$30(input.expired);
|
|
1205
1208
|
const availableBeforeClaim = certificatesIssued + carryforwardFromPriorYear;
|
|
1206
1209
|
const cap = Math.min(availableBeforeClaim, roomCap);
|
|
1207
|
-
const wanted = input.amountApplied != null ? nn$
|
|
1210
|
+
const wanted = input.amountApplied != null ? nn$30(input.amountApplied) : cap;
|
|
1208
1211
|
const amountApplied = Math.max(0, Math.min(wanted, cap));
|
|
1209
1212
|
const carryforwardToNextYear = availableBeforeClaim - amountApplied - expired;
|
|
1210
1213
|
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.`);
|
|
@@ -1218,17 +1221,17 @@ function computeItc(input, roomCap, issues) {
|
|
|
1218
1221
|
};
|
|
1219
1222
|
}
|
|
1220
1223
|
function computeCitc(input, roomCap, itcApplied, itcCarryforwardRemaining, issues) {
|
|
1221
|
-
const certificatesIssued = nn$
|
|
1222
|
-
const carryforwardFromPriorYear = nn$
|
|
1223
|
-
const expired = nn$
|
|
1224
|
+
const certificatesIssued = nn$30(input.certificatesIssued);
|
|
1225
|
+
const carryforwardFromPriorYear = nn$30(input.carryforwardFromPriorYear);
|
|
1226
|
+
const expired = nn$30(input.expired);
|
|
1224
1227
|
const availableBeforeClaim = certificatesIssued + carryforwardFromPriorYear;
|
|
1225
1228
|
let amountApplied;
|
|
1226
1229
|
if (itcCarryforwardRemaining > 0) {
|
|
1227
1230
|
amountApplied = 0;
|
|
1228
|
-
if (input.amountApplied != null && nn$
|
|
1231
|
+
if (input.amountApplied != null && nn$30(input.amountApplied) > 0) issues.push(`Alberta Schedule 3 (CITC): a claim of ${nn$30(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.`);
|
|
1229
1232
|
} else {
|
|
1230
1233
|
const cap = Math.min(availableBeforeClaim, Math.max(0, roomCap - itcApplied));
|
|
1231
|
-
const wanted = input.amountApplied != null ? nn$
|
|
1234
|
+
const wanted = input.amountApplied != null ? nn$30(input.amountApplied) : cap;
|
|
1232
1235
|
amountApplied = Math.max(0, Math.min(wanted, cap));
|
|
1233
1236
|
}
|
|
1234
1237
|
const carryforwardToNextYear = availableBeforeClaim - amountApplied - expired;
|
|
@@ -1247,18 +1250,18 @@ function computeApitc(input, roomCap, issues) {
|
|
|
1247
1250
|
const first = input.firstPreceding ?? {};
|
|
1248
1251
|
const second = input.secondPreceding ?? {};
|
|
1249
1252
|
const thirdToTenth = input.thirdToTenthPreceding ?? {};
|
|
1250
|
-
const currentAvailable = nn$
|
|
1251
|
-
const firstAvailable = nn$
|
|
1252
|
-
const secondAvailable = nn$
|
|
1253
|
-
const thirdToTenthAvailable = nn$
|
|
1253
|
+
const currentAvailable = nn$30(current.received);
|
|
1254
|
+
const firstAvailable = nn$30(first.availableAtBeginning);
|
|
1255
|
+
const secondAvailable = nn$30(second.availableAtBeginning);
|
|
1256
|
+
const thirdToTenthAvailable = nn$30(thirdToTenth.availableAtBeginning);
|
|
1254
1257
|
const currentCap = Math.round(currentAvailable * .2);
|
|
1255
1258
|
const firstCap = Math.round(firstAvailable * .3);
|
|
1256
1259
|
const secondCap = Math.round(secondAvailable * .5);
|
|
1257
1260
|
const thirdToTenthCap = thirdToTenthAvailable;
|
|
1258
|
-
const currentAsk = Math.min(current.amountApplied != null ? nn$
|
|
1259
|
-
const firstAsk = Math.min(first.amountApplied != null ? nn$
|
|
1260
|
-
const secondAsk = Math.min(second.amountApplied != null ? nn$
|
|
1261
|
-
const thirdToTenthAsk = Math.min(thirdToTenth.amountApplied != null ? nn$
|
|
1261
|
+
const currentAsk = Math.min(current.amountApplied != null ? nn$30(current.amountApplied) : currentCap, currentCap);
|
|
1262
|
+
const firstAsk = Math.min(first.amountApplied != null ? nn$30(first.amountApplied) : firstCap, firstCap);
|
|
1263
|
+
const secondAsk = Math.min(second.amountApplied != null ? nn$30(second.amountApplied) : secondCap, secondCap);
|
|
1264
|
+
const thirdToTenthAsk = Math.min(thirdToTenth.amountApplied != null ? nn$30(thirdToTenth.amountApplied) : thirdToTenthCap, thirdToTenthCap);
|
|
1262
1265
|
const totalRequested = currentAsk + firstAsk + secondAsk + thirdToTenthAsk;
|
|
1263
1266
|
let room = Math.max(0, roomCap);
|
|
1264
1267
|
const allocate = (ask) => {
|
|
@@ -1274,7 +1277,7 @@ function computeApitc(input, roomCap, issues) {
|
|
|
1274
1277
|
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.`);
|
|
1275
1278
|
const totalReceived = currentAvailable;
|
|
1276
1279
|
const carryforwardFromPriorYear = firstAvailable + secondAvailable + thirdToTenthAvailable;
|
|
1277
|
-
const expired = nn$
|
|
1280
|
+
const expired = nn$30(input.expiredThisYear);
|
|
1278
1281
|
const availableForCarryforward = totalReceived + carryforwardFromPriorYear - totalApplied - expired;
|
|
1279
1282
|
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.`);
|
|
1280
1283
|
return {
|
|
@@ -1308,11 +1311,11 @@ function computeApitc(input, roomCap, issues) {
|
|
|
1308
1311
|
function computeSchedule3(input) {
|
|
1309
1312
|
const issues = [];
|
|
1310
1313
|
const mad = input.mad ?? {};
|
|
1311
|
-
const line068 = nn$
|
|
1312
|
-
const line070 = nn$
|
|
1313
|
-
const line071 = nn$
|
|
1314
|
-
const line072 = nn$
|
|
1315
|
-
const line074 = nn$
|
|
1314
|
+
const line068 = nn$30(mad.taxPayableBeforeDeduction);
|
|
1315
|
+
const line070 = nn$30(mad.line070);
|
|
1316
|
+
const line071 = nn$30(mad.line071);
|
|
1317
|
+
const line072 = nn$30(mad.line072);
|
|
1318
|
+
const line074 = nn$30(mad.line074);
|
|
1316
1319
|
const room602 = raw(line068) - (line070 + line071 + line072 + line074);
|
|
1317
1320
|
const itcCitcCap = Math.max(0, room602);
|
|
1318
1321
|
const itc = computeItc(input.itc ?? {}, itcCitcCap, issues);
|
|
@@ -1378,26 +1381,26 @@ function schedule3Values(result) {
|
|
|
1378
1381
|
}
|
|
1379
1382
|
//#endregion
|
|
1380
1383
|
//#region src/t2/at1/schedules/schedule4-foreign-investment-tax-credit.ts
|
|
1381
|
-
const nn$
|
|
1384
|
+
const nn$29 = (v) => Math.max(0, v ?? 0);
|
|
1382
1385
|
/** Round to 3 decimal places, half-up — the precision the spec directs for D and G. */
|
|
1383
1386
|
function round3(v) {
|
|
1384
1387
|
return Math.round((v + Number.EPSILON) * 1e3) / 1e3;
|
|
1385
1388
|
}
|
|
1386
1389
|
function computeSchedule4(input) {
|
|
1387
1390
|
const issues = [];
|
|
1388
|
-
const albertaTaxableIncome = nn$
|
|
1389
|
-
const royaltyTaxDeduction = nn$
|
|
1391
|
+
const albertaTaxableIncome = nn$29(input.albertaTaxableIncome);
|
|
1392
|
+
const royaltyTaxDeduction = nn$29(input.royaltyTaxDeduction);
|
|
1390
1393
|
const allocationFactor = input.allocationFactor ?? 0;
|
|
1391
|
-
const basicAlbertaTax = nn$
|
|
1394
|
+
const basicAlbertaTax = nn$29(input.basicAlbertaTax);
|
|
1392
1395
|
const denominator = (albertaTaxableIncome - royaltyTaxDeduction) * allocationFactor;
|
|
1393
1396
|
const countries = input.countries.map((c) => {
|
|
1394
1397
|
if (!c.country) issues.push("Alberta Schedule 4: a country code is required for each occurrence (004002).");
|
|
1395
|
-
const netForeignInvestmentIncome = nn$
|
|
1396
|
-
const fedForeignTaxPaid = nn$
|
|
1397
|
-
const fedIta2012Deduction = nn$
|
|
1398
|
+
const netForeignInvestmentIncome = nn$29(c.netForeignInvestmentIncome);
|
|
1399
|
+
const fedForeignTaxPaid = nn$29(c.fedForeignTaxPaid);
|
|
1400
|
+
const fedIta2012Deduction = nn$29(c.fedIta2012Deduction);
|
|
1398
1401
|
const albertaDeduction = Math.max(c.albertaActa82Deduction ?? fedIta2012Deduction, fedIta2012Deduction);
|
|
1399
1402
|
const taxPaidNetOfDeduction = Math.max(0, fedForeignTaxPaid - albertaDeduction);
|
|
1400
|
-
const federalNonBusinessForeignTaxCredit = nn$
|
|
1403
|
+
const federalNonBusinessForeignTaxCredit = nn$29(c.fedNonBusinessForeignTaxCredit);
|
|
1401
1404
|
let incomeProrationAmount;
|
|
1402
1405
|
if (denominator === 0) {
|
|
1403
1406
|
incomeProrationAmount = 0;
|
|
@@ -1453,765 +1456,6 @@ function schedule4Values(result) {
|
|
|
1453
1456
|
};
|
|
1454
1457
|
}
|
|
1455
1458
|
//#endregion
|
|
1456
|
-
//#region src/t2/at1/schedules/schedule5-royalty-tax-deduction.ts
|
|
1457
|
-
const nn$31 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1458
|
-
const num$2 = (v) => Math.round(v ?? 0);
|
|
1459
|
-
function processSuccessoredPool(entries, label, issues) {
|
|
1460
|
-
return (entries ?? []).map((e, i) => {
|
|
1461
|
-
const hasBrought = e.poolBroughtForward !== void 0;
|
|
1462
|
-
const hasAcquired = e.acquisitionAmount !== void 0;
|
|
1463
|
-
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.`);
|
|
1464
|
-
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.`);
|
|
1465
|
-
const base = nn$31(hasBrought ? e.poolBroughtForward : e.acquisitionAmount);
|
|
1466
|
-
const baseKind = hasBrought ? "broughtForward" : hasAcquired ? "acquired" : "unspecified";
|
|
1467
|
-
const propertyIncome = nn$31(e.propertyIncome);
|
|
1468
|
-
const claim = Math.min(base, propertyIncome);
|
|
1469
|
-
return {
|
|
1470
|
-
vendorName: e.vendorName,
|
|
1471
|
-
dateOfEvent: e.dateOfEvent,
|
|
1472
|
-
base,
|
|
1473
|
-
baseKind,
|
|
1474
|
-
propertyIncome,
|
|
1475
|
-
claim,
|
|
1476
|
-
carryForwardBeforeTransfer: base - claim
|
|
1477
|
-
};
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
function computeAlbertaSchedule5(input) {
|
|
1481
|
-
const issues = [];
|
|
1482
|
-
const crownCharges = nn$31(input.crownChargesNetOfReimbursements);
|
|
1483
|
-
const resourceAllowance = nn$31(input.albertaResourceAllowance ?? input.federalResourceAllowance);
|
|
1484
|
-
const reimbursements = nn$31(input.reimbursementsForCrownCharges);
|
|
1485
|
-
const predecessorTransfersTotal = (input.predecessorTransfers ?? []).reduce((s, t) => s + nn$31(t.amountTransferred), 0);
|
|
1486
|
-
const attributedRoyaltyIncomeCarryForwardIn = nn$31(input.openingUnsuccessoredPoolBalance) + predecessorTransfersTotal;
|
|
1487
|
-
const unsuccessoredPoolAvailable = crownCharges - resourceAllowance - reimbursements + attributedRoyaltyIncomeCarryForwardIn;
|
|
1488
|
-
const hasSuccessoredPools = input.hasSuccessoredPools ?? false;
|
|
1489
|
-
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.");
|
|
1490
|
-
const secondSuccessoredPools = hasSuccessoredPools ? processSuccessoredPool(input.secondSuccessoredPools, "second successored (SSPI)", issues) : [];
|
|
1491
|
-
const firstSuccessoredPools = hasSuccessoredPools ? processSuccessoredPool(input.firstSuccessoredPools, "first successored (FSPI)", issues) : [];
|
|
1492
|
-
const secondSuccessoredSubtotal = secondSuccessoredPools.reduce((s, e) => s + e.carryForwardBeforeTransfer, 0);
|
|
1493
|
-
const firstSuccessoredSubtotal = firstSuccessoredPools.reduce((s, e) => s + e.carryForwardBeforeTransfer, 0);
|
|
1494
|
-
const successoredTotal = secondSuccessoredPools.reduce((s, e) => s + e.claim, 0) + firstSuccessoredPools.reduce((s, e) => s + e.claim, 0);
|
|
1495
|
-
const albertaTaxableIncome = num$2(input.albertaTaxableIncomeBeforeDeduction);
|
|
1496
|
-
const crtdMaxClaimable = Math.max(0, Math.min(unsuccessoredPoolAvailable, albertaTaxableIncome - successoredTotal));
|
|
1497
|
-
const crtdClaim = input.crtdAmountClaimed != null ? Math.max(0, Math.min(Math.round(input.crtdAmountClaimed), crtdMaxClaimable)) : crtdMaxClaimable;
|
|
1498
|
-
const poolAvailableCarryForward = unsuccessoredPoolAvailable - crtdClaim;
|
|
1499
|
-
const transferredOnDisposal = nn$31(input.transferredOnDisposal);
|
|
1500
|
-
const uncappedTotal = crtdClaim + successoredTotal;
|
|
1501
|
-
const totalRoyaltyTaxDeduction = Math.max(0, Math.min(uncappedTotal, albertaTaxableIncome));
|
|
1502
|
-
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.`);
|
|
1503
|
-
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.`);
|
|
1504
|
-
const netOfResourceAllowance = crownCharges - resourceAllowance;
|
|
1505
|
-
const carryForwardRaw = netOfResourceAllowance >= 0 ? netOfResourceAllowance + attributedRoyaltyIncomeCarryForwardIn - totalRoyaltyTaxDeduction - transferredOnDisposal : attributedRoyaltyIncomeCarryForwardIn - totalRoyaltyTaxDeduction - transferredOnDisposal;
|
|
1506
|
-
const attributedRoyaltyIncomeCarryForwardOut = Math.max(0, carryForwardRaw);
|
|
1507
|
-
if (input.poolTransfer) {
|
|
1508
|
-
const { type, acquirerName } = input.poolTransfer;
|
|
1509
|
-
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.");
|
|
1510
|
-
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.");
|
|
1511
|
-
}
|
|
1512
|
-
if (input.changeInControlEndedPrecedingYear !== void 0 && input.at1TaxYearEndChanged !== void 0 && input.at1TaxYearEndChangeReason !== void 0) {
|
|
1513
|
-
const cicChangedYearEnd = input.at1TaxYearEndChanged && input.at1TaxYearEndChangeReason === 2;
|
|
1514
|
-
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.`);
|
|
1515
|
-
}
|
|
1516
|
-
const formRequired = crownCharges > 0 || attributedRoyaltyIncomeCarryForwardIn > 0 || hasSuccessoredPools;
|
|
1517
|
-
return {
|
|
1518
|
-
crownCharges,
|
|
1519
|
-
resourceAllowance,
|
|
1520
|
-
reimbursements,
|
|
1521
|
-
predecessorTransfersTotal,
|
|
1522
|
-
attributedRoyaltyIncomeCarryForwardIn,
|
|
1523
|
-
unsuccessoredPoolAvailable,
|
|
1524
|
-
crtdMaxClaimable,
|
|
1525
|
-
crtdClaim,
|
|
1526
|
-
poolAvailableCarryForward,
|
|
1527
|
-
transferredOnDisposal,
|
|
1528
|
-
secondSuccessoredPools,
|
|
1529
|
-
firstSuccessoredPools,
|
|
1530
|
-
secondSuccessoredSubtotal,
|
|
1531
|
-
firstSuccessoredSubtotal,
|
|
1532
|
-
successoredTotal,
|
|
1533
|
-
totalRoyaltyTaxDeduction,
|
|
1534
|
-
attributedRoyaltyIncomeCarryForwardOut,
|
|
1535
|
-
...input.poolTransfer !== void 0 ? { poolTransfer: input.poolTransfer } : {},
|
|
1536
|
-
...input.changeInControlEndedPrecedingYear !== void 0 ? { changeInControlEndedPrecedingYear: input.changeInControlEndedPrecedingYear } : {},
|
|
1537
|
-
formRequired,
|
|
1538
|
-
issues
|
|
1539
|
-
};
|
|
1540
|
-
}
|
|
1541
|
-
/** `005` + field id + 3-digit occurrence — the nine-digit AT1 line item id. */
|
|
1542
|
-
function schedule5LineItemId(fieldId, occurrence = 1) {
|
|
1543
|
-
return `005${fieldId}${String(occurrence).padStart(3, "0")}`;
|
|
1544
|
-
}
|
|
1545
|
-
function putSuccessoredPool(values, entries, fields) {
|
|
1546
|
-
entries.forEach((e, i) => {
|
|
1547
|
-
const n = i + 1;
|
|
1548
|
-
const put = (fieldId, value) => values.push({
|
|
1549
|
-
lineItemId: schedule5LineItemId(fieldId, n),
|
|
1550
|
-
value
|
|
1551
|
-
});
|
|
1552
|
-
put(fields.vendorName, e.vendorName);
|
|
1553
|
-
put(fields.dateOfEvent, e.dateOfEvent);
|
|
1554
|
-
if (e.baseKind === "broughtForward") put(fields.broughtForward, e.base);
|
|
1555
|
-
else if (e.baseKind === "acquired") put(fields.acquired, e.base);
|
|
1556
|
-
put(fields.propertyIncome, e.propertyIncome);
|
|
1557
|
-
put(fields.claim, e.claim);
|
|
1558
|
-
put(fields.carryForward, e.carryForwardBeforeTransfer);
|
|
1559
|
-
});
|
|
1560
|
-
}
|
|
1561
|
-
function schedule5Values(result) {
|
|
1562
|
-
const values = [];
|
|
1563
|
-
const put = (fieldId, value) => values.push({
|
|
1564
|
-
lineItemId: schedule5LineItemId(fieldId),
|
|
1565
|
-
value
|
|
1566
|
-
});
|
|
1567
|
-
put("001", result.crownCharges);
|
|
1568
|
-
put("005", result.resourceAllowance);
|
|
1569
|
-
put("007", result.reimbursements);
|
|
1570
|
-
put("011", result.attributedRoyaltyIncomeCarryForwardIn);
|
|
1571
|
-
put("016", result.crtdClaim);
|
|
1572
|
-
put("017", result.poolAvailableCarryForward);
|
|
1573
|
-
put("023", result.transferredOnDisposal);
|
|
1574
|
-
put("025", result.attributedRoyaltyIncomeCarryForwardOut);
|
|
1575
|
-
if (result.poolTransfer) {
|
|
1576
|
-
put("026", result.poolTransfer.type);
|
|
1577
|
-
if (result.poolTransfer.acquirerName) put("027", result.poolTransfer.acquirerName);
|
|
1578
|
-
}
|
|
1579
|
-
if (result.changeInControlEndedPrecedingYear !== void 0) put("100", result.changeInControlEndedPrecedingYear ? 1 : 2);
|
|
1580
|
-
putSuccessoredPool(values, result.secondSuccessoredPools, {
|
|
1581
|
-
vendorName: "101",
|
|
1582
|
-
dateOfEvent: "103",
|
|
1583
|
-
broughtForward: "105",
|
|
1584
|
-
acquired: "107",
|
|
1585
|
-
propertyIncome: "109",
|
|
1586
|
-
claim: "111",
|
|
1587
|
-
carryForward: "113"
|
|
1588
|
-
});
|
|
1589
|
-
put("115", result.secondSuccessoredSubtotal);
|
|
1590
|
-
putSuccessoredPool(values, result.firstSuccessoredPools, {
|
|
1591
|
-
vendorName: "121",
|
|
1592
|
-
dateOfEvent: "123",
|
|
1593
|
-
broughtForward: "125",
|
|
1594
|
-
acquired: "127",
|
|
1595
|
-
propertyIncome: "129",
|
|
1596
|
-
claim: "131",
|
|
1597
|
-
carryForward: "133"
|
|
1598
|
-
});
|
|
1599
|
-
put("135", result.firstSuccessoredSubtotal);
|
|
1600
|
-
put("140", result.successoredTotal);
|
|
1601
|
-
return {
|
|
1602
|
-
scheduleId: "005",
|
|
1603
|
-
values
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
//#endregion
|
|
1607
|
-
//#region src/t2/at1/schedules/schedule6-royalty-tax-credit.ts
|
|
1608
|
-
const rd$1 = (v) => Math.round(v ?? 0);
|
|
1609
|
-
const nn$30 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1610
|
-
const round4$1 = (v) => Math.round(v * 1e4) / 1e4;
|
|
1611
|
-
function computeWeightedAverageRate(quarters, issues) {
|
|
1612
|
-
if (quarters.length === 0) {
|
|
1613
|
-
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.");
|
|
1614
|
-
return 0;
|
|
1615
|
-
}
|
|
1616
|
-
const totalDays = quarters.reduce((s, q) => s + Math.max(0, q.days), 0);
|
|
1617
|
-
if (totalDays <= 0) {
|
|
1618
|
-
issues.push("Alberta Schedule 6: the quarters supplied for 006008 total zero days.");
|
|
1619
|
-
return 0;
|
|
1620
|
-
}
|
|
1621
|
-
const weighted = quarters.reduce((s, q) => s + Math.max(0, q.days) / totalDays * q.rate, 0);
|
|
1622
|
-
return round4$1(weighted);
|
|
1623
|
-
}
|
|
1624
|
-
function resolveAllocations(allocations, pool, issues) {
|
|
1625
|
-
const totalRequested = allocations.reduce((s, a) => s + nn$30(a.allocatedAmount), 0);
|
|
1626
|
-
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.`);
|
|
1627
|
-
let remaining = pool;
|
|
1628
|
-
return allocations.map((a) => {
|
|
1629
|
-
const requestedAmount = nn$30(a.allocatedAmount);
|
|
1630
|
-
const allocatedAmount = Math.min(requestedAmount, remaining);
|
|
1631
|
-
remaining -= allocatedAmount;
|
|
1632
|
-
return {
|
|
1633
|
-
name: a.name,
|
|
1634
|
-
...a.albertaCan !== void 0 ? { albertaCan: a.albertaCan } : {},
|
|
1635
|
-
allocatedAmount,
|
|
1636
|
-
requestedAmount
|
|
1637
|
-
};
|
|
1638
|
-
});
|
|
1639
|
-
}
|
|
1640
|
-
function schedule6LineItemId(fieldId, occurrence = 1) {
|
|
1641
|
-
return `006${fieldId}${String(occurrence).padStart(3, "0")}`;
|
|
1642
|
-
}
|
|
1643
|
-
/**
|
|
1644
|
-
* Emits every field this module's own MAPPINGS transcription actually defines
|
|
1645
|
-
* a number for: 006002/004/006/008, and — only when the corporation is
|
|
1646
|
-
* associated — the ACRS section (006022-028) and the AACRS allocation table
|
|
1647
|
-
* (006030-034, one occurrence per row).
|
|
1648
|
-
*
|
|
1649
|
-
* ── No credit amount is filed here, because none exists on this schedule ────
|
|
1650
|
-
*
|
|
1651
|
-
* See the module docstring's "CONFIRMED: there is no 'credit' dollar amount
|
|
1652
|
-
* to compute here" section — the Alberta Royalty Tax Credit is an instalment
|
|
1653
|
-
* program (AT1 jacket line 000082, the shared "Payments & Instalments"
|
|
1654
|
-
* schedule), not a value Schedule 6 computes and files. This builder emits
|
|
1655
|
-
* exactly the three components the schedule DOES define (006004, 006006,
|
|
1656
|
-
* 006008) plus the ACRS/AACRS detail when associated — matching
|
|
1657
|
-
* `schedule3Values`'s `At1ScheduleDataLike` shape exactly: `{ scheduleId,
|
|
1658
|
-
* values }`.
|
|
1659
|
-
*/
|
|
1660
|
-
function schedule6Values(result) {
|
|
1661
|
-
const values = [];
|
|
1662
|
-
const put = (fieldId, value, occurrence = 1) => values.push({
|
|
1663
|
-
lineItemId: schedule6LineItemId(fieldId, occurrence),
|
|
1664
|
-
value
|
|
1665
|
-
});
|
|
1666
|
-
put("002", result.associatedWithCrownRoyaltyCorporations ? 1 : 2);
|
|
1667
|
-
put("004", result.albertaCrownRoyaltyIncurred);
|
|
1668
|
-
put("006", result.crownRoyaltyShelter);
|
|
1669
|
-
put("008", result.weightedAverageRate);
|
|
1670
|
-
if (result.associatedWithCrownRoyaltyCorporations && result.longestAssociatedYear) {
|
|
1671
|
-
if (result.longestAssociatedYear.albertaCan !== void 0) values.push({
|
|
1672
|
-
lineItemId: schedule6LineItemId("022"),
|
|
1673
|
-
value: result.longestAssociatedYear.albertaCan
|
|
1674
|
-
});
|
|
1675
|
-
if (result.longestAssociatedYear.taxationYearBeginning !== void 0) values.push({
|
|
1676
|
-
lineItemId: schedule6LineItemId("024"),
|
|
1677
|
-
value: result.longestAssociatedYear.taxationYearBeginning
|
|
1678
|
-
});
|
|
1679
|
-
if (result.longestAssociatedYear.taxationYearEnding !== void 0) values.push({
|
|
1680
|
-
lineItemId: schedule6LineItemId("026"),
|
|
1681
|
-
value: result.longestAssociatedYear.taxationYearEnding
|
|
1682
|
-
});
|
|
1683
|
-
put("028", result.longestAssociatedYear.days);
|
|
1684
|
-
result.allocations.forEach((a, i) => {
|
|
1685
|
-
const occurrence = i + 1;
|
|
1686
|
-
values.push({
|
|
1687
|
-
lineItemId: schedule6LineItemId("030", occurrence),
|
|
1688
|
-
value: a.name
|
|
1689
|
-
});
|
|
1690
|
-
if (a.albertaCan !== void 0) values.push({
|
|
1691
|
-
lineItemId: schedule6LineItemId("032", occurrence),
|
|
1692
|
-
value: a.albertaCan
|
|
1693
|
-
});
|
|
1694
|
-
put("034", a.allocatedAmount, occurrence);
|
|
1695
|
-
});
|
|
1696
|
-
}
|
|
1697
|
-
return {
|
|
1698
|
-
scheduleId: "006",
|
|
1699
|
-
values
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
function computeAlbertaSchedule6(input) {
|
|
1703
|
-
const issues = [];
|
|
1704
|
-
const associatedWithCrownRoyaltyCorporations = input.associatedWithCrownRoyaltyCorporations ?? false;
|
|
1705
|
-
const albertaCrownRoyaltyIncurred = rd$1(input.albertaCrownRoyaltyIncurred);
|
|
1706
|
-
const weightedAverageRate = computeWeightedAverageRate(input.quarters ?? [], issues);
|
|
1707
|
-
const formRequired = albertaCrownRoyaltyIncurred > 0;
|
|
1708
|
-
if (!associatedWithCrownRoyaltyCorporations) {
|
|
1709
|
-
const days = Math.max(0, Math.min(input.taxationYearDays ?? 365, 365));
|
|
1710
|
-
return {
|
|
1711
|
-
associatedWithCrownRoyaltyCorporations,
|
|
1712
|
-
albertaCrownRoyaltyIncurred,
|
|
1713
|
-
crownRoyaltyShelter: Math.round(2e6 * (days / 365)),
|
|
1714
|
-
weightedAverageRate,
|
|
1715
|
-
aggregateShelterPool: 0,
|
|
1716
|
-
allocations: [],
|
|
1717
|
-
totalAllocated: 0,
|
|
1718
|
-
formRequired,
|
|
1719
|
-
issues
|
|
1720
|
-
};
|
|
1721
|
-
}
|
|
1722
|
-
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.");
|
|
1723
|
-
const days = Math.max(0, Math.min(input.longestAssociatedYear?.days ?? 0, 365));
|
|
1724
|
-
const aggregateShelterPool = Math.round(2e6 * (days / 365));
|
|
1725
|
-
const rawAllocations = input.allocations ?? [];
|
|
1726
|
-
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.");
|
|
1727
|
-
const allocations = resolveAllocations(rawAllocations, aggregateShelterPool, issues);
|
|
1728
|
-
const totalAllocated = allocations.reduce((s, a) => s + a.allocatedAmount, 0);
|
|
1729
|
-
return {
|
|
1730
|
-
associatedWithCrownRoyaltyCorporations,
|
|
1731
|
-
albertaCrownRoyaltyIncurred,
|
|
1732
|
-
crownRoyaltyShelter: allocations[0]?.allocatedAmount ?? 0,
|
|
1733
|
-
weightedAverageRate,
|
|
1734
|
-
longestAssociatedYear: {
|
|
1735
|
-
...input.longestAssociatedYear?.albertaCan !== void 0 ? { albertaCan: input.longestAssociatedYear.albertaCan } : {},
|
|
1736
|
-
...input.longestAssociatedYear?.taxationYearBeginning !== void 0 ? { taxationYearBeginning: input.longestAssociatedYear.taxationYearBeginning } : {},
|
|
1737
|
-
...input.longestAssociatedYear?.taxationYearEnding !== void 0 ? { taxationYearEnding: input.longestAssociatedYear.taxationYearEnding } : {},
|
|
1738
|
-
days
|
|
1739
|
-
},
|
|
1740
|
-
aggregateShelterPool,
|
|
1741
|
-
allocations,
|
|
1742
|
-
totalAllocated,
|
|
1743
|
-
formRequired,
|
|
1744
|
-
issues
|
|
1745
|
-
};
|
|
1746
|
-
}
|
|
1747
|
-
//#endregion
|
|
1748
|
-
//#region src/t2/at1/schedules/schedule7-royalty-supplemental.ts
|
|
1749
|
-
/** Signed whole-dollar rounding — no floor, several of these lines are marked "+/-". */
|
|
1750
|
-
const rd = (v) => Math.round(v ?? 0);
|
|
1751
|
-
/** Non-negative whole-dollar rounding, for the "+"-only lines. */
|
|
1752
|
-
const nn$29 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1753
|
-
const round4 = (v) => Math.round(v * 1e4) / 1e4;
|
|
1754
|
-
function resolvePartnership(p, index, issues) {
|
|
1755
|
-
if (!p.name) issues.push(`Alberta Schedule 7: partnership row ${index + 1} has no name (007071 is mandatory for every PITI occurrence).`);
|
|
1756
|
-
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%).`);
|
|
1757
|
-
return {
|
|
1758
|
-
name: p.name,
|
|
1759
|
-
interestPercent: round4(p.interestPercent),
|
|
1760
|
-
...p.fiscalPeriodEnd !== void 0 ? { fiscalPeriodEnd: p.fiscalPeriodEnd } : {},
|
|
1761
|
-
shareEligibleForCredit: nn$29(p.shareEligibleForCredit),
|
|
1762
|
-
shareOtherRoyaltiesNotEligible: nn$29(p.shareOtherRoyaltiesNotEligible),
|
|
1763
|
-
shareOtherCrownChargesEligibleForDeduction: nn$29(p.shareOtherCrownChargesEligibleForDeduction)
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
function resolveAdjustment(a, index, issues) {
|
|
1767
|
-
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).`);
|
|
1768
|
-
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).`);
|
|
1769
|
-
return {
|
|
1770
|
-
...a.priorProductionPeriodEnd !== void 0 ? { priorProductionPeriodEnd: a.priorProductionPeriodEnd } : {},
|
|
1771
|
-
...a.sourceOfAdjustment !== void 0 ? { sourceOfAdjustment: a.sourceOfAdjustment } : {},
|
|
1772
|
-
increase: nn$29(a.increase),
|
|
1773
|
-
decrease: nn$29(a.decrease),
|
|
1774
|
-
adjustmentNotEligibleForCredit: rd(a.adjustmentNotEligibleForCredit)
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
|
-
function schedule7LineItemId(fieldId, occurrence = 1) {
|
|
1778
|
-
return `007${fieldId}${String(occurrence).padStart(3, "0")}`;
|
|
1779
|
-
}
|
|
1780
|
-
/**
|
|
1781
|
-
* Emits CPI (007003-029), the computed totals 007051 and 007061, and the two
|
|
1782
|
-
* repeating sections — PITI (007071-081, one occurrence per partnership) and
|
|
1783
|
-
* ACRA (007083-091, one occurrence per prior-year adjustment).
|
|
1784
|
-
*
|
|
1785
|
-
* 007061 is filed even though it has no defining row of its own anywhere in
|
|
1786
|
-
* this schedule's MAPPINGS block (spec lines 6654-7251) — see the module
|
|
1787
|
-
* docstring's "Two totals with NO defining row of their own in this MAPPINGS
|
|
1788
|
-
* table" section. It is a real Schedule 7 output line: AT1 Schedule 5's own
|
|
1789
|
-
* field 005001 definition (spec lines 5237-5248) names it explicitly as
|
|
1790
|
-
* "Schedule 7, line 061" and gives its formula in full, so it is filed here
|
|
1791
|
-
* under that citation rather than omitted for lack of a home row.
|
|
1792
|
-
*/
|
|
1793
|
-
function schedule7Values(result) {
|
|
1794
|
-
const values = [];
|
|
1795
|
-
const put = (fieldId, value, occurrence = 1) => values.push({
|
|
1796
|
-
lineItemId: schedule7LineItemId(fieldId, occurrence),
|
|
1797
|
-
value
|
|
1798
|
-
});
|
|
1799
|
-
put("003", result.eligibleCrownRoyalty);
|
|
1800
|
-
put("005", result.otherRoyaltiesNotEligible);
|
|
1801
|
-
put("007", result.royaltyPaidToOtherJurisdictions);
|
|
1802
|
-
put("009", result.nonDeductibleCrownLeaseRentals);
|
|
1803
|
-
put("011", result.mineralTaxes);
|
|
1804
|
-
put("013", result.saskatchewanResourcesSurcharge);
|
|
1805
|
-
result.otherNonDeductibleCrownChargeTypes.forEach((name, i) => {
|
|
1806
|
-
values.push({
|
|
1807
|
-
lineItemId: schedule7LineItemId(String(14 + i).padStart(3, "0")),
|
|
1808
|
-
value: name
|
|
1809
|
-
});
|
|
1810
|
-
});
|
|
1811
|
-
put("017", result.otherNonDeductibleCrownCharges);
|
|
1812
|
-
put("025", result.crownLeaseRentalsCapitalized);
|
|
1813
|
-
if (result.otherBalanceSheetDeductionName !== void 0) values.push({
|
|
1814
|
-
lineItemId: schedule7LineItemId("027"),
|
|
1815
|
-
value: result.otherBalanceSheetDeductionName
|
|
1816
|
-
});
|
|
1817
|
-
put("029", result.otherBalanceSheetDeduction);
|
|
1818
|
-
result.partnerships.forEach((p, i) => {
|
|
1819
|
-
const occurrence = i + 1;
|
|
1820
|
-
values.push({
|
|
1821
|
-
lineItemId: schedule7LineItemId("071", occurrence),
|
|
1822
|
-
value: p.name
|
|
1823
|
-
});
|
|
1824
|
-
put("073", p.interestPercent, occurrence);
|
|
1825
|
-
if (p.fiscalPeriodEnd !== void 0) values.push({
|
|
1826
|
-
lineItemId: schedule7LineItemId("075", occurrence),
|
|
1827
|
-
value: p.fiscalPeriodEnd
|
|
1828
|
-
});
|
|
1829
|
-
put("077", p.shareEligibleForCredit, occurrence);
|
|
1830
|
-
put("079", p.shareOtherRoyaltiesNotEligible, occurrence);
|
|
1831
|
-
put("081", p.shareOtherCrownChargesEligibleForDeduction, occurrence);
|
|
1832
|
-
});
|
|
1833
|
-
result.priorYearAdjustments.forEach((a, i) => {
|
|
1834
|
-
const occurrence = i + 1;
|
|
1835
|
-
if (a.priorProductionPeriodEnd !== void 0) values.push({
|
|
1836
|
-
lineItemId: schedule7LineItemId("083", occurrence),
|
|
1837
|
-
value: a.priorProductionPeriodEnd
|
|
1838
|
-
});
|
|
1839
|
-
if (a.sourceOfAdjustment !== void 0) put("085", a.sourceOfAdjustment, occurrence);
|
|
1840
|
-
put("087", a.increase, occurrence);
|
|
1841
|
-
put("089", a.decrease, occurrence);
|
|
1842
|
-
put("091", a.adjustmentNotEligibleForCredit, occurrence);
|
|
1843
|
-
});
|
|
1844
|
-
put("051", result.totalAdjustments);
|
|
1845
|
-
put("061", result.crownChargesNetOfReimbursements);
|
|
1846
|
-
return {
|
|
1847
|
-
scheduleId: "007",
|
|
1848
|
-
values
|
|
1849
|
-
};
|
|
1850
|
-
}
|
|
1851
|
-
function computeAlbertaSchedule7(input) {
|
|
1852
|
-
const issues = [];
|
|
1853
|
-
const eligibleCrownRoyalty = rd(input.eligibleCrownRoyalty);
|
|
1854
|
-
const otherRoyaltiesNotEligible = rd(input.otherRoyaltiesNotEligible);
|
|
1855
|
-
const royaltyPaidToOtherJurisdictions = rd(input.royaltyPaidToOtherJurisdictions);
|
|
1856
|
-
const nonDeductibleCrownLeaseRentals = rd(input.nonDeductibleCrownLeaseRentals);
|
|
1857
|
-
const mineralTaxes = rd(input.mineralTaxes);
|
|
1858
|
-
const saskatchewanResourcesSurcharge = rd(input.saskatchewanResourcesSurcharge);
|
|
1859
|
-
const otherNonDeductibleCrownChargeTypes = (input.otherNonDeductibleCrownChargeTypes ?? []).slice(0, 3);
|
|
1860
|
-
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.");
|
|
1861
|
-
let otherNonDeductibleCrownCharges = rd(input.otherNonDeductibleCrownCharges);
|
|
1862
|
-
if (otherNonDeductibleCrownChargeTypes.length === 0 && otherNonDeductibleCrownCharges !== 0) {
|
|
1863
|
-
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.");
|
|
1864
|
-
otherNonDeductibleCrownCharges = 0;
|
|
1865
|
-
}
|
|
1866
|
-
const crownLeaseRentalsCapitalized = rd(input.crownLeaseRentalsCapitalized);
|
|
1867
|
-
let otherBalanceSheetDeduction = nn$29(input.otherBalanceSheetDeduction);
|
|
1868
|
-
if (!input.otherBalanceSheetDeductionName && otherBalanceSheetDeduction !== 0) {
|
|
1869
|
-
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.");
|
|
1870
|
-
otherBalanceSheetDeduction = 0;
|
|
1871
|
-
}
|
|
1872
|
-
const partnerships = (input.partnerships ?? []).map((p, i) => resolvePartnership(p, i, issues));
|
|
1873
|
-
const totalPartnershipShareEligibleForCredit = partnerships.reduce((s, p) => s + p.shareEligibleForCredit, 0);
|
|
1874
|
-
const totalPartnershipShareOtherRoyaltiesNotEligible = partnerships.reduce((s, p) => s + p.shareOtherRoyaltiesNotEligible, 0);
|
|
1875
|
-
const totalPartnershipShareOtherCrownCharges = partnerships.reduce((s, p) => s + p.shareOtherCrownChargesEligibleForDeduction, 0);
|
|
1876
|
-
const priorYearAdjustments = (input.priorYearAdjustments ?? []).map((a, i) => resolveAdjustment(a, i, issues));
|
|
1877
|
-
const totalIncrease = priorYearAdjustments.reduce((s, a) => s + a.increase, 0);
|
|
1878
|
-
const totalDecrease = priorYearAdjustments.reduce((s, a) => s + a.decrease, 0);
|
|
1879
|
-
const totalAdjustmentNotEligible = priorYearAdjustments.reduce((s, a) => s + a.adjustmentNotEligibleForCredit, 0);
|
|
1880
|
-
const totalAdjustments = priorYearAdjustments.length === 0 ? 0 : totalIncrease - totalDecrease + totalAdjustmentNotEligible;
|
|
1881
|
-
const crownChargesNetOfReimbursements = eligibleCrownRoyalty + otherRoyaltiesNotEligible + royaltyPaidToOtherJurisdictions + nonDeductibleCrownLeaseRentals + mineralTaxes + saskatchewanResourcesSurcharge + otherNonDeductibleCrownCharges + crownLeaseRentalsCapitalized + otherBalanceSheetDeduction + totalPartnershipShareEligibleForCredit + totalPartnershipShareOtherRoyaltiesNotEligible + totalPartnershipShareOtherCrownCharges - totalAdjustments;
|
|
1882
|
-
const albertaCrownRoyaltyForSchedule6 = eligibleCrownRoyalty + totalPartnershipShareEligibleForCredit - totalIncrease + totalDecrease;
|
|
1883
|
-
return {
|
|
1884
|
-
eligibleCrownRoyalty,
|
|
1885
|
-
otherRoyaltiesNotEligible,
|
|
1886
|
-
royaltyPaidToOtherJurisdictions,
|
|
1887
|
-
nonDeductibleCrownLeaseRentals,
|
|
1888
|
-
mineralTaxes,
|
|
1889
|
-
saskatchewanResourcesSurcharge,
|
|
1890
|
-
otherNonDeductibleCrownChargeTypes,
|
|
1891
|
-
otherNonDeductibleCrownCharges,
|
|
1892
|
-
crownLeaseRentalsCapitalized,
|
|
1893
|
-
...input.otherBalanceSheetDeductionName !== void 0 ? { otherBalanceSheetDeductionName: input.otherBalanceSheetDeductionName } : {},
|
|
1894
|
-
otherBalanceSheetDeduction,
|
|
1895
|
-
partnerships,
|
|
1896
|
-
totalPartnershipShareEligibleForCredit,
|
|
1897
|
-
totalPartnershipShareOtherRoyaltiesNotEligible,
|
|
1898
|
-
totalPartnershipShareOtherCrownCharges,
|
|
1899
|
-
priorYearAdjustments,
|
|
1900
|
-
totalAdjustments,
|
|
1901
|
-
crownChargesNetOfReimbursements,
|
|
1902
|
-
albertaCrownRoyaltyForSchedule6,
|
|
1903
|
-
issues
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
//#endregion
|
|
1907
|
-
//#region src/t2/at1/schedules/schedule8-political-contributions.ts
|
|
1908
|
-
const nn$28 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
1909
|
-
function yearOf(dateIso) {
|
|
1910
|
-
if (!dateIso || dateIso.length < 4) return void 0;
|
|
1911
|
-
const y = Number(dateIso.slice(0, 4));
|
|
1912
|
-
return Number.isFinite(y) ? y : void 0;
|
|
1913
|
-
}
|
|
1914
|
-
/** Tiered rate for the "all made in 2003 or earlier" branch. */
|
|
1915
|
-
function tierTo2003(a) {
|
|
1916
|
-
if (a <= 150) return a * .75;
|
|
1917
|
-
if (a <= 825) return 112.5 + (a - 150) * .5;
|
|
1918
|
-
return 450 + (a - 825) * .333;
|
|
1919
|
-
}
|
|
1920
|
-
/** Tiered rate for the "all made in 2004 or later" branch. */
|
|
1921
|
-
function tierFrom2004(a) {
|
|
1922
|
-
if (a <= 200) return a * .75;
|
|
1923
|
-
if (a <= 900) return 150 + (a - 200) * .5;
|
|
1924
|
-
return 600 + (a - 900) * .333;
|
|
1925
|
-
}
|
|
1926
|
-
function computeSchedule8$1(input) {
|
|
1927
|
-
const issues = [];
|
|
1928
|
-
const contributions = input.contributions ?? [];
|
|
1929
|
-
contributions.forEach((c, i) => {
|
|
1930
|
-
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.`);
|
|
1931
|
-
if (!c.receiptNumber) issues.push(`Alberta Schedule 8: contribution #${i + 1} (${c.name || "unnamed"}) has no official receipt number (008004).`);
|
|
1932
|
-
});
|
|
1933
|
-
const partnershipContributionsTo2003 = nn$28(input.partnershipContributionsTo2003);
|
|
1934
|
-
const partnershipContributionsFrom2004 = nn$28(input.partnershipContributionsFrom2004);
|
|
1935
|
-
const totalDirect = contributions.reduce((sum, c) => sum + nn$28(c.amount), 0);
|
|
1936
|
-
const directTo2003 = contributions.filter((c) => (yearOf(c.dateOfDonation) ?? 0) <= 2003).reduce((sum, c) => sum + nn$28(c.amount), 0);
|
|
1937
|
-
const directFrom2004 = contributions.filter((c) => (yearOf(c.dateOfDonation) ?? 9999) >= 2004).reduce((sum, c) => sum + nn$28(c.amount), 0);
|
|
1938
|
-
const directIn2004Only = contributions.filter((c) => yearOf(c.dateOfDonation) === 2004).reduce((sum, c) => sum + nn$28(c.amount), 0);
|
|
1939
|
-
const hasTo2003 = directTo2003 > 0 || partnershipContributionsTo2003 > 0;
|
|
1940
|
-
const hasFrom2004 = directFrom2004 > 0 || partnershipContributionsFrom2004 > 0;
|
|
1941
|
-
if (!hasTo2003 && !hasFrom2004) return {
|
|
1942
|
-
contributions,
|
|
1943
|
-
partnershipContributionsTo2003,
|
|
1944
|
-
partnershipContributionsFrom2004,
|
|
1945
|
-
period: "none",
|
|
1946
|
-
creditBeforeCeiling: 0,
|
|
1947
|
-
credit: 0,
|
|
1948
|
-
issues
|
|
1949
|
-
};
|
|
1950
|
-
const taxYearBeginYear = yearOf(input.taxYearBegin);
|
|
1951
|
-
const taxYearEndYear = yearOf(input.taxYearEnd);
|
|
1952
|
-
const taxYearStraddles2003to2004 = taxYearBeginYear === 2003 && taxYearEndYear === 2004;
|
|
1953
|
-
const ceiling = input.remainingBasicTax;
|
|
1954
|
-
let period;
|
|
1955
|
-
let rawCredit;
|
|
1956
|
-
let credit;
|
|
1957
|
-
if (hasTo2003 && !hasFrom2004) {
|
|
1958
|
-
period = "to-2003";
|
|
1959
|
-
rawCredit = tierTo2003(totalDirect + partnershipContributionsTo2003);
|
|
1960
|
-
if (ceiling == null) {
|
|
1961
|
-
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.");
|
|
1962
|
-
credit = 0;
|
|
1963
|
-
} else credit = Math.max(0, Math.round(Math.min(rawCredit, 750, ceiling)));
|
|
1964
|
-
} else if (hasFrom2004 && !hasTo2003) {
|
|
1965
|
-
period = "from-2004";
|
|
1966
|
-
rawCredit = tierFrom2004(totalDirect + partnershipContributionsFrom2004);
|
|
1967
|
-
if (ceiling == null) {
|
|
1968
|
-
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.");
|
|
1969
|
-
credit = 0;
|
|
1970
|
-
} else credit = Math.max(0, Math.round(Math.min(rawCredit, 1e3, ceiling)));
|
|
1971
|
-
} else if (taxYearStraddles2003to2004) {
|
|
1972
|
-
period = "straddle-2003-2004";
|
|
1973
|
-
const x = directIn2004Only + partnershipContributionsFrom2004;
|
|
1974
|
-
const y = totalDirect + partnershipContributionsTo2003 + partnershipContributionsFrom2004;
|
|
1975
|
-
const a = Math.min(y, 150);
|
|
1976
|
-
const b = Math.min(Math.max(0, x - a), 50);
|
|
1977
|
-
const c = Math.min(Math.max(0, y - (a + b)), 675);
|
|
1978
|
-
const d = Math.min(Math.max(0, x - (a + b + c)), 225);
|
|
1979
|
-
const e = Math.min(Math.max(0, y - (a + b + c + d)), 900);
|
|
1980
|
-
const f = Math.min(Math.max(0, x - (a + b + c + d + e)), 300);
|
|
1981
|
-
rawCredit = .75 * a + .75 * b + .5 * c + .5 * d + e / 3 + f / 3;
|
|
1982
|
-
credit = Math.max(0, Math.round(rawCredit));
|
|
1983
|
-
} else {
|
|
1984
|
-
period = "none";
|
|
1985
|
-
rawCredit = 0;
|
|
1986
|
-
credit = 0;
|
|
1987
|
-
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.");
|
|
1988
|
-
}
|
|
1989
|
-
return {
|
|
1990
|
-
contributions,
|
|
1991
|
-
partnershipContributionsTo2003,
|
|
1992
|
-
partnershipContributionsFrom2004,
|
|
1993
|
-
period,
|
|
1994
|
-
creditBeforeCeiling: Math.round(rawCredit),
|
|
1995
|
-
credit,
|
|
1996
|
-
issues
|
|
1997
|
-
};
|
|
1998
|
-
}
|
|
1999
|
-
/**
|
|
2000
|
-
* Net File line items for AT1 Schedule 8: one PCD occurrence per contribution
|
|
2001
|
-
* (002 name, 004 receipt number, 006 date, 008 amount), plus the two APC
|
|
2002
|
-
* partnership totals (012, 013).
|
|
2003
|
-
*
|
|
2004
|
-
* Does NOT emit jacket line 000074 (the actual tax credit) — that is a
|
|
2005
|
-
* jacket line, not a Schedule 8 line. Use `result.credit` for that.
|
|
2006
|
-
*/
|
|
2007
|
-
function schedule8Values(result) {
|
|
2008
|
-
const values = [];
|
|
2009
|
-
result.contributions.forEach((c, i) => {
|
|
2010
|
-
const n = i + 1;
|
|
2011
|
-
const put = (fieldId, value) => values.push({
|
|
2012
|
-
lineItemId: at1LineItemId("008", fieldId, n),
|
|
2013
|
-
value
|
|
2014
|
-
});
|
|
2015
|
-
put("002", c.name);
|
|
2016
|
-
put("004", c.receiptNumber);
|
|
2017
|
-
put("006", c.dateOfDonation);
|
|
2018
|
-
put("008", Math.max(0, Math.round(c.amount)));
|
|
2019
|
-
});
|
|
2020
|
-
values.push({
|
|
2021
|
-
lineItemId: at1LineItemId("008", "012"),
|
|
2022
|
-
value: result.partnershipContributionsTo2003
|
|
2023
|
-
});
|
|
2024
|
-
values.push({
|
|
2025
|
-
lineItemId: at1LineItemId("008", "013"),
|
|
2026
|
-
value: result.partnershipContributionsFrom2004
|
|
2027
|
-
});
|
|
2028
|
-
return {
|
|
2029
|
-
scheduleId: "008",
|
|
2030
|
-
values
|
|
2031
|
-
};
|
|
2032
|
-
}
|
|
2033
|
-
//#endregion
|
|
2034
|
-
//#region src/t2/at1/schedules/schedule9-sred-tax-credit.ts
|
|
2035
|
-
const nn$27 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
2036
|
-
/** Signed whole-dollar rounding, for fields the spec marks "+/-". */
|
|
2037
|
-
const signed = (v) => Math.round(v);
|
|
2038
|
-
/** 009120's flat rate — "lesser of line 009031 and 009108 X 10%". */
|
|
2039
|
-
const ALBERTA_SRED_TAX_CREDIT_RATE = .1;
|
|
2040
|
-
/** Alberta's SR&ED program did not exist before this date (line 009104's note). */
|
|
2041
|
-
const ALBERTA_SRED_PROGRAM_START = "2009-01-01";
|
|
2042
|
-
/** Alberta SR&ED expenditures carried out after this date are not eligible (module docstring). */
|
|
2043
|
-
const ALBERTA_SRED_EXPENDITURE_CUTOFF = "2019-12-31";
|
|
2044
|
-
/**
|
|
2045
|
-
* 009104 / 009206's day-prorated $4,000,000 expenditure limit. Days are clamped
|
|
2046
|
-
* to [0, 366] — 366 only for a year genuinely spanning a February 29, per the
|
|
2047
|
-
* spec's own note.
|
|
2048
|
-
*/
|
|
2049
|
-
function computeSchedule9MaximumExpenditureLimit(daysInTaxYear = 365) {
|
|
2050
|
-
return Math.round(4e6 * (Math.max(0, Math.min(daysInTaxYear, 366)) / 365));
|
|
2051
|
-
}
|
|
2052
|
-
function computeAlbertaSchedule9(input) {
|
|
2053
|
-
const issues = [];
|
|
2054
|
-
const federalQualifiedExpenditures = nn$27(input.federalQualifiedExpenditures);
|
|
2055
|
-
const albertaPortionOfExpenditures = nn$27(input.albertaPortionOfExpenditures);
|
|
2056
|
-
const federalProxyAmountInAlbertaPortion = nn$27(input.federalProxyAmountInAlbertaPortion);
|
|
2057
|
-
const albertaProxyAmount = nn$27(input.albertaProxyAmount);
|
|
2058
|
-
const albertaCreditReducingFederalExpense = nn$27(input.albertaCreditReducingFederalExpense);
|
|
2059
|
-
const priorYearFederalItcReceived = nn$27(input.priorYearFederalItcReceived);
|
|
2060
|
-
const totalAlbertaExpendituresAllYears = nn$27(input.totalAlbertaExpendituresAllYears);
|
|
2061
|
-
const totalFederalExpendituresAllYears = nn$27(input.totalFederalExpendituresAllYears);
|
|
2062
|
-
const albertaPortionOfRepayments = nn$27(input.albertaPortionOfRepayments);
|
|
2063
|
-
const disposalRecapture = nn$27(input.disposalRecapture);
|
|
2064
|
-
const priorYearFederalItcAdjustment = nn$27(input.priorYearFederalItcAdjustment);
|
|
2065
|
-
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.`);
|
|
2066
|
-
const priorYearItcAlbertaPortion = totalFederalExpendituresAllYears > 0 ? Math.round(priorYearFederalItcReceived * totalAlbertaExpendituresAllYears / totalFederalExpendituresAllYears) : 0;
|
|
2067
|
-
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.");
|
|
2068
|
-
const derivedEligibleExpenditures = albertaPortionOfExpenditures - federalProxyAmountInAlbertaPortion + albertaProxyAmount + albertaCreditReducingFederalExpense - priorYearItcAlbertaPortion + albertaPortionOfRepayments;
|
|
2069
|
-
const eligibleExpenditures = input.eligibleExpenditures !== void 0 ? signed(input.eligibleExpenditures) : derivedEligibleExpenditures;
|
|
2070
|
-
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.`);
|
|
2071
|
-
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.");
|
|
2072
|
-
const isAssociated = input.isAssociated ?? false;
|
|
2073
|
-
const nonAssociatedMaximumExpenditureLimit = computeSchedule9MaximumExpenditureLimit(input.daysInTaxYear ?? 365);
|
|
2074
|
-
let maximumExpenditureLimit;
|
|
2075
|
-
if (isAssociated) if (input.allocatedExpenditureLimit === void 0) {
|
|
2076
|
-
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.");
|
|
2077
|
-
maximumExpenditureLimit = 0;
|
|
2078
|
-
} else maximumExpenditureLimit = nn$27(input.allocatedExpenditureLimit);
|
|
2079
|
-
else maximumExpenditureLimit = nonAssociatedMaximumExpenditureLimit;
|
|
2080
|
-
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.`);
|
|
2081
|
-
const netCredit = Math.round(Math.min(eligibleExpenditures, maximumExpenditureLimit) * ALBERTA_SRED_TAX_CREDIT_RATE) - disposalRecapture - priorYearFederalItcAdjustment;
|
|
2082
|
-
return {
|
|
2083
|
-
federalQualifiedExpenditures,
|
|
2084
|
-
albertaPortionOfExpenditures,
|
|
2085
|
-
federalProxyAmountInAlbertaPortion,
|
|
2086
|
-
albertaProxyAmount,
|
|
2087
|
-
albertaCreditReducingFederalExpense,
|
|
2088
|
-
priorYearFederalItcReceived,
|
|
2089
|
-
totalAlbertaExpendituresAllYears,
|
|
2090
|
-
totalFederalExpendituresAllYears,
|
|
2091
|
-
priorYearItcAlbertaPortion,
|
|
2092
|
-
albertaPortionOfRepayments,
|
|
2093
|
-
eligibleExpenditures,
|
|
2094
|
-
derivedEligibleExpenditures,
|
|
2095
|
-
fieldOfScience: input.fieldOfScience,
|
|
2096
|
-
isAssociated,
|
|
2097
|
-
nonAssociatedMaximumExpenditureLimit,
|
|
2098
|
-
maximumExpenditureLimit,
|
|
2099
|
-
disposalRecapture,
|
|
2100
|
-
priorYearFederalItcAdjustment,
|
|
2101
|
-
netCredit,
|
|
2102
|
-
issues
|
|
2103
|
-
};
|
|
2104
|
-
}
|
|
2105
|
-
/**
|
|
2106
|
-
* Allocate the day-prorated $4,000,000 maximum expenditure limit among an
|
|
2107
|
-
* associated group (page 3). Per the spec, EACH occurrence of 009240 and the
|
|
2108
|
-
* SUM of all occurrences are independently capped at the limit — unlike a
|
|
2109
|
-
* running-remainder split, one member requesting more than the limit does not
|
|
2110
|
-
* consume another member's room; it is simply capped and flagged.
|
|
2111
|
-
*/
|
|
2112
|
-
function allocateSchedule9ExpenditureLimit(daysInLongestYear, requested) {
|
|
2113
|
-
const issues = [];
|
|
2114
|
-
const days = Math.max(0, Math.min(daysInLongestYear, 366));
|
|
2115
|
-
const maximumExpenditureLimit = computeSchedule9MaximumExpenditureLimit(days);
|
|
2116
|
-
const members = requested.map((m) => {
|
|
2117
|
-
const requestedAmount = nn$27(m.allocated);
|
|
2118
|
-
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}.`);
|
|
2119
|
-
return {
|
|
2120
|
-
name: m.name,
|
|
2121
|
-
...m.albertaCan !== void 0 ? { albertaCan: m.albertaCan } : {},
|
|
2122
|
-
allocated: Math.min(requestedAmount, maximumExpenditureLimit)
|
|
2123
|
-
};
|
|
2124
|
-
});
|
|
2125
|
-
const totalAllocated = members.reduce((s, m) => s + m.allocated, 0);
|
|
2126
|
-
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.`);
|
|
2127
|
-
return {
|
|
2128
|
-
daysInLongestYear: days,
|
|
2129
|
-
maximumExpenditureLimit,
|
|
2130
|
-
members,
|
|
2131
|
-
totalAllocated,
|
|
2132
|
-
claimantAllocatedAmount: members[0]?.allocated ?? 0,
|
|
2133
|
-
unallocated: Math.max(0, maximumExpenditureLimit - totalAllocated),
|
|
2134
|
-
issues
|
|
2135
|
-
};
|
|
2136
|
-
}
|
|
2137
|
-
function schedule9LineItemId(fieldId, occurrence = 1) {
|
|
2138
|
-
return `009${fieldId}${String(occurrence).padStart(3, "0")}`;
|
|
2139
|
-
}
|
|
2140
|
-
/**
|
|
2141
|
-
* Field ids per the spec transcription in the module docstring: 003-025 (the
|
|
2142
|
-
* expenditure buildup), 040 (field of science), 100-120 (the credit
|
|
2143
|
-
* calculation), and — when `group` is supplied — 200-240 (page 3's
|
|
2144
|
-
* allocation). Line 031 has no confirmed transmitted status of its own (see
|
|
2145
|
-
* the module docstring's "line 009031 has no formula in the spec text"), but
|
|
2146
|
-
* is filed anyway alongside 106 since both carry the identical "Total
|
|
2147
|
-
* eligible expenditures for Alberta purposes" figure per the spec's own
|
|
2148
|
-
* cross-reference ("106 ... Value must equal 009031").
|
|
2149
|
-
*/
|
|
2150
|
-
function schedule9Values(result, group) {
|
|
2151
|
-
const values = [];
|
|
2152
|
-
const put = (fieldId, value) => values.push({
|
|
2153
|
-
lineItemId: schedule9LineItemId(fieldId),
|
|
2154
|
-
value
|
|
2155
|
-
});
|
|
2156
|
-
put("003", result.federalQualifiedExpenditures);
|
|
2157
|
-
put("005", result.albertaPortionOfExpenditures);
|
|
2158
|
-
put("007", result.federalProxyAmountInAlbertaPortion);
|
|
2159
|
-
put("009", result.albertaProxyAmount);
|
|
2160
|
-
put("011", result.albertaCreditReducingFederalExpense);
|
|
2161
|
-
put("015", result.priorYearFederalItcReceived);
|
|
2162
|
-
put("017", result.totalAlbertaExpendituresAllYears);
|
|
2163
|
-
put("019", result.totalFederalExpendituresAllYears);
|
|
2164
|
-
put("023", result.priorYearItcAlbertaPortion);
|
|
2165
|
-
put("025", result.albertaPortionOfRepayments);
|
|
2166
|
-
put("031", result.eligibleExpenditures);
|
|
2167
|
-
if (result.fieldOfScience !== void 0) put("040", result.fieldOfScience);
|
|
2168
|
-
put("100", result.isAssociated ? 1 : 2);
|
|
2169
|
-
if (result.isAssociated) put("102", result.maximumExpenditureLimit);
|
|
2170
|
-
else put("104", result.nonAssociatedMaximumExpenditureLimit);
|
|
2171
|
-
put("106", result.eligibleExpenditures);
|
|
2172
|
-
put("108", result.maximumExpenditureLimit);
|
|
2173
|
-
put("112", result.disposalRecapture);
|
|
2174
|
-
put("116", result.priorYearFederalItcAdjustment);
|
|
2175
|
-
put("120", result.netCredit);
|
|
2176
|
-
if (group) {
|
|
2177
|
-
if (group.longestYearCan !== void 0) values.push({
|
|
2178
|
-
lineItemId: schedule9LineItemId("200"),
|
|
2179
|
-
value: group.longestYearCan
|
|
2180
|
-
});
|
|
2181
|
-
if (group.longestYearBegin !== void 0) values.push({
|
|
2182
|
-
lineItemId: schedule9LineItemId("202"),
|
|
2183
|
-
value: group.longestYearBegin
|
|
2184
|
-
});
|
|
2185
|
-
if (group.longestYearEnd !== void 0) values.push({
|
|
2186
|
-
lineItemId: schedule9LineItemId("204"),
|
|
2187
|
-
value: group.longestYearEnd
|
|
2188
|
-
});
|
|
2189
|
-
values.push({
|
|
2190
|
-
lineItemId: schedule9LineItemId("206"),
|
|
2191
|
-
value: group.allocation.daysInLongestYear
|
|
2192
|
-
});
|
|
2193
|
-
group.allocation.members.forEach((m, i) => {
|
|
2194
|
-
const occurrence = i + 1;
|
|
2195
|
-
values.push({
|
|
2196
|
-
lineItemId: schedule9LineItemId("220", occurrence),
|
|
2197
|
-
value: m.name
|
|
2198
|
-
});
|
|
2199
|
-
if (m.albertaCan !== void 0) values.push({
|
|
2200
|
-
lineItemId: schedule9LineItemId("230", occurrence),
|
|
2201
|
-
value: m.albertaCan
|
|
2202
|
-
});
|
|
2203
|
-
values.push({
|
|
2204
|
-
lineItemId: schedule9LineItemId("240", occurrence),
|
|
2205
|
-
value: m.allocated
|
|
2206
|
-
});
|
|
2207
|
-
});
|
|
2208
|
-
}
|
|
2209
|
-
return {
|
|
2210
|
-
scheduleId: "009",
|
|
2211
|
-
values
|
|
2212
|
-
};
|
|
2213
|
-
}
|
|
2214
|
-
//#endregion
|
|
2215
1459
|
//#region src/t2/at1/schedules/schedule15-resource-related-deductions.ts
|
|
2216
1460
|
/**
|
|
2217
1461
|
* Alberta AT1 Schedule 15 — Alberta Resource Related Deductions.
|
|
@@ -2358,7 +1602,7 @@ function schedule9Values(result, group) {
|
|
|
2358
1602
|
*
|
|
2359
1603
|
* Whole dollars, pure functions, no I/O.
|
|
2360
1604
|
*/
|
|
2361
|
-
const nn$
|
|
1605
|
+
const nn$28 = (v) => Math.max(0, v ?? 0);
|
|
2362
1606
|
const num$1 = (v) => v ?? 0;
|
|
2363
1607
|
const round$1 = (v) => Math.round(v);
|
|
2364
1608
|
/** `override ?? federal`, tracking whether an Alberta-specific figure was actually entered. */
|
|
@@ -2862,7 +2106,7 @@ function computeCfreRegular(entries) {
|
|
|
2862
2106
|
const partA = Math.max(CCOGPE_CLAIM_RATE * linear * pool, Math.min(CDE_CLAIM_RATE * linear * pool, foreignResourceIncome, sumForeignResourceIncome));
|
|
2863
2107
|
const remainder = Math.max(0, pool - partA);
|
|
2864
2108
|
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).`);
|
|
2865
|
-
const partB = Math.min(remainder, nn$
|
|
2109
|
+
const partB = Math.min(remainder, nn$28(e.globalForeignResourceLimit));
|
|
2866
2110
|
claim = claimUpToCap$1(e.claimed, partA + partB, issues, `Schedule 15 CFRE regular, country ${e.federal.countryCode} (015293)`);
|
|
2867
2111
|
}
|
|
2868
2112
|
const closingBalance = pool - claim;
|
|
@@ -3294,15 +2538,15 @@ function schedule15Values(result) {
|
|
|
3294
2538
|
function iegT661SourceLine(taxationYearEnd) {
|
|
3295
2539
|
return taxationYearEnd >= "2024-12-16" ? "557" : "559";
|
|
3296
2540
|
}
|
|
3297
|
-
const nn$
|
|
2541
|
+
const nn$27 = (v) => Math.round(v ?? 0);
|
|
3298
2542
|
/** Line 031 = 005 − 007 + 009 + 011 + 025. Every term but 005 defaults to nil. */
|
|
3299
2543
|
function computeIegEligibleExpenditures(input) {
|
|
3300
|
-
const federalAmount = nn$
|
|
3301
|
-
const albertaPortion = nn$
|
|
3302
|
-
const federalProxyAmount = nn$
|
|
3303
|
-
const albertaProxyAmount = nn$
|
|
3304
|
-
const iegReducingFederalExpenditure = nn$
|
|
3305
|
-
const repaymentOrContractPayment = nn$
|
|
2544
|
+
const federalAmount = nn$27(input.federalAmount);
|
|
2545
|
+
const albertaPortion = nn$27(input.albertaPortion);
|
|
2546
|
+
const federalProxyAmount = nn$27(input.federalProxyAmount);
|
|
2547
|
+
const albertaProxyAmount = nn$27(input.albertaProxyAmount);
|
|
2548
|
+
const iegReducingFederalExpenditure = nn$27(input.iegReducingFederalExpenditure);
|
|
2549
|
+
const repaymentOrContractPayment = nn$27(input.repaymentOrContractPayment);
|
|
3306
2550
|
return {
|
|
3307
2551
|
federalAmount,
|
|
3308
2552
|
albertaPortion,
|
|
@@ -3522,7 +2766,7 @@ function computeIeg(input) {
|
|
|
3522
2766
|
*
|
|
3523
2767
|
* Whole dollars, pure.
|
|
3524
2768
|
*/
|
|
3525
|
-
const nn$
|
|
2769
|
+
const nn$26 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
3526
2770
|
/**
|
|
3527
2771
|
* The group's taxable capital and base level of spending, from every associated
|
|
3528
2772
|
* member's own figures.
|
|
@@ -3533,11 +2777,11 @@ const nn$24 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
|
3533
2777
|
function computeIegGroupFigures(members, priorYears = IEG_2024.PRIOR_YEARS) {
|
|
3534
2778
|
const issues = [];
|
|
3535
2779
|
const detail = members.map((m) => {
|
|
3536
|
-
const priorYearAlbertaExpenditures = (m.priorYearAlbertaExpenditures ?? []).map(nn$
|
|
2780
|
+
const priorYearAlbertaExpenditures = (m.priorYearAlbertaExpenditures ?? []).map(nn$26);
|
|
3537
2781
|
if (priorYearAlbertaExpenditures.length > priorYears) issues.push(`Alberta Schedule 29: ${m.name} supplied ${priorYearAlbertaExpenditures.length} prior-year expenditure figures, but the base level of spending averages the prior ${priorYears} years. Every figure supplied was included, which raises the base — check that only the prior ${priorYears} taxation years were given.`);
|
|
3538
2782
|
return {
|
|
3539
2783
|
name: m.name,
|
|
3540
|
-
taxableCapital: nn$
|
|
2784
|
+
taxableCapital: nn$26(m.taxableCapital),
|
|
3541
2785
|
priorYearAlbertaExpenditures,
|
|
3542
2786
|
priorYearTotal: priorYearAlbertaExpenditures.reduce((a, b) => a + b, 0)
|
|
3543
2787
|
};
|
|
@@ -3564,11 +2808,11 @@ function computeIegGroupFigures(members, priorYears = IEG_2024.PRIOR_YEARS) {
|
|
|
3564
2808
|
function allocateIegExpenditureLimit(groupExpenditureLimit, requested) {
|
|
3565
2809
|
const issues = [];
|
|
3566
2810
|
const limit = Math.max(0, Math.round(groupExpenditureLimit));
|
|
3567
|
-
const totalRequested = requested.reduce((s, a) => s + nn$
|
|
2811
|
+
const totalRequested = requested.reduce((s, a) => s + nn$26(a.allocated), 0);
|
|
3568
2812
|
if (totalRequested > limit) issues.push(`Alberta Schedule 29: the associated group allocated ${totalRequested} of an expenditure limit of ${limit}. The allocation cannot exceed the limit; members were capped in the order given and the group must agree a valid split.`);
|
|
3569
2813
|
let remaining = limit;
|
|
3570
2814
|
const allocations = requested.map((a) => {
|
|
3571
|
-
const allocated = Math.min(nn$
|
|
2815
|
+
const allocated = Math.min(nn$26(a.allocated), remaining);
|
|
3572
2816
|
remaining -= allocated;
|
|
3573
2817
|
return {
|
|
3574
2818
|
name: a.name,
|
|
@@ -3602,10 +2846,10 @@ function allocateIegEvenly(groupExpenditureLimit, names) {
|
|
|
3602
2846
|
})));
|
|
3603
2847
|
}
|
|
3604
2848
|
function computeAgreementMember(m, index, issues) {
|
|
3605
|
-
const allocatedExpenditureLimit = nn$
|
|
3606
|
-
const currentYearExpenditures = nn$
|
|
3607
|
-
const priorYear1 = nn$
|
|
3608
|
-
const priorYear2 = nn$
|
|
2849
|
+
const allocatedExpenditureLimit = nn$26(m.allocatedExpenditureLimit);
|
|
2850
|
+
const currentYearExpenditures = nn$26(m.currentYearExpenditures);
|
|
2851
|
+
const priorYear1 = nn$26(m.priorYear1);
|
|
2852
|
+
const priorYear2 = nn$26(m.priorYear2);
|
|
3609
2853
|
const base = Math.round((priorYear1 + priorYear2) / 2);
|
|
3610
2854
|
const individualMaximumAllowedAmount = currentYearExpenditures - base;
|
|
3611
2855
|
const days = Math.max(0, Math.min(m.daysInTaxYear ?? 365, 366));
|
|
@@ -3624,7 +2868,7 @@ function computeAgreementMember(m, index, issues) {
|
|
|
3624
2868
|
currentYearExpenditures,
|
|
3625
2869
|
priorYear1,
|
|
3626
2870
|
priorYear2,
|
|
3627
|
-
taxableCapitalPriorYear: nn$
|
|
2871
|
+
taxableCapitalPriorYear: nn$26(m.taxableCapitalPriorYear),
|
|
3628
2872
|
hasAlbertaPermanentEstablishment,
|
|
3629
2873
|
individualMaximumAllowedAmount,
|
|
3630
2874
|
allocatedAllowedAmount
|
|
@@ -3752,10 +2996,10 @@ function computeAlbertaReturn(input) {
|
|
|
3752
2996
|
const schedulePayloads = [];
|
|
3753
2997
|
const sched = input.schedules;
|
|
3754
2998
|
if (sched?.smallBusinessDeduction) {
|
|
3755
|
-
schedulePayloads.push(schedule1Values(sched.smallBusinessDeduction));
|
|
2999
|
+
schedulePayloads.push(schedule1Values$1(sched.smallBusinessDeduction));
|
|
3756
3000
|
issues.push(...sched.smallBusinessDeduction.result.issues);
|
|
3757
3001
|
}
|
|
3758
|
-
if (sched?.allocation) schedulePayloads.push(schedule2Values(sched.allocation));
|
|
3002
|
+
if (sched?.allocation) schedulePayloads.push(schedule2Values$1(sched.allocation));
|
|
3759
3003
|
if (sched?.otherDeductionsCredits) {
|
|
3760
3004
|
schedulePayloads.push(schedule3Values(sched.otherDeductionsCredits));
|
|
3761
3005
|
issues.push(...sched.otherDeductionsCredits.issues);
|
|
@@ -3764,26 +3008,6 @@ function computeAlbertaReturn(input) {
|
|
|
3764
3008
|
schedulePayloads.push(schedule4Values(sched.foreignInvestmentTaxCredit));
|
|
3765
3009
|
issues.push(...sched.foreignInvestmentTaxCredit.issues);
|
|
3766
3010
|
}
|
|
3767
|
-
if (sched?.royaltyTaxDeduction) {
|
|
3768
|
-
schedulePayloads.push(schedule5Values(sched.royaltyTaxDeduction));
|
|
3769
|
-
issues.push(...sched.royaltyTaxDeduction.issues);
|
|
3770
|
-
}
|
|
3771
|
-
if (sched?.royaltyTaxCredit) {
|
|
3772
|
-
schedulePayloads.push(schedule6Values(sched.royaltyTaxCredit));
|
|
3773
|
-
issues.push(...sched.royaltyTaxCredit.issues);
|
|
3774
|
-
}
|
|
3775
|
-
if (sched?.royaltySupplemental) {
|
|
3776
|
-
schedulePayloads.push(schedule7Values(sched.royaltySupplemental));
|
|
3777
|
-
issues.push(...sched.royaltySupplemental.issues);
|
|
3778
|
-
}
|
|
3779
|
-
if (sched?.politicalContributions) {
|
|
3780
|
-
schedulePayloads.push(schedule8Values(sched.politicalContributions));
|
|
3781
|
-
issues.push(...sched.politicalContributions.issues);
|
|
3782
|
-
}
|
|
3783
|
-
if (sched?.sredTaxCredit) {
|
|
3784
|
-
schedulePayloads.push(schedule9Values(sched.sredTaxCredit.result, sched.sredTaxCredit.group));
|
|
3785
|
-
issues.push(...sched.sredTaxCredit.result.issues);
|
|
3786
|
-
}
|
|
3787
3011
|
if (sched?.lossCarryback) schedulePayloads.push(schedule10Values(sched.lossCarryback));
|
|
3788
3012
|
if (sched?.reconciliation) schedulePayloads.push(schedule12Values(sched.reconciliation));
|
|
3789
3013
|
if (sched?.cca) {
|
|
@@ -3812,7 +3036,7 @@ function computeAlbertaReturn(input) {
|
|
|
3812
3036
|
issues.push(...sched.donations.gifts?.issues ?? []);
|
|
3813
3037
|
}
|
|
3814
3038
|
if (sched?.losses) {
|
|
3815
|
-
schedulePayloads.push(schedule21Values(sched.losses));
|
|
3039
|
+
schedulePayloads.push(schedule21Values$1(sched.losses));
|
|
3816
3040
|
issues.push(...sched.losses.limitedPartnershipLosses?.issues ?? []);
|
|
3817
3041
|
issues.push(...sched.losses.nonCapitalByYearOfOrigin?.issues ?? []);
|
|
3818
3042
|
issues.push(...sched.losses.otherLossesByYearOfOrigin?.issues ?? []);
|
|
@@ -3861,15 +3085,29 @@ const at1Engine = {
|
|
|
3861
3085
|
//#endregion
|
|
3862
3086
|
//#region src/t2/at1/filing/at1-line-items.ts
|
|
3863
3087
|
/**
|
|
3864
|
-
* AT1 line 000090 — balance unpaid (overpayment),
|
|
3865
|
-
*
|
|
3866
|
-
*
|
|
3867
|
-
*
|
|
3868
|
-
*
|
|
3869
|
-
*
|
|
3088
|
+
* AT1 line 000090 — balance unpaid (overpayment), as the PRINTED form strikes
|
|
3089
|
+
* it:
|
|
3090
|
+
*
|
|
3091
|
+
* 000088 = 000129 + 000082 + 000085 + 000086 + 000115 + 000087
|
|
3092
|
+
* 000090 = 000080 − 000088
|
|
3093
|
+
*
|
|
3094
|
+
* 088 is a printed subtotal with no line code of its own, so only 090 is
|
|
3095
|
+
* filed. Signed — a negative result is an overpayment, and line 000092 then
|
|
3096
|
+
* chooses between a refund and applying it to next year.
|
|
3097
|
+
*
|
|
3098
|
+
* This used to follow §3.2.3.1's own line-090 rule,
|
|
3099
|
+
* `000080 - (000081 + 000082 + 00085 + 000086 + 000087)`, which nets the
|
|
3100
|
+
* ELIMINATED Alberta SR&ED tax credit and omits both the Innovation
|
|
3101
|
+
* Employment Grant and the Film and Television Tax Credit — two credits the
|
|
3102
|
+
* same specification marks mandatory. Every corporation claiming an IEG was
|
|
3103
|
+
* therefore filed with a balance overstated by the whole grant. See
|
|
3104
|
+
* `AT1_BALANCE_CREDIT_LINES` in `../forms/jacket.ts` for the full evidence,
|
|
3105
|
+
* and `albertaBalanceUnpaidPerSpec` below for the figure the specification
|
|
3106
|
+
* would produce — the review layer reports the difference rather than letting
|
|
3107
|
+
* the disagreement pass silently.
|
|
3870
3108
|
*/
|
|
3871
3109
|
function albertaBalanceUnpaid(d) {
|
|
3872
|
-
return d.albertaTaxPayable - ((d.
|
|
3110
|
+
return d.albertaTaxPayable - ((d.innovationEmploymentGrant ?? 0) + (d.instalmentsPaid ?? 0) + (d.interactiveDigitalMediaTaxCredit ?? 0) + (d.capitalGainsRefund ?? 0) + (d.filmAndTelevisionTaxCredit ?? 0) + (d.otherCredits ?? 0));
|
|
3873
3111
|
}
|
|
3874
3112
|
/**
|
|
3875
3113
|
* "Critical Mandatory" fields, named in §3.2.3 of the specification:
|
|
@@ -4648,6 +3886,128 @@ function toRsiHeader(data) {
|
|
|
4648
3886
|
};
|
|
4649
3887
|
}
|
|
4650
3888
|
//#endregion
|
|
3889
|
+
//#region src/t2/at1/filing/at1-transmitter-validation.ts
|
|
3890
|
+
/**
|
|
3891
|
+
* §3.2.1.18 — the characters TRA accepts anywhere on the AT1 RSI.
|
|
3892
|
+
*
|
|
3893
|
+
* Latin letters, digits, space, the listed punctuation, and the accented Latin
|
|
3894
|
+
* set. The accented characters are given in the spec as a literal glyph list
|
|
3895
|
+
* that `pdftotext` cannot round-trip, so they are expressed here as the
|
|
3896
|
+
* Latin-1 supplement range they come from (À-ÿ) rather than transcribed
|
|
3897
|
+
* one by one from mojibake.
|
|
3898
|
+
*/
|
|
3899
|
+
const ALLOWABLE = /^[A-Za-z0-9 \-'/&*#!@+=?$%()_;:",.<>~`^{}[\]|\\À-ÿ]*$/;
|
|
3900
|
+
/** Canadian provinces and territories, per the spec's province table. */
|
|
3901
|
+
const CA_PROVINCES = /* @__PURE__ */ new Set([
|
|
3902
|
+
"AB",
|
|
3903
|
+
"BC",
|
|
3904
|
+
"MB",
|
|
3905
|
+
"NB",
|
|
3906
|
+
"NL",
|
|
3907
|
+
"NS",
|
|
3908
|
+
"NT",
|
|
3909
|
+
"NU",
|
|
3910
|
+
"ON",
|
|
3911
|
+
"PE",
|
|
3912
|
+
"QC",
|
|
3913
|
+
"SK",
|
|
3914
|
+
"YT"
|
|
3915
|
+
]);
|
|
3916
|
+
/** A9A 9A9, with or without the space. */
|
|
3917
|
+
const CA_POSTAL = /^[A-Za-z]\d[A-Za-z][ ]?\d[A-Za-z]\d$/;
|
|
3918
|
+
/** Five digits, or nine as 12345-6789 / 123456789. */
|
|
3919
|
+
const US_ZIP = /^\d{5}(-?\d{4})?$/;
|
|
3920
|
+
/** Deliberately permissive: TRA checks format and length, not deliverability. */
|
|
3921
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3922
|
+
/**
|
|
3923
|
+
* The dev placeholder certification code. Blocked because TRA validates the
|
|
3924
|
+
* SCC against its own registry (20010), so `AB0000` cannot succeed — unlike
|
|
3925
|
+
* the serial number, where the rule is presence only (20013) and `SR_DEV` is
|
|
3926
|
+
* a legitimate, if unlovely, value. Only rules TRA actually applies are
|
|
3927
|
+
* enforced here; being stricter than TRA would refuse returns TRA accepts.
|
|
3928
|
+
*/
|
|
3929
|
+
const PLACEHOLDER_CERT_CODE = "AB0000";
|
|
3930
|
+
function validateAt1Transmitter(info) {
|
|
3931
|
+
const defects = [];
|
|
3932
|
+
const add = (field, traCode, message) => defects.push({
|
|
3933
|
+
field,
|
|
3934
|
+
traCode,
|
|
3935
|
+
message
|
|
3936
|
+
});
|
|
3937
|
+
/** Present, within `max` characters, and drawn from the allowable set. */
|
|
3938
|
+
const text = (value, field, traCode, label, max, required = true) => {
|
|
3939
|
+
const v = (value ?? "").trim();
|
|
3940
|
+
if (v === "") {
|
|
3941
|
+
if (required) add(field, traCode, `${label} must be provided.`);
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
if (v.length > max) add(field, traCode, `${label} must not exceed ${max} characters (is ${v.length}).`);
|
|
3945
|
+
if (!ALLOWABLE.test(v)) add(field, "20145", `${label} contains a character TRA does not accept — see the allowable set in specification §3.2.1.18.`);
|
|
3946
|
+
};
|
|
3947
|
+
if (!info.softwareCertCode?.trim()) add("softwareCertCode", "20010", "The TRA-issued Software Certification Code must be provided.");
|
|
3948
|
+
else if (info.softwareCertCode.trim() === PLACEHOLDER_CERT_CODE) add("softwareCertCode", "20010", `The Software Certification Code is still the development placeholder "${info.softwareCertCode}". Set TRA_SOFTWARE_CERT_CODE to the code TRA issued at certification.`);
|
|
3949
|
+
if (!info.webServiceVersion?.trim()) add("webServiceVersion", "20011", "Web Service Version must be provided.");
|
|
3950
|
+
if (!info.softwareVersion?.trim()) add("softwareVersion", "20012", "Software Version must be provided.");
|
|
3951
|
+
if (!info.serialNumber?.trim()) add("serialNumber", "20013", "Software Serial Number must be provided.");
|
|
3952
|
+
const isThirdParty = info.thirdPartyIndicator === "1";
|
|
3953
|
+
if (info.thirdPartyIndicator !== "1" && info.thirdPartyIndicator !== "2") add("thirdPartyIndicator", "20065", "Third Party Service Provider Indicator must be \"1\" (yes) or \"2\" (no).");
|
|
3954
|
+
text(info.legalName, "legalName", "20070", "Organization Legal Name", 70, isThirdParty);
|
|
3955
|
+
if (isThirdParty && !info.organizationType) add("organizationType", "20080", "Type of Organization must be CORPORATION, PARTNERSHIP or INDIVIDUAL when filing as a third party.");
|
|
3956
|
+
const c = info.contact;
|
|
3957
|
+
text(c?.firstName, "contact.firstName", "20085", "Contact First Name", 35);
|
|
3958
|
+
text(c?.lastName, "contact.lastName", "20090", "Contact Last Name", 35);
|
|
3959
|
+
text(c?.position, "contact.position", "20095", "Position", 40);
|
|
3960
|
+
const phone = (c?.phone ?? "").trim();
|
|
3961
|
+
const phoneDigits = phone.replace(/\D/g, "");
|
|
3962
|
+
if (phone === "") add("contact.phone", "20100", "Phone Number must be provided.");
|
|
3963
|
+
else if (phoneDigits.length < 10 || phoneDigits.length > 15) add("contact.phone", "20100", `Phone Number must be 10 to 15 digits (is ${phoneDigits.length}).`);
|
|
3964
|
+
else if (new Set(phoneDigits).size === 1) add("contact.phone", "20100", `Phone Number "${phone}" is a placeholder, not a number TRA will accept. Set TRANSMITTER_PHONE to the filer's real phone number.`);
|
|
3965
|
+
const email = (c?.email ?? "").trim();
|
|
3966
|
+
if (email === "") add("contact.email", "20110", "Email Address must be provided.");
|
|
3967
|
+
else if (email.length > 50) add("contact.email", "20110", `Email Address must not exceed 50 characters (is ${email.length}).`);
|
|
3968
|
+
else if (!EMAIL.test(email)) add("contact.email", "20110", `Email Address "${email}" is not a valid format.`);
|
|
3969
|
+
const a = info.address;
|
|
3970
|
+
if (isThirdParty && !a) add("address", "20115", "A third-party filer must supply a mailing address.");
|
|
3971
|
+
else if (a) {
|
|
3972
|
+
text(a.street, "address.street", "20115", "Address Line 1", 35);
|
|
3973
|
+
if (a.line2?.trim()) {
|
|
3974
|
+
text(a.line2, "address.line2", "20120", "Address Line 2", 35, false);
|
|
3975
|
+
if (a.line2.trim().toUpperCase() === (a.street ?? "").trim().toUpperCase()) add("address.line2", "20120", "Address Line 2 cannot repeat Address Line 1.");
|
|
3976
|
+
}
|
|
3977
|
+
text(a.city, "address.city", "20125", "City", 35);
|
|
3978
|
+
const country = (a.country ?? "").trim().toUpperCase();
|
|
3979
|
+
const province = (a.province ?? "").trim().toUpperCase();
|
|
3980
|
+
const postal = (a.postalCode ?? "").trim();
|
|
3981
|
+
if (country === "") add("address.country", "20140", "Country must be provided.");
|
|
3982
|
+
else if (!/^[A-Z]{2}$/.test(country)) add("address.country", "20140", `Country must be a two-character code accepted by Canada Post (is "${a.country}").`);
|
|
3983
|
+
if (country === "CA" || country === "US") {
|
|
3984
|
+
if (province === "") add("address.province", "20130", `Province must be provided when the country is ${country}.`);
|
|
3985
|
+
else if (country === "CA" && !CA_PROVINCES.has(province)) add("address.province", "20130", `"${a.province}" is not a Canadian province or territory code.`);
|
|
3986
|
+
else if (country === "US" && !/^[A-Z]{2}$/.test(province)) add("address.province", "20130", `"${a.province}" is not a two-letter US state code.`);
|
|
3987
|
+
if (postal === "") add("address.postalCode", "20135", `Postal Code must be provided when the country is ${country}.`);
|
|
3988
|
+
else if (country === "CA" && !CA_POSTAL.test(postal)) add("address.postalCode", "20135", `Postal Code "${postal}" is not in Canadian A9A 9A9 format.`);
|
|
3989
|
+
else if (country === "US" && !US_ZIP.test(postal)) add("address.postalCode", "20135", `Postal Code "${postal}" is not a 5- or 9-digit US ZIP.`);
|
|
3990
|
+
} else if (country !== "") {
|
|
3991
|
+
if (province !== "") add("address.province", "20130", "Province must be blank when the country is not CA or US.");
|
|
3992
|
+
if (postal !== "") add("address.postalCode", "20135", "Postal Code must be blank when the country is not CA or US.");
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
return defects;
|
|
3996
|
+
}
|
|
3997
|
+
var At1TransmitterInvalidError = class extends Error {
|
|
3998
|
+
defects;
|
|
3999
|
+
constructor(defects) {
|
|
4000
|
+
super(`AT1 cannot be transmitted: the filer (transmitter) configuration would be rejected by TRA — ` + defects.map((d) => `${d.field} [TRA ${d.traCode}]: ${d.message}`).join(" ") + " These are deployment settings, not anything on the return.");
|
|
4001
|
+
this.name = "At1TransmitterInvalidError";
|
|
4002
|
+
this.defects = defects;
|
|
4003
|
+
}
|
|
4004
|
+
};
|
|
4005
|
+
/** Throw unless every filer-detail rule TRA applies is satisfied. */
|
|
4006
|
+
function assertAt1TransmitterValid(info) {
|
|
4007
|
+
const defects = validateAt1Transmitter(info);
|
|
4008
|
+
if (defects.length > 0) throw new At1TransmitterInvalidError(defects);
|
|
4009
|
+
}
|
|
4010
|
+
//#endregion
|
|
4651
4011
|
//#region src/t2/schedules/loss-carryback.ts
|
|
4652
4012
|
var LossCarrybackError = class extends Error {
|
|
4653
4013
|
constructor(message) {
|
|
@@ -5675,33 +5035,33 @@ function computeAlbertaSchedule13(input) {
|
|
|
5675
5035
|
}
|
|
5676
5036
|
//#endregion
|
|
5677
5037
|
//#region src/t2/at1/schedules/schedule16-sred.ts
|
|
5678
|
-
const nn$
|
|
5038
|
+
const nn$25 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
5679
5039
|
/**
|
|
5680
5040
|
* Line 016004 for federal T661 versions from 2008 onward: lines 429 + 431 + 432.
|
|
5681
5041
|
* The 2007-and-earlier form used a single line 430, which can be passed to
|
|
5682
5042
|
* `assistance` directly.
|
|
5683
5043
|
*/
|
|
5684
5044
|
function assistanceFrom(line429, line431, line432) {
|
|
5685
|
-
return nn$
|
|
5045
|
+
return nn$25(line429) + nn$25(line431) + nn$25(line432);
|
|
5686
5046
|
}
|
|
5687
5047
|
function computeAlbertaSchedule16(input) {
|
|
5688
5048
|
const issues = [];
|
|
5689
|
-
const currentYearExpenditures = nn$
|
|
5690
|
-
const assistance = nn$
|
|
5691
|
-
const priorYearItcClaimed = nn$
|
|
5692
|
-
const saleOfCapitalAssetsAndOther = nn$
|
|
5693
|
-
const assistanceRepayments = nn$
|
|
5694
|
-
const openingPoolBalance = nn$
|
|
5695
|
-
const poolTransferredIn = nn$
|
|
5696
|
-
const priorYearItcRecaptured = nn$
|
|
5049
|
+
const currentYearExpenditures = nn$25(input.currentYearExpenditures);
|
|
5050
|
+
const assistance = nn$25(input.assistance);
|
|
5051
|
+
const priorYearItcClaimed = nn$25(input.priorYearItcClaimed);
|
|
5052
|
+
const saleOfCapitalAssetsAndOther = nn$25(input.saleOfCapitalAssetsAndOther);
|
|
5053
|
+
const assistanceRepayments = nn$25(input.assistanceRepayments);
|
|
5054
|
+
const openingPoolBalance = nn$25(input.openingPoolBalance);
|
|
5055
|
+
const poolTransferredIn = nn$25(input.poolTransferredIn);
|
|
5056
|
+
const priorYearItcRecaptured = nn$25(input.priorYearItcRecaptured);
|
|
5697
5057
|
const totalDeductions = assistance + priorYearItcClaimed + saleOfCapitalAssetsAndOther;
|
|
5698
5058
|
const totalAdditions = assistanceRepayments + openingPoolBalance + poolTransferredIn + priorYearItcRecaptured;
|
|
5699
5059
|
const subtotal = currentYearExpenditures - totalDeductions + totalAdditions;
|
|
5700
5060
|
const deductionAvailable = Math.max(0, subtotal);
|
|
5701
5061
|
if (subtotal < 0) issues.push(`Alberta Schedule 16: assistance, prior-year credits and asset sales exceed the SR&ED expenditures by ${-subtotal}. No pool deduction is available and the pool carries forward at nil.`);
|
|
5702
5062
|
const amountClaimed = input.amountClaimed != null ? Math.max(0, Math.min(Math.round(input.amountClaimed), deductionAvailable)) : deductionAvailable;
|
|
5703
|
-
const openingDiffers = input.federalOpeningPoolBalance !== void 0 && nn$
|
|
5704
|
-
const claimDiffers = input.federalAmountClaimed !== void 0 && nn$
|
|
5063
|
+
const openingDiffers = input.federalOpeningPoolBalance !== void 0 && nn$25(input.federalOpeningPoolBalance) !== openingPoolBalance;
|
|
5064
|
+
const claimDiffers = input.federalAmountClaimed !== void 0 && nn$25(input.federalAmountClaimed) !== amountClaimed;
|
|
5705
5065
|
const formRequired = openingDiffers || claimDiffers;
|
|
5706
5066
|
const formPermitted = (input.reportsDifferentAlbertaIncome ?? false) || (input.electsDifferentDiscretionaryAmounts ?? false);
|
|
5707
5067
|
if (formRequired && !formPermitted) issues.push("Alberta Schedule 16: the SR&ED opening pool balance or the deduction claimed 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.");
|
|
@@ -5829,10 +5189,10 @@ const AT1_RESERVE_LINES = Object.freeze({
|
|
|
5829
5189
|
closing: "017077"
|
|
5830
5190
|
}
|
|
5831
5191
|
});
|
|
5832
|
-
const nn$
|
|
5192
|
+
const nn$24 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
5833
5193
|
/** `undefined` falls back to federal; a `0` is a real Alberta override. */
|
|
5834
5194
|
function pick(alberta, federal) {
|
|
5835
|
-
return nn$
|
|
5195
|
+
return nn$24(alberta !== void 0 ? alberta : federal);
|
|
5836
5196
|
}
|
|
5837
5197
|
function computeAlbertaSchedule17(input) {
|
|
5838
5198
|
const issues = [];
|
|
@@ -5935,7 +5295,7 @@ const AT1_DISPOSITION_CATEGORIES = [
|
|
|
5935
5295
|
* Line 018076 then applies the inclusion rate once, to the total.
|
|
5936
5296
|
*/
|
|
5937
5297
|
const SECTION_34_2_GROSS_UP = 2;
|
|
5938
|
-
const nn$
|
|
5298
|
+
const nn$23 = (v) => Math.max(0, v ?? 0);
|
|
5939
5299
|
const num = (v) => v ?? 0;
|
|
5940
5300
|
/** Merge one category: an omitted Alberta field takes federal; a `0` overrides. */
|
|
5941
5301
|
function mergeCategory(federal, alberta) {
|
|
@@ -5946,9 +5306,9 @@ function mergeCategory(federal, alberta) {
|
|
|
5946
5306
|
const o = f.outlays;
|
|
5947
5307
|
return {
|
|
5948
5308
|
totals: {
|
|
5949
|
-
proceeds: nn$
|
|
5309
|
+
proceeds: nn$23(p),
|
|
5950
5310
|
acb: num(c),
|
|
5951
|
-
outlays: nn$
|
|
5311
|
+
outlays: nn$23(o)
|
|
5952
5312
|
},
|
|
5953
5313
|
differs: a.proceeds !== void 0 || a.acb !== void 0
|
|
5954
5314
|
};
|
|
@@ -5970,17 +5330,17 @@ function computeAlbertaSchedule18(input) {
|
|
|
5970
5330
|
const gainOf = (c) => categories.find((x) => x.category === c)?.gain ?? 0;
|
|
5971
5331
|
const personalUseGain = gainOf("personalUse");
|
|
5972
5332
|
const listedPersonalGain = gainOf("listedPersonal");
|
|
5973
|
-
const lppLossesApplied = listedPersonalGain > 0 ? Math.min(nn$
|
|
5333
|
+
const lppLossesApplied = listedPersonalGain > 0 ? Math.min(nn$23(input.unappliedLppLosses), listedPersonalGain) : 0;
|
|
5974
5334
|
const ordinaryGains = gainOf("shares") + gainOf("realEstate") + gainOf("bonds") + gainOf("otherProperties");
|
|
5975
5335
|
const lppComponent = listedPersonalGain >= 0 ? listedPersonalGain - lppLossesApplied : 0;
|
|
5976
|
-
const reserveOpening = nn$
|
|
5977
|
-
const reserveClosing = nn$
|
|
5978
|
-
const netBeforeInclusionRate = ordinaryGains + personalUseGain + lppComponent + nn$
|
|
5336
|
+
const reserveOpening = nn$23(input.albertaReserveOpening ?? input.federalReserveOpening);
|
|
5337
|
+
const reserveClosing = nn$23(input.albertaReserveClosing ?? input.federalReserveClosing);
|
|
5338
|
+
const netBeforeInclusionRate = ordinaryGains + personalUseGain + lppComponent + nn$23(input.capitalGainsDividends) + reserveOpening - reserveClosing - nn$23(input.gainOnDonatedSecurities) - nn$23(input.gainOnDonatedEcologicalLand) + Math.min(nn$23(input.exemptionThreshold), nn$23(input.capitalGainsFromActualProperty)) + 2 * nn$23(input.section342TaxableCapitalGains) - 2 * nn$23(input.section342AllowableCapitalLosses);
|
|
5979
5339
|
const taxableCapitalGain = Math.max(0, Math.round(inclusionRate * netBeforeInclusionRate));
|
|
5980
5340
|
const abil = (input.abilEntries ?? []).map((e) => ({
|
|
5981
5341
|
name: e.name,
|
|
5982
5342
|
kind: e.kind,
|
|
5983
|
-
businessInvestmentLoss: nn$
|
|
5343
|
+
businessInvestmentLoss: nn$23(e.proceeds) - (nn$23(e.acb) + nn$23(e.outlays))
|
|
5984
5344
|
}));
|
|
5985
5345
|
const totalBil = abil.reduce((s, e) => s + Math.min(0, e.businessInvestmentLoss), 0);
|
|
5986
5346
|
const allowableBusinessInvestmentLoss = -Math.round(inclusionRate * -totalBil);
|
|
@@ -6011,7 +5371,7 @@ function computeAlbertaSchedule18(input) {
|
|
|
6011
5371
|
}
|
|
6012
5372
|
//#endregion
|
|
6013
5373
|
//#region src/t2/at1/schedules/schedule20-donations.ts
|
|
6014
|
-
const nn$
|
|
5374
|
+
const nn$22 = (v) => Math.max(0, v ?? 0);
|
|
6015
5375
|
/**
|
|
6016
5376
|
* Alberta figures default to federal, per-field. A `0` entered on the Alberta
|
|
6017
5377
|
* side is a real override and must not be swallowed by the default, so the
|
|
@@ -6036,11 +5396,11 @@ function resolveCarryforward(federal, alberta) {
|
|
|
6036
5396
|
return out;
|
|
6037
5397
|
}
|
|
6038
5398
|
function computeSchedule20(input) {
|
|
6039
|
-
const openingBalance = nn$
|
|
6040
|
-
const expired = nn$
|
|
6041
|
-
const transferredIn = nn$
|
|
6042
|
-
const currentYearGifts = nn$
|
|
6043
|
-
const acquisitionOfControlAdjustment = nn$
|
|
5399
|
+
const openingBalance = nn$22(input.openingBalance);
|
|
5400
|
+
const expired = nn$22(input.expired);
|
|
5401
|
+
const transferredIn = nn$22(input.transferredIn);
|
|
5402
|
+
const currentYearGifts = nn$22(input.currentYearGifts);
|
|
5403
|
+
const acquisitionOfControlAdjustment = nn$22(input.acquisitionOfControlAdjustment);
|
|
6044
5404
|
const availableBeforeClaim = Math.max(0, openingBalance - expired + transferredIn + currentYearGifts - acquisitionOfControlAdjustment);
|
|
6045
5405
|
const issues = [];
|
|
6046
5406
|
let ceiling;
|
|
@@ -6111,12 +5471,12 @@ function computeSchedule20(input) {
|
|
|
6111
5471
|
const AT1_DONATION_INCOME_RATE = .75;
|
|
6112
5472
|
/** Gains and recapture on gifted capital property add back at 25%. */
|
|
6113
5473
|
const AT1_DONATION_GAIN_RATE = .25;
|
|
6114
|
-
const nn$
|
|
5474
|
+
const nn$21 = (v) => Math.max(0, v ?? 0);
|
|
6115
5475
|
function computeDonationMaximum(input) {
|
|
6116
5476
|
const incomeComponent = Math.round(AT1_DONATION_INCOME_RATE * Math.max(0, input.albertaNetIncomeForTax));
|
|
6117
|
-
const lesserOfProceedsAndCost = Math.min(nn$
|
|
6118
|
-
const allowableRecapture = Math.min(nn$
|
|
6119
|
-
const gainsComponent = Math.round(AT1_DONATION_GAIN_RATE * (nn$
|
|
5477
|
+
const lesserOfProceedsAndCost = Math.min(nn$21(input.proceedsNetOfOutlays), nn$21(input.capitalCost));
|
|
5478
|
+
const allowableRecapture = Math.min(nn$21(input.recaptureOnGifts), lesserOfProceedsAndCost);
|
|
5479
|
+
const gainsComponent = Math.round(AT1_DONATION_GAIN_RATE * (nn$21(input.taxableCapitalGainsOnGifts) + nn$21(input.deemedGiftGains) + allowableRecapture));
|
|
6120
5480
|
return {
|
|
6121
5481
|
incomeComponent,
|
|
6122
5482
|
lesserOfProceedsAndCost,
|
|
@@ -6209,6 +5569,37 @@ function computeLossSchedule(input) {
|
|
|
6209
5569
|
};
|
|
6210
5570
|
}
|
|
6211
5571
|
//#endregion
|
|
5572
|
+
//#region src/t2/at1/schedules/schedule21-rife.ts
|
|
5573
|
+
function computeRifeContinuity(input = {}) {
|
|
5574
|
+
const openingBalance = Math.max(0, input.openingBalance ?? 0);
|
|
5575
|
+
const transferredOnWindUp = Math.max(0, input.transferredOnWindUp ?? 0);
|
|
5576
|
+
const acquisitionOfControlAdjustment = Math.max(0, input.acquisitionOfControlAdjustment ?? 0);
|
|
5577
|
+
const currentYearRife = Math.max(0, input.currentYearRife ?? 0);
|
|
5578
|
+
const excessCapacity = Math.max(0, input.excessCapacity ?? 0);
|
|
5579
|
+
const receivedCapacity = Math.max(0, input.receivedCapacity ?? 0);
|
|
5580
|
+
const rifeFromPreviousYears = Math.max(0, openingBalance + transferredOnWindUp - acquisitionOfControlAdjustment);
|
|
5581
|
+
const totalCapacity = excessCapacity + receivedCapacity;
|
|
5582
|
+
const maxDeductible = Math.max(0, Math.min(rifeFromPreviousYears, totalCapacity));
|
|
5583
|
+
const issues = [];
|
|
5584
|
+
const requestedClaim = Math.max(0, input.deductedClaim ?? maxDeductible);
|
|
5585
|
+
if (requestedClaim > maxDeductible) issues.push(`Line 240 (RIFE deducted, ${requestedClaim}) cannot exceed line 350 (${maxDeductible}). Capped at ${maxDeductible}.`);
|
|
5586
|
+
const deducted = Math.min(requestedClaim, maxDeductible);
|
|
5587
|
+
return {
|
|
5588
|
+
openingBalance,
|
|
5589
|
+
transferredOnWindUp,
|
|
5590
|
+
acquisitionOfControlAdjustment,
|
|
5591
|
+
currentYearRife,
|
|
5592
|
+
rifeFromPreviousYears,
|
|
5593
|
+
excessCapacity,
|
|
5594
|
+
receivedCapacity,
|
|
5595
|
+
totalCapacity,
|
|
5596
|
+
maxDeductible,
|
|
5597
|
+
deducted,
|
|
5598
|
+
closingBalance: openingBalance + transferredOnWindUp - acquisitionOfControlAdjustment + currentYearRife - deducted,
|
|
5599
|
+
issues
|
|
5600
|
+
};
|
|
5601
|
+
}
|
|
5602
|
+
//#endregion
|
|
6212
5603
|
//#region src/t2/at1/schedules/schedule21-year-of-origin.ts
|
|
6213
5604
|
function sumRows(rows) {
|
|
6214
5605
|
const sum = (f) => rows.reduce((s, r) => s + f(r), 0);
|
|
@@ -6458,35 +5849,270 @@ const T2_CERTIFICATION_FIXTURES = [
|
|
|
6458
5849
|
}
|
|
6459
5850
|
];
|
|
6460
5851
|
//#endregion
|
|
6461
|
-
//#region src/t2/
|
|
5852
|
+
//#region src/t2/filing/t2-schedule-line-items.ts
|
|
6462
5853
|
/**
|
|
6463
|
-
* Federal
|
|
6464
|
-
*
|
|
6465
|
-
*
|
|
6466
|
-
*
|
|
5854
|
+
* Federal T2 — the per-schedule, per-line breakdown of a computed return.
|
|
5855
|
+
*
|
|
5856
|
+
* Alberta has had this since the AT1 filing path was built
|
|
5857
|
+
* (`at1-schedule-line-items.ts`): every schedule's result turned into a flat
|
|
5858
|
+
* list of `{lineItemId, value}` pairs, persisted with the computed return, and
|
|
5859
|
+
* read back by the paper Form Views so a preparer sees the actual figure
|
|
5860
|
+
* against the actual line. Federal had nothing equivalent. Its computed return
|
|
5861
|
+
* carried only summary fields under symbolic names — `netIncomeForTax`,
|
|
5862
|
+
* `ccaClaimed` — which no form can be keyed by, so every federal paper view
|
|
5863
|
+
* rendered "not available" against every computed line of every schedule.
|
|
5864
|
+
*
|
|
5865
|
+
* ── The rule this file follows, and why it is strict ────────────────────────
|
|
5866
|
+
*
|
|
5867
|
+
* A value is emitted ONLY where the line it belongs on is recorded in code:
|
|
5868
|
+
* carried in the data (Schedule 1's own `Schedule1Line.line`), exported as a
|
|
5869
|
+
* named constant (`SCHEDULE_8_CCA_LINE`), or stated in the result type's own
|
|
5870
|
+
* doc comment. Nothing here is a line number typed from memory or inferred from
|
|
5871
|
+
* a caption that looks close.
|
|
5872
|
+
*
|
|
5873
|
+
* The reason is the failure this repository keeps hitting: a figure filed under
|
|
5874
|
+
* the wrong line is not a missing figure, it is a WRONG return, and it looks
|
|
5875
|
+
* completely correct on screen. Schedule 4's jacket references claimed lines 150
|
|
5876
|
+
* and 250 where the form prints 130 and 225; Schedule 33's own result type said
|
|
5877
|
+
* line 690 where the form has 790. Both were plausible, both were wrong, and
|
|
5878
|
+
* neither was caught by a type.
|
|
5879
|
+
*
|
|
5880
|
+
* So a schedule whose result has no recorded line mapping produces NO entry
|
|
5881
|
+
* rather than a guessed one, and the paper view keeps saying "not available"
|
|
5882
|
+
* for it. That is the honest state, and it is visibly incomplete, which is what
|
|
5883
|
+
* makes it safe to extend one verified line at a time.
|
|
5884
|
+
*
|
|
5885
|
+
* ── Identifier shape ────────────────────────────────────────────────────────
|
|
5886
|
+
*
|
|
5887
|
+
* Six characters: the three-digit CRA line, then a three-digit occurrence.
|
|
5888
|
+
* Alberta uses nine (`SSSFFFOOO`) because a TRA line item id names its schedule
|
|
5889
|
+
* too; a federal line number is already unique across the whole return, so the
|
|
5890
|
+
* schedule is carried once on the envelope instead of repeated on every row.
|
|
5891
|
+
* The trailing occurrence exists for the grid forms, where one line number
|
|
5892
|
+
* repeats down a column — Schedule 8 has one row per capital cost allowance
|
|
5893
|
+
* class, all of them line 217.
|
|
5894
|
+
*/
|
|
5895
|
+
/** `LLLOOO` — three-digit CRA line, three-digit occurrence. Occurrence is 1-based. */
|
|
5896
|
+
function t2LineItemId(line, occurrence = 1) {
|
|
5897
|
+
return `${line}${String(occurrence).padStart(3, "0")}`;
|
|
5898
|
+
}
|
|
5899
|
+
/**
|
|
5900
|
+
* Split a federal line item id back into its line and occurrence.
|
|
6467
5901
|
*
|
|
6468
|
-
*
|
|
6469
|
-
*
|
|
6470
|
-
*
|
|
6471
|
-
*
|
|
6472
|
-
|
|
5902
|
+
* Returns `undefined` for anything that is not six digits, so a caller handed
|
|
5903
|
+
* an Alberta nine-digit id (or a malformed one) drops it rather than reading
|
|
5904
|
+
* the first three characters as a line number and displaying a figure against
|
|
5905
|
+
* a line it does not belong to.
|
|
5906
|
+
*/
|
|
5907
|
+
function parseT2LineItemId(lineItemId) {
|
|
5908
|
+
if (!/^\d{6}$/.test(lineItemId)) return void 0;
|
|
5909
|
+
return {
|
|
5910
|
+
line: lineItemId.slice(0, 3),
|
|
5911
|
+
occurrence: Number(lineItemId.slice(3, 6))
|
|
5912
|
+
};
|
|
5913
|
+
}
|
|
5914
|
+
/** Drop entries with no value, so an absent figure stays absent rather than becoming 0. */
|
|
5915
|
+
const present = (values) => values.filter((v) => v !== null);
|
|
5916
|
+
/** One value, or `null` when the figure was not computed. */
|
|
5917
|
+
const at = (line, value, occurrence = 1) => value === void 0 ? null : {
|
|
5918
|
+
lineItemId: t2LineItemId(line, occurrence),
|
|
5919
|
+
value
|
|
5920
|
+
};
|
|
5921
|
+
/**
|
|
5922
|
+
* Schedule 1 — the only federal schedule that needs no line table here.
|
|
6473
5923
|
*
|
|
6474
|
-
*
|
|
6475
|
-
*
|
|
5924
|
+
* `Schedule1Line` carries its own `line`, and has since the schedule was built,
|
|
5925
|
+
* precisely because "a reconciling item recorded only as a description with an
|
|
5926
|
+
* amount has nowhere to go on the wire". Reading it back out is the whole job.
|
|
5927
|
+
*
|
|
5928
|
+
* A line may legitimately repeat: the form provides open rows (135, 295, 395,
|
|
5929
|
+
* 495) for items it does not name, and a return can carry several. Those get
|
|
5930
|
+
* successive occurrences rather than being summed, so the paper view can show
|
|
5931
|
+
* each on its own row. A line with no number is skipped — `assertSchedule1Fileable`
|
|
5932
|
+
* is what refuses the return over it, not this.
|
|
6476
5933
|
*/
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
5934
|
+
function schedule1Values(r) {
|
|
5935
|
+
const values = [];
|
|
5936
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5937
|
+
for (const l of [...r.additions, ...r.deductions]) {
|
|
5938
|
+
if (!l.line) continue;
|
|
5939
|
+
const occurrence = (seen.get(l.line) ?? 0) + 1;
|
|
5940
|
+
seen.set(l.line, occurrence);
|
|
5941
|
+
values.push({
|
|
5942
|
+
lineItemId: t2LineItemId(l.line, occurrence),
|
|
5943
|
+
value: l.amount
|
|
5944
|
+
});
|
|
5945
|
+
}
|
|
5946
|
+
values.push({
|
|
5947
|
+
lineItemId: t2LineItemId("500"),
|
|
5948
|
+
value: r.totalAdditions
|
|
5949
|
+
});
|
|
5950
|
+
values.push({
|
|
5951
|
+
lineItemId: t2LineItemId("510"),
|
|
5952
|
+
value: r.totalDeductions
|
|
5953
|
+
});
|
|
5954
|
+
return {
|
|
5955
|
+
scheduleId: "T2SCH1",
|
|
5956
|
+
values
|
|
5957
|
+
};
|
|
5958
|
+
}
|
|
5959
|
+
/** Schedule 2 — the donation claim. Only line 210 is a named constant. */
|
|
5960
|
+
function schedule2Values(r) {
|
|
5961
|
+
return {
|
|
5962
|
+
scheduleId: "T2SCH2",
|
|
5963
|
+
values: present([at("210", r.donationsClaimed)])
|
|
5964
|
+
};
|
|
5965
|
+
}
|
|
5966
|
+
/**
|
|
5967
|
+
* Schedule 7 — the adjusted aggregate investment income that grinds the
|
|
5968
|
+
* business limit. Line 745 is exported by the form module; the rest of Part 2's
|
|
5969
|
+
* working lines are not recorded anywhere, so they are not emitted.
|
|
5970
|
+
*/
|
|
5971
|
+
function schedule7Values(r) {
|
|
5972
|
+
return {
|
|
5973
|
+
scheduleId: "T2SCH7",
|
|
5974
|
+
values: present([at("745", r.adjustedAggregateInvestmentIncome)])
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
/**
|
|
5978
|
+
* Schedule 8 — capital cost allowance, one occurrence per class.
|
|
5979
|
+
*
|
|
5980
|
+
* Only the three lines the form module exports are emitted: recapture (213),
|
|
5981
|
+
* terminal loss (215) and the claim (217). The grid's other twenty columns are
|
|
5982
|
+
* either intermediate arithmetic the form shows without numbering, or columns
|
|
5983
|
+
* whose number this package has not recorded — see `SCHEDULE_8_COLUMNS`.
|
|
5984
|
+
*/
|
|
5985
|
+
function schedule8Values(r) {
|
|
5986
|
+
const values = [];
|
|
5987
|
+
r.classes.forEach((c, i) => {
|
|
5988
|
+
const occurrence = i + 1;
|
|
5989
|
+
values.push(...present([
|
|
5990
|
+
at("213", c.recapture, occurrence),
|
|
5991
|
+
at("215", c.terminalLoss, occurrence),
|
|
5992
|
+
at("217", c.ccaClaimed, occurrence)
|
|
5993
|
+
]));
|
|
5994
|
+
});
|
|
5995
|
+
return {
|
|
5996
|
+
scheduleId: "T2SCH8",
|
|
5997
|
+
values
|
|
5998
|
+
};
|
|
5999
|
+
}
|
|
6000
|
+
/**
|
|
6001
|
+
* Schedule 21 — the two foreign tax credits.
|
|
6002
|
+
*
|
|
6003
|
+
* These land on different jacket lines (632 non-business, 636 business) and the
|
|
6004
|
+
* form module's own doc comment warns that swapping them "is not cosmetic".
|
|
6005
|
+
* Both come from named constants for exactly that reason.
|
|
6006
|
+
*/
|
|
6007
|
+
function schedule21Values(r) {
|
|
6008
|
+
return {
|
|
6009
|
+
scheduleId: "T2SCH21",
|
|
6010
|
+
values: present([at("180", r.nonBusinessFtc), at("280", r.businessFtc)])
|
|
6011
|
+
};
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Schedule 33 — taxable capital.
|
|
6015
|
+
*
|
|
6016
|
+
* The four figures whose lines the result type states: capital (190), the
|
|
6017
|
+
* investment allowance (490), taxable capital (500) and taxable capital
|
|
6018
|
+
* employed in Canada (790, from the form module's constant — the result type's
|
|
6019
|
+
* comment used to say 690, which is not a line of this form).
|
|
6020
|
+
*/
|
|
6021
|
+
function schedule33Values(r) {
|
|
6022
|
+
return {
|
|
6023
|
+
scheduleId: "T2SCH33",
|
|
6024
|
+
values: present([
|
|
6025
|
+
at("190", r.capital),
|
|
6026
|
+
at("490", r.investmentAllowance),
|
|
6027
|
+
at("500", r.taxableCapital),
|
|
6028
|
+
at("790", r.taxableCapitalEmployedInCanada)
|
|
6029
|
+
])
|
|
6030
|
+
};
|
|
6031
|
+
}
|
|
6032
|
+
/** Schedule 53 — the closing general rate income pool. */
|
|
6033
|
+
function schedule53Values(r) {
|
|
6034
|
+
return {
|
|
6035
|
+
scheduleId: "T2SCH53",
|
|
6036
|
+
values: present([at("590", r.closingGrip)])
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
/**
|
|
6040
|
+
* Schedule 55 — Part III.1 tax.
|
|
6041
|
+
*
|
|
6042
|
+
* The result type states "20% of amount B (s.185.1(1)(a)) — line 190 / line
|
|
6043
|
+
* 290". Two lines for one figure because the form splits by corporation type:
|
|
6044
|
+
* Part 1 (line 190) for CCPCs and deposit insurance corporations, Part 2 (line
|
|
6045
|
+
* 290) for everyone else. Nothing in the result says which part applied, so the
|
|
6046
|
+
* base tax is emitted against BOTH and the paper view shows it under whichever
|
|
6047
|
+
* part the reader is looking at, rather than this file picking one and being
|
|
6048
|
+
* wrong for half of all filers.
|
|
6049
|
+
*/
|
|
6050
|
+
function schedule55Values(r) {
|
|
6051
|
+
return {
|
|
6052
|
+
scheduleId: "T2SCH55",
|
|
6053
|
+
values: present([at("190", r.baseTax), at("290", r.baseTax)])
|
|
6054
|
+
};
|
|
6055
|
+
}
|
|
6056
|
+
/** The jacket — the three headline figures the form module names. */
|
|
6057
|
+
function jacketValues(r) {
|
|
6058
|
+
return {
|
|
6059
|
+
scheduleId: "T2",
|
|
6060
|
+
values: present([
|
|
6061
|
+
at("300", r.netIncomeForTax),
|
|
6062
|
+
at("360", r.taxableIncome),
|
|
6063
|
+
at("770", r.totalFederalTax)
|
|
6064
|
+
])
|
|
6065
|
+
};
|
|
6066
|
+
}
|
|
6067
|
+
/**
|
|
6068
|
+
* Every schedule's filed line items for one computed federal return.
|
|
6069
|
+
*
|
|
6070
|
+
* A schedule that was not computed is ABSENT, not present and empty — the same
|
|
6071
|
+
* rule Alberta's assembler follows. An empty schedule on a return says "this
|
|
6072
|
+
* schedule was completed and everything on it is nil", which is a different
|
|
6073
|
+
* statement from "this schedule does not apply", and only one of them is true.
|
|
6074
|
+
*/
|
|
6075
|
+
function federalSchedulePayloads(r) {
|
|
6076
|
+
const payloads = [jacketValues(r), schedule1Values(r.schedule1)];
|
|
6077
|
+
if (r.donations) payloads.push(schedule2Values(r.donations));
|
|
6078
|
+
if (r.adjustedAggregateInvestmentIncomeSchedule) payloads.push(schedule7Values(r.adjustedAggregateInvestmentIncomeSchedule));
|
|
6079
|
+
if (r.cca) payloads.push(schedule8Values(r.cca));
|
|
6080
|
+
if (r.foreignTaxCredit) payloads.push(schedule21Values(r.foreignTaxCredit));
|
|
6081
|
+
if (r.taxableCapitalSchedule) payloads.push(schedule33Values(r.taxableCapitalSchedule));
|
|
6082
|
+
if (r.grip) payloads.push(schedule53Values(r.grip));
|
|
6083
|
+
if (r.partIII1) payloads.push(schedule55Values(r.partIII1));
|
|
6084
|
+
return payloads.filter((p) => p.values.length > 0);
|
|
6085
|
+
}
|
|
6086
|
+
//#endregion
|
|
6087
|
+
//#region src/t2/rates/corporate-rates.ts
|
|
6088
|
+
/**
|
|
6089
|
+
* Federal corporate-tax rate table — the Part I rate stack, the small-business
|
|
6090
|
+
* limit + grind bands, and the refundable (Part IV / AAII) fractions. This is
|
|
6091
|
+
* the DATA the engine's pure formulas consume; a host supplies future years or
|
|
6092
|
+
* overrides these reference values via the {@link CORP_TAX_RATE_BOOK} seam.
|
|
6093
|
+
*
|
|
6094
|
+
* Rate stack (ITA Part I):
|
|
6095
|
+
* basic rate 38% of taxable income
|
|
6096
|
+
* − federal abatement 10% of taxable income earned in a province (s.124) → 28%
|
|
6097
|
+
* − small-business ded. 19% of income eligible for the SBD (CCPC) → 9%
|
|
6098
|
+
* − general rate red. 13% of full-rate income (not SBD/AAII/M&P) → 15%
|
|
6099
|
+
*
|
|
6100
|
+
* Never mutate a shipped year — add a new {@link RateBookEntry} for the new
|
|
6101
|
+
* year (in the host book) so prior-year returns stay reproducible.
|
|
6102
|
+
*/
|
|
6103
|
+
const CORP_TAX_2024 = Object.freeze({
|
|
6104
|
+
BASIC_RATE: .38,
|
|
6105
|
+
FEDERAL_ABATEMENT: .1,
|
|
6106
|
+
SBD_RATE: .19,
|
|
6107
|
+
GENERAL_RATE_REDUCTION: .13,
|
|
6108
|
+
BUSINESS_LIMIT: 5e5,
|
|
6109
|
+
TC_GRIND_LOWER: 1e7,
|
|
6110
|
+
TC_GRIND_UPPER: 5e7,
|
|
6111
|
+
AAII_THRESHOLD: 5e4,
|
|
6112
|
+
AAII_REDUCTION_PER_DOLLAR: 5,
|
|
6113
|
+
PART_IV_RATE: 115 / 300,
|
|
6114
|
+
REFUNDABLE_PART_I_RATE: 92 / 300,
|
|
6115
|
+
CAPITAL_GAINS_INCLUSION_RATE: .5,
|
|
6490
6116
|
GRIP_FACTOR: .72,
|
|
6491
6117
|
SRED_ITC_ENHANCED_RATE: .35,
|
|
6492
6118
|
SRED_ITC_BASIC_RATE: .15,
|
|
@@ -6560,12 +6186,12 @@ function computeSBD(input, rates = CORP_TAX_2024) {
|
|
|
6560
6186
|
sbdAmount: Math.round(rates.SBD_RATE * sbdIncome)
|
|
6561
6187
|
};
|
|
6562
6188
|
}
|
|
6563
|
-
const nn$
|
|
6189
|
+
const nn$20 = (v) => v ?? 0;
|
|
6564
6190
|
function computeAggregateInvestmentIncome(input) {
|
|
6565
|
-
const amountA = nn$
|
|
6566
|
-
const amountB = Math.max(0, nn$
|
|
6567
|
-
const amountC = nn$
|
|
6568
|
-
const amountD = nn$
|
|
6191
|
+
const amountA = nn$20(input.allowableCapitalLosses) + nn$20(input.netCapitalLossesClaimed);
|
|
6192
|
+
const amountB = Math.max(0, nn$20(input.taxableCapitalGains) - amountA);
|
|
6193
|
+
const amountC = nn$20(input.exemptIncome) + nn$20(input.agriInvestFundReceived) + nn$20(input.taxableDividendsDeductible) + nn$20(input.trustPropertyIncome);
|
|
6194
|
+
const amountD = nn$20(input.incomeFromProperty) - amountC;
|
|
6569
6195
|
const amountE = amountB + amountD;
|
|
6570
6196
|
return {
|
|
6571
6197
|
amountA,
|
|
@@ -6573,20 +6199,20 @@ function computeAggregateInvestmentIncome(input) {
|
|
|
6573
6199
|
amountC,
|
|
6574
6200
|
amountD,
|
|
6575
6201
|
amountE,
|
|
6576
|
-
aggregateInvestmentIncome: Math.max(0, amountE - nn$
|
|
6202
|
+
aggregateInvestmentIncome: Math.max(0, amountE - nn$20(input.lossesFromProperty))
|
|
6577
6203
|
};
|
|
6578
6204
|
}
|
|
6579
6205
|
function computeAdjustedAggregateInvestmentIncome(input) {
|
|
6580
|
-
const amountF = Math.max(0, nn$
|
|
6581
|
-
const amountG = nn$
|
|
6582
|
-
const amountH = nn$
|
|
6206
|
+
const amountF = Math.max(0, nn$20(input.taxableCapitalGains) - nn$20(input.allowableCapitalLosses));
|
|
6207
|
+
const amountG = nn$20(input.exemptIncome) + nn$20(input.agriInvestFundReceived) + nn$20(input.dividendsFromConnectedCorporations) + nn$20(input.trustPropertyIncome);
|
|
6208
|
+
const amountH = nn$20(input.incomeFromProperty) - amountG;
|
|
6583
6209
|
const amountI = amountF + amountH;
|
|
6584
6210
|
return {
|
|
6585
6211
|
amountF,
|
|
6586
6212
|
amountG,
|
|
6587
6213
|
amountH,
|
|
6588
6214
|
amountI,
|
|
6589
|
-
adjustedAggregateInvestmentIncome: Math.max(0, amountI - nn$
|
|
6215
|
+
adjustedAggregateInvestmentIncome: Math.max(0, amountI - nn$20(input.lossesFromProperty) + nn$20(input.subsection91_4Deduction))
|
|
6590
6216
|
};
|
|
6591
6217
|
}
|
|
6592
6218
|
//#endregion
|
|
@@ -6827,6 +6453,165 @@ function isSchedule5Province(code) {
|
|
|
6827
6453
|
return code in PROVINCE_RATES_2024;
|
|
6828
6454
|
}
|
|
6829
6455
|
//#endregion
|
|
6456
|
+
//#region src/t2/schedules/eifel-adjusted-taxable-income.ts
|
|
6457
|
+
/**
|
|
6458
|
+
* ITA subsection 18.2(1) — **adjusted taxable income**, the base the EIFEL
|
|
6459
|
+
* ceiling is computed on.
|
|
6460
|
+
*
|
|
6461
|
+
* `eifel-limitation.ts` took this as a required input because deriving it
|
|
6462
|
+
* partially would produce a plausible figure from an incomplete definition. This
|
|
6463
|
+
* derives it, and is explicit about the components it does and does not cover.
|
|
6464
|
+
*
|
|
6465
|
+
* ── What it is ──────────────────────────────────────────────────────────────
|
|
6466
|
+
*
|
|
6467
|
+
* An EBITDA-like measure, built from taxable income by adding back the things the
|
|
6468
|
+
* regime is measuring against and removing the things that would double-count.
|
|
6469
|
+
*
|
|
6470
|
+
* ATI = A + B − C
|
|
6471
|
+
*
|
|
6472
|
+
* A = D − E the income base
|
|
6473
|
+
* B the ADD-BACKS
|
|
6474
|
+
* C the REDUCTIONS
|
|
6475
|
+
*
|
|
6476
|
+
* **The add-backs include the interest and financing expenses themselves.** That
|
|
6477
|
+
* is the point of the measure and the thing to hold on to: the ceiling is a
|
|
6478
|
+
* percentage of income computed *before* the very expenses being limited, so a
|
|
6479
|
+
* corporation cannot shrink its own ceiling by borrowing more.
|
|
6480
|
+
*
|
|
6481
|
+
* ── A — the income base (D − E) ─────────────────────────────────────────────
|
|
6482
|
+
*
|
|
6483
|
+
* **D** is taxable income for the year, determined **without regard to** s.18.2(2)
|
|
6484
|
+
* itself, paragraphs 12(1)(l.2) and 111(1)(a.1), and clause 95(2)(f.11)(ii)(D) —
|
|
6485
|
+
* a non-resident uses taxable income earned in Canada on the same basis. The
|
|
6486
|
+
* circularity is deliberate: the limitation cannot be an input to its own base.
|
|
6487
|
+
*
|
|
6488
|
+
* **E** subtracts the year's non-capital loss on the same basis, any amount
|
|
6489
|
+
* claimed under paragraph 111(1)(a) that did not actually reduce taxable income,
|
|
6490
|
+
* and a controlled-foreign-affiliate component (`T × U ÷ V`).
|
|
6491
|
+
*
|
|
6492
|
+
* ── B — the add-backs ───────────────────────────────────────────────────────
|
|
6493
|
+
*
|
|
6494
|
+
* (a) interest and financing expenses for the year
|
|
6495
|
+
* (b) capital cost allowance and resource deductions — paragraph 20(1)(a),
|
|
6496
|
+
* 59.1(a) and subsections 66(4), 66.1(2)/(3), 66.2(2), 66.21(4), 66.4(2),
|
|
6497
|
+
* 66.7(1)-(5)
|
|
6498
|
+
* (c) terminal losses — subsection 20(16)
|
|
6499
|
+
* (d) the taxpayer's share of a partnership's 20(1)(a) and 20(16) deductions
|
|
6500
|
+
* (e) the portion of a paragraph 111(1)(e) limited-partnership-loss claim
|
|
6501
|
+
* attributable to those amounts
|
|
6502
|
+
*
|
|
6503
|
+
* ── C — the reductions ──────────────────────────────────────────────────────
|
|
6504
|
+
*
|
|
6505
|
+
* (a) interest and financing revenues
|
|
6506
|
+
* (b) recapture — subsection 13(1)
|
|
6507
|
+
* (c) the taxpayer's share of a partnership's 13(1) inclusion
|
|
6508
|
+
* (d) resource inclusions — subsections 59(1), 59(3.2), paragraph 59.1(b)
|
|
6509
|
+
* (e) for a corporation, a grossed-up foreign tax credit amount:
|
|
6510
|
+
* **100/28** of what would be deductible under s.126(1), and
|
|
6511
|
+
* the s.126(2) amounts times the relevant factor
|
|
6512
|
+
*
|
|
6513
|
+
* ── Not modelled ────────────────────────────────────────────────────────────
|
|
6514
|
+
*
|
|
6515
|
+
* The trust variant of C(e), and the later paragraphs of B and C dealing with
|
|
6516
|
+
* foreign affiliate income and exempt interest. Each is available as an explicit
|
|
6517
|
+
* `otherAdditions` / `otherReductions` input rather than silently omitted, so a
|
|
6518
|
+
* preparer with one of those amounts can still arrive at the right figure and the
|
|
6519
|
+
* engine does not pretend the definition is shorter than it is.
|
|
6520
|
+
*
|
|
6521
|
+
* Source: `research/sources/legislation/ITA-section-18.2-EIFEL.txt`.
|
|
6522
|
+
*
|
|
6523
|
+
* Pure, whole dollars.
|
|
6524
|
+
*/
|
|
6525
|
+
/** C(e)(i) — s.126(1) amounts are grossed up by 100/28. */
|
|
6526
|
+
const FOREIGN_TAX_CREDIT_GROSS_UP = 100 / 28;
|
|
6527
|
+
const nn$19 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
6528
|
+
function computeAdjustedTaxableIncome(input) {
|
|
6529
|
+
const issues = [];
|
|
6530
|
+
const e = nn$19(input.nonCapitalLossForYear) + nn$19(input.lossClaimNotReducingTaxableIncome) + nn$19(input.foreignAccrualPropertyLossComponent);
|
|
6531
|
+
const incomeBase = Math.round(input.taxableIncome) - e;
|
|
6532
|
+
const totalAdditions = nn$19(input.interestAndFinancingExpenses) + nn$19(input.capitalCostAllowance) + nn$19(input.resourceDeductions) + nn$19(input.terminalLoss) + nn$19(input.partnershipCapitalAndTerminalShare) + nn$19(input.limitedPartnershipLossPortion) + nn$19(input.partVI1TaxDeduction) + nn$19(input.lossPortionDerivedFromIfe) + nn$19(input.exemptIfeActivityLoss) + nn$19(input.otherAdditions);
|
|
6533
|
+
const totalReductions = nn$19(input.interestAndFinancingRevenues) + nn$19(input.recapture) + nn$19(input.partnershipRecaptureShare) + nn$19(input.resourceInclusions) + Math.round(FOREIGN_TAX_CREDIT_GROSS_UP * nn$19(input.section126_1ForeignTaxCredits)) + nn$19(input.section126_2GrossedUp) + nn$19(input.exemptIfeActivityIncome) + nn$19(input.otherReductions);
|
|
6534
|
+
const adjustedTaxableIncome = incomeBase + totalAdditions - totalReductions;
|
|
6535
|
+
if (adjustedTaxableIncome < 0) issues.push(`EIFEL: adjusted taxable income is negative (${adjustedTaxableIncome}), so the ceiling is nil and every interest and financing expense is denied. Check that the loss and add-back figures are complete before filing on that basis.`);
|
|
6536
|
+
if (input.interestAndFinancingExpenses === void 0) issues.push("EIFEL: no interest and financing expenses were added back to adjusted taxable income. They are paragraph (a) of the add-backs, and omitting them understates the ceiling — which denies more expense than the rules require.");
|
|
6537
|
+
return {
|
|
6538
|
+
incomeBase,
|
|
6539
|
+
totalAdditions,
|
|
6540
|
+
totalReductions,
|
|
6541
|
+
adjustedTaxableIncome,
|
|
6542
|
+
issues
|
|
6543
|
+
};
|
|
6544
|
+
}
|
|
6545
|
+
//#endregion
|
|
6546
|
+
//#region src/t2/schedules/eifel-capacity.ts
|
|
6547
|
+
const nn$18 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
6548
|
+
/**
|
|
6549
|
+
* Part 2O on its own — the total of lines 159 to 162.
|
|
6550
|
+
*
|
|
6551
|
+
* Exported separately because of the order the form imposes: line 159 is the
|
|
6552
|
+
* denial from Part 2L, which cannot be computed until Parts 2G-2J have supplied
|
|
6553
|
+
* `excessReceivedCapacity` and `absorbedCapacity` to Part 2K. A caller running
|
|
6554
|
+
* the whole chain therefore computes capacity, then the limitation, then calls
|
|
6555
|
+
* this — rather than computing capacity twice.
|
|
6556
|
+
*/
|
|
6557
|
+
function computeRifeUnderSubsection111_8(input) {
|
|
6558
|
+
return nn$18(input.excessInterestAndFinancingExpenses) + nn$18(input.partnershipIfeAddBack) + nn$18(input.clause95FapiAmountI) + nn$18(input.clause95FapiAmountII);
|
|
6559
|
+
}
|
|
6560
|
+
function computeEifelCapacity(input) {
|
|
6561
|
+
const issues = [];
|
|
6562
|
+
const ati = Math.round(input.adjustedTaxableIncome);
|
|
6563
|
+
const atiFloored = Math.max(0, ati);
|
|
6564
|
+
const ife = nn$18(input.interestAndFinancingExpenses);
|
|
6565
|
+
const ifr = nn$18(input.interestAndFinancingRevenues);
|
|
6566
|
+
const ratio = input.ratioOfPermissibleExpenses;
|
|
6567
|
+
const usedGroupRatio = input.hasGroupRatioElection === true;
|
|
6568
|
+
const receivedCapacity = (input.receivedCapacity ?? []).reduce((s, r) => s + nn$18(r.amount), 0);
|
|
6569
|
+
const permittedAmount = usedGroupRatio ? 0 : Math.round(atiFloored * ratio);
|
|
6570
|
+
const revenueOverExpense = usedGroupRatio ? 0 : Math.max(0, ifr - ife);
|
|
6571
|
+
const negativeAtiAbsolute = usedGroupRatio ? 0 : ati < 0 ? Math.abs(ati) : 0;
|
|
6572
|
+
const negativeAtiOffset = Math.min(revenueOverExpense, negativeAtiAbsolute);
|
|
6573
|
+
const negativeAtiOffsetAtRatio = Math.round(negativeAtiOffset * ratio);
|
|
6574
|
+
const revenueCapacity = usedGroupRatio ? 0 : Math.max(0, ifr - negativeAtiOffsetAtRatio);
|
|
6575
|
+
const totalCapacityBeforeExpenses = permittedAmount + revenueCapacity;
|
|
6576
|
+
const excessCapacityBeforeRife = usedGroupRatio ? 0 : Math.max(0, totalCapacityBeforeExpenses - ife);
|
|
6577
|
+
const rifeFromPreviousYears = nn$18(input.rifeFromPreviousYears);
|
|
6578
|
+
const rifeCapacityAvailable = excessCapacityBeforeRife + receivedCapacity;
|
|
6579
|
+
const rifeDeductible = Math.min(rifeFromPreviousYears, rifeCapacityAvailable);
|
|
6580
|
+
const excessCapacityForYear = Math.max(0, excessCapacityBeforeRife - rifeDeductible);
|
|
6581
|
+
const vintages = input.priorYearExcessCapacity ?? [];
|
|
6582
|
+
for (const v of vintages) if (v.yearsAgo < 1 || v.yearsAgo > 3) issues.push(`EIFEL: an excess-capacity vintage was supplied for ${v.yearsAgo} year(s) ago, but Schedule 130 Part 2I only carries the three immediately preceding years. It was ignored.`);
|
|
6583
|
+
const priorYearUnusedCapacity = vintages.filter((v) => v.yearsAgo >= 1 && v.yearsAgo <= 3).reduce((s, v) => s + Math.max(0, nn$18(v.excessCapacity) - nn$18(v.previouslyTransferred) - nn$18(v.previouslyAbsorbed)), 0);
|
|
6584
|
+
const cumulativeUnusedExcessCapacityBeforeAbsorption = priorYearUnusedCapacity + excessCapacityForYear;
|
|
6585
|
+
const absorbedShelter = (usedGroupRatio ? nn$18(input.groupRatioAmount) : Math.round(atiFloored * ratio)) + ifr;
|
|
6586
|
+
const absorbedRoom = Math.max(0, ife - absorbedShelter);
|
|
6587
|
+
const absorbedCapacity = Math.min(cumulativeUnusedExcessCapacityBeforeAbsorption, absorbedRoom);
|
|
6588
|
+
const cumulativeUnusedExcessCapacity = Math.max(0, cumulativeUnusedExcessCapacityBeforeAbsorption - absorbedCapacity);
|
|
6589
|
+
const excessReceivedCapacity = Math.max(0, receivedCapacity - rifeDeductible);
|
|
6590
|
+
const rifeForYear = computeRifeUnderSubsection111_8(input);
|
|
6591
|
+
if (usedGroupRatio && input.groupRatioAmount === void 0) issues.push("EIFEL: a group ratio election under subsection 18.21(2) was declared but no allocated group ratio amount was supplied, so absorbed capacity was computed with nil group ratio shelter — which absorbs more capacity than the election allows.");
|
|
6592
|
+
return {
|
|
6593
|
+
receivedCapacity,
|
|
6594
|
+
permittedAmount,
|
|
6595
|
+
revenueOverExpense,
|
|
6596
|
+
negativeAtiAbsolute,
|
|
6597
|
+
negativeAtiOffset,
|
|
6598
|
+
negativeAtiOffsetAtRatio,
|
|
6599
|
+
revenueCapacity,
|
|
6600
|
+
totalCapacityBeforeExpenses,
|
|
6601
|
+
excessCapacityBeforeRife,
|
|
6602
|
+
excessCapacityForYear,
|
|
6603
|
+
rifeCapacityAvailable,
|
|
6604
|
+
rifeDeductible,
|
|
6605
|
+
priorYearUnusedCapacity,
|
|
6606
|
+
cumulativeUnusedExcessCapacityBeforeAbsorption,
|
|
6607
|
+
cumulativeUnusedExcessCapacity,
|
|
6608
|
+
absorbedCapacity,
|
|
6609
|
+
excessReceivedCapacity,
|
|
6610
|
+
rifeForYear,
|
|
6611
|
+
issues
|
|
6612
|
+
};
|
|
6613
|
+
}
|
|
6614
|
+
//#endregion
|
|
6830
6615
|
//#region src/t2/schedules/eifel-excluded-entity.ts
|
|
6831
6616
|
/**
|
|
6832
6617
|
* EIFEL — excessive interest and financing expenses limitation (s.18.2, 18.21).
|
|
@@ -6888,7 +6673,7 @@ function assessEifel(input, thresholds) {
|
|
|
6888
6673
|
if (input.domesticExceptionApplies === true) return exclude("domestic");
|
|
6889
6674
|
const issues = [];
|
|
6890
6675
|
if (ife == null) issues.push("EIFEL: net interest and financing expenses were not provided, and the small-CCPC exception does not apply. The excluded-entity status cannot be established, so the return cannot be filed.");
|
|
6891
|
-
else issues.push(`EIFEL: the corporation is not an excluded entity (net interest and financing expenses of ${ife} exceed the de minimis threshold and no other exception applies). The
|
|
6676
|
+
else issues.push(`EIFEL: the corporation is not an excluded entity (net interest and financing expenses of ${ife} exceed the de minimis threshold and no other exception applies). The restriction under s.18.2 is computed — Schedule 130 Parts 2F-2L — but from figures that must be supplied rather than derived (gross IFE and IFR, received capacity, prior-year excess capacity, any group ratio amount), and several parts of the schedule are not modelled at all: exempt IFE, capitalized IFE, the partnership and controlled-foreign-affiliate components, and capacity transfers under s.18.2(4). Have this return reviewed by a qualified practitioner before filing.`);
|
|
6892
6677
|
return {
|
|
6893
6678
|
inForce: true,
|
|
6894
6679
|
isExcludedEntity: false,
|
|
@@ -6898,6 +6683,289 @@ function assessEifel(input, thresholds) {
|
|
|
6898
6683
|
};
|
|
6899
6684
|
}
|
|
6900
6685
|
//#endregion
|
|
6686
|
+
//#region src/t2/schedules/eifel-ife.ts
|
|
6687
|
+
const nn$17 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
6688
|
+
const sum = (rows, pick) => rows.reduce((s, r) => s + pick(r), 0);
|
|
6689
|
+
function computeExemptIfe(rows) {
|
|
6690
|
+
return {
|
|
6691
|
+
totalExemptIfe: sum(rows, (r) => nn$17(r.ifeIncurred)),
|
|
6692
|
+
incomeFromExemptActivities: sum(rows, (r) => nn$17(r.incomeFromFundedActivities)),
|
|
6693
|
+
lossFromExemptActivities: sum(rows, (r) => nn$17(r.lossFromFundedActivities))
|
|
6694
|
+
};
|
|
6695
|
+
}
|
|
6696
|
+
function computeBorrowings(rows) {
|
|
6697
|
+
return {
|
|
6698
|
+
interestPaidOrPayable: sum(rows, (r) => nn$17(r.interestPaidOrPayable)),
|
|
6699
|
+
fundingCostAmounts: sum(rows, (r) => nn$17(r.fundingCostAmounts)),
|
|
6700
|
+
costReducingAmounts: sum(rows, (r) => nn$17(r.costReducingAmounts))
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
function computeLoans(rows) {
|
|
6704
|
+
return {
|
|
6705
|
+
returnAmounts: sum(rows, (r) => nn$17(r.returnAmounts)),
|
|
6706
|
+
returnReducingAmounts: sum(rows, (r) => nn$17(r.returnReducingAmounts))
|
|
6707
|
+
};
|
|
6708
|
+
}
|
|
6709
|
+
function computePartnershipIfe(rows) {
|
|
6710
|
+
const computed = rows.map((r) => ({
|
|
6711
|
+
...r.partnershipName !== void 0 ? { partnershipName: r.partnershipName } : {},
|
|
6712
|
+
includedAmount: Math.max(0, nn$17(r.shareOfPartnershipIfe) - nn$17(r.portionUnderParagraph12_1_l1) - nn$17(r.portionDeniedBySubsection96_2_1))
|
|
6713
|
+
}));
|
|
6714
|
+
return {
|
|
6715
|
+
rows: computed,
|
|
6716
|
+
totalIncluded: sum(computed, (r) => r.includedAmount)
|
|
6717
|
+
};
|
|
6718
|
+
}
|
|
6719
|
+
function computeCapitalizedIfe(rows) {
|
|
6720
|
+
const issues = [];
|
|
6721
|
+
const computed = rows.map((r) => {
|
|
6722
|
+
const ifeInUcc = Math.max(0, nn$17(r.ifeInOpeningUcc) + Math.round(r.ifeInAcquisitionsAndDispositions ?? 0));
|
|
6723
|
+
const requested = nn$17(r.ifeInCca);
|
|
6724
|
+
if (requested > ifeInUcc) issues.push(`EIFEL: capitalized IFE in CCA${r.ccaClass ? ` for class ${r.ccaClass}` : ""} (${requested}) cannot exceed the IFE in the pool (${ifeInUcc}). Capped.`);
|
|
6725
|
+
const ifeInCca = Math.min(requested, ifeInUcc);
|
|
6726
|
+
return {
|
|
6727
|
+
...r.ccaClass !== void 0 ? { ccaClass: r.ccaClass } : {},
|
|
6728
|
+
ifeInUcc,
|
|
6729
|
+
ifeInCca,
|
|
6730
|
+
closingIfeInUcc: Math.max(0, ifeInUcc - ifeInCca)
|
|
6731
|
+
};
|
|
6732
|
+
});
|
|
6733
|
+
return {
|
|
6734
|
+
rows: computed,
|
|
6735
|
+
totalIfeInTerminalLoss: sum(rows, (r) => nn$17(r.ifeInTerminalLoss)),
|
|
6736
|
+
totalIfeInCca: sum(computed, (r) => r.ifeInCca),
|
|
6737
|
+
issues
|
|
6738
|
+
};
|
|
6739
|
+
}
|
|
6740
|
+
function computeResourceIfe(rows) {
|
|
6741
|
+
const issues = [];
|
|
6742
|
+
const computed = rows.map((r) => {
|
|
6743
|
+
const ifeAvailable = Math.max(0, nn$17(r.ifeInOpeningBalance) + Math.round(r.ifeAddedOrDeducted ?? 0));
|
|
6744
|
+
const requested = nn$17(r.ifeInCurrentYearClaim);
|
|
6745
|
+
if (requested > ifeAvailable) issues.push(`EIFEL: IFE claimed in the ${r.pool} pool (${requested}) cannot exceed the IFE available before the claim (${ifeAvailable}). Capped.`);
|
|
6746
|
+
const ifeClaimed = Math.min(requested, ifeAvailable);
|
|
6747
|
+
return {
|
|
6748
|
+
pool: r.pool,
|
|
6749
|
+
ifeAvailable,
|
|
6750
|
+
ifeClaimed,
|
|
6751
|
+
closing: Math.max(0, ifeAvailable - ifeClaimed)
|
|
6752
|
+
};
|
|
6753
|
+
});
|
|
6754
|
+
return {
|
|
6755
|
+
rows: computed,
|
|
6756
|
+
totalIfeInResourceClaims: sum(computed, (r) => r.ifeClaimed),
|
|
6757
|
+
issues
|
|
6758
|
+
};
|
|
6759
|
+
}
|
|
6760
|
+
function computeInterestAndFinancingExpenses(input) {
|
|
6761
|
+
const variableA = nn$17(input.interestOnBorrowings) + nn$17(input.otherInterest) + nn$17(input.subsection20_1_eAmounts) + nn$17(input.ifeInCca) + nn$17(input.ifeInResourceExpenses) + nn$17(input.ifeInTerminalLoss) + nn$17(input.fundingCostAmounts) + nn$17(input.fundingCostLoss) + nn$17(input.fundingCostCapitalLoss) + nn$17(input.feeGivingRiseToIfe) + nn$17(input.feeReducingIfe) + nn$17(input.leaseFinancingAmount) + nn$17(input.partnershipShare) + nn$17(input.reinstatedPartnershipLoss) + nn$17(input.affiliateRaife);
|
|
6762
|
+
const variableB = nn$17(input.costReducingAmounts) + nn$17(input.costReducingGain) + nn$17(input.costReducingPartnershipShare);
|
|
6763
|
+
return {
|
|
6764
|
+
variableA,
|
|
6765
|
+
variableB,
|
|
6766
|
+
totalIfe: Math.max(0, variableA - variableB)
|
|
6767
|
+
};
|
|
6768
|
+
}
|
|
6769
|
+
function computeInterestAndFinancingRevenues(input) {
|
|
6770
|
+
const variableA = nn$17(input.interestReceived) + nn$17(input.subsection12_9Amounts) + nn$17(input.guaranteeFees) + nn$17(input.returnAmounts) + nn$17(input.returnGain) + nn$17(input.leaseFinancingAmount) + nn$17(input.partnershipShare) + nn$17(input.affiliateRaifr);
|
|
6771
|
+
const variableB = nn$17(input.returnReducingAmounts) + nn$17(input.returnReducingLoss) + nn$17(input.returnReducingCapitalLoss) + nn$17(input.returnReducingPartnershipShare) + nn$17(input.shelteredByForeignTaxRelief) + nn$17(input.exemptFromPartITax);
|
|
6772
|
+
return {
|
|
6773
|
+
variableA,
|
|
6774
|
+
variableB,
|
|
6775
|
+
totalIfr: Math.max(0, variableA - variableB)
|
|
6776
|
+
};
|
|
6777
|
+
}
|
|
6778
|
+
function computeLossPortionFromIfe(rows) {
|
|
6779
|
+
const issues = [];
|
|
6780
|
+
const computed = rows.map((r) => {
|
|
6781
|
+
const loss = nn$17(r.nonCapitalLoss);
|
|
6782
|
+
const variableJ = Math.min(loss, nn$17(r.variableJSecondAmount));
|
|
6783
|
+
if (loss === 0) {
|
|
6784
|
+
if (nn$17(r.amountDeducted) > 0) issues.push(`EIFEL: a loss deducted under paragraph 111(1)(a)${r.taxYearOfOrigin ? ` from ${r.taxYearOfOrigin}` : ""} was supplied with a nil non-capital loss for that year, so the IFE-attributable portion cannot be apportioned. It contributed nothing.`);
|
|
6785
|
+
return {
|
|
6786
|
+
...r.taxYearOfOrigin !== void 0 ? { taxYearOfOrigin: r.taxYearOfOrigin } : {},
|
|
6787
|
+
variableJ,
|
|
6788
|
+
attributableToIfe: 0
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
return {
|
|
6792
|
+
...r.taxYearOfOrigin !== void 0 ? { taxYearOfOrigin: r.taxYearOfOrigin } : {},
|
|
6793
|
+
variableJ,
|
|
6794
|
+
attributableToIfe: Math.round(nn$17(r.amountDeducted) * variableJ / loss)
|
|
6795
|
+
};
|
|
6796
|
+
});
|
|
6797
|
+
return {
|
|
6798
|
+
rows: computed,
|
|
6799
|
+
totalAttributableToIfe: sum(computed, (r) => r.attributableToIfe),
|
|
6800
|
+
issues
|
|
6801
|
+
};
|
|
6802
|
+
}
|
|
6803
|
+
/**
|
|
6804
|
+
* The amount actually denied, and the amount actually added back on Schedule 1
|
|
6805
|
+
* line 251.
|
|
6806
|
+
*
|
|
6807
|
+
* This is NOT simply "the shortfall". Part 2K computes a **proportion** (amount
|
|
6808
|
+
* G) and Part 2L applies it to a base that deliberately excludes two things
|
|
6809
|
+
* already counted elsewhere:
|
|
6810
|
+
*
|
|
6811
|
+
* 141 variable A of IFE
|
|
6812
|
+
* 142 less the partnership share (Part 1E amount A) — denied instead through
|
|
6813
|
+
* the paragraph 12(1)(l.2) add-back in Part 2N
|
|
6814
|
+
* 143 less a CFA's relevant affiliate IFE — denied instead through
|
|
6815
|
+
* clause 95(2)(f.11)(ii)(D) in Part 2M
|
|
6816
|
+
*
|
|
6817
|
+
* With neither of those present the base is variable A and the result equals
|
|
6818
|
+
* the raw shortfall, which is why a simple corporation sees no difference. With
|
|
6819
|
+
* either present, using the shortfall directly double-counts the denial.
|
|
6820
|
+
*/
|
|
6821
|
+
function computeExcessIfe(input) {
|
|
6822
|
+
const base = Math.max(0, nn$17(input.variableAOfIfe) - nn$17(input.partnershipShare) - nn$17(input.affiliateRaife));
|
|
6823
|
+
return {
|
|
6824
|
+
base,
|
|
6825
|
+
excessIfe: Math.round(base * Math.max(0, input.deniedProportion))
|
|
6826
|
+
};
|
|
6827
|
+
}
|
|
6828
|
+
/**
|
|
6829
|
+
* Part 2N line 158 — the partnership IFE add-back under paragraph 12(1)(l.2):
|
|
6830
|
+
* the Part 1E total (line 156) at the Part 2K proportion (line 157). Feeds
|
|
6831
|
+
* Schedule 1 **line 252** and Part 2O **line 160**.
|
|
6832
|
+
*/
|
|
6833
|
+
function computePartnershipIfeAddBack(partnershipShare, deniedProportion) {
|
|
6834
|
+
return Math.round(nn$17(partnershipShare) * Math.max(0, deniedProportion));
|
|
6835
|
+
}
|
|
6836
|
+
/**
|
|
6837
|
+
* @param deniedProportion Amount G from Part 2K — the proportion of each
|
|
6838
|
+
* expense denied under subsection 18.2(2). This is why Part 2M runs after the
|
|
6839
|
+
* limitation rather than before it.
|
|
6840
|
+
*/
|
|
6841
|
+
function computeClause95Amounts(denied, included, deniedProportion) {
|
|
6842
|
+
return {
|
|
6843
|
+
deniedUnderSubclauseI: sum(denied, (r) => Math.round(nn$17(r.variableAForAffiliate) * deniedProportion * (r.specifiedParticipatingPercentage ?? 0))),
|
|
6844
|
+
includedUnderSubclauseII: sum(included, (r) => Math.round(nn$17(r.amountInAffiliateFapi) * (r.specifiedParticipatingPercentage ?? 0)))
|
|
6845
|
+
};
|
|
6846
|
+
}
|
|
6847
|
+
//#endregion
|
|
6848
|
+
//#region src/t2/schedules/eifel-limitation.ts
|
|
6849
|
+
/**
|
|
6850
|
+
* ITA subsection 18.2(2) — the excessive interest and financing expenses
|
|
6851
|
+
* limitation itself.
|
|
6852
|
+
*
|
|
6853
|
+
* `eifel-excluded-entity.ts` decides **whether** the regime applies. This decides
|
|
6854
|
+
* **how much** it denies, which was previously left unbuilt on the grounds that
|
|
6855
|
+
* computing it from an unbuilt definition would be confidently wrong.
|
|
6856
|
+
*
|
|
6857
|
+
* ── The provision ───────────────────────────────────────────────────────────
|
|
6858
|
+
*
|
|
6859
|
+
* s.18.2(2) denies a *proportion* of each interest and financing expense:
|
|
6860
|
+
*
|
|
6861
|
+
* (A − (B + C + D + E)) ÷ F
|
|
6862
|
+
*
|
|
6863
|
+
* A the taxpayer's interest and financing expenses for the year
|
|
6864
|
+
* B the group-ratio amount under s.18.21(2) where that applies, otherwise
|
|
6865
|
+
* **G × H** — the ratio of permissible expenses times adjusted taxable income
|
|
6866
|
+
* C the taxpayer's interest and financing revenues for the year
|
|
6867
|
+
* D received capacity, to the extent it exceeds the amount deductible under
|
|
6868
|
+
* paragraph 111(1)(a.1)
|
|
6869
|
+
* E absorbed capacity
|
|
6870
|
+
* F ordinarily the same figure as A
|
|
6871
|
+
*
|
|
6872
|
+
* Because F is A in the ordinary case, the *amount* denied is simply
|
|
6873
|
+
*
|
|
6874
|
+
* denied = A − (B + C + D + E), floored at nil
|
|
6875
|
+
*
|
|
6876
|
+
* which is the form this module computes, while still reporting the proportion —
|
|
6877
|
+
* the statute denies a fraction of *each* expense, and a preparer allocating the
|
|
6878
|
+
* denial across expense lines needs the fraction rather than the total.
|
|
6879
|
+
*
|
|
6880
|
+
* ── The ratio of permissible expenses ───────────────────────────────────────
|
|
6881
|
+
*
|
|
6882
|
+
* Keyed off when the taxation year **BEGINS**, not when it ends:
|
|
6883
|
+
*
|
|
6884
|
+
* begins on or after 2023-10-01 and before 2024-01-01 → **40%**
|
|
6885
|
+
* begins on or after 2024-01-01 → **30%**
|
|
6886
|
+
*
|
|
6887
|
+
* The 40% band is transitional and narrow — one quarter — and it does **not**
|
|
6888
|
+
* apply when determining cumulative unused excess capacity for a year beginning
|
|
6889
|
+
* on or after 1 January 2024. That carve-out is not modelled; excess-capacity
|
|
6890
|
+
* carry-forward is a separate mechanism this module does not compute.
|
|
6891
|
+
*
|
|
6892
|
+
* ── What this module does NOT compute ───────────────────────────────────────
|
|
6893
|
+
*
|
|
6894
|
+
* **Adjusted taxable income** is an input to THIS module, not a derivation — it
|
|
6895
|
+
* is a build-up from taxable income through a dozen add-backs and reductions,
|
|
6896
|
+
* and deriving it partially here would produce a plausible number from an
|
|
6897
|
+
* incomplete definition. It is therefore **required**, and an absent one denies
|
|
6898
|
+
* nothing while saying so. `eifel-adjusted-taxable-income.ts` derives it
|
|
6899
|
+
* (Schedule 130 Part 2F), and `computeFederalT2` feeds that result in.
|
|
6900
|
+
*
|
|
6901
|
+
* Likewise the received and absorbed capacity amounts, which come from the
|
|
6902
|
+
* excess-capacity regime — `eifel-capacity.ts` computes those (Parts 1A and
|
|
6903
|
+
* 2G-2J), and the engine threads them in as `excessReceivedCapacity` (Part 2K
|
|
6904
|
+
* amount C) and `absorbedCapacity` (Part 2H amount D). The group-ratio election
|
|
6905
|
+
* under s.18.21 remains a preparer assertion.
|
|
6906
|
+
*
|
|
6907
|
+
* Source: `research/sources/legislation/ITA-section-18.2-EIFEL.txt`.
|
|
6908
|
+
*
|
|
6909
|
+
* Pure, whole dollars.
|
|
6910
|
+
*/
|
|
6911
|
+
/** The ratio bands, keyed off the taxation year START. */
|
|
6912
|
+
const EIFEL_TRANSITIONAL_RATIO = .4;
|
|
6913
|
+
const EIFEL_STANDARD_RATIO = .3;
|
|
6914
|
+
/** The regime's first day — years beginning before this are outside it. */
|
|
6915
|
+
const EIFEL_FIRST_YEAR_START = "2023-10-01";
|
|
6916
|
+
/** The transitional 40% band ends when years beginning in 2024 start. */
|
|
6917
|
+
const EIFEL_STANDARD_RATIO_FROM = "2024-01-01";
|
|
6918
|
+
const nn$16 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
6919
|
+
function utcDay$1(iso) {
|
|
6920
|
+
return Date.parse(`${iso.slice(0, 10)}T00:00:00Z`);
|
|
6921
|
+
}
|
|
6922
|
+
/**
|
|
6923
|
+
* The ratio of permissible expenses for a year beginning on `taxYearStart`.
|
|
6924
|
+
* Returns 0 for a year beginning before the regime applies at all.
|
|
6925
|
+
*/
|
|
6926
|
+
function ratioOfPermissibleExpenses(taxYearStart) {
|
|
6927
|
+
const start = utcDay$1(taxYearStart);
|
|
6928
|
+
if (Number.isNaN(start) || start < utcDay$1("2023-10-01")) return 0;
|
|
6929
|
+
return start < utcDay$1("2024-01-01") ? EIFEL_TRANSITIONAL_RATIO : EIFEL_STANDARD_RATIO;
|
|
6930
|
+
}
|
|
6931
|
+
function computeEifelLimitation(input) {
|
|
6932
|
+
const issues = [];
|
|
6933
|
+
const ife = nn$16(input.interestAndFinancingExpenses);
|
|
6934
|
+
const nil = (ratio) => ({
|
|
6935
|
+
ratioOfPermissibleExpenses: ratio,
|
|
6936
|
+
permittedAmount: 0,
|
|
6937
|
+
usedGroupRatio: false,
|
|
6938
|
+
totalShelter: 0,
|
|
6939
|
+
deniedAmount: 0,
|
|
6940
|
+
deniedProportion: 0,
|
|
6941
|
+
deductibleAmount: ife,
|
|
6942
|
+
issues
|
|
6943
|
+
});
|
|
6944
|
+
const ratio = ratioOfPermissibleExpenses(input.taxYearStart);
|
|
6945
|
+
if (ratio === 0) {
|
|
6946
|
+
issues.push(`EIFEL: the taxation year beginning ${input.taxYearStart} is before the regime applies (${EIFEL_FIRST_YEAR_START}), so nothing is denied.`);
|
|
6947
|
+
return nil(0);
|
|
6948
|
+
}
|
|
6949
|
+
const usedGroupRatio = input.groupRatioAmount !== void 0;
|
|
6950
|
+
if (!usedGroupRatio && input.adjustedTaxableIncome === void 0) {
|
|
6951
|
+
issues.push("EIFEL: adjusted taxable income was not supplied, so no interest and financing expense was denied. The limitation cannot be computed without it — the figure is a build-up from taxable income that this engine does not derive, and it must be supplied or the return reviewed by hand.");
|
|
6952
|
+
return nil(ratio);
|
|
6953
|
+
}
|
|
6954
|
+
const permittedAmount = usedGroupRatio ? nn$16(input.groupRatioAmount) : Math.round(ratio * Math.max(0, input.adjustedTaxableIncome ?? 0));
|
|
6955
|
+
const totalShelter = permittedAmount + nn$16(input.interestAndFinancingRevenues) + nn$16(input.excessReceivedCapacity) + nn$16(input.absorbedCapacity);
|
|
6956
|
+
const deniedAmount = Math.max(0, ife - totalShelter);
|
|
6957
|
+
return {
|
|
6958
|
+
ratioOfPermissibleExpenses: ratio,
|
|
6959
|
+
permittedAmount,
|
|
6960
|
+
usedGroupRatio,
|
|
6961
|
+
totalShelter,
|
|
6962
|
+
deniedAmount,
|
|
6963
|
+
deniedProportion: ife > 0 ? deniedAmount / ife : 0,
|
|
6964
|
+
deductibleAmount: ife - deniedAmount,
|
|
6965
|
+
issues
|
|
6966
|
+
};
|
|
6967
|
+
}
|
|
6968
|
+
//#endregion
|
|
6901
6969
|
//#region src/t2/schedules/part-vi-1-deduction.ts
|
|
6902
6970
|
const PART_VI_1_DEDUCTION_BANDS = [
|
|
6903
6971
|
{
|
|
@@ -6913,15 +6981,15 @@ const PART_VI_1_DEDUCTION_BANDS = [
|
|
|
6913
6981
|
multiple: 3.5
|
|
6914
6982
|
}
|
|
6915
6983
|
];
|
|
6916
|
-
function utcDay
|
|
6984
|
+
function utcDay(iso) {
|
|
6917
6985
|
return Date.parse(`${iso.slice(0, 10)}T00:00:00Z`);
|
|
6918
6986
|
}
|
|
6919
6987
|
/** The multiple in force for a taxation year ending on `taxYearEnd`. */
|
|
6920
6988
|
function partVI1DeductionMultiple(taxYearEnd, bands = PART_VI_1_DEDUCTION_BANDS) {
|
|
6921
|
-
const end = utcDay
|
|
6989
|
+
const end = utcDay(taxYearEnd);
|
|
6922
6990
|
if (Number.isNaN(end)) return 0;
|
|
6923
6991
|
let multiple = 0;
|
|
6924
|
-
for (const band of [...bands].sort((a, b) => utcDay
|
|
6992
|
+
for (const band of [...bands].sort((a, b) => utcDay(a.from) - utcDay(b.from))) if (end >= utcDay(band.from)) multiple = band.multiple;
|
|
6925
6993
|
return multiple;
|
|
6926
6994
|
}
|
|
6927
6995
|
/**
|
|
@@ -7080,10 +7148,10 @@ function assertSchedule1Fileable(result) {
|
|
|
7080
7148
|
}
|
|
7081
7149
|
//#endregion
|
|
7082
7150
|
//#region src/t2/schedules/schedule2-donations.ts
|
|
7083
|
-
const nn$
|
|
7151
|
+
const nn$15 = (v) => Math.max(0, v ?? 0);
|
|
7084
7152
|
function computeSchedule2(input, rates) {
|
|
7085
|
-
const openingDonationPool = nn$
|
|
7086
|
-
const currentYearDonations = nn$
|
|
7153
|
+
const openingDonationPool = nn$15(input.openingDonationPool);
|
|
7154
|
+
const currentYearDonations = nn$15(input.currentYearDonations);
|
|
7087
7155
|
const available = openingDonationPool + currentYearDonations;
|
|
7088
7156
|
const deductionLimit = Math.max(0, Math.round(rates.DONATION_INCOME_LIMIT_RATE * input.netIncomeForTax));
|
|
7089
7157
|
const donationsClaimed = Math.min(available, deductionLimit);
|
|
@@ -7122,25 +7190,25 @@ function computeSchedule2(input, rates) {
|
|
|
7122
7190
|
const PART_IV_RATE = 115 / 300;
|
|
7123
7191
|
/** 30⅔% — the refundable portion of Part I tax on aggregate investment income. */
|
|
7124
7192
|
const REFUNDABLE_PART_I_RATE = 92 / 300;
|
|
7125
|
-
const nn$
|
|
7193
|
+
const nn$14 = (v) => Math.max(0, v ?? 0);
|
|
7126
7194
|
const DEFAULT_PART4_RATES = {
|
|
7127
7195
|
PART_IV_RATE,
|
|
7128
7196
|
REFUNDABLE_PART_I_RATE
|
|
7129
7197
|
};
|
|
7130
7198
|
function computePart4Rdtoh(input, rates = DEFAULT_PART4_RATES) {
|
|
7131
|
-
const portfolio = nn$
|
|
7132
|
-
const eligible = Math.min(portfolio, nn$
|
|
7199
|
+
const portfolio = nn$14(input.portfolioDividendsReceived);
|
|
7200
|
+
const eligible = Math.min(portfolio, nn$14(input.eligiblePortfolioDividends));
|
|
7133
7201
|
const nonEligible = portfolio - eligible;
|
|
7134
|
-
const aaii = nn$
|
|
7202
|
+
const aaii = nn$14(input.aggregateInvestmentIncome);
|
|
7135
7203
|
const partIvOnEligible = Math.round(eligible * rates.PART_IV_RATE);
|
|
7136
7204
|
const partIvOnNonEligible = Math.round(nonEligible * rates.PART_IV_RATE);
|
|
7137
7205
|
const partIvTax = partIvOnEligible + partIvOnNonEligible;
|
|
7138
7206
|
const refundablePartI = Math.round(aaii * rates.REFUNDABLE_PART_I_RATE);
|
|
7139
7207
|
const erdtohAddition = partIvOnEligible;
|
|
7140
7208
|
const nerdtohAddition = partIvOnNonEligible + refundablePartI;
|
|
7141
|
-
const erdtohClosing = nn$
|
|
7142
|
-
const nerdtohClosing = nn$
|
|
7143
|
-
const refundClaim = Math.round(nn$
|
|
7209
|
+
const erdtohClosing = nn$14(input.openingErdtoh) + erdtohAddition;
|
|
7210
|
+
const nerdtohClosing = nn$14(input.openingNerdtoh) + nerdtohAddition;
|
|
7211
|
+
const refundClaim = Math.round(nn$14(input.taxableDividendsPaid) * PART_IV_RATE);
|
|
7144
7212
|
return {
|
|
7145
7213
|
partIvTax,
|
|
7146
7214
|
refundablePartI,
|
|
@@ -7304,7 +7372,7 @@ const PROVINCE_NAMES = {
|
|
|
7304
7372
|
AB: "Alberta",
|
|
7305
7373
|
QC: "Quebec"
|
|
7306
7374
|
};
|
|
7307
|
-
const nn$
|
|
7375
|
+
const nn$13 = (v) => Math.max(0, v ?? 0);
|
|
7308
7376
|
function computeProvincialAllocation(input, rates = PROVINCE_RATES_2024) {
|
|
7309
7377
|
const byProvince = /* @__PURE__ */ new Map();
|
|
7310
7378
|
for (const pe of input.permanentEstablishments) {
|
|
@@ -7313,8 +7381,8 @@ function computeProvincialAllocation(input, rates = PROVINCE_RATES_2024) {
|
|
|
7313
7381
|
grossRevenue: 0,
|
|
7314
7382
|
salariesWages: 0
|
|
7315
7383
|
};
|
|
7316
|
-
cur.grossRevenue += nn$
|
|
7317
|
-
cur.salariesWages += nn$
|
|
7384
|
+
cur.grossRevenue += nn$13(pe.grossRevenue);
|
|
7385
|
+
cur.salariesWages += nn$13(pe.salariesWages);
|
|
7318
7386
|
byProvince.set(key, cur);
|
|
7319
7387
|
}
|
|
7320
7388
|
const totalRevenue = [...byProvince.values()].reduce((s, p) => s + p.grossRevenue, 0);
|
|
@@ -7458,7 +7526,7 @@ function computeSchedule6(dispositions, inclusionRate) {
|
|
|
7458
7526
|
*
|
|
7459
7527
|
* Whole dollars, pure functions, no I/O.
|
|
7460
7528
|
*/
|
|
7461
|
-
const nn$
|
|
7529
|
+
const nn$12 = (v) => Math.max(0, v ?? 0);
|
|
7462
7530
|
const round = (v) => Math.round(v);
|
|
7463
7531
|
/** CDE/CCOGPE-style short-tax-year proration: full rate at ≥357 days, else rate × days/365. */
|
|
7464
7532
|
function stepYearFactor(daysInTaxYear) {
|
|
@@ -7493,13 +7561,13 @@ function forcedOrCappedClaim(requested, subtotal, issues, label) {
|
|
|
7493
7561
|
}
|
|
7494
7562
|
function computeDepletion(input = {}) {
|
|
7495
7563
|
const issues = [];
|
|
7496
|
-
const edaRegularPool = nn$
|
|
7564
|
+
const edaRegularPool = nn$12(input.edaRegularOpening);
|
|
7497
7565
|
const edaRegularClaim = claimUpToCap(input.edaRegularClaim, edaRegularPool, issues, "Schedule 12 EDA regular (115)");
|
|
7498
7566
|
const edaRegularClosing = edaRegularPool - edaRegularClaim;
|
|
7499
|
-
const edaSuccessorPool = nn$
|
|
7567
|
+
const edaSuccessorPool = nn$12(input.edaSuccessorOpening);
|
|
7500
7568
|
const edaSuccessorClaim = claimUpToCap(input.edaSuccessorClaim, edaSuccessorPool, issues, "Schedule 12 EDA successor (140)");
|
|
7501
7569
|
const edaSuccessorClosing = edaSuccessorPool - edaSuccessorClaim;
|
|
7502
|
-
const cmedbPool = nn$
|
|
7570
|
+
const cmedbPool = nn$12(input.cmedbOpening);
|
|
7503
7571
|
const cmedbClaim = claimUpToCap(input.cmedbClaim, cmedbPool, issues, "Schedule 12 CMEDB (170)");
|
|
7504
7572
|
return {
|
|
7505
7573
|
edaRegularPool,
|
|
@@ -7517,10 +7585,10 @@ function computeDepletion(input = {}) {
|
|
|
7517
7585
|
}
|
|
7518
7586
|
function computeCee(input = {}) {
|
|
7519
7587
|
const issues = [];
|
|
7520
|
-
const regularSubtotal = nn$
|
|
7588
|
+
const regularSubtotal = nn$12(input.regularOpening) + nn$12(input.regularCurrentYearExpenses) + nn$12(input.regularOtherAdditions) - nn$12(input.regularGovernmentAssistance) - nn$12(input.regularOtherDeductions);
|
|
7521
7589
|
const regularClaim = forcedOrCappedClaim(input.regularClaim, regularSubtotal, issues, "Schedule 12 CEE regular (245)");
|
|
7522
7590
|
const regularClosing = regularSubtotal > 0 ? regularSubtotal - regularClaim : 0;
|
|
7523
|
-
const successorSubtotal = nn$
|
|
7591
|
+
const successorSubtotal = nn$12(input.successorOpening) - nn$12(input.successorOtherDeductions);
|
|
7524
7592
|
const successorClaim = forcedOrCappedClaim(input.successorClaim, successorSubtotal, issues, "Schedule 12 CEE successor (295)");
|
|
7525
7593
|
return {
|
|
7526
7594
|
regularSubtotal,
|
|
@@ -7542,15 +7610,15 @@ function computeCde(input = {}, daysInTaxYear, cogpe = {
|
|
|
7542
7610
|
successorSubtotal: 0
|
|
7543
7611
|
}) {
|
|
7544
7612
|
const issues = [];
|
|
7545
|
-
const regularCreditBalance = cogpe.regularSubtotal < 0 ? round(cogpe.regularSubtotal) : nn$
|
|
7546
|
-
const regularSubtotal = nn$
|
|
7547
|
-
const rcdePortion = Math.min(nn$
|
|
7613
|
+
const regularCreditBalance = cogpe.regularSubtotal < 0 ? round(cogpe.regularSubtotal) : nn$12(input.regularCreditBalanceInCogpePool);
|
|
7614
|
+
const regularSubtotal = nn$12(input.regularOpening) + nn$12(input.regularCurrentYearExpenses) + nn$12(input.regularOtherAdditions) - nn$12(input.regularGovernmentAssistance) - nn$12(input.regularReceivableOnDisposition) - regularCreditBalance - nn$12(input.regularOtherDeductions);
|
|
7615
|
+
const rcdePortion = Math.min(nn$12(input.regularCurrentYearExpenses), Math.max(0, regularSubtotal));
|
|
7548
7616
|
const regularCap = CDE_BASE_RATE * stepYearFactor(daysInTaxYear) * regularSubtotal + CDE_RCDE_BONUS_RATE * stepYearFactor(daysInTaxYear) * rcdePortion;
|
|
7549
7617
|
const regularClaim = regularSubtotal > 0 ? claimUpToCap(input.regularClaim, regularCap, issues, "Schedule 12 CDE regular (345)") : 0;
|
|
7550
7618
|
const regularClosing = Math.max(0, regularSubtotal - regularClaim);
|
|
7551
7619
|
if (regularSubtotal < 0) issues.push("Schedule 12 CDE regular (345): the pool subtotal is negative — per the form, include it as income in \"Other additions\" on Schedule 1 (this module does not auto-apply that; add a manual Schedule 1 addition for the negative amount).");
|
|
7552
|
-
const successorCreditBalance = cogpe.successorSubtotal < 0 ? round(cogpe.successorSubtotal) : nn$
|
|
7553
|
-
const successorSubtotal = nn$
|
|
7620
|
+
const successorCreditBalance = cogpe.successorSubtotal < 0 ? round(cogpe.successorSubtotal) : nn$12(input.successorCreditBalanceInCogpePool);
|
|
7621
|
+
const successorSubtotal = nn$12(input.successorOpening) - successorCreditBalance - nn$12(input.successorOtherDeductions);
|
|
7554
7622
|
const successorCap = CDE_BASE_RATE * stepYearFactor(daysInTaxYear) * successorSubtotal;
|
|
7555
7623
|
const successorClaim = successorSubtotal > 0 ? claimUpToCap(input.successorClaim, successorCap, issues, "Schedule 12 CDE successor (395)") : 0;
|
|
7556
7624
|
return {
|
|
@@ -7570,13 +7638,13 @@ const COGPE_BASE_RATE = .1;
|
|
|
7570
7638
|
const COGPE_RCOGPE_BONUS_RATE = .05;
|
|
7571
7639
|
function computeCogpe(input = {}, daysInTaxYear) {
|
|
7572
7640
|
const issues = [];
|
|
7573
|
-
const regularSubtotal = nn$
|
|
7574
|
-
const rcogpePortion = Math.min(nn$
|
|
7641
|
+
const regularSubtotal = nn$12(input.regularOpening) + nn$12(input.regularCurrentYearExpenses) + nn$12(input.regularOtherAdditions) - nn$12(input.regularReceivableOnDisposition) - nn$12(input.regularGovernmentAssistance) - nn$12(input.regularOtherDeductions);
|
|
7642
|
+
const rcogpePortion = Math.min(nn$12(input.regularCurrentYearExpenses), Math.max(0, regularSubtotal));
|
|
7575
7643
|
const regularCap = COGPE_BASE_RATE * stepYearFactor(daysInTaxYear) * regularSubtotal + COGPE_RCOGPE_BONUS_RATE * stepYearFactor(daysInTaxYear) * rcogpePortion;
|
|
7576
7644
|
const regularClaim = regularSubtotal > 0 ? claimUpToCap(input.regularClaim, regularCap, issues, "Schedule 12 COGPE regular (445)") : 0;
|
|
7577
7645
|
const regularClosing = regularSubtotal > 0 ? regularSubtotal - regularClaim : 0;
|
|
7578
7646
|
if (regularSubtotal < 0) issues.push("Schedule 12 COGPE regular (445): the pool subtotal is negative. Per the form this carries to CDE regular line 330 (no s.66.7(4)(a)(iii) designation) or CDE successor line 380 (with a designation) — computeCde auto-applies the 330 route unconditionally when this subtotal is negative; confirm the designation status before relying on that routing.");
|
|
7579
|
-
const successorSubtotal = nn$
|
|
7647
|
+
const successorSubtotal = nn$12(input.successorOpening) - nn$12(input.successorReceivableOnDisposition) - nn$12(input.successorOtherDeductions);
|
|
7580
7648
|
const successorCap = COGPE_BASE_RATE * stepYearFactor(daysInTaxYear) * successorSubtotal;
|
|
7581
7649
|
const successorClaim = successorSubtotal > 0 ? claimUpToCap(input.successorClaim, successorCap, issues, "Schedule 12 COGPE successor (495)") : 0;
|
|
7582
7650
|
const successorClosing = Math.max(0, successorSubtotal - successorClaim);
|
|
@@ -7595,18 +7663,18 @@ function computeCogpe(input = {}, daysInTaxYear) {
|
|
|
7595
7663
|
/** Part 7 — Foreign exploration and development expenses (pre-2001 tax years; still a real, if rare, carryforward). */
|
|
7596
7664
|
function computeForeignExploration(input = {}, daysInTaxYear) {
|
|
7597
7665
|
const issues = [];
|
|
7598
|
-
const regularPool = nn$
|
|
7666
|
+
const regularPool = nn$12(input.regularOpening) - nn$12(input.regularOtherDeductions);
|
|
7599
7667
|
let regularClaim = 0;
|
|
7600
7668
|
if (regularPool < 0) issues.push("Schedule 12 FEDE regular (520): the pool balance is negative — include it as income in \"Other additions\" on Schedule 1 (not auto-applied by this module).");
|
|
7601
7669
|
else if (regularPool > 0) {
|
|
7602
|
-
const cap = Math.max(COGPE_BASE_RATE * stepYearFactor(daysInTaxYear) * regularPool, nn$
|
|
7670
|
+
const cap = Math.max(COGPE_BASE_RATE * stepYearFactor(daysInTaxYear) * regularPool, nn$12(input.regularForeignResourceIncome));
|
|
7603
7671
|
regularClaim = claimUpToCap(input.regularClaim, Math.min(regularPool, cap), issues, "Schedule 12 FEDE regular (520)");
|
|
7604
7672
|
}
|
|
7605
7673
|
const regularClosing = regularPool - regularClaim;
|
|
7606
|
-
const successorPool = nn$
|
|
7674
|
+
const successorPool = nn$12(input.successorOpening) - nn$12(input.successorOtherDeductions);
|
|
7607
7675
|
let successorClaim = 0;
|
|
7608
7676
|
if (successorPool < 0) issues.push("Schedule 12 FEDE successor (570): the pool balance is negative — include it as income in \"Other additions\" on Schedule 1 (not auto-applied by this module).");
|
|
7609
|
-
else if (successorPool > 0) successorClaim = claimUpToCap(input.successorClaim, Math.min(successorPool, nn$
|
|
7677
|
+
else if (successorPool > 0) successorClaim = claimUpToCap(input.successorClaim, Math.min(successorPool, nn$12(input.successorForeignResourceIncome)), issues, "Schedule 12 FEDE successor (570)");
|
|
7610
7678
|
const successorClosing = successorPool - successorClaim;
|
|
7611
7679
|
return {
|
|
7612
7680
|
regularPool,
|
|
@@ -7621,10 +7689,10 @@ function computeForeignExploration(input = {}, daysInTaxYear) {
|
|
|
7621
7689
|
/** Part 8 — specified foreign exploration/development, regular OR successor (call once per column; rates are identical). */
|
|
7622
7690
|
function computeSpecifiedForeignExploration(input = {}, label) {
|
|
7623
7691
|
const issues = [];
|
|
7624
|
-
const pool = nn$
|
|
7692
|
+
const pool = nn$12(input.openingBalance) - nn$12(input.otherDeductions);
|
|
7625
7693
|
let claim = 0;
|
|
7626
7694
|
if (pool < 0) issues.push(`${label}: a country-level pool is negative — include it as income on Schedule 1 manually.`);
|
|
7627
|
-
else if (pool > 0) claim = claimUpToCap(input.claim, Math.min(pool, Math.max(nn$
|
|
7695
|
+
else if (pool > 0) claim = claimUpToCap(input.claim, Math.min(pool, Math.max(nn$12(input.foreignResourceIncome), pool)), issues, label);
|
|
7628
7696
|
return {
|
|
7629
7697
|
pool,
|
|
7630
7698
|
claim,
|
|
@@ -7635,12 +7703,12 @@ function computeSpecifiedForeignExploration(input = {}, label) {
|
|
|
7635
7703
|
/** Part 9 — cumulative foreign resource expenses, regular OR successor. */
|
|
7636
7704
|
function computeCumulativeForeignResource(input = {}, daysInTaxYear, label) {
|
|
7637
7705
|
const issues = [];
|
|
7638
|
-
const pool = nn$
|
|
7706
|
+
const pool = nn$12(input.openingBalance) + nn$12(input.currentYearExpenses) - nn$12(input.otherDeductions);
|
|
7639
7707
|
let claim = 0;
|
|
7640
7708
|
if (pool < 0) issues.push(`${label}: a country-level pool is negative — include it as income on Schedule 1 manually.`);
|
|
7641
7709
|
else if (pool > 0) {
|
|
7642
|
-
const a = Math.max(.1 * stepYearFactor(daysInTaxYear) * pool, Math.min(.3 * stepYearFactor(daysInTaxYear) * pool, nn$
|
|
7643
|
-
const cap = a + Math.min(pool - a, nn$
|
|
7710
|
+
const a = Math.max(.1 * stepYearFactor(daysInTaxYear) * pool, Math.min(.3 * stepYearFactor(daysInTaxYear) * pool, nn$12(input.foreignResourceIncome)));
|
|
7711
|
+
const cap = a + Math.min(pool - a, nn$12(input.foreignResourceIncome));
|
|
7644
7712
|
claim = claimUpToCap(input.claim, Math.min(pool, cap), issues, label);
|
|
7645
7713
|
}
|
|
7646
7714
|
return {
|
|
@@ -7694,15 +7762,15 @@ function computeSchedule12ResourceDeductions(input) {
|
|
|
7694
7762
|
}
|
|
7695
7763
|
//#endregion
|
|
7696
7764
|
//#region src/t2/schedules/schedule13-reserves.ts
|
|
7697
|
-
const nn$
|
|
7765
|
+
const nn$11 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
7698
7766
|
function computeSchedule13(rows = []) {
|
|
7699
7767
|
let totalOpening = 0;
|
|
7700
7768
|
let totalTransfer = 0;
|
|
7701
7769
|
let totalClosing = 0;
|
|
7702
7770
|
for (const r of rows) {
|
|
7703
|
-
totalOpening += nn$
|
|
7704
|
-
totalTransfer += nn$
|
|
7705
|
-
totalClosing += nn$
|
|
7771
|
+
totalOpening += nn$11(r.opening);
|
|
7772
|
+
totalTransfer += nn$11(r.transfer);
|
|
7773
|
+
totalClosing += nn$11(r.closing);
|
|
7706
7774
|
}
|
|
7707
7775
|
const schedule1Addition = totalOpening + totalTransfer;
|
|
7708
7776
|
const schedule1Deduction = totalClosing;
|
|
@@ -7717,15 +7785,15 @@ function computeSchedule13(rows = []) {
|
|
|
7717
7785
|
}
|
|
7718
7786
|
//#endregion
|
|
7719
7787
|
//#region src/t2/schedules/schedule21-foreign-tax-credit.ts
|
|
7720
|
-
const nn$
|
|
7788
|
+
const nn$10 = (v) => Math.max(0, v ?? 0);
|
|
7721
7789
|
function computeSchedule21(input) {
|
|
7722
7790
|
const taxableIncome = input.taxableIncome;
|
|
7723
|
-
const partITax = nn$
|
|
7724
|
-
const fNonBizIncome = nn$
|
|
7725
|
-
const fNonBizTax = nn$
|
|
7726
|
-
const fBizIncome = nn$
|
|
7727
|
-
const fBizTax = nn$
|
|
7728
|
-
const openingPool = nn$
|
|
7791
|
+
const partITax = nn$10(input.partITax);
|
|
7792
|
+
const fNonBizIncome = nn$10(input.foreignNonBusinessIncome);
|
|
7793
|
+
const fNonBizTax = nn$10(input.foreignNonBusinessTaxPaid);
|
|
7794
|
+
const fBizIncome = nn$10(input.foreignBusinessIncome);
|
|
7795
|
+
const fBizTax = nn$10(input.foreignBusinessTaxPaid);
|
|
7796
|
+
const openingPool = nn$10(input.openingBusinessFtcPool);
|
|
7729
7797
|
if (taxableIncome <= 0 || partITax <= 0) return {
|
|
7730
7798
|
nonBusinessFtc: 0,
|
|
7731
7799
|
businessFtc: 0,
|
|
@@ -7795,11 +7863,11 @@ function allocateEvenly(count, totalLimit = DEFAULT_BUSINESS_LIMIT) {
|
|
|
7795
7863
|
}
|
|
7796
7864
|
//#endregion
|
|
7797
7865
|
//#region src/t2/schedules/schedule27-zetm.ts
|
|
7798
|
-
const nn$
|
|
7866
|
+
const nn$9 = (v) => Math.max(0, v ?? 0);
|
|
7799
7867
|
function computeZetm(input, rates) {
|
|
7800
|
-
const sbdIncome = nn$
|
|
7801
|
-
const generalRateIncome = nn$
|
|
7802
|
-
const zetmIncome = Math.min(nn$
|
|
7868
|
+
const sbdIncome = nn$9(input.sbdIncome);
|
|
7869
|
+
const generalRateIncome = nn$9(input.generalRateIncome);
|
|
7870
|
+
const zetmIncome = Math.min(nn$9(input.zetmIncome), sbdIncome + generalRateIncome);
|
|
7803
7871
|
const zetmSbdIncome = Math.min(zetmIncome, sbdIncome);
|
|
7804
7872
|
const zetmGeneralIncome = Math.min(zetmIncome - zetmSbdIncome, generalRateIncome);
|
|
7805
7873
|
const sbdDifferential = Math.max(0, input.sbdEffectiveRate - rates.ZETM_SBD_RATE);
|
|
@@ -7872,13 +7940,13 @@ function computeZetm(input, rates) {
|
|
|
7872
7940
|
*/
|
|
7873
7941
|
/** s.127(27)-(29) — the recapture window. */
|
|
7874
7942
|
const ITC_RECAPTURE_PERIOD_YEARS = 20;
|
|
7875
|
-
const nn$
|
|
7943
|
+
const nn$8 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
7876
7944
|
function computeItcRecapture(items) {
|
|
7877
7945
|
const issues = [];
|
|
7878
7946
|
const results = items.map((item) => {
|
|
7879
7947
|
const label = item.description ? ` (${item.description})` : "";
|
|
7880
7948
|
const usedFairMarketValue = item.armsLengthProceeds === void 0;
|
|
7881
|
-
const dispositionValue = usedFairMarketValue ? nn$
|
|
7949
|
+
const dispositionValue = usedFairMarketValue ? nn$8(item.fairMarketValue) : nn$8(item.armsLengthProceeds);
|
|
7882
7950
|
if (usedFairMarketValue && item.fairMarketValue === void 0) issues.push(`ITC recapture${label}: neither arm's length proceeds nor a fair market value was supplied, so the recapture computed as nil. A disposition or conversion needs one of the two.`);
|
|
7883
7951
|
const outsideRecapturePeriod = item.yearsSinceCredit !== void 0 && item.yearsSinceCredit > 20;
|
|
7884
7952
|
const base = {
|
|
@@ -7891,12 +7959,12 @@ function computeItcRecapture(items) {
|
|
|
7891
7959
|
outsideRecapturePeriod
|
|
7892
7960
|
};
|
|
7893
7961
|
if (outsideRecapturePeriod) return base;
|
|
7894
|
-
const ownRecapture = Math.min(nn$
|
|
7962
|
+
const ownRecapture = Math.min(nn$8(item.itcEarned), Math.round(Math.max(0, item.itcRate) * dispositionValue));
|
|
7895
7963
|
let transferredRecapture = 0;
|
|
7896
7964
|
if (item.transferred) {
|
|
7897
7965
|
const t = item.transferred;
|
|
7898
|
-
const formula = Math.round(Math.max(0, t.transfereeItcRate) * dispositionValue) - nn$
|
|
7899
|
-
transferredRecapture = Math.max(0, Math.min(nn$
|
|
7966
|
+
const formula = Math.round(Math.max(0, t.transfereeItcRate) * dispositionValue) - nn$8(t.alreadyRecaptured);
|
|
7967
|
+
transferredRecapture = Math.max(0, Math.min(nn$8(t.transfereeItcEarned), formula));
|
|
7900
7968
|
}
|
|
7901
7969
|
return {
|
|
7902
7970
|
...base,
|
|
@@ -7913,9 +7981,9 @@ function computeItcRecapture(items) {
|
|
|
7913
7981
|
}
|
|
7914
7982
|
//#endregion
|
|
7915
7983
|
//#region src/t2/schedules/schedule31-sred-itc.ts
|
|
7916
|
-
const nn$
|
|
7984
|
+
const nn$7 = (v) => Math.max(0, v ?? 0);
|
|
7917
7985
|
function computeSchedule31(input, rates) {
|
|
7918
|
-
const qualifiedExpenditures = nn$
|
|
7986
|
+
const qualifiedExpenditures = nn$7(input.qualifiedExpenditures);
|
|
7919
7987
|
const isCcpc = input.isCcpc ?? false;
|
|
7920
7988
|
const expenditureLimit = input.expenditureLimit ?? rates.SRED_EXPENDITURE_LIMIT;
|
|
7921
7989
|
const enhancedBase = isCcpc ? Math.min(qualifiedExpenditures, expenditureLimit) : 0;
|
|
@@ -7924,8 +7992,8 @@ function computeSchedule31(input, rates) {
|
|
|
7924
7992
|
const basicItc = Math.round(rates.SRED_ITC_BASIC_RATE * basicBase);
|
|
7925
7993
|
const itcEarned = enhancedItc + basicItc;
|
|
7926
7994
|
const refundableItc = enhancedItc;
|
|
7927
|
-
const availableNonRefundable = basicItc + nn$
|
|
7928
|
-
const itcAppliedAgainstTax = Math.min(availableNonRefundable, nn$
|
|
7995
|
+
const availableNonRefundable = basicItc + nn$7(input.openingItcPool);
|
|
7996
|
+
const itcAppliedAgainstTax = Math.min(availableNonRefundable, nn$7(input.partITaxAvailable));
|
|
7929
7997
|
return {
|
|
7930
7998
|
qualifiedExpenditures,
|
|
7931
7999
|
expenditureLimit,
|
|
@@ -7964,17 +8032,17 @@ function computeSchedule31(input, rates) {
|
|
|
7964
8032
|
/** The large-corporation filing / grind threshold (s.181.5, s.125(5.1)). */
|
|
7965
8033
|
const LARGE_CORPORATION_THRESHOLD = 1e7;
|
|
7966
8034
|
const n$1 = (v) => Math.round(v ?? 0);
|
|
7967
|
-
const nn$
|
|
8035
|
+
const nn$6 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
7968
8036
|
function computeTaxableCapital(input = {}) {
|
|
7969
|
-
const additions = nn$
|
|
7970
|
-
const deductions = nn$
|
|
8037
|
+
const additions = nn$6(input.reservesNotDeducted) + nn$6(input.capitalStock) + n$1(input.retainedEarnings) + nn$6(input.contributedSurplus) + nn$6(input.otherSurpluses) + nn$6(input.deferredForexGains) + nn$6(input.loansAndAdvances) + nn$6(input.bondsAndDebentures) + nn$6(input.dividendsDeclaredUnpaid) + nn$6(input.otherLongTermDebt) + nn$6(input.partnershipInterest);
|
|
8038
|
+
const deductions = nn$6(input.deferredTaxDebit) + nn$6(input.deficitInEquity) + nn$6(input.patronageDeducted) + nn$6(input.deferredForexLosses);
|
|
7971
8039
|
const capital = Math.max(0, additions - deductions);
|
|
7972
|
-
const investmentAllowance = nn$
|
|
8040
|
+
const investmentAllowance = nn$6(input.sharesOfOtherCorporations) + nn$6(input.loansToOtherCorporations) + nn$6(input.bondsOfOtherCorporations) + nn$6(input.longTermDebtOfFinancialInstitution) + nn$6(input.dividendsReceivable) + nn$6(input.partnershipObligations) + nn$6(input.partnershipInterestAsset);
|
|
7973
8041
|
const taxableCapital = Math.max(0, capital - investmentAllowance);
|
|
7974
8042
|
let ratio = 1;
|
|
7975
8043
|
if (input.taxableIncomeEarnedInCanada !== void 0 && input.taxableIncome !== void 0) {
|
|
7976
8044
|
const denom = input.taxableIncome > 0 ? input.taxableIncome : 1e3;
|
|
7977
|
-
ratio = Math.max(0, Math.min(1, nn$
|
|
8045
|
+
ratio = Math.max(0, Math.min(1, nn$6(input.taxableIncomeEarnedInCanada) / denom));
|
|
7978
8046
|
}
|
|
7979
8047
|
const taxableCapitalEmployedInCanada = Math.round(taxableCapital * ratio);
|
|
7980
8048
|
return {
|
|
@@ -7987,18 +8055,18 @@ function computeTaxableCapital(input = {}) {
|
|
|
7987
8055
|
}
|
|
7988
8056
|
//#endregion
|
|
7989
8057
|
//#region src/t2/schedules/schedule43-part6-1.ts
|
|
7990
|
-
const nn$
|
|
8058
|
+
const nn$5 = (v) => Math.max(0, v ?? 0);
|
|
7991
8059
|
function computeSchedule43(input, rates) {
|
|
7992
|
-
const shortTerm = nn$
|
|
7993
|
-
const other = nn$
|
|
8060
|
+
const shortTerm = nn$5(input.shortTermPreferredDividends);
|
|
8061
|
+
const other = nn$5(input.otherPreferredDividends);
|
|
7994
8062
|
const issues = [];
|
|
7995
8063
|
let dividendAllowance;
|
|
7996
8064
|
if (input.isAssociated === true) if (input.allocatedAllowance == null) {
|
|
7997
8065
|
dividendAllowance = 0;
|
|
7998
8066
|
if (shortTerm + other > 0) issues.push("Associated corporation: the Part VI.1 dividend allowance is nil unless the group files an allocation agreement (s.191.1(3)). Enter the allocated amount or the full dividend is taxable.");
|
|
7999
|
-
} else dividendAllowance = Math.min(nn$
|
|
8067
|
+
} else dividendAllowance = Math.min(nn$5(input.allocatedAllowance), rates.DIVIDEND_ALLOWANCE);
|
|
8000
8068
|
else dividendAllowance = rates.DIVIDEND_ALLOWANCE;
|
|
8001
|
-
const priorExcess = Math.max(0, nn$
|
|
8069
|
+
const priorExcess = Math.max(0, nn$5(input.priorYearPreferredDividends) - rates.ALLOWANCE_GRIND_THRESHOLD);
|
|
8002
8070
|
dividendAllowance = Math.max(0, dividendAllowance - priorExcess);
|
|
8003
8071
|
const shortTermTaxable = Math.max(0, shortTerm - dividendAllowance);
|
|
8004
8072
|
const allowanceLeft = Math.max(0, dividendAllowance - shortTerm);
|
|
@@ -8021,12 +8089,12 @@ function computeSchedule43(input, rates) {
|
|
|
8021
8089
|
}
|
|
8022
8090
|
//#endregion
|
|
8023
8091
|
//#region src/t2/schedules/schedule53-grip.ts
|
|
8024
|
-
const nn$
|
|
8092
|
+
const nn$4 = (v) => Math.max(0, v ?? 0);
|
|
8025
8093
|
function computeGrip(input, gripFactor = .72) {
|
|
8026
|
-
const openingGrip = nn$
|
|
8094
|
+
const openingGrip = nn$4(input.openingGrip);
|
|
8027
8095
|
const generalRateAddition = Math.round(gripFactor * Math.max(0, input.generalRateIncome));
|
|
8028
|
-
const eligibleDividendsReceived = nn$
|
|
8029
|
-
const eligibleDividendsDesignated = nn$
|
|
8096
|
+
const eligibleDividendsReceived = nn$4(input.eligibleDividendsReceived);
|
|
8097
|
+
const eligibleDividendsDesignated = nn$4(input.eligibleDividendsDesignated);
|
|
8030
8098
|
const gripBeforeDesignation = openingGrip + generalRateAddition + eligibleDividendsReceived;
|
|
8031
8099
|
const excessiveDesignation = Math.max(0, eligibleDividendsDesignated - gripBeforeDesignation);
|
|
8032
8100
|
return {
|
|
@@ -8128,24 +8196,24 @@ function computeGrip(input, gripFactor = .72) {
|
|
|
8128
8196
|
const LRIP_INVESTMENT_INCOME_FACTOR = .8;
|
|
8129
8197
|
/** Line 160 — the investment corporation deduction enters the pool at four times its value. */
|
|
8130
8198
|
const LRIP_INVESTMENT_CORPORATION_MULTIPLE = 4;
|
|
8131
|
-
const nn$
|
|
8199
|
+
const nn$3 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
8132
8200
|
const sn = (v) => Math.round(v ?? 0);
|
|
8133
8201
|
function computeSchedule54(input) {
|
|
8134
8202
|
const issues = [];
|
|
8135
|
-
const openingLrip = nn$
|
|
8136
|
-
const investmentIncomeAddition = Math.round(LRIP_INVESTMENT_INCOME_FACTOR * nn$
|
|
8137
|
-
const investmentCorporationAddition = 4 * nn$
|
|
8203
|
+
const openingLrip = nn$3(input.openingLrip);
|
|
8204
|
+
const investmentIncomeAddition = Math.round(LRIP_INVESTMENT_INCOME_FACTOR * nn$3(input.priorYearAggregateInvestmentIncome));
|
|
8205
|
+
const investmentCorporationAddition = 4 * nn$3(input.priorYearInvestmentCorporationDeduction);
|
|
8138
8206
|
const lripBeforeDividends = openingLrip + investmentIncomeAddition + investmentCorporationAddition;
|
|
8139
8207
|
const ordered = [...input.dividendEvents ?? []].sort((a, b) => a.date.localeCompare(b.date));
|
|
8140
8208
|
const events = [];
|
|
8141
8209
|
let designationsSoFar = 0;
|
|
8142
8210
|
for (const e of ordered) {
|
|
8143
|
-
const poolBeforeReductions = lripBeforeDividends + nn$
|
|
8211
|
+
const poolBeforeReductions = lripBeforeDividends + nn$3(e.dividendsReceivableBefore) + sn(e.adjustmentsBefore);
|
|
8144
8212
|
const derivedPrior = designationsSoFar;
|
|
8145
|
-
const excessiveDesignationsBefore = e.excessiveDesignationsBefore !== void 0 ? nn$
|
|
8146
|
-
if (e.excessiveDesignationsBefore !== void 0 && nn$
|
|
8147
|
-
const lripAtDate = Math.max(0, poolBeforeReductions - nn$
|
|
8148
|
-
const eligibleDividendsPaid = nn$
|
|
8213
|
+
const excessiveDesignationsBefore = e.excessiveDesignationsBefore !== void 0 ? nn$3(e.excessiveDesignationsBefore) : derivedPrior;
|
|
8214
|
+
if (e.excessiveDesignationsBefore !== void 0 && nn$3(e.excessiveDesignationsBefore) !== derivedPrior) issues.push(`Schedule 54: for the dividend dated ${e.date}, line 250 was given as ${nn$3(e.excessiveDesignationsBefore)} but the earlier rows of this schedule total ${derivedPrior}. The supplied figure has been used; confirm which is right before filing, because line 250 feeds every later row.`);
|
|
8215
|
+
const lripAtDate = Math.max(0, poolBeforeReductions - nn$3(e.dividendsPayableBefore) - excessiveDesignationsBefore);
|
|
8216
|
+
const eligibleDividendsPaid = nn$3(e.eligibleDividendsPaid);
|
|
8149
8217
|
const excessiveDesignation = Math.max(0, Math.min(lripAtDate, eligibleDividendsPaid));
|
|
8150
8218
|
events.push({
|
|
8151
8219
|
date: e.date,
|
|
@@ -8158,8 +8226,8 @@ function computeSchedule54(input) {
|
|
|
8158
8226
|
designationsSoFar += excessiveDesignation;
|
|
8159
8227
|
}
|
|
8160
8228
|
const totalExcessiveDesignation = events.reduce((s, e) => s + e.excessiveDesignation, 0);
|
|
8161
|
-
const poolIncludingReceipts = lripBeforeDividends + nn$
|
|
8162
|
-
const totalReductions = nn$
|
|
8229
|
+
const poolIncludingReceipts = lripBeforeDividends + nn$3(input.dividendsReceivableInYear) + sn(input.adjustmentsInYear);
|
|
8230
|
+
const totalReductions = nn$3(input.dividendsPayableInYear) + totalExcessiveDesignation;
|
|
8163
8231
|
const closingLrip = Math.max(0, poolIncludingReceipts - totalReductions);
|
|
8164
8232
|
if (totalExcessiveDesignation > 0) issues.push(`Schedule 54: ${totalExcessiveDesignation} of eligible dividends were designated while low rate income pool remained, so they are excessive eligible dividend designations. Carry this to amount C of Schedule 55, where it bears Part III.1 tax at 20% unless an election under subsection 185.1(2) is made.`);
|
|
8165
8233
|
return {
|
|
@@ -8177,7 +8245,7 @@ function computeSchedule54(input) {
|
|
|
8177
8245
|
}
|
|
8178
8246
|
//#endregion
|
|
8179
8247
|
//#region src/t2/schedules/schedule55-part3-1.ts
|
|
8180
|
-
const nn$
|
|
8248
|
+
const nn$2 = (v) => Math.max(0, v ?? 0);
|
|
8181
8249
|
/**
|
|
8182
8250
|
* Compute Part III.1 tax.
|
|
8183
8251
|
*
|
|
@@ -8186,10 +8254,10 @@ const nn$4 = (v) => Math.max(0, v ?? 0);
|
|
|
8186
8254
|
*/
|
|
8187
8255
|
function computeSchedule55(input, baseRate = .2, paragraphCRate = .1) {
|
|
8188
8256
|
const issues = [];
|
|
8189
|
-
const excessiveDesignation = nn$
|
|
8257
|
+
const excessiveDesignation = nn$2(input.excessiveDesignation);
|
|
8190
8258
|
const arisesUnderParagraphC = input.arisesUnderParagraphC === true;
|
|
8191
8259
|
const electionAvailable = !arisesUnderParagraphC;
|
|
8192
|
-
const claimed = input.electedAmount !== void 0 ? nn$
|
|
8260
|
+
const claimed = input.electedAmount !== void 0 ? nn$2(input.electedAmount) : input.electsMaximum === true ? excessiveDesignation : 0;
|
|
8193
8261
|
if (claimed > 0 && !electionAvailable) issues.push(`Part III.1: a subsection 185.1(2) election of ${claimed} was claimed, but the election is not available where the excess arises under paragraph (c) of the section 89(1) definition. The full ${excessiveDesignation} remains taxable at ${(baseRate + paragraphCRate) * 100}%.`);
|
|
8194
8262
|
let reclassifiedAsOrdinary = electionAvailable ? Math.min(claimed, excessiveDesignation) : 0;
|
|
8195
8263
|
if (electionAvailable && claimed > excessiveDesignation) {
|
|
@@ -8314,6 +8382,24 @@ function computeSchedule101(input) {
|
|
|
8314
8382
|
* schedules): AAII refundable tax + RDTOH, Part IV tax, M&P, associated-group
|
|
8315
8383
|
* allocation, provincial/AT1 tax (computed separately).
|
|
8316
8384
|
*/
|
|
8385
|
+
/**
|
|
8386
|
+
* Every `issues` string the given sub-results raised, in one flat list.
|
|
8387
|
+
*
|
|
8388
|
+
* Deliberately structural rather than a hand-written list of fields: a result
|
|
8389
|
+
* qualifies by HAVING an `issues` array, so a schedule that starts raising them
|
|
8390
|
+
* is surfaced without anyone remembering to come back here. Duplicates are
|
|
8391
|
+
* dropped — two schedules can reach the same conclusion about the same missing
|
|
8392
|
+
* input, and a preparer should be told once.
|
|
8393
|
+
*/
|
|
8394
|
+
function collectIssues(results) {
|
|
8395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8396
|
+
for (const value of Object.values(results)) {
|
|
8397
|
+
const issues = value?.issues;
|
|
8398
|
+
if (!Array.isArray(issues)) continue;
|
|
8399
|
+
for (const issue of issues) if (typeof issue === "string" && issue !== "") seen.add(issue);
|
|
8400
|
+
}
|
|
8401
|
+
return [...seen];
|
|
8402
|
+
}
|
|
8317
8403
|
/** Date | ISO string | undefined → `YYYY-MM-DD` ('' when absent/invalid). */
|
|
8318
8404
|
function isoDay(v) {
|
|
8319
8405
|
if (v instanceof Date) return Number.isNaN(v.getTime()) ? "" : v.toISOString().slice(0, 10);
|
|
@@ -8456,76 +8542,266 @@ function computeFederalT2(input) {
|
|
|
8456
8542
|
ref: "s.66(4)/66.7(2)"
|
|
8457
8543
|
});
|
|
8458
8544
|
}
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8545
|
+
/**
|
|
8546
|
+
* Schedule 1 → net income → Division C → taxable income → Schedule 33, as a
|
|
8547
|
+
* closure, because EIFEL makes it run **twice**.
|
|
8548
|
+
*
|
|
8549
|
+
* Subsection 18.2(1) defines adjusted taxable income from taxable income
|
|
8550
|
+
* "determined without regard to subsection 18.2(2), paragraphs 12(1)(l.2)
|
|
8551
|
+
* and 111(1)(a.1)" — and those three are exactly the three adjustments the
|
|
8552
|
+
* EIFEL computation produces (Schedule 1 lines 251 and 252, and the jacket
|
|
8553
|
+
* line 336 deduction). So the limitation cannot be an input to the base it
|
|
8554
|
+
* is computed from: the first pass supplies that base, the EIFEL chain runs
|
|
8555
|
+
* on it, and the second pass applies the three adjustments it produced.
|
|
8556
|
+
*
|
|
8557
|
+
* Every step in here is a pure function of its arguments, so running it
|
|
8558
|
+
* twice costs only arithmetic and cannot drift between the passes — which
|
|
8559
|
+
* is the reason it is one closure rather than two copies of the sequence.
|
|
8560
|
+
*/
|
|
8561
|
+
const runIncomeSequence = (eifelAdjustments) => {
|
|
8562
|
+
const passAdditions = eifelAdjustments ? [...schedule1Additions, ...eifelAdjustments.additions] : schedule1Additions;
|
|
8563
|
+
const schedule1 = computeSchedule1({
|
|
8564
|
+
bookNetIncome: input.bookNetIncome,
|
|
8565
|
+
...passAdditions.length ? { additions: passAdditions } : {},
|
|
8566
|
+
...schedule1Deductions.length ? { deductions: schedule1Deductions } : {}
|
|
8567
|
+
});
|
|
8568
|
+
const partVI1 = input.preferredShareDividends && ((input.preferredShareDividends.shortTermPreferredDividends ?? 0) > 0 || (input.preferredShareDividends.otherPreferredDividends ?? 0) > 0) ? computeSchedule43(input.preferredShareDividends, {
|
|
8569
|
+
SHORT_TERM_RATE: rates.PART_VI_1_SHORT_TERM_RATE,
|
|
8570
|
+
ELECTED_RATE: rates.PART_VI_1_ELECTED_RATE,
|
|
8571
|
+
BASE_RATE: rates.PART_VI_1_BASE_RATE,
|
|
8572
|
+
DIVIDEND_ALLOWANCE: rates.PART_VI_1_DIVIDEND_ALLOWANCE,
|
|
8573
|
+
ALLOWANCE_GRIND_THRESHOLD: rates.PART_VI_1_ALLOWANCE_GRIND_THRESHOLD
|
|
8574
|
+
}) : void 0;
|
|
8575
|
+
const partVI1Deduction = partVI1 ? computePartVI1Deduction(partVI1.partVI1Tax, isoDay(input.periodEnd)) : void 0;
|
|
8576
|
+
const donations = input.charitableDonations !== void 0 || input.openingDonationPool !== void 0 ? computeSchedule2({
|
|
8577
|
+
...input.openingDonationPool !== void 0 ? { openingDonationPool: input.openingDonationPool } : {},
|
|
8578
|
+
...input.charitableDonations !== void 0 ? { currentYearDonations: input.charitableDonations } : {},
|
|
8579
|
+
netIncomeForTax: schedule1.netIncomeForTax
|
|
8580
|
+
}, rates) : void 0;
|
|
8581
|
+
const culturalEcologicalGiftsClaimed = Math.max(0, input.culturalEcologicalGifts ?? 0);
|
|
8582
|
+
const divisionCDeductions = [
|
|
8583
|
+
...donations && donations.donationsClaimed > 0 ? [{
|
|
8584
|
+
line: "112",
|
|
8585
|
+
label: "Charitable donations and gifts from Schedule 2",
|
|
8586
|
+
amount: donations.donationsClaimed,
|
|
8587
|
+
ref: "s.110.1"
|
|
8588
|
+
}] : [],
|
|
8589
|
+
...culturalEcologicalGiftsClaimed > 0 ? [{
|
|
8590
|
+
line: "112",
|
|
8591
|
+
label: "Gifts of certified cultural property and ecologically sensitive land from Schedule 2",
|
|
8592
|
+
amount: culturalEcologicalGiftsClaimed,
|
|
8593
|
+
ref: "s.110.1(1)(b)/(c) — not subject to the 75% income limit"
|
|
8594
|
+
}] : [],
|
|
8595
|
+
...partVI1Deduction && partVI1Deduction.deduction > 0 ? [{
|
|
8596
|
+
line: "325",
|
|
8597
|
+
label: "Part VI.1 tax deduction",
|
|
8598
|
+
amount: partVI1Deduction.deduction,
|
|
8599
|
+
ref: `s.110(1)(k) — ${partVI1Deduction.multiple}× Part VI.1 tax payable`
|
|
8600
|
+
}] : [],
|
|
8601
|
+
...input.divisionCDeductions ?? [],
|
|
8602
|
+
...eifelAdjustments?.deductions ?? []
|
|
8603
|
+
];
|
|
8604
|
+
const taxableIncomeCalc = computeTaxableIncome({
|
|
8605
|
+
netIncomeForTax: schedule1.netIncomeForTax,
|
|
8606
|
+
...divisionCDeductions.length ? { deductions: divisionCDeductions } : {}
|
|
8607
|
+
});
|
|
8608
|
+
const currentYearNonCapitalLoss = Math.max(0, -schedule1.netIncomeForTax);
|
|
8609
|
+
const lossCarryback = input.lossCarrybacks?.length ? computeLossCarryback({
|
|
8610
|
+
currentYearLoss: currentYearNonCapitalLoss,
|
|
8611
|
+
carrybacks: input.lossCarrybacks
|
|
8612
|
+
}) : void 0;
|
|
8613
|
+
const nonCapitalCarriedBack = lossCarryback ? lossCarryback.totalCarriedBack : input.nonCapitalLossCarriedBack;
|
|
8614
|
+
const losses = computeSchedule4Losses({
|
|
8615
|
+
...input.openingNonCapitalLoss !== void 0 ? { openingNonCapitalLoss: input.openingNonCapitalLoss } : {},
|
|
8616
|
+
...input.openingNetCapitalLoss !== void 0 ? { openingNetCapitalLoss: input.openingNetCapitalLoss } : {},
|
|
8617
|
+
netIncomeForTax: schedule1.netIncomeForTax,
|
|
8618
|
+
taxableIncomeBeforeLosses: taxableIncomeCalc.taxableIncome,
|
|
8619
|
+
...capitalGains ? { taxableCapitalGains: capitalGains.taxableCapitalGain } : input.taxableCapitalGains !== void 0 ? { taxableCapitalGains: input.taxableCapitalGains } : {},
|
|
8620
|
+
...input.nonCapitalLossToApply !== void 0 ? { nonCapitalLossToApply: input.nonCapitalLossToApply } : {},
|
|
8621
|
+
...input.netCapitalLossToApply !== void 0 ? { netCapitalLossToApply: input.netCapitalLossToApply } : {},
|
|
8622
|
+
...nonCapitalCarriedBack !== void 0 ? { nonCapitalCarriedBack } : {},
|
|
8623
|
+
...input.nonCapitalLossExpired !== void 0 ? { nonCapitalExpired: input.nonCapitalLossExpired } : {},
|
|
8624
|
+
...input.netCapitalLossExpired !== void 0 ? { netCapitalExpired: input.netCapitalLossExpired } : {},
|
|
8625
|
+
...capitalGains && capitalGains.currentYearNetCapitalLoss > 0 ? { currentYearNetCapitalLoss: capitalGains.currentYearNetCapitalLoss } : input.currentYearNetCapitalLoss !== void 0 ? { currentYearNetCapitalLoss: input.currentYearNetCapitalLoss } : {}
|
|
8626
|
+
});
|
|
8627
|
+
const taxableIncome = Math.max(0, taxableIncomeCalc.taxableIncome - losses.totalApplied);
|
|
8628
|
+
const associatedAllocation = input.associatedMembers ? computeBusinessLimitAllocation({ members: input.associatedMembers }) : void 0;
|
|
8629
|
+
const taxableCapitalSchedule = input.taxableCapitalDetail ? computeTaxableCapital({
|
|
8630
|
+
...input.taxableCapitalDetail,
|
|
8631
|
+
...input.taxableCapitalDetail.taxableIncome === void 0 ? { taxableIncome } : {}
|
|
8632
|
+
}) : void 0;
|
|
8633
|
+
return {
|
|
8634
|
+
schedule1,
|
|
8635
|
+
partVI1,
|
|
8636
|
+
partVI1Deduction,
|
|
8637
|
+
donations,
|
|
8638
|
+
culturalEcologicalGiftsClaimed,
|
|
8639
|
+
divisionCDeductions,
|
|
8640
|
+
taxableIncomeCalc,
|
|
8641
|
+
currentYearNonCapitalLoss,
|
|
8642
|
+
lossCarryback,
|
|
8643
|
+
nonCapitalCarriedBack,
|
|
8644
|
+
losses,
|
|
8645
|
+
taxableIncome,
|
|
8646
|
+
associatedAllocation,
|
|
8647
|
+
taxableCapitalSchedule,
|
|
8648
|
+
grindTaxableCapital: input.taxableCapital ?? taxableCapitalSchedule?.taxableCapitalEmployedInCanada
|
|
8649
|
+
};
|
|
8650
|
+
};
|
|
8651
|
+
const provisional = runIncomeSequence();
|
|
8652
|
+
const eifel = assessEifel({
|
|
8653
|
+
taxYearStart: isoDay(input.periodStart ?? input.period?.start),
|
|
8654
|
+
...(input.eifel?.isCcpc ?? input.isCcpc) !== void 0 ? { isCcpc: input.eifel?.isCcpc ?? input.isCcpc } : {},
|
|
8655
|
+
...(() => {
|
|
8656
|
+
const tc = input.eifel?.groupTaxableCapital ?? provisional.taxableCapitalSchedule?.taxableCapitalEmployedInCanada ?? input.taxableCapital;
|
|
8657
|
+
return tc !== void 0 ? { groupTaxableCapital: tc } : {};
|
|
8658
|
+
})(),
|
|
8659
|
+
...input.eifel?.netInterestAndFinancingExpenses !== void 0 ? { netInterestAndFinancingExpenses: input.eifel.netInterestAndFinancingExpenses } : {},
|
|
8660
|
+
...input.eifel?.domesticExceptionApplies !== void 0 ? { domesticExceptionApplies: input.eifel.domesticExceptionApplies } : {}
|
|
8661
|
+
}, {
|
|
8662
|
+
SMALL_CCPC_TAXABLE_CAPITAL: rates.EIFEL_SMALL_CCPC_TAXABLE_CAPITAL,
|
|
8663
|
+
DE_MINIMIS_NET_IFE: rates.EIFEL_DE_MINIMIS_NET_IFE
|
|
8521
8664
|
});
|
|
8522
|
-
const
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8665
|
+
const eifelChain = (() => {
|
|
8666
|
+
if (!eifel.requiresLimitation) return null;
|
|
8667
|
+
const e = input.eifel;
|
|
8668
|
+
const taxYearStart = isoDay(input.periodStart ?? input.period?.start);
|
|
8669
|
+
const exemptIfe = computeExemptIfe(e?.exemptIfe ?? []);
|
|
8670
|
+
const borrowings = computeBorrowings(e?.borrowings ?? []);
|
|
8671
|
+
const loans = computeLoans(e?.loans ?? []);
|
|
8672
|
+
const partnership = computePartnershipIfe(e?.partnershipIfe ?? []);
|
|
8673
|
+
const capitalized = computeCapitalizedIfe(e?.capitalizedIfe ?? []);
|
|
8674
|
+
const resourceIfe = computeResourceIfe(e?.resourceIfe ?? []);
|
|
8675
|
+
const lossPortion = computeLossPortionFromIfe(e?.lossPortionFromIfe ?? []);
|
|
8676
|
+
const ife = computeInterestAndFinancingExpenses({
|
|
8677
|
+
...e?.ifeDetail ?? {},
|
|
8678
|
+
interestOnBorrowings: borrowings.interestPaidOrPayable,
|
|
8679
|
+
ifeInCca: capitalized.totalIfeInCca,
|
|
8680
|
+
ifeInResourceExpenses: resourceIfe.totalIfeInResourceClaims,
|
|
8681
|
+
ifeInTerminalLoss: capitalized.totalIfeInTerminalLoss,
|
|
8682
|
+
fundingCostAmounts: borrowings.fundingCostAmounts,
|
|
8683
|
+
partnershipShare: partnership.totalIncluded,
|
|
8684
|
+
costReducingAmounts: borrowings.costReducingAmounts
|
|
8685
|
+
});
|
|
8686
|
+
const ifr = computeInterestAndFinancingRevenues({
|
|
8687
|
+
...e?.ifrDetail ?? {},
|
|
8688
|
+
returnAmounts: loans.returnAmounts,
|
|
8689
|
+
returnReducingAmounts: loans.returnReducingAmounts
|
|
8690
|
+
});
|
|
8691
|
+
const eifelIfe = e?.interestAndFinancingExpenses ?? ife.totalIfe;
|
|
8692
|
+
const eifelIfr = e?.interestAndFinancingRevenues ?? ifr.totalIfr;
|
|
8693
|
+
const ati = computeAdjustedTaxableIncome({
|
|
8694
|
+
...e?.adjustedTaxableIncomeDetail ?? {},
|
|
8695
|
+
taxableIncome: provisional.taxableIncome,
|
|
8696
|
+
nonCapitalLossForYear: provisional.currentYearNonCapitalLoss,
|
|
8697
|
+
lossClaimNotReducingTaxableIncome: Math.max(0, provisional.losses.totalApplied - Math.max(0, provisional.taxableIncomeCalc.taxableIncome)),
|
|
8698
|
+
interestAndFinancingExpenses: eifelIfe,
|
|
8699
|
+
capitalCostAllowance: cca?.totalCca ?? 0,
|
|
8700
|
+
resourceDeductions: resourceDeductions ? resourceDeductions.depletionClaim + resourceDeductions.ceeClaim + resourceDeductions.cdeClaim + resourceDeductions.cogpeClaim + resourceDeductions.foreignClaim : 0,
|
|
8701
|
+
terminalLoss: cca?.totalTerminalLoss ?? 0,
|
|
8702
|
+
partVI1TaxDeduction: provisional.partVI1Deduction?.deduction ?? 0,
|
|
8703
|
+
lossPortionDerivedFromIfe: lossPortion.totalAttributableToIfe,
|
|
8704
|
+
exemptIfeActivityLoss: exemptIfe.lossFromExemptActivities,
|
|
8705
|
+
exemptIfeActivityIncome: exemptIfe.incomeFromExemptActivities,
|
|
8706
|
+
interestAndFinancingRevenues: eifelIfr,
|
|
8707
|
+
recapture: cca?.totalRecapture ?? 0
|
|
8708
|
+
});
|
|
8709
|
+
const resolvedAti = e?.adjustedTaxableIncome ?? ati.adjustedTaxableIncome;
|
|
8710
|
+
const capacityBase = computeEifelCapacity({
|
|
8711
|
+
adjustedTaxableIncome: resolvedAti,
|
|
8712
|
+
interestAndFinancingExpenses: eifelIfe,
|
|
8713
|
+
interestAndFinancingRevenues: eifelIfr,
|
|
8714
|
+
ratioOfPermissibleExpenses: ratioOfPermissibleExpenses(taxYearStart),
|
|
8715
|
+
...e?.hasGroupRatioElection !== void 0 ? { hasGroupRatioElection: e.hasGroupRatioElection } : {},
|
|
8716
|
+
...e?.groupRatioAmount !== void 0 ? { groupRatioAmount: e.groupRatioAmount } : {},
|
|
8717
|
+
...e?.receivedCapacity ? { receivedCapacity: e.receivedCapacity } : {},
|
|
8718
|
+
...e?.rifeFromPreviousYears !== void 0 ? { rifeFromPreviousYears: e.rifeFromPreviousYears } : {},
|
|
8719
|
+
...e?.priorYearExcessCapacity ? { priorYearExcessCapacity: e.priorYearExcessCapacity } : {}
|
|
8720
|
+
});
|
|
8721
|
+
const limitation = computeEifelLimitation({
|
|
8722
|
+
interestAndFinancingExpenses: eifelIfe,
|
|
8723
|
+
adjustedTaxableIncome: resolvedAti,
|
|
8724
|
+
interestAndFinancingRevenues: eifelIfr,
|
|
8725
|
+
excessReceivedCapacity: capacityBase.excessReceivedCapacity,
|
|
8726
|
+
absorbedCapacity: capacityBase.absorbedCapacity,
|
|
8727
|
+
...e?.hasGroupRatioElection && e?.groupRatioAmount !== void 0 ? { groupRatioAmount: e.groupRatioAmount } : {},
|
|
8728
|
+
taxYearStart
|
|
8729
|
+
});
|
|
8730
|
+
const { excessIfe } = computeExcessIfe({
|
|
8731
|
+
variableAOfIfe: e?.interestAndFinancingExpenses ?? ife.variableA,
|
|
8732
|
+
partnershipShare: partnership.totalIncluded,
|
|
8733
|
+
...e?.ifeDetail?.affiliateRaife !== void 0 ? { affiliateRaife: e.ifeDetail.affiliateRaife } : {},
|
|
8734
|
+
deniedProportion: limitation.deniedProportion
|
|
8735
|
+
});
|
|
8736
|
+
const partnershipIfeAddBack = e?.partnershipIfeAddBack ?? computePartnershipIfeAddBack(partnership.totalIncluded, limitation.deniedProportion);
|
|
8737
|
+
const clause95 = computeClause95Amounts(e?.clause95Denied ?? [], e?.clause95Included ?? [], limitation.deniedProportion);
|
|
8738
|
+
return {
|
|
8739
|
+
ati,
|
|
8740
|
+
limitation,
|
|
8741
|
+
capacity: {
|
|
8742
|
+
...capacityBase,
|
|
8743
|
+
rifeForYear: computeRifeUnderSubsection111_8({
|
|
8744
|
+
excessInterestAndFinancingExpenses: excessIfe,
|
|
8745
|
+
partnershipIfeAddBack,
|
|
8746
|
+
clause95FapiAmountI: e?.clause95FapiAmountI ?? clause95.deniedUnderSubclauseI,
|
|
8747
|
+
clause95FapiAmountII: e?.clause95FapiAmountII ?? clause95.includedUnderSubclauseII
|
|
8748
|
+
}),
|
|
8749
|
+
issues: [
|
|
8750
|
+
...capacityBase.issues,
|
|
8751
|
+
...capitalized.issues,
|
|
8752
|
+
...resourceIfe.issues,
|
|
8753
|
+
...lossPortion.issues
|
|
8754
|
+
]
|
|
8755
|
+
},
|
|
8756
|
+
ife,
|
|
8757
|
+
ifr,
|
|
8758
|
+
excessIfe,
|
|
8759
|
+
partnershipIfeAddBack
|
|
8760
|
+
};
|
|
8761
|
+
})();
|
|
8762
|
+
/**
|
|
8763
|
+
* A return inside the regime that never supplied its GROSS interest and
|
|
8764
|
+
* financing expenses computes a denial of nil — which looks like "nothing was
|
|
8765
|
+
* restricted" and is really "nothing was measured". The group NET figure that
|
|
8766
|
+
* settled the de-minimis test above is not a substitute: it is a different
|
|
8767
|
+
* figure, for a different purpose, at group level.
|
|
8768
|
+
*/
|
|
8769
|
+
const eifelWithIssues = eifel.requiresLimitation && !((eifelChain?.ife.totalIfe ?? 0) > 0) ? {
|
|
8770
|
+
...eifel,
|
|
8771
|
+
issues: [...eifel.issues, "EIFEL: the corporation is inside the regime but its gross interest and financing expenses (Schedule 130 Part 2A line 045) were not supplied, so the limitation measured nothing and denied nothing. Supply that figure — the group net figure used for the de minimis test is not the same amount."]
|
|
8772
|
+
} : eifel;
|
|
8773
|
+
/**
|
|
8774
|
+
* The three adjustments the EIFEL computation produces, on the lines the
|
|
8775
|
+
* forms name for them:
|
|
8776
|
+
*
|
|
8777
|
+
* Schedule 1 line 251 the denial under s.18.2(2) (Part 2L amount B)
|
|
8778
|
+
* Schedule 1 line 252 the 12(1)(l.2) partnership add-back (Part 2N)
|
|
8779
|
+
* jacket line 336 the 111(1)(a.1) RIFE deduction (Part 2J amount B)
|
|
8780
|
+
*
|
|
8781
|
+
* Each is one of the three things adjusted taxable income is defined to
|
|
8782
|
+
* disregard, which is why applying them needs the second pass below rather
|
|
8783
|
+
* than being folded into the first.
|
|
8784
|
+
*/
|
|
8785
|
+
const eifelAdjustments = eifelChain ? {
|
|
8786
|
+
additions: [...eifelChain.excessIfe > 0 ? [{
|
|
8787
|
+
line: "251",
|
|
8788
|
+
label: "Excess IFE under subsection 18.2(2) from Schedule 130",
|
|
8789
|
+
amount: eifelChain.excessIfe,
|
|
8790
|
+
ref: "s.18.2(2)"
|
|
8791
|
+
}] : [], ...eifelChain.partnershipIfeAddBack > 0 ? [{
|
|
8792
|
+
line: "252",
|
|
8793
|
+
label: "Partnership IFE add-back under paragraph 12(1)(l.2) from Schedule 130",
|
|
8794
|
+
amount: eifelChain.partnershipIfeAddBack,
|
|
8795
|
+
ref: "s.12(1)(l.2)"
|
|
8796
|
+
}] : []],
|
|
8797
|
+
deductions: eifelChain.capacity.rifeDeductible > 0 ? [{
|
|
8798
|
+
line: "336",
|
|
8799
|
+
label: "Restricted interest and financing expenses from Schedule 4",
|
|
8800
|
+
amount: eifelChain.capacity.rifeDeductible,
|
|
8801
|
+
ref: "s.111(1)(a.1)"
|
|
8802
|
+
}] : []
|
|
8803
|
+
} : void 0;
|
|
8804
|
+
const { schedule1, partVI1, partVI1Deduction, donations, taxableIncomeCalc, lossCarryback, losses, taxableIncome, associatedAllocation, taxableCapitalSchedule, grindTaxableCapital } = ((eifelAdjustments?.additions.length ?? 0) > 0 || (eifelAdjustments?.deductions.length ?? 0) > 0) && eifelAdjustments ? runIncomeSequence(eifelAdjustments) : provisional;
|
|
8529
8805
|
const adjustedAggregateInvestmentIncomeSchedule = input.adjustedAggregateInvestmentIncomeDetail ? computeAdjustedAggregateInvestmentIncome(input.adjustedAggregateInvestmentIncomeDetail) : void 0;
|
|
8530
8806
|
const resolvedAaii = input.aaii ?? adjustedAggregateInvestmentIncomeSchedule?.adjustedAggregateInvestmentIncome;
|
|
8531
8807
|
const aggregateInvestmentIncomeSchedule = input.aggregateInvestmentIncomeDetail ? computeAggregateInvestmentIncome(input.aggregateInvestmentIncomeDetail) : void 0;
|
|
@@ -8617,19 +8893,6 @@ function computeFederalT2(input) {
|
|
|
8617
8893
|
...input.provincialAllocationFactor !== void 0 ? { allocationFactor: input.provincialAllocationFactor } : {}
|
|
8618
8894
|
}, provinceRates) : void 0;
|
|
8619
8895
|
const totalTax = totalFederalTax + (provincialAllocation?.totalProvincialTax ?? provincial?.provincialTax ?? 0);
|
|
8620
|
-
const eifel = assessEifel({
|
|
8621
|
-
taxYearStart: isoDay(input.periodStart ?? input.period?.start),
|
|
8622
|
-
...(input.eifel?.isCcpc ?? input.isCcpc) !== void 0 ? { isCcpc: input.eifel?.isCcpc ?? input.isCcpc } : {},
|
|
8623
|
-
...(() => {
|
|
8624
|
-
const tc = input.eifel?.groupTaxableCapital ?? taxableCapitalSchedule?.taxableCapitalEmployedInCanada ?? input.taxableCapital;
|
|
8625
|
-
return tc !== void 0 ? { groupTaxableCapital: tc } : {};
|
|
8626
|
-
})(),
|
|
8627
|
-
...input.eifel?.netInterestAndFinancingExpenses !== void 0 ? { netInterestAndFinancingExpenses: input.eifel.netInterestAndFinancingExpenses } : {},
|
|
8628
|
-
...input.eifel?.domesticExceptionApplies !== void 0 ? { domesticExceptionApplies: input.eifel.domesticExceptionApplies } : {}
|
|
8629
|
-
}, {
|
|
8630
|
-
SMALL_CCPC_TAXABLE_CAPITAL: rates.EIFEL_SMALL_CCPC_TAXABLE_CAPITAL,
|
|
8631
|
-
DE_MINIMIS_NET_IFE: rates.EIFEL_DE_MINIMIS_NET_IFE
|
|
8632
|
-
});
|
|
8633
8896
|
const internetBusiness = input.internetBusiness ? normalizeSchedule88(input.internetBusiness) : void 0;
|
|
8634
8897
|
const firstReturn = input.firstReturn ? computeSchedule101(input.firstReturn) : void 0;
|
|
8635
8898
|
return {
|
|
@@ -8657,7 +8920,14 @@ function computeFederalT2(input) {
|
|
|
8657
8920
|
...partIII1 ? { partIII1 } : {},
|
|
8658
8921
|
...partVI1 ? { partVI1 } : {},
|
|
8659
8922
|
...partVI1Deduction ? { partVI1Deduction } : {},
|
|
8660
|
-
eifel,
|
|
8923
|
+
eifel: eifelWithIssues,
|
|
8924
|
+
...eifelChain ? {
|
|
8925
|
+
eifelAdjustedTaxableIncome: eifelChain.ati,
|
|
8926
|
+
eifelLimitation: eifelChain.limitation,
|
|
8927
|
+
eifelCapacity: eifelChain.capacity,
|
|
8928
|
+
eifelIfe: eifelChain.ife,
|
|
8929
|
+
eifelIfr: eifelChain.ifr
|
|
8930
|
+
} : {},
|
|
8661
8931
|
...internetBusiness ? { internetBusiness } : {},
|
|
8662
8932
|
...firstReturn ? { firstReturn } : {},
|
|
8663
8933
|
...sredItc ? { sredItc } : {},
|
|
@@ -8672,7 +8942,37 @@ function computeFederalT2(input) {
|
|
|
8672
8942
|
taxableIncome,
|
|
8673
8943
|
federalTaxPayable: partI.partITaxPayable,
|
|
8674
8944
|
totalFederalTax,
|
|
8675
|
-
totalTax
|
|
8945
|
+
totalTax,
|
|
8946
|
+
issues: collectIssues({
|
|
8947
|
+
eifel: eifelWithIssues,
|
|
8948
|
+
...eifelChain ? {
|
|
8949
|
+
eifelAdjustedTaxableIncome: eifelChain.ati,
|
|
8950
|
+
eifelLimitation: eifelChain.limitation,
|
|
8951
|
+
eifelCapacity: eifelChain.capacity,
|
|
8952
|
+
eifelIfe: eifelChain.ife,
|
|
8953
|
+
eifelIfr: eifelChain.ifr
|
|
8954
|
+
} : {},
|
|
8955
|
+
...partVI1 ? { partVI1 } : {},
|
|
8956
|
+
...class13 ? { class13 } : {},
|
|
8957
|
+
...class14 ? { class14 } : {},
|
|
8958
|
+
...class141AdditionalAllowance ? { class141AdditionalAllowance } : {},
|
|
8959
|
+
...associatedAllocation ? { associatedAllocation } : {},
|
|
8960
|
+
...sredItc ? { sredItc } : {},
|
|
8961
|
+
...firstReturn ? { firstReturn } : {}
|
|
8962
|
+
}),
|
|
8963
|
+
schedulePayloads: federalSchedulePayloads({
|
|
8964
|
+
schedule1,
|
|
8965
|
+
netIncomeForTax: schedule1.netIncomeForTax,
|
|
8966
|
+
taxableIncome,
|
|
8967
|
+
totalFederalTax,
|
|
8968
|
+
...donations ? { donations } : {},
|
|
8969
|
+
...adjustedAggregateInvestmentIncomeSchedule ? { adjustedAggregateInvestmentIncomeSchedule } : {},
|
|
8970
|
+
...cca ? { cca } : {},
|
|
8971
|
+
...foreignTaxCredit ? { foreignTaxCredit } : {},
|
|
8972
|
+
...taxableCapitalSchedule ? { taxableCapitalSchedule } : {},
|
|
8973
|
+
...grip ? { grip } : {},
|
|
8974
|
+
...partIII1 ? { partIII1 } : {}
|
|
8975
|
+
})
|
|
8676
8976
|
};
|
|
8677
8977
|
}
|
|
8678
8978
|
//#endregion
|
|
@@ -8910,7 +9210,7 @@ function computeQuebecTax(input, rates = QC_TAX_2024) {
|
|
|
8910
9210
|
}
|
|
8911
9211
|
//#endregion
|
|
8912
9212
|
//#region src/t2/co17/schedules/quebec-allocation.ts
|
|
8913
|
-
const nn$
|
|
9213
|
+
const nn$1 = (v) => Math.max(0, v ?? 0);
|
|
8914
9214
|
function computeQuebecAllocationFactor(establishments, quebecCode = "QC") {
|
|
8915
9215
|
let quebecRevenue = 0;
|
|
8916
9216
|
let totalRevenue = 0;
|
|
@@ -8919,8 +9219,8 @@ function computeQuebecAllocationFactor(establishments, quebecCode = "QC") {
|
|
|
8919
9219
|
let quebecCount = 0;
|
|
8920
9220
|
const total = establishments.length;
|
|
8921
9221
|
for (const pe of establishments) {
|
|
8922
|
-
const rev = nn$
|
|
8923
|
-
const pay = nn$
|
|
9222
|
+
const rev = nn$1(pe.grossRevenue);
|
|
9223
|
+
const pay = nn$1(pe.salariesWages);
|
|
8924
9224
|
totalRevenue += rev;
|
|
8925
9225
|
totalSalaries += pay;
|
|
8926
9226
|
if (pe.province === quebecCode) {
|
|
@@ -9408,215 +9708,6 @@ function computeT2Settlement(input) {
|
|
|
9408
9708
|
};
|
|
9409
9709
|
}
|
|
9410
9710
|
//#endregion
|
|
9411
|
-
//#region src/t2/schedules/eifel-adjusted-taxable-income.ts
|
|
9412
|
-
/**
|
|
9413
|
-
* ITA subsection 18.2(1) — **adjusted taxable income**, the base the EIFEL
|
|
9414
|
-
* ceiling is computed on.
|
|
9415
|
-
*
|
|
9416
|
-
* `eifel-limitation.ts` took this as a required input because deriving it
|
|
9417
|
-
* partially would produce a plausible figure from an incomplete definition. This
|
|
9418
|
-
* derives it, and is explicit about the components it does and does not cover.
|
|
9419
|
-
*
|
|
9420
|
-
* ── What it is ──────────────────────────────────────────────────────────────
|
|
9421
|
-
*
|
|
9422
|
-
* An EBITDA-like measure, built from taxable income by adding back the things the
|
|
9423
|
-
* regime is measuring against and removing the things that would double-count.
|
|
9424
|
-
*
|
|
9425
|
-
* ATI = A + B − C
|
|
9426
|
-
*
|
|
9427
|
-
* A = D − E the income base
|
|
9428
|
-
* B the ADD-BACKS
|
|
9429
|
-
* C the REDUCTIONS
|
|
9430
|
-
*
|
|
9431
|
-
* **The add-backs include the interest and financing expenses themselves.** That
|
|
9432
|
-
* is the point of the measure and the thing to hold on to: the ceiling is a
|
|
9433
|
-
* percentage of income computed *before* the very expenses being limited, so a
|
|
9434
|
-
* corporation cannot shrink its own ceiling by borrowing more.
|
|
9435
|
-
*
|
|
9436
|
-
* ── A — the income base (D − E) ─────────────────────────────────────────────
|
|
9437
|
-
*
|
|
9438
|
-
* **D** is taxable income for the year, determined **without regard to** s.18.2(2)
|
|
9439
|
-
* itself, paragraphs 12(1)(l.2) and 111(1)(a.1), and clause 95(2)(f.11)(ii)(D) —
|
|
9440
|
-
* a non-resident uses taxable income earned in Canada on the same basis. The
|
|
9441
|
-
* circularity is deliberate: the limitation cannot be an input to its own base.
|
|
9442
|
-
*
|
|
9443
|
-
* **E** subtracts the year's non-capital loss on the same basis, any amount
|
|
9444
|
-
* claimed under paragraph 111(1)(a) that did not actually reduce taxable income,
|
|
9445
|
-
* and a controlled-foreign-affiliate component (`T × U ÷ V`).
|
|
9446
|
-
*
|
|
9447
|
-
* ── B — the add-backs ───────────────────────────────────────────────────────
|
|
9448
|
-
*
|
|
9449
|
-
* (a) interest and financing expenses for the year
|
|
9450
|
-
* (b) capital cost allowance and resource deductions — paragraph 20(1)(a),
|
|
9451
|
-
* 59.1(a) and subsections 66(4), 66.1(2)/(3), 66.2(2), 66.21(4), 66.4(2),
|
|
9452
|
-
* 66.7(1)-(5)
|
|
9453
|
-
* (c) terminal losses — subsection 20(16)
|
|
9454
|
-
* (d) the taxpayer's share of a partnership's 20(1)(a) and 20(16) deductions
|
|
9455
|
-
* (e) the portion of a paragraph 111(1)(e) limited-partnership-loss claim
|
|
9456
|
-
* attributable to those amounts
|
|
9457
|
-
*
|
|
9458
|
-
* ── C — the reductions ──────────────────────────────────────────────────────
|
|
9459
|
-
*
|
|
9460
|
-
* (a) interest and financing revenues
|
|
9461
|
-
* (b) recapture — subsection 13(1)
|
|
9462
|
-
* (c) the taxpayer's share of a partnership's 13(1) inclusion
|
|
9463
|
-
* (d) resource inclusions — subsections 59(1), 59(3.2), paragraph 59.1(b)
|
|
9464
|
-
* (e) for a corporation, a grossed-up foreign tax credit amount:
|
|
9465
|
-
* **100/28** of what would be deductible under s.126(1), and
|
|
9466
|
-
* the s.126(2) amounts times the relevant factor
|
|
9467
|
-
*
|
|
9468
|
-
* ── Not modelled ────────────────────────────────────────────────────────────
|
|
9469
|
-
*
|
|
9470
|
-
* The trust variant of C(e), and the later paragraphs of B and C dealing with
|
|
9471
|
-
* foreign affiliate income and exempt interest. Each is available as an explicit
|
|
9472
|
-
* `otherAdditions` / `otherReductions` input rather than silently omitted, so a
|
|
9473
|
-
* preparer with one of those amounts can still arrive at the right figure and the
|
|
9474
|
-
* engine does not pretend the definition is shorter than it is.
|
|
9475
|
-
*
|
|
9476
|
-
* Source: `research/sources/legislation/ITA-section-18.2-EIFEL.txt`.
|
|
9477
|
-
*
|
|
9478
|
-
* Pure, whole dollars.
|
|
9479
|
-
*/
|
|
9480
|
-
/** C(e)(i) — s.126(1) amounts are grossed up by 100/28. */
|
|
9481
|
-
const FOREIGN_TAX_CREDIT_GROSS_UP = 100 / 28;
|
|
9482
|
-
const nn$2 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
9483
|
-
function computeAdjustedTaxableIncome(input) {
|
|
9484
|
-
const issues = [];
|
|
9485
|
-
const e = nn$2(input.nonCapitalLossForYear) + nn$2(input.lossClaimNotReducingTaxableIncome) + nn$2(input.foreignAccrualPropertyLossComponent);
|
|
9486
|
-
const incomeBase = Math.round(input.taxableIncome) - e;
|
|
9487
|
-
const totalAdditions = nn$2(input.interestAndFinancingExpenses) + nn$2(input.capitalCostAllowance) + nn$2(input.resourceDeductions) + nn$2(input.terminalLoss) + nn$2(input.partnershipCapitalAndTerminalShare) + nn$2(input.limitedPartnershipLossPortion) + nn$2(input.otherAdditions);
|
|
9488
|
-
const totalReductions = nn$2(input.interestAndFinancingRevenues) + nn$2(input.recapture) + nn$2(input.partnershipRecaptureShare) + nn$2(input.resourceInclusions) + Math.round(FOREIGN_TAX_CREDIT_GROSS_UP * nn$2(input.section126_1ForeignTaxCredits)) + nn$2(input.section126_2GrossedUp) + nn$2(input.otherReductions);
|
|
9489
|
-
const adjustedTaxableIncome = incomeBase + totalAdditions - totalReductions;
|
|
9490
|
-
if (adjustedTaxableIncome < 0) issues.push(`EIFEL: adjusted taxable income is negative (${adjustedTaxableIncome}), so the ceiling is nil and every interest and financing expense is denied. Check that the loss and add-back figures are complete before filing on that basis.`);
|
|
9491
|
-
if (input.interestAndFinancingExpenses === void 0) issues.push("EIFEL: no interest and financing expenses were added back to adjusted taxable income. They are paragraph (a) of the add-backs, and omitting them understates the ceiling — which denies more expense than the rules require.");
|
|
9492
|
-
return {
|
|
9493
|
-
incomeBase,
|
|
9494
|
-
totalAdditions,
|
|
9495
|
-
totalReductions,
|
|
9496
|
-
adjustedTaxableIncome,
|
|
9497
|
-
issues
|
|
9498
|
-
};
|
|
9499
|
-
}
|
|
9500
|
-
//#endregion
|
|
9501
|
-
//#region src/t2/schedules/eifel-limitation.ts
|
|
9502
|
-
/**
|
|
9503
|
-
* ITA subsection 18.2(2) — the excessive interest and financing expenses
|
|
9504
|
-
* limitation itself.
|
|
9505
|
-
*
|
|
9506
|
-
* `eifel-excluded-entity.ts` decides **whether** the regime applies. This decides
|
|
9507
|
-
* **how much** it denies, which was previously left unbuilt on the grounds that
|
|
9508
|
-
* computing it from an unbuilt definition would be confidently wrong.
|
|
9509
|
-
*
|
|
9510
|
-
* ── The provision ───────────────────────────────────────────────────────────
|
|
9511
|
-
*
|
|
9512
|
-
* s.18.2(2) denies a *proportion* of each interest and financing expense:
|
|
9513
|
-
*
|
|
9514
|
-
* (A − (B + C + D + E)) ÷ F
|
|
9515
|
-
*
|
|
9516
|
-
* A the taxpayer's interest and financing expenses for the year
|
|
9517
|
-
* B the group-ratio amount under s.18.21(2) where that applies, otherwise
|
|
9518
|
-
* **G × H** — the ratio of permissible expenses times adjusted taxable income
|
|
9519
|
-
* C the taxpayer's interest and financing revenues for the year
|
|
9520
|
-
* D received capacity, to the extent it exceeds the amount deductible under
|
|
9521
|
-
* paragraph 111(1)(a.1)
|
|
9522
|
-
* E absorbed capacity
|
|
9523
|
-
* F ordinarily the same figure as A
|
|
9524
|
-
*
|
|
9525
|
-
* Because F is A in the ordinary case, the *amount* denied is simply
|
|
9526
|
-
*
|
|
9527
|
-
* denied = A − (B + C + D + E), floored at nil
|
|
9528
|
-
*
|
|
9529
|
-
* which is the form this module computes, while still reporting the proportion —
|
|
9530
|
-
* the statute denies a fraction of *each* expense, and a preparer allocating the
|
|
9531
|
-
* denial across expense lines needs the fraction rather than the total.
|
|
9532
|
-
*
|
|
9533
|
-
* ── The ratio of permissible expenses ───────────────────────────────────────
|
|
9534
|
-
*
|
|
9535
|
-
* Keyed off when the taxation year **BEGINS**, not when it ends:
|
|
9536
|
-
*
|
|
9537
|
-
* begins on or after 2023-10-01 and before 2024-01-01 → **40%**
|
|
9538
|
-
* begins on or after 2024-01-01 → **30%**
|
|
9539
|
-
*
|
|
9540
|
-
* The 40% band is transitional and narrow — one quarter — and it does **not**
|
|
9541
|
-
* apply when determining cumulative unused excess capacity for a year beginning
|
|
9542
|
-
* on or after 1 January 2024. That carve-out is not modelled; excess-capacity
|
|
9543
|
-
* carry-forward is a separate mechanism this module does not compute.
|
|
9544
|
-
*
|
|
9545
|
-
* ── What this module does NOT compute ───────────────────────────────────────
|
|
9546
|
-
*
|
|
9547
|
-
* **Adjusted taxable income** is an input, not a derivation. Its definition is a
|
|
9548
|
-
* build-up from taxable income through a dozen add-backs and reductions —
|
|
9549
|
-
* interest and financing expenses, capital cost allowance, resource deductions,
|
|
9550
|
-
* loss claims with their own nested formulas — each with its own defined term. A
|
|
9551
|
-
* partial implementation would produce a plausible number from an incomplete
|
|
9552
|
-
* definition, which is precisely the failure this module was deferred to avoid.
|
|
9553
|
-
* It is therefore **required**, and an absent one denies nothing while saying so.
|
|
9554
|
-
*
|
|
9555
|
-
* Likewise the group-ratio election under s.18.21, and the received/absorbed
|
|
9556
|
-
* capacity amounts, which come from the excess-capacity regime.
|
|
9557
|
-
*
|
|
9558
|
-
* Source: `research/sources/legislation/ITA-section-18.2-EIFEL.txt`.
|
|
9559
|
-
*
|
|
9560
|
-
* Pure, whole dollars.
|
|
9561
|
-
*/
|
|
9562
|
-
/** The ratio bands, keyed off the taxation year START. */
|
|
9563
|
-
const EIFEL_TRANSITIONAL_RATIO = .4;
|
|
9564
|
-
const EIFEL_STANDARD_RATIO = .3;
|
|
9565
|
-
/** The regime's first day — years beginning before this are outside it. */
|
|
9566
|
-
const EIFEL_FIRST_YEAR_START = "2023-10-01";
|
|
9567
|
-
/** The transitional 40% band ends when years beginning in 2024 start. */
|
|
9568
|
-
const EIFEL_STANDARD_RATIO_FROM = "2024-01-01";
|
|
9569
|
-
const nn$1 = (v) => Math.max(0, Math.round(v ?? 0));
|
|
9570
|
-
function utcDay(iso) {
|
|
9571
|
-
return Date.parse(`${iso.slice(0, 10)}T00:00:00Z`);
|
|
9572
|
-
}
|
|
9573
|
-
/**
|
|
9574
|
-
* The ratio of permissible expenses for a year beginning on `taxYearStart`.
|
|
9575
|
-
* Returns 0 for a year beginning before the regime applies at all.
|
|
9576
|
-
*/
|
|
9577
|
-
function ratioOfPermissibleExpenses(taxYearStart) {
|
|
9578
|
-
const start = utcDay(taxYearStart);
|
|
9579
|
-
if (Number.isNaN(start) || start < utcDay("2023-10-01")) return 0;
|
|
9580
|
-
return start < utcDay("2024-01-01") ? EIFEL_TRANSITIONAL_RATIO : EIFEL_STANDARD_RATIO;
|
|
9581
|
-
}
|
|
9582
|
-
function computeEifelLimitation(input) {
|
|
9583
|
-
const issues = [];
|
|
9584
|
-
const ife = nn$1(input.interestAndFinancingExpenses);
|
|
9585
|
-
const nil = (ratio) => ({
|
|
9586
|
-
ratioOfPermissibleExpenses: ratio,
|
|
9587
|
-
permittedAmount: 0,
|
|
9588
|
-
usedGroupRatio: false,
|
|
9589
|
-
totalShelter: 0,
|
|
9590
|
-
deniedAmount: 0,
|
|
9591
|
-
deniedProportion: 0,
|
|
9592
|
-
deductibleAmount: ife,
|
|
9593
|
-
issues
|
|
9594
|
-
});
|
|
9595
|
-
const ratio = ratioOfPermissibleExpenses(input.taxYearStart);
|
|
9596
|
-
if (ratio === 0) {
|
|
9597
|
-
issues.push(`EIFEL: the taxation year beginning ${input.taxYearStart} is before the regime applies (${EIFEL_FIRST_YEAR_START}), so nothing is denied.`);
|
|
9598
|
-
return nil(0);
|
|
9599
|
-
}
|
|
9600
|
-
const usedGroupRatio = input.groupRatioAmount !== void 0;
|
|
9601
|
-
if (!usedGroupRatio && input.adjustedTaxableIncome === void 0) {
|
|
9602
|
-
issues.push("EIFEL: adjusted taxable income was not supplied, so no interest and financing expense was denied. The limitation cannot be computed without it — the figure is a build-up from taxable income that this engine does not derive, and it must be supplied or the return reviewed by hand.");
|
|
9603
|
-
return nil(ratio);
|
|
9604
|
-
}
|
|
9605
|
-
const permittedAmount = usedGroupRatio ? nn$1(input.groupRatioAmount) : Math.round(ratio * Math.max(0, input.adjustedTaxableIncome ?? 0));
|
|
9606
|
-
const totalShelter = permittedAmount + nn$1(input.interestAndFinancingRevenues) + nn$1(input.excessReceivedCapacity) + nn$1(input.absorbedCapacity);
|
|
9607
|
-
const deniedAmount = Math.max(0, ife - totalShelter);
|
|
9608
|
-
return {
|
|
9609
|
-
ratioOfPermissibleExpenses: ratio,
|
|
9610
|
-
permittedAmount,
|
|
9611
|
-
usedGroupRatio,
|
|
9612
|
-
totalShelter,
|
|
9613
|
-
deniedAmount,
|
|
9614
|
-
deniedProportion: ife > 0 ? deniedAmount / ife : 0,
|
|
9615
|
-
deductibleAmount: ife - deniedAmount,
|
|
9616
|
-
issues
|
|
9617
|
-
};
|
|
9618
|
-
}
|
|
9619
|
-
//#endregion
|
|
9620
9711
|
//#region src/t2/schedules/schedule27-mp.ts
|
|
9621
9712
|
/**
|
|
9622
9713
|
* T2 Schedule 27 — Canadian Manufacturing and Processing Profits Deduction
|
|
@@ -9794,4 +9885,4 @@ function computeMpDeduction(input, rates = MP_RATES_2024) {
|
|
|
9794
9885
|
};
|
|
9795
9886
|
}
|
|
9796
9887
|
//#endregion
|
|
9797
|
-
export {
|
|
9888
|
+
export { computeSchedule6 as $, resolveRates as $i, resolveCcaRates as $n, computeIegReductionFactor as $r, dividendsDeductibleS112 as $t, LRIP_INVESTMENT_CORPORATION_MULTIPLE as A, schedule12LossDeductions as Ai, AT1_RESERVE_LINES as An, renderRsiHeader as Ar, computeExcessIfe as At, computeZetm as B, schedule2Values$1 as Bi, CLASS_14_1_MINIMUM_DEDUCTION as Bn, assertCriticalFields as Br, assessEifel as Bt, runConformance as C, SINGLE_JURISDICTION_ALBERTA_FACTOR as Ci, AT1_DONATION_INCOME_RATE as Cn, RSI_NEGATIVE_PREFIX as Cr, EIFEL_STANDARD_RATIO_FROM as Ct, SCHEDULE_88_MAX_URLS as D, AT1_SCHEDULES_WITH_BUILDERS as Di, SECTION_34_2_GROSS_UP as Dn, formatRsiDate as Dr, computeBorrowings as Dt, computeSchedule101 as E, AT1_SCHEDULES_WITHOUT_BUILDERS as Ei, AT1_DISPOSITION_CATEGORIES as En, formatRsiAmount as Er, ratioOfPermissibleExpenses as Et, LARGE_CORPORATION_THRESHOLD as F, schedule18Values as Fi, computeAlbertaSchedule13 as Fn, At1MandatoryFieldMissingError as Fr, computeLossPortionFromIfe as Ft, computeCde as G, computeDayWeightedGeneralTax as Gi, computeClass13 as Gn, computeAlbertaReturn as Gr, PROVINCE_RATES_2024 as Gt, computeBusinessLimitAllocation as H, computeAlbertaTax as Hi, CLASS_14_1_TRANSITIONAL_RATE as Hn, at1YesNo as Hr, computeRifeUnderSubsection111_8 as Ht, computeTaxableCapital as I, schedule1Values$1 as Ii, UnsupportedCcaClassError as In, At1TaxPayableMismatchError as Ir, computePartnershipIfe as It, computeCumulativeForeignResource as J, resolveAlbertaTaxRates as Ji, computeClass141RecaptureReduction as Jn, computeIegAgreement as Jr, resolveProvinceRates as Jt, computeCee as K, AB_TAX_2024 as Ki, computeClass14 as Kn, allocateIegEvenly as Kr, PROVINCE_RATE_BOOK as Kt, computeSchedule31 as L, schedule20Values as Li, computeCcaClass as Ln, albertaBalanceUnpaid as Lr, computePartnershipIfeAddBack as Lt, computeSchedule54 as M, schedule13Values as Mi, computeAlbertaSchedule17 as Mn, renderAt1NetFile as Mr, computeInterestAndFinancingExpenses as Mt, computeGrip as N, schedule16Values as Ni, assistanceFrom as Nn, AT1_CRITICAL_MANDATORY_FIELDS as Nr, computeInterestAndFinancingRevenues as Nt, normalizeSchedule88 as O, at1LineItemId as Oi, computeAlbertaSchedule18 as On, formatRsiText as Or, computeCapitalizedIfe as Ot, computeSchedule43 as P, schedule17Values as Pi, computeAlbertaSchedule16 as Pn, At1CriticalFieldMissingError as Pr, computeLoans as Pt, computeSpecifiedForeignExploration as Q, latestRateYear as Qi, isDecliningBalanceClass as Qn, computeIegBaseAmount as Qr, computeTaxableIncome as Qt, ITC_RECAPTURE_PERIOD_YEARS as R, schedule21Values$1 as Ri, computeCcaSchedule as Rn, assertAt1MandatoryComplete as Rr, computeResourceIfe as Rt, formatConformanceReport as S, schedule3Values as Si, AT1_DONATION_GAIN_RATE as Sn, RSI_DELIMITER as Sr, EIFEL_STANDARD_RATIO as St, computeFederalT2 as T, computeAt4970 as Ti, computeSchedule20 as Tn, RsiLineItemError as Tr, computeEifelLimitation as Tt, computeSchedule21 as U, computeAlbertaSbd as Ui, MAX_LEASEHOLD_PERIODS as Un, xmlEscape as Ur, FOREIGN_TAX_CREDIT_GROSS_UP as Ut, allocateEvenly as V, schedule4970Values as Vi, CLASS_14_1_RECAPTURE_REDUCTION_RATE as Vn, at1TaxPayableDeductions as Vr, computeEifelCapacity as Vt, computeSchedule13 as W, AB_GENERAL_RATE_BANDS as Wi, MIN_LEASEHOLD_PERIODS as Wn, at1Engine as Wr, computeAdjustedTaxableIncome as Wt, computeForeignExploration as X, extendRateBook as Xi, CCA_DECLINING_BALANCE_RATES_2024 as Xn, IEG_2024 as Xr, dayWeightedRate as Xt, computeDepletion as Y, earliestRateYear as Yi, leaseholdPeriods as Yn, computeIegGroupFigures as Yr, blendProvinceRateTable as Yt, computeSchedule12ResourceDeductions as Z, hasExactRateYear as Zi, CCA_RATE_BOOK as Zn, computeIeg as Zr, charitableDonationsDeduction as Zt, QC_TAX_2024 as _, computeSfedeCountrySuccessor as _i, computeRifeContinuity as _n, toRsiHeader as _r, terminalLossDeduction as _t, computeCanadianMPProfits as a, computeCdeRegular as ai, computeAggregateInvestmentIncome as an, albertaDispositionAdjustments as ar, computePart4Rdtoh as at, T2_LINE_META as b, schedule4Values as bi, computeLimitedPartnershipLossRow as bn, toRsiSchedule as br, partVI1DeductionMultiple as bt, computeSmallManufacturerTest as c, computeCeeSuccessor as ci, CORP_TAX_2024 as cn, albertaResourceDeductionDifference as cr, amortizationAddBack as ct, t2Engine as d, computeCmedb as di, federalSchedulePayloads as dn, reconcileAlbertaNetIncome as dr, computeSchedule1 as dt, computeIegEligibleExpenditures as ei, netCapitalLossApplied as en, albertaAbilDifference as er, computeProvincialAllocation as et, renderCo17DraftReturn as f, computeEdaRegular as fi, parseT2LineItemId as fn, LossCarrybackError as fr, deferredIncomeTaxProvisionAddBack as ft, computeQuebecTax as g, computeSfedeCountryRegular as gi, computeOtherLossByYearOfOrigin as gn, validateAt1Transmitter as gr, recaptureAddBack as gt, computeQuebecAllocationFactor as h, computeFedeSuccessor as hi, computeNonCapitalLossByYearOfOrigin as hn, assertAt1TransmitterValid as hr, mealsAndEntertainmentAddBack as ht, SMALL_MANUFACTURER_INCOME_THRESHOLD as i, computeCcogpeSuccessor as ii, computeAdjustedAggregateInvestmentIncome as in, albertaCurrentYearLoss as ir, REFUNDABLE_PART_I_RATE as it, LRIP_INVESTMENT_INCOME_FACTOR as j, schedule12Values as ji, AT1_RESERVE_TOTAL_LINES as jn, renderRsiLineItem as jr, computeExemptIfe as jt, computeSchedule55 as k, schedule10Values as ki, AT1_RESERVE_KINDS as kn, renderAt1Rsi as kr, computeClause95Amounts as kt, computeT2Settlement as l, computeCfreRegular as li, CORP_TAX_RATE_BOOK as ln, albertaTerminalLossDifference as lr, assertSchedule1Fileable as lt, computeQuebecReturn as m, computeFedeRegular as mi, T2_CERTIFICATION_FIXTURES as mn, At1TransmitterInvalidError as mr, incomeTaxProvisionAddBack as mt, MP_GROSS_REVENUE_THRESHOLD as n, computeAlbertaSchedule15 as ni, computeCcpcActiveBusinessTax as nn, albertaCcaDifference as nr, computeSchedule4Losses as nt, computeMpDeduction as o, computeCdeSuccessor as oi, computeBusinessLimit as on, albertaRecaptureDifference as or, computeSchedule2 as ot, co17Engine as p, computeEdaSuccessor as pi, t2LineItemId as pn, computeLossCarryback as pr, findSchedule1LineDefects as pt, computeCogpe as q, AB_TAX_RATE_BOOK as qi, computeClass141AdditionalAllowance as qn, allocateIegExpenditureLimit as qr, isSchedule5Province as qt, MP_RATES_2024 as r, computeCcogpeRegular as ri, computePartITax as rn, albertaCcaScheduleAdjustments as rr, PART_IV_RATE as rt, computePart2MPProfits as s, computeCeeRegular as si, computeSBD as sn, albertaReserveDifference as sr, Schedule1NotFileableError as st, MP_EXCLUDED_ACTIVITIES as t, iegT661SourceLine as ti, nonCapitalLossApplied as tn, albertaCapitalGainDifference as tr, computeSchedule5 as tt, renderT2DraftReturn as u, computeCfreSuccessor as ui, resolveCorpTaxRates as un, computeSchedule12 as ur, ccaDeduction as ut, QC_TAX_RATE_BOOK as v, schedule15Values as vi, computeLossSchedule as vn, toRsiJacketSchedules as vr, PART_VI_1_DEDUCTION_BANDS as vt, runConformanceSuite as w, computeAllocationFactor as wi, computeDonationMaximum as wn, RSI_WORD_GAP as wr, EIFEL_TRANSITIONAL_RATIO as wt, foldT2Lines as x, computeSchedule3 as xi, computeLimitedPartnershipLosses as xn, RSI_COLUMN_GAP as xr, EIFEL_FIRST_YEAR_START as xt, resolveQuebecTaxRates as y, computeSchedule4 as yi, computeLossContinuity as yn, toRsiLineItems as yr, computePartVI1Deduction as yt, computeItcRecapture as z, schedule29Values as zi, computeSchedule8 as zn, assertAt1TaxPayableReconciles as zr, EIFEL_EFFECTIVE_FROM as zt };
|