@novha/calc-engines 1.0.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.
package/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # Welcome to your CDK TypeScript project
2
+
3
+ This is a blank project for CDK development with TypeScript.
4
+
5
+ The `cdk.json` file tells the CDK Toolkit how to execute your app.
6
+
7
+ ## Useful commands
8
+
9
+ * `npm run build` compile typescript to js
10
+ * `npm run watch` watch for changes and compile
11
+ * `npm run test` perform the jest unit tests
12
+ * `npx cdk deploy` deploy this stack to your default AWS account/region
13
+ * `npx cdk diff` compare deployed stack with current state
14
+ * `npx cdk synth` emits the synthesized CloudFormation template
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CalculatorType = void 0;
4
+ var CalculatorType;
5
+ (function (CalculatorType) {
6
+ CalculatorType["INCOME_TAX"] = "INCOME_TAX";
7
+ CalculatorType["MORTGAGE"] = "MORTGAGE";
8
+ CalculatorType["LOAN"] = "LOAN";
9
+ })(CalculatorType || (exports.CalculatorType = CalculatorType = {}));
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLElBQVksY0FJWDtBQUpELFdBQVksY0FBYztJQUN0QiwyQ0FBeUIsQ0FBQTtJQUN6Qix1Q0FBcUIsQ0FBQTtJQUNyQiwrQkFBYSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxjQUFjLDhCQUFkLGNBQWMsUUFJekIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZW51bSBDYWxjdWxhdG9yVHlwZSB7XG4gICAgSU5DT01FX1RBWCA9ICdJTkNPTUVfVEFYJyxcbiAgICBNT1JUR0FHRSA9ICdNT1JUR0FHRScsXG4gICAgTE9BTiA9ICdMT0FOJ1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVNZXRhIHtcbiAgICBpZDogc3RyaW5nO1xuICAgIGNvdW50cnk6IHN0cmluZztcbiAgICByZWdpb246IHN0cmluZztcbiAgICBjYWxjdWxhdG9yOiBDYWxjdWxhdG9yVHlwZTtcbiAgICB2ZXJzaW9uOiBzdHJpbmc7XG4gICAgZWZmZWN0aXZlX2Zyb206IHN0cmluZztcbiAgICBlZmZlY3RpdmVfdG86IHN0cmluZyB8IG51bGw7XG4gICAgc291cmNlPzogUnVsZVNvdXJjZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVTb3VyY2Uge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1cmw6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSdWxlSW5wdXQge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB0eXBlOiAnbnVtYmVyJyB8ICdzZWxlY3QnIHwgJ3RleHQnO1xuICAgIHJlcXVpcmVkOiBib29sZWFuO1xuICAgIHVuaXQ/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUnVsZU91dHB1dCB7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHR5cGU6ICdudW1iZXInIHwgJ3N0cmluZyc7XG4gICAgdW5pdD86IHN0cmluZztcbn0iXX0=
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FuYWRhSW5jb21lVGF4U2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9pbmNvbWUtdGF4L2NhbmFkYS9DYW5hZGFJbmNvbWVUYXhTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wdXRlZEluY29tZVRheFZhbHVlcywgSW5jb21lVGF4UnVsZXMgfSBmcm9tIFwiLi9kb21haW4vdHlwZXNcIjtcblxuZXhwb3J0IGludGVyZmFjZSBDYW5hZGFJbmNvbWVUYXhTZXJ2aWNlIHtcbiAgICBjYWxjdWxhdGVOZXRJbmNvbWUoXG4gICAgICAgIGluY29tZTogbnVtYmVyLFxuICAgICAgICBydWxlczogSW5jb21lVGF4UnVsZXMsXG4gICAgKTogQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXM7XG59Il19
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CanadaIncomeTaxServiceImpl = void 0;
4
+ class CanadaIncomeTaxServiceImpl {
5
+ constructor(income, rules) {
6
+ this._income = income;
7
+ this._rules = rules;
8
+ }
9
+ calculateNetIncome() {
10
+ const grossTax = this.computeGrossTax(this._income, this._rules.tax_brackets);
11
+ const netTax = this.applyCredits(grossTax, this._rules.credits);
12
+ const cpp = this.computeCPP(this._income, this._rules.contributions?.cpp);
13
+ const ei = this.computeEI(this._income, this._rules.contributions?.ei);
14
+ return {
15
+ grossIncome: this._income,
16
+ incomeTax: netTax,
17
+ cpp,
18
+ ei,
19
+ totalDeductions: netTax + cpp + ei,
20
+ netIncome: this._income - netTax - cpp - ei,
21
+ effectiveTaxRate: netTax / this._income,
22
+ };
23
+ }
24
+ computeGrossTax(income, brackets) {
25
+ return brackets.reduce((total, b) => {
26
+ if (income <= b.from)
27
+ return total;
28
+ const upper = b.to ?? income;
29
+ const taxable = Math.min(income, upper) - b.from;
30
+ return total + taxable * b.rate;
31
+ }, 0);
32
+ }
33
+ applyCredits(tax, credits = {}) {
34
+ const totalCredits = Object.values(credits).reduce((sum, c) => sum + c.amount * c.rate, 0);
35
+ return Math.max(0, tax - totalCredits);
36
+ }
37
+ computeCPP(income, cpp) {
38
+ if (!cpp || income <= cpp.exemption)
39
+ return 0;
40
+ const contributable = income - cpp.exemption;
41
+ return Math.min(contributable * cpp.rate, cpp.max_contribution);
42
+ }
43
+ computeEI(income, ei) {
44
+ if (!ei)
45
+ return 0;
46
+ const insurableIncome = Math.min(income, ei.max_insurable_earnings);
47
+ return Math.min(insurableIncome * ei.rate, ei.max_contribution);
48
+ }
49
+ }
50
+ exports.CanadaIncomeTaxServiceImpl = CanadaIncomeTaxServiceImpl;
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBV0EsTUFBYSwwQkFBMEI7SUFJbkMsWUFBWSxNQUFjLEVBQUUsS0FBcUI7UUFDN0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFDeEIsQ0FBQztJQUVNLGtCQUFrQjtRQUVyQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUM5RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMxRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFdkUsT0FBTztZQUNILFdBQVcsRUFBRSxJQUFJLENBQUMsT0FBTztZQUN6QixTQUFTLEVBQUUsTUFBTTtZQUNqQixHQUFHO1lBQ0gsRUFBRTtZQUNGLGVBQWUsRUFBRSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7WUFDbEMsU0FBUyxFQUFFLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO1lBQzNDLGdCQUFnQixFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTztTQUMxQyxDQUFDO0lBQ04sQ0FBQztJQUVPLGVBQWUsQ0FBQyxNQUFjLEVBQUUsUUFBc0I7UUFDMUQsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ2hDLElBQUksTUFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJO2dCQUFFLE9BQU8sS0FBSyxDQUFDO1lBRW5DLE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksTUFBTSxDQUFDO1lBQzdCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFFakQsT0FBTyxLQUFLLEdBQUcsT0FBTyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDcEMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1YsQ0FBQztJQUVPLFlBQVksQ0FBQyxHQUFXLEVBQUUsVUFBcUMsRUFBRTtRQUNyRSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FDOUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsSUFBSSxFQUNuQyxDQUFDLENBQ0osQ0FBQztRQUVGLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxHQUFHLFlBQVksQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFFTyxVQUFVLENBQUMsTUFBYyxFQUFFLEdBQXFCO1FBQ3BELElBQUksQ0FBQyxHQUFHLElBQUksTUFBTSxJQUFJLEdBQUcsQ0FBQyxTQUFTO1lBQUUsT0FBTyxDQUFDLENBQUM7UUFFOUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDN0MsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUNYLGFBQWEsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUN4QixHQUFHLENBQUMsZ0JBQWdCLENBQ3ZCLENBQUM7SUFDTixDQUFDO0lBRU8sU0FBUyxDQUFDLE1BQWMsRUFBRSxFQUFtQjtRQUNqRCxJQUFJLENBQUMsRUFBRTtZQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRWxCLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQzVCLE1BQU0sRUFDTixFQUFFLENBQUMsc0JBQXNCLENBQzVCLENBQUM7UUFFRixPQUFPLElBQUksQ0FBQyxHQUFHLENBQ1gsZUFBZSxHQUFHLEVBQUUsQ0FBQyxJQUFJLEVBQ3pCLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FDdEIsQ0FBQztJQUNOLENBQUM7Q0FDSjtBQXRFRCxnRUFzRUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDYW5hZGFJbmNvbWVUYXhTZXJ2aWNlIH0gZnJvbSBcIi4vQ2FuYWRhSW5jb21lVGF4U2VydmljZVwiO1xuaW1wb3J0IHtcbiAgICBDUFBDb250cmlidXRpb24sXG4gICAgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMsXG4gICAgRUlDb250cmlidXRpb24sXG4gICAgSW5jb21lVGF4UnVsZXMsXG4gICAgVGF4QnJhY2tldCxcbiAgICBUYXhDcmVkaXRcbn0gZnJvbSBcIi4vZG9tYWluL3R5cGVzXCI7XG5cblxuZXhwb3J0IGNsYXNzIENhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsIGltcGxlbWVudHMgQ2FuYWRhSW5jb21lVGF4U2VydmljZSB7XG4gICAgcHJpdmF0ZSBfaW5jb21lOiBudW1iZXI7XG4gICAgcHJpdmF0ZSBfcnVsZXM6IEluY29tZVRheFJ1bGVzO1xuXG4gICAgY29uc3RydWN0b3IoaW5jb21lOiBudW1iZXIsIHJ1bGVzOiBJbmNvbWVUYXhSdWxlcykge1xuICAgICAgICB0aGlzLl9pbmNvbWUgPSBpbmNvbWU7XG4gICAgICAgIHRoaXMuX3J1bGVzID0gcnVsZXM7XG4gICAgfVxuXG4gICAgcHVibGljIGNhbGN1bGF0ZU5ldEluY29tZSgpOiBDb21wdXRlZEluY29tZVRheFZhbHVlcyB7XG5cbiAgICAgICAgY29uc3QgZ3Jvc3NUYXggPSB0aGlzLmNvbXB1dGVHcm9zc1RheCh0aGlzLl9pbmNvbWUsIHRoaXMuX3J1bGVzLnRheF9icmFja2V0cyk7XG4gICAgICAgIGNvbnN0IG5ldFRheCA9IHRoaXMuYXBwbHlDcmVkaXRzKGdyb3NzVGF4LCB0aGlzLl9ydWxlcy5jcmVkaXRzKTtcbiAgICAgICAgY29uc3QgY3BwID0gdGhpcy5jb21wdXRlQ1BQKHRoaXMuX2luY29tZSwgdGhpcy5fcnVsZXMuY29udHJpYnV0aW9ucz8uY3BwKTtcbiAgICAgICAgY29uc3QgZWkgPSB0aGlzLmNvbXB1dGVFSSh0aGlzLl9pbmNvbWUsIHRoaXMuX3J1bGVzLmNvbnRyaWJ1dGlvbnM/LmVpKTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgZ3Jvc3NJbmNvbWU6IHRoaXMuX2luY29tZSxcbiAgICAgICAgICAgIGluY29tZVRheDogbmV0VGF4LFxuICAgICAgICAgICAgY3BwLFxuICAgICAgICAgICAgZWksXG4gICAgICAgICAgICB0b3RhbERlZHVjdGlvbnM6IG5ldFRheCArIGNwcCArIGVpLFxuICAgICAgICAgICAgbmV0SW5jb21lOiB0aGlzLl9pbmNvbWUgLSBuZXRUYXggLSBjcHAgLSBlaSxcbiAgICAgICAgICAgIGVmZmVjdGl2ZVRheFJhdGU6IG5ldFRheCAvIHRoaXMuX2luY29tZSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbXB1dGVHcm9zc1RheChpbmNvbWU6IG51bWJlciwgYnJhY2tldHM6IFRheEJyYWNrZXRbXSk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBicmFja2V0cy5yZWR1Y2UoKHRvdGFsLCBiKSA9PiB7XG4gICAgICAgICAgICBpZiAoaW5jb21lIDw9IGIuZnJvbSkgcmV0dXJuIHRvdGFsO1xuICAgIFxuICAgICAgICAgICAgY29uc3QgdXBwZXIgPSBiLnRvID8/IGluY29tZTtcbiAgICAgICAgICAgIGNvbnN0IHRheGFibGUgPSBNYXRoLm1pbihpbmNvbWUsIHVwcGVyKSAtIGIuZnJvbTtcbiAgICBcbiAgICAgICAgICAgIHJldHVybiB0b3RhbCArIHRheGFibGUgKiBiLnJhdGU7XG4gICAgICAgIH0sIDApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXBwbHlDcmVkaXRzKHRheDogbnVtYmVyLCBjcmVkaXRzOiBSZWNvcmQ8c3RyaW5nLCBUYXhDcmVkaXQ+ID0ge30pOiBudW1iZXIge1xuICAgICAgICBjb25zdCB0b3RhbENyZWRpdHMgPSBPYmplY3QudmFsdWVzKGNyZWRpdHMpLnJlZHVjZShcbiAgICAgICAgICAgIChzdW0sIGMpID0+IHN1bSArIGMuYW1vdW50ICogYy5yYXRlLFxuICAgICAgICAgICAgMCxcbiAgICAgICAgKTtcbiAgICBcbiAgICAgICAgcmV0dXJuIE1hdGgubWF4KDAsIHRheCAtIHRvdGFsQ3JlZGl0cyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjb21wdXRlQ1BQKGluY29tZTogbnVtYmVyLCBjcHA/OiBDUFBDb250cmlidXRpb24pOiBudW1iZXIge1xuICAgICAgICBpZiAoIWNwcCB8fCBpbmNvbWUgPD0gY3BwLmV4ZW1wdGlvbikgcmV0dXJuIDA7XG4gICAgXG4gICAgICAgIGNvbnN0IGNvbnRyaWJ1dGFibGUgPSBpbmNvbWUgLSBjcHAuZXhlbXB0aW9uO1xuICAgICAgICByZXR1cm4gTWF0aC5taW4oXG4gICAgICAgICAgICBjb250cmlidXRhYmxlICogY3BwLnJhdGUsXG4gICAgICAgICAgICBjcHAubWF4X2NvbnRyaWJ1dGlvbixcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbXB1dGVFSShpbmNvbWU6IG51bWJlciwgZWk/OiBFSUNvbnRyaWJ1dGlvbik6IG51bWJlciB7XG4gICAgICAgIGlmICghZWkpIHJldHVybiAwO1xuICAgIFxuICAgICAgICBjb25zdCBpbnN1cmFibGVJbmNvbWUgPSBNYXRoLm1pbihcbiAgICAgICAgICAgIGluY29tZSxcbiAgICAgICAgICAgIGVpLm1heF9pbnN1cmFibGVfZWFybmluZ3MsXG4gICAgICAgICk7XG4gICAgXG4gICAgICAgIHJldHVybiBNYXRoLm1pbihcbiAgICAgICAgICAgIGluc3VyYWJsZUluY29tZSAqIGVpLnJhdGUsXG4gICAgICAgICAgICBlaS5tYXhfY29udHJpYnV0aW9uLFxuICAgICAgICApO1xuICAgIH1cbn0iXX0=
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgaW50ZXJmYWNlIEluY29tZVRheFJ1bGVzIHtcbiAgICB0YXhfYnJhY2tldHM6IFRheEJyYWNrZXRbXTtcbiAgICBjcmVkaXRzPzogUmVjb3JkPHN0cmluZywgVGF4Q3JlZGl0PjtcbiAgICBjb250cmlidXRpb25zPzogQ29udHJpYnV0aW9ucztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUYXhCcmFja2V0IHtcbiAgICBmcm9tOiBudW1iZXI7XG4gICAgdG86IG51bWJlciB8IG51bGw7XG4gICAgcmF0ZTogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRheENyZWRpdCB7XG4gICAgYW1vdW50OiBudW1iZXI7XG4gICAgdHlwZTogJ25vblJlZnVuZGFibGUnIHwgJ3JlZnVuZGFibGUnO1xuICAgIHJhdGU6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250cmlidXRpb25zIHtcbiAgICBjcHA/OiBDUFBDb250cmlidXRpb247XG4gICAgZWk/OiBFSUNvbnRyaWJ1dGlvbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDUFBDb250cmlidXRpb24ge1xuICAgIHJhdGU6IG51bWJlcjtcbiAgICBtYXhfY29udHJpYnV0aW9uOiBudW1iZXI7XG4gICAgZXhlbXB0aW9uOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRUlDb250cmlidXRpb24ge1xuICAgIHJhdGU6IG51bWJlcjtcbiAgICBtYXhfaW5zdXJhYmxlX2Vhcm5pbmdzOiBudW1iZXI7XG4gICAgbWF4X2NvbnRyaWJ1dGlvbjogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENvbXB1dGVkSW5jb21lVGF4VmFsdWVzIHtcbiAgICBncm9zc0luY29tZTogbnVtYmVyO1xuICAgIGluY29tZVRheDogbnVtYmVyO1xuICAgIGNwcDogbnVtYmVyO1xuICAgIGVpOiBudW1iZXI7XG4gICAgdG90YWxEZWR1Y3Rpb25zOiBudW1iZXI7XG4gICAgbmV0SW5jb21lOiBudW1iZXI7XG4gICAgZWZmZWN0aXZlVGF4UmF0ZTogbnVtYmVyO1xufVxuIl19
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CanadaIncomeTaxService = void 0;
4
+ var CanadaIncomeTaxServiceImpl_1 = require("./income-tax/canada/CanadaIncomeTaxServiceImpl");
5
+ Object.defineProperty(exports, "CanadaIncomeTaxService", { enumerable: true, get: function () { return CanadaIncomeTaxServiceImpl_1.CanadaIncomeTaxServiceImpl; } });
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkZBQXNIO0FBQTdHLG9JQUFBLDBCQUEwQixPQUEwQiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IENhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsIGFzIENhbmFkYUluY29tZVRheFNlcnZpY2UgfSBmcm9tICcuL2luY29tZS10YXgvY2FuYWRhL0NhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsJztcbmV4cG9ydCB7IENvbXB1dGVkSW5jb21lVGF4VmFsdWVzLCBJbmNvbWVUYXhSdWxlcyB9IGZyb20gJy4vaW5jb21lLXRheC9jYW5hZGEvZG9tYWluL3R5cGVzJzsiXX0=
@@ -0,0 +1,30 @@
1
+ export declare enum CalculatorType {
2
+ INCOME_TAX = "INCOME_TAX",
3
+ MORTGAGE = "MORTGAGE",
4
+ LOAN = "LOAN"
5
+ }
6
+ export interface RuleMeta {
7
+ id: string;
8
+ country: string;
9
+ region: string;
10
+ calculator: CalculatorType;
11
+ version: string;
12
+ effective_from: string;
13
+ effective_to: string | null;
14
+ source?: RuleSource[];
15
+ }
16
+ export interface RuleSource {
17
+ name: string;
18
+ url: string;
19
+ }
20
+ export interface RuleInput {
21
+ name: string;
22
+ type: 'number' | 'select' | 'text';
23
+ required: boolean;
24
+ unit?: string;
25
+ }
26
+ export interface RuleOutput {
27
+ name: string;
28
+ type: 'number' | 'string';
29
+ unit?: string;
30
+ }
@@ -0,0 +1,4 @@
1
+ import { ComputedIncomeTaxValues, IncomeTaxRules } from "./domain/types";
2
+ export interface CanadaIncomeTaxService {
3
+ calculateNetIncome(income: number, rules: IncomeTaxRules): ComputedIncomeTaxValues;
4
+ }
@@ -0,0 +1,12 @@
1
+ import { CanadaIncomeTaxService } from "./CanadaIncomeTaxService";
2
+ import { ComputedIncomeTaxValues, IncomeTaxRules } from "./domain/types";
3
+ export declare class CanadaIncomeTaxServiceImpl implements CanadaIncomeTaxService {
4
+ private _income;
5
+ private _rules;
6
+ constructor(income: number, rules: IncomeTaxRules);
7
+ calculateNetIncome(): ComputedIncomeTaxValues;
8
+ private computeGrossTax;
9
+ private applyCredits;
10
+ private computeCPP;
11
+ private computeEI;
12
+ }
@@ -0,0 +1,38 @@
1
+ export interface IncomeTaxRules {
2
+ tax_brackets: TaxBracket[];
3
+ credits?: Record<string, TaxCredit>;
4
+ contributions?: Contributions;
5
+ }
6
+ export interface TaxBracket {
7
+ from: number;
8
+ to: number | null;
9
+ rate: number;
10
+ }
11
+ export interface TaxCredit {
12
+ amount: number;
13
+ type: 'nonRefundable' | 'refundable';
14
+ rate: number;
15
+ }
16
+ export interface Contributions {
17
+ cpp?: CPPContribution;
18
+ ei?: EIContribution;
19
+ }
20
+ export interface CPPContribution {
21
+ rate: number;
22
+ max_contribution: number;
23
+ exemption: number;
24
+ }
25
+ export interface EIContribution {
26
+ rate: number;
27
+ max_insurable_earnings: number;
28
+ max_contribution: number;
29
+ }
30
+ export interface ComputedIncomeTaxValues {
31
+ grossIncome: number;
32
+ incomeTax: number;
33
+ cpp: number;
34
+ ei: number;
35
+ totalDeductions: number;
36
+ netIncome: number;
37
+ effectiveTaxRate: number;
38
+ }
@@ -0,0 +1,2 @@
1
+ export { CanadaIncomeTaxServiceImpl as CanadaIncomeTaxService } from './income-tax/canada/CanadaIncomeTaxServiceImpl';
2
+ export { ComputedIncomeTaxValues, IncomeTaxRules } from './income-tax/canada/domain/types';
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ roots: ['<rootDir>/test'],
4
+ testMatch: ['**/*.test.ts'],
5
+ transform: {
6
+ '^.+\\.tsx?$': 'ts-jest'
7
+ }
8
+ };
Binary file
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@novha/calc-engines",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/types/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc",
8
+ "watch": "tsc -w",
9
+ "test": "jest"
10
+ },
11
+ "devDependencies": {
12
+ "@types/jest": "^29.5.12",
13
+ "@types/node": "20.12.7",
14
+ "aws-cdk": "2.142.1",
15
+ "jest": "^29.7.0",
16
+ "ts-jest": "^29.1.2",
17
+ "ts-node": "^10.9.2",
18
+ "typescript": "~5.4.5"
19
+ },
20
+ "dependencies": {
21
+ "class-validator": "^0.14.1",
22
+ "constructs": "^10.0.0",
23
+ "source-map-support": "^0.5.21"
24
+ },
25
+ "description": "This is a blank project for CDK development with TypeScript.",
26
+ "keywords": [
27
+ "calculator",
28
+ "engine"
29
+ ],
30
+ "author": "Yannick Kazadi & Novha team",
31
+ "license": "ISC"
32
+ }
@@ -0,0 +1,34 @@
1
+ export enum CalculatorType {
2
+ INCOME_TAX = 'INCOME_TAX',
3
+ MORTGAGE = 'MORTGAGE',
4
+ LOAN = 'LOAN'
5
+ }
6
+
7
+ export interface RuleMeta {
8
+ id: string;
9
+ country: string;
10
+ region: string;
11
+ calculator: CalculatorType;
12
+ version: string;
13
+ effective_from: string;
14
+ effective_to: string | null;
15
+ source?: RuleSource[];
16
+ }
17
+
18
+ export interface RuleSource {
19
+ name: string;
20
+ url: string;
21
+ }
22
+
23
+ export interface RuleInput {
24
+ name: string;
25
+ type: 'number' | 'select' | 'text';
26
+ required: boolean;
27
+ unit?: string;
28
+ }
29
+
30
+ export interface RuleOutput {
31
+ name: string;
32
+ type: 'number' | 'string';
33
+ unit?: string;
34
+ }
@@ -0,0 +1,8 @@
1
+ import { ComputedIncomeTaxValues, IncomeTaxRules } from "./domain/types";
2
+
3
+ export interface CanadaIncomeTaxService {
4
+ calculateNetIncome(
5
+ income: number,
6
+ rules: IncomeTaxRules,
7
+ ): ComputedIncomeTaxValues;
8
+ }
@@ -0,0 +1,82 @@
1
+ import { CanadaIncomeTaxService } from "./CanadaIncomeTaxService";
2
+ import {
3
+ CPPContribution,
4
+ ComputedIncomeTaxValues,
5
+ EIContribution,
6
+ IncomeTaxRules,
7
+ TaxBracket,
8
+ TaxCredit
9
+ } from "./domain/types";
10
+
11
+
12
+ export class CanadaIncomeTaxServiceImpl implements CanadaIncomeTaxService {
13
+ private _income: number;
14
+ private _rules: IncomeTaxRules;
15
+
16
+ constructor(income: number, rules: IncomeTaxRules) {
17
+ this._income = income;
18
+ this._rules = rules;
19
+ }
20
+
21
+ public calculateNetIncome(): ComputedIncomeTaxValues {
22
+
23
+ const grossTax = this.computeGrossTax(this._income, this._rules.tax_brackets);
24
+ const netTax = this.applyCredits(grossTax, this._rules.credits);
25
+ const cpp = this.computeCPP(this._income, this._rules.contributions?.cpp);
26
+ const ei = this.computeEI(this._income, this._rules.contributions?.ei);
27
+
28
+ return {
29
+ grossIncome: this._income,
30
+ incomeTax: netTax,
31
+ cpp,
32
+ ei,
33
+ totalDeductions: netTax + cpp + ei,
34
+ netIncome: this._income - netTax - cpp - ei,
35
+ effectiveTaxRate: netTax / this._income,
36
+ };
37
+ }
38
+
39
+ private computeGrossTax(income: number, brackets: TaxBracket[]): number {
40
+ return brackets.reduce((total, b) => {
41
+ if (income <= b.from) return total;
42
+
43
+ const upper = b.to ?? income;
44
+ const taxable = Math.min(income, upper) - b.from;
45
+
46
+ return total + taxable * b.rate;
47
+ }, 0);
48
+ }
49
+
50
+ private applyCredits(tax: number, credits: Record<string, TaxCredit> = {}): number {
51
+ const totalCredits = Object.values(credits).reduce(
52
+ (sum, c) => sum + c.amount * c.rate,
53
+ 0,
54
+ );
55
+
56
+ return Math.max(0, tax - totalCredits);
57
+ }
58
+
59
+ private computeCPP(income: number, cpp?: CPPContribution): number {
60
+ if (!cpp || income <= cpp.exemption) return 0;
61
+
62
+ const contributable = income - cpp.exemption;
63
+ return Math.min(
64
+ contributable * cpp.rate,
65
+ cpp.max_contribution,
66
+ );
67
+ }
68
+
69
+ private computeEI(income: number, ei?: EIContribution): number {
70
+ if (!ei) return 0;
71
+
72
+ const insurableIncome = Math.min(
73
+ income,
74
+ ei.max_insurable_earnings,
75
+ );
76
+
77
+ return Math.min(
78
+ insurableIncome * ei.rate,
79
+ ei.max_contribution,
80
+ );
81
+ }
82
+ }
@@ -0,0 +1,44 @@
1
+ export interface IncomeTaxRules {
2
+ tax_brackets: TaxBracket[];
3
+ credits?: Record<string, TaxCredit>;
4
+ contributions?: Contributions;
5
+ }
6
+
7
+ export interface TaxBracket {
8
+ from: number;
9
+ to: number | null;
10
+ rate: number;
11
+ }
12
+
13
+ export interface TaxCredit {
14
+ amount: number;
15
+ type: 'nonRefundable' | 'refundable';
16
+ rate: number;
17
+ }
18
+
19
+ export interface Contributions {
20
+ cpp?: CPPContribution;
21
+ ei?: EIContribution;
22
+ }
23
+
24
+ export interface CPPContribution {
25
+ rate: number;
26
+ max_contribution: number;
27
+ exemption: number;
28
+ }
29
+
30
+ export interface EIContribution {
31
+ rate: number;
32
+ max_insurable_earnings: number;
33
+ max_contribution: number;
34
+ }
35
+
36
+ export interface ComputedIncomeTaxValues {
37
+ grossIncome: number;
38
+ incomeTax: number;
39
+ cpp: number;
40
+ ei: number;
41
+ totalDeductions: number;
42
+ netIncome: number;
43
+ effectiveTaxRate: number;
44
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { CanadaIncomeTaxServiceImpl as CanadaIncomeTaxService } from './income-tax/canada/CanadaIncomeTaxServiceImpl';
2
+ export { ComputedIncomeTaxValues, IncomeTaxRules } from './income-tax/canada/domain/types';
package/tsconfig.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "lib": [
7
+ "es2020",
8
+ "dom"
9
+ ],
10
+ "declarationDir": "./dist/types",
11
+ "declaration": true,
12
+ "esModuleInterop": true,
13
+ "strict": true,
14
+ "noImplicitAny": true,
15
+ "strictNullChecks": true,
16
+ "noImplicitThis": true,
17
+ "alwaysStrict": true,
18
+ "noUnusedLocals": false,
19
+ "noUnusedParameters": false,
20
+ "noImplicitReturns": true,
21
+ "noFallthroughCasesInSwitch": false,
22
+ "inlineSourceMap": true,
23
+ "inlineSources": true,
24
+ "experimentalDecorators": true,
25
+ "strictPropertyInitialization": false,
26
+ "typeRoots": [
27
+ "./node_modules/@types"
28
+ ],
29
+ },
30
+ "include": ["src/**/*"],
31
+ "exclude": [
32
+ "node_modules",
33
+ "cdk.out"
34
+ ]
35
+ }