@novha/calc-engines 6.1.0 → 6.3.0
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.
|
@@ -20,47 +20,35 @@ class CanadaCapitalGainsServiceImpl {
|
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
const taxableGain = gain * this._rules.inclusionRate;
|
|
23
|
-
|
|
24
|
-
const
|
|
23
|
+
// income BEFORE the capital gain
|
|
24
|
+
const baseIncome = this._input.totalTaxableIncome;
|
|
25
|
+
const incomeWithGain = baseIncome + taxableGain;
|
|
26
|
+
const baseTax = this.calculateProgressiveTax(baseIncome, this._rules.taxBrackets);
|
|
27
|
+
const taxWithGain = this.calculateProgressiveTax(incomeWithGain, this._rules.taxBrackets);
|
|
28
|
+
const capitalGainTax = taxWithGain - baseTax;
|
|
25
29
|
return {
|
|
26
30
|
taxableGain,
|
|
27
|
-
capitalGainTax
|
|
31
|
+
capitalGainTax,
|
|
28
32
|
socialContributions: 0,
|
|
29
33
|
netInvestmentIncomeTax: 0,
|
|
30
|
-
totalTax:
|
|
31
|
-
effectiveRate: gain > 0 ? (
|
|
32
|
-
breakdowns
|
|
34
|
+
totalTax: capitalGainTax,
|
|
35
|
+
effectiveRate: gain > 0 ? (capitalGainTax / gain) * 100 : 0,
|
|
36
|
+
breakdowns: []
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
|
-
|
|
39
|
+
calculateProgressiveTax(income, brackets) {
|
|
36
40
|
let tax = 0;
|
|
37
|
-
const breakdowns = [];
|
|
38
|
-
let remaining = taxableGain;
|
|
39
|
-
let incomeUsed = otherIncome;
|
|
40
41
|
for (const bracket of brackets) {
|
|
41
|
-
if (remaining <= 0)
|
|
42
|
-
break;
|
|
43
42
|
const upper = bracket.to ?? Infinity;
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const bracketSpace = upper - bracketStart;
|
|
48
|
-
const taxable = Math.min(bracketSpace, remaining);
|
|
43
|
+
if (income <= bracket.from)
|
|
44
|
+
break;
|
|
45
|
+
const taxable = Math.min(income, upper) - bracket.from;
|
|
49
46
|
if (taxable > 0) {
|
|
50
|
-
|
|
51
|
-
tax += bracketTax;
|
|
52
|
-
remaining -= taxable;
|
|
53
|
-
incomeUsed += taxable;
|
|
54
|
-
breakdowns.push({
|
|
55
|
-
from: `${bracket.from}`,
|
|
56
|
-
to: `${bracket.to ?? 'Above'}`,
|
|
57
|
-
rate: bracket.rate,
|
|
58
|
-
amount: bracketTax,
|
|
59
|
-
});
|
|
47
|
+
tax += taxable * bracket.rate;
|
|
60
48
|
}
|
|
61
49
|
}
|
|
62
|
-
return
|
|
50
|
+
return tax;
|
|
63
51
|
}
|
|
64
52
|
}
|
|
65
53
|
exports.CanadaCapitalGainsServiceImpl = CanadaCapitalGainsServiceImpl;
|
|
66
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
54
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FuYWRhQ2FwaXRhbEdhaW5zU2VydmljZUltcGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY2FwaXRhbC1nYWlucy9jYW5hZGEvQ2FuYWRhQ2FwaXRhbEdhaW5zU2VydmljZUltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBSUEsTUFBYSw2QkFBNkI7SUFJdEMsWUFBWSxLQUFZLEVBQUUsS0FBWTtRQUNsQyxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztJQUN4QixDQUFDO0lBRUQsU0FBUztRQUNMLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDO1FBRXJDLElBQUksSUFBSSxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ1osT0FBTztnQkFDSCxXQUFXLEVBQUUsQ0FBQztnQkFDZCxjQUFjLEVBQUUsQ0FBQztnQkFDakIsbUJBQW1CLEVBQUUsQ0FBQztnQkFDdEIsc0JBQXNCLEVBQUUsQ0FBQztnQkFDekIsUUFBUSxFQUFFLENBQUM7Z0JBQ1gsYUFBYSxFQUFFLENBQUM7Z0JBQ2hCLFVBQVUsRUFBRSxFQUFFO2FBQ2pCLENBQUM7UUFDTixDQUFDO1FBRUQsTUFBTSxXQUFXLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDO1FBRXJELGlDQUFpQztRQUNqQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGtCQUFrQixDQUFDO1FBRWxELE1BQU0sY0FBYyxHQUFHLFVBQVUsR0FBRyxXQUFXLENBQUM7UUFFaEQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLHVCQUF1QixDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ2xGLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUUxRixNQUFNLGNBQWMsR0FBRyxXQUFXLEdBQUcsT0FBTyxDQUFDO1FBRTdDLE9BQU87WUFDSCxXQUFXO1lBQ1gsY0FBYztZQUNkLG1CQUFtQixFQUFFLENBQUM7WUFDdEIsc0JBQXNCLEVBQUUsQ0FBQztZQUN6QixRQUFRLEVBQUUsY0FBYztZQUN4QixhQUFhLEVBQUUsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxjQUFjLEdBQUcsSUFBSSxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzNELFVBQVUsRUFBRSxFQUFFO1NBQ2pCLENBQUM7SUFDTixDQUFDO0lBRU8sdUJBQXVCLENBQUMsTUFBYyxFQUFFLFFBQXNCO1FBQ2xFLElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztRQUVaLEtBQUssTUFBTSxPQUFPLElBQUksUUFBUSxFQUFFLENBQUM7WUFDN0IsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLEVBQUUsSUFBSSxRQUFRLENBQUM7WUFFckMsSUFBSSxNQUFNLElBQUksT0FBTyxDQUFDLElBQUk7Z0JBQUUsTUFBTTtZQUVsQyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO1lBRXZELElBQUksT0FBTyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUNkLEdBQUcsSUFBSSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztZQUNsQyxDQUFDO1FBQ0wsQ0FBQztRQUVELE9BQU8sR0FBRyxDQUFDO0lBQ2YsQ0FBQztDQUNKO0FBaEVELHNFQWdFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFJlc3VsdCB9IGZyb20gXCIuLi9kb21haW4vdHlwZXNcIjtcbmltcG9ydCB7IENhbmFkYUNhcGl0YWxHYWluc1NlcnZpY2UgfSBmcm9tIFwiLi9DYW5hZGFDYXBpdGFsR2FpbnNTZXJ2aWNlXCI7XG5pbXBvcnQgeyBJbnB1dCwgUnVsZXMsIFRheEJyYWNrZXQgfSBmcm9tIFwiLi9kb21haW4vdHlwZXNcIjtcblxuZXhwb3J0IGNsYXNzIENhbmFkYUNhcGl0YWxHYWluc1NlcnZpY2VJbXBsIGltcGxlbWVudHMgQ2FuYWRhQ2FwaXRhbEdhaW5zU2VydmljZSB7XG4gICAgcHJpdmF0ZSBfaW5wdXQ6IElucHV0O1xuICAgIHByaXZhdGUgX3J1bGVzOiBSdWxlcztcblxuICAgIGNvbnN0cnVjdG9yKGlucHV0OiBJbnB1dCwgcnVsZXM6IFJ1bGVzKSB7XG4gICAgICAgIHRoaXMuX2lucHV0ID0gaW5wdXQ7XG4gICAgICAgIHRoaXMuX3J1bGVzID0gcnVsZXM7XG4gICAgfVxuXG4gICAgY2FsY3VsYXRlKCk6IFJlc3VsdCB7XG4gICAgICAgIGNvbnN0IGdhaW4gPSB0aGlzLl9pbnB1dC5jYXBpdGFsR2FpbjtcblxuICAgICAgICBpZiAoZ2FpbiA8PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHRheGFibGVHYWluOiAwLFxuICAgICAgICAgICAgICAgIGNhcGl0YWxHYWluVGF4OiAwLFxuICAgICAgICAgICAgICAgIHNvY2lhbENvbnRyaWJ1dGlvbnM6IDAsXG4gICAgICAgICAgICAgICAgbmV0SW52ZXN0bWVudEluY29tZVRheDogMCxcbiAgICAgICAgICAgICAgICB0b3RhbFRheDogMCxcbiAgICAgICAgICAgICAgICBlZmZlY3RpdmVSYXRlOiAwLFxuICAgICAgICAgICAgICAgIGJyZWFrZG93bnM6IFtdXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGF4YWJsZUdhaW4gPSBnYWluICogdGhpcy5fcnVsZXMuaW5jbHVzaW9uUmF0ZTtcblxuICAgICAgICAvLyBpbmNvbWUgQkVGT1JFIHRoZSBjYXBpdGFsIGdhaW5cbiAgICAgICAgY29uc3QgYmFzZUluY29tZSA9IHRoaXMuX2lucHV0LnRvdGFsVGF4YWJsZUluY29tZTtcblxuICAgICAgICBjb25zdCBpbmNvbWVXaXRoR2FpbiA9IGJhc2VJbmNvbWUgKyB0YXhhYmxlR2FpbjtcblxuICAgICAgICBjb25zdCBiYXNlVGF4ID0gdGhpcy5jYWxjdWxhdGVQcm9ncmVzc2l2ZVRheChiYXNlSW5jb21lLCB0aGlzLl9ydWxlcy50YXhCcmFja2V0cyk7XG4gICAgICAgIGNvbnN0IHRheFdpdGhHYWluID0gdGhpcy5jYWxjdWxhdGVQcm9ncmVzc2l2ZVRheChpbmNvbWVXaXRoR2FpbiwgdGhpcy5fcnVsZXMudGF4QnJhY2tldHMpO1xuXG4gICAgICAgIGNvbnN0IGNhcGl0YWxHYWluVGF4ID0gdGF4V2l0aEdhaW4gLSBiYXNlVGF4O1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0YXhhYmxlR2FpbixcbiAgICAgICAgICAgIGNhcGl0YWxHYWluVGF4LFxuICAgICAgICAgICAgc29jaWFsQ29udHJpYnV0aW9uczogMCxcbiAgICAgICAgICAgIG5ldEludmVzdG1lbnRJbmNvbWVUYXg6IDAsXG4gICAgICAgICAgICB0b3RhbFRheDogY2FwaXRhbEdhaW5UYXgsXG4gICAgICAgICAgICBlZmZlY3RpdmVSYXRlOiBnYWluID4gMCA/IChjYXBpdGFsR2FpblRheCAvIGdhaW4pICogMTAwIDogMCxcbiAgICAgICAgICAgIGJyZWFrZG93bnM6IFtdXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjYWxjdWxhdGVQcm9ncmVzc2l2ZVRheChpbmNvbWU6IG51bWJlciwgYnJhY2tldHM6IFRheEJyYWNrZXRbXSk6IG51bWJlciB7XG4gICAgICAgIGxldCB0YXggPSAwO1xuXG4gICAgICAgIGZvciAoY29uc3QgYnJhY2tldCBvZiBicmFja2V0cykge1xuICAgICAgICAgICAgY29uc3QgdXBwZXIgPSBicmFja2V0LnRvID8/IEluZmluaXR5O1xuXG4gICAgICAgICAgICBpZiAoaW5jb21lIDw9IGJyYWNrZXQuZnJvbSkgYnJlYWs7XG5cbiAgICAgICAgICAgIGNvbnN0IHRheGFibGUgPSBNYXRoLm1pbihpbmNvbWUsIHVwcGVyKSAtIGJyYWNrZXQuZnJvbTtcblxuICAgICAgICAgICAgaWYgKHRheGFibGUgPiAwKSB7XG4gICAgICAgICAgICAgICAgdGF4ICs9IHRheGFibGUgKiBicmFja2V0LnJhdGU7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gdGF4O1xuICAgIH1cbn0iXX0=
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Result } from "../domain/types";
|
|
2
2
|
import { CanadaCapitalGainsService } from "./CanadaCapitalGainsService";
|
|
3
3
|
import { Input, Rules, TaxBracket } from "./domain/types";
|
|
4
4
|
|
|
@@ -27,56 +27,43 @@ export class CanadaCapitalGainsServiceImpl implements CanadaCapitalGainsService
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const taxableGain = gain * this._rules.inclusionRate;
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
|
|
31
|
+
// income BEFORE the capital gain
|
|
32
|
+
const baseIncome = this._input.totalTaxableIncome;
|
|
33
|
+
|
|
34
|
+
const incomeWithGain = baseIncome + taxableGain;
|
|
35
|
+
|
|
36
|
+
const baseTax = this.calculateProgressiveTax(baseIncome, this._rules.taxBrackets);
|
|
37
|
+
const taxWithGain = this.calculateProgressiveTax(incomeWithGain, this._rules.taxBrackets);
|
|
38
|
+
|
|
39
|
+
const capitalGainTax = taxWithGain - baseTax;
|
|
32
40
|
|
|
33
41
|
return {
|
|
34
42
|
taxableGain,
|
|
35
|
-
capitalGainTax
|
|
43
|
+
capitalGainTax,
|
|
36
44
|
socialContributions: 0,
|
|
37
45
|
netInvestmentIncomeTax: 0,
|
|
38
|
-
totalTax:
|
|
39
|
-
effectiveRate: gain > 0 ? (
|
|
40
|
-
breakdowns
|
|
46
|
+
totalTax: capitalGainTax,
|
|
47
|
+
effectiveRate: gain > 0 ? (capitalGainTax / gain) * 100 : 0,
|
|
48
|
+
breakdowns: []
|
|
41
49
|
};
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
private
|
|
45
|
-
taxableGain: number,
|
|
46
|
-
otherIncome: number,
|
|
47
|
-
brackets: TaxBracket[],
|
|
48
|
-
): { tax: number; breakdowns: Breakdown[] } {
|
|
52
|
+
private calculateProgressiveTax(income: number, brackets: TaxBracket[]): number {
|
|
49
53
|
let tax = 0;
|
|
50
|
-
const breakdowns: Breakdown[] = [];
|
|
51
|
-
let remaining = taxableGain;
|
|
52
|
-
let incomeUsed = otherIncome;
|
|
53
54
|
|
|
54
55
|
for (const bracket of brackets) {
|
|
55
|
-
if (remaining <= 0) break;
|
|
56
|
-
|
|
57
56
|
const upper = bracket.to ?? Infinity;
|
|
58
57
|
|
|
59
|
-
if (
|
|
58
|
+
if (income <= bracket.from) break;
|
|
60
59
|
|
|
61
|
-
const
|
|
62
|
-
const bracketSpace = upper - bracketStart;
|
|
63
|
-
const taxable = Math.min(bracketSpace, remaining);
|
|
60
|
+
const taxable = Math.min(income, upper) - bracket.from;
|
|
64
61
|
|
|
65
62
|
if (taxable > 0) {
|
|
66
|
-
|
|
67
|
-
tax += bracketTax;
|
|
68
|
-
remaining -= taxable;
|
|
69
|
-
incomeUsed += taxable;
|
|
70
|
-
|
|
71
|
-
breakdowns.push({
|
|
72
|
-
from: `${bracket.from}`,
|
|
73
|
-
to: `${bracket.to ?? 'Above'}`,
|
|
74
|
-
rate: bracket.rate,
|
|
75
|
-
amount: bracketTax,
|
|
76
|
-
});
|
|
63
|
+
tax += taxable * bracket.rate;
|
|
77
64
|
}
|
|
78
65
|
}
|
|
79
66
|
|
|
80
|
-
return
|
|
67
|
+
return tax;
|
|
81
68
|
}
|
|
82
|
-
}
|
|
69
|
+
}
|