@novha/calc-engines 1.0.0 → 1.2.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.
@@ -7,4 +7,4 @@ var CalculatorType;
7
7
  CalculatorType["MORTGAGE"] = "MORTGAGE";
8
8
  CalculatorType["LOAN"] = "LOAN";
9
9
  })(CalculatorType || (exports.CalculatorType = CalculatorType = {}));
10
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLElBQVksY0FJWDtBQUpELFdBQVksY0FBYztJQUN0QiwyQ0FBeUIsQ0FBQTtJQUN6Qix1Q0FBcUIsQ0FBQTtJQUNyQiwrQkFBYSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxjQUFjLDhCQUFkLGNBQWMsUUFJekIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZW51bSBDYWxjdWxhdG9yVHlwZSB7XG4gICAgSU5DT01FX1RBWCA9ICdJTkNPTUVfVEFYJyxcbiAgICBNT1JUR0FHRSA9ICdNT1JUR0FHRScsXG4gICAgTE9BTiA9ICdMT0FOJ1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVNZXRhIHtcbiAgICBpZDogc3RyaW5nO1xuICAgIGNvdW50cnk6IHN0cmluZztcbiAgICByZWdpb246IHN0cmluZztcbiAgICBjYWxjdWxhdG9yOiBDYWxjdWxhdG9yVHlwZTtcbiAgICB2ZXJzaW9uOiBzdHJpbmc7XG4gICAgZWZmZWN0aXZlX2Zyb206IHN0cmluZztcbiAgICBlZmZlY3RpdmVfdG86IHN0cmluZyB8IG51bGw7XG4gICAgc291cmNlPzogUnVsZVNvdXJjZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVTb3VyY2Uge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB1cmw6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSdWxlSW5wdXQge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB0eXBlOiAnbnVtYmVyJyB8ICdzZWxlY3QnIHwgJ3RleHQnO1xuICAgIHJlcXVpcmVkOiBib29sZWFuO1xuICAgIHVuaXQ/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUnVsZU91dHB1dCB7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHR5cGU6ICdudW1iZXInIHwgJ3N0cmluZyc7XG4gICAgdW5pdD86IHN0cmluZztcbn0iXX0=
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLElBQVksY0FJWDtBQUpELFdBQVksY0FBYztJQUN0QiwyQ0FBeUIsQ0FBQTtJQUN6Qix1Q0FBcUIsQ0FBQTtJQUNyQiwrQkFBYSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxjQUFjLDhCQUFkLGNBQWMsUUFJekIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgZW51bSBDYWxjdWxhdG9yVHlwZSB7XG4gICAgSU5DT01FX1RBWCA9ICdJTkNPTUVfVEFYJyxcbiAgICBNT1JUR0FHRSA9ICdNT1JUR0FHRScsXG4gICAgTE9BTiA9ICdMT0FOJ1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVNZXRhIHtcbiAgICBpZDogc3RyaW5nO1xuICAgIGNvdW50cnk6IHN0cmluZztcbiAgICByZWdpb246IHN0cmluZztcbiAgICBjYWxjdWxhdG9yOiBDYWxjdWxhdG9yVHlwZTtcbiAgICB2ZXJzaW9uOiBzdHJpbmc7XG4gICAgZWZmZWN0aXZlZnJvbTogc3RyaW5nO1xuICAgIGVmZmVjdGl2ZXRvOiBzdHJpbmcgfCBudWxsO1xuICAgIHNvdXJjZT86IFJ1bGVTb3VyY2VbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSdWxlU291cmNlIHtcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgdXJsOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUnVsZUlucHV0IHtcbiAgICBuYW1lOiBzdHJpbmc7XG4gICAgdHlwZTogJ251bWJlcicgfCAnc2VsZWN0JyB8ICd0ZXh0JztcbiAgICByZXF1aXJlZDogYm9vbGVhbjtcbiAgICB1bml0Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJ1bGVPdXRwdXQge1xuICAgIG5hbWU6IHN0cmluZztcbiAgICB0eXBlOiAnbnVtYmVyJyB8ICdzdHJpbmcnO1xuICAgIHVuaXQ/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVGF4QnJhY2tldCB7XG4gICAgZnJvbTogbnVtYmVyO1xuICAgIHRvOiBudW1iZXIgfCBudWxsO1xuICAgIHJhdGU6IG51bWJlcjtcbn0iXX0=
@@ -7,7 +7,7 @@ class CanadaIncomeTaxServiceImpl {
7
7
  this._rules = rules;
8
8
  }
9
9
  calculateNetIncome() {
10
- const grossTax = this.computeGrossTax(this._income, this._rules.tax_brackets);
10
+ const grossTax = this.computeGrossTax(this._income, this._rules.taxBrackets);
11
11
  const netTax = this.applyCredits(grossTax, this._rules.credits);
12
12
  const cpp = this.computeCPP(this._income, this._rules.contributions?.cpp);
13
13
  const ei = this.computeEI(this._income, this._rules.contributions?.ei);
@@ -38,14 +38,14 @@ class CanadaIncomeTaxServiceImpl {
38
38
  if (!cpp || income <= cpp.exemption)
39
39
  return 0;
40
40
  const contributable = income - cpp.exemption;
41
- return Math.min(contributable * cpp.rate, cpp.max_contribution);
41
+ return Math.min(contributable * cpp.rate, cpp.maxContribution);
42
42
  }
43
43
  computeEI(income, ei) {
44
44
  if (!ei)
45
45
  return 0;
46
- const insurableIncome = Math.min(income, ei.max_insurable_earnings);
47
- return Math.min(insurableIncome * ei.rate, ei.max_contribution);
46
+ const insurableIncome = Math.min(income, ei.maxInsurableEarnings);
47
+ return Math.min(insurableIncome * ei.rate, ei.maxContribution);
48
48
  }
49
49
  }
50
50
  exports.CanadaIncomeTaxServiceImpl = CanadaIncomeTaxServiceImpl;
51
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBV0EsTUFBYSwwQkFBMEI7SUFJbkMsWUFBWSxNQUFjLEVBQUUsS0FBcUI7UUFDN0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFDeEIsQ0FBQztJQUVNLGtCQUFrQjtRQUVyQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUM5RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMxRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFdkUsT0FBTztZQUNILFdBQVcsRUFBRSxJQUFJLENBQUMsT0FBTztZQUN6QixTQUFTLEVBQUUsTUFBTTtZQUNqQixHQUFHO1lBQ0gsRUFBRTtZQUNGLGVBQWUsRUFBRSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7WUFDbEMsU0FBUyxFQUFFLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO1lBQzNDLGdCQUFnQixFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTztTQUMxQyxDQUFDO0lBQ04sQ0FBQztJQUVPLGVBQWUsQ0FBQyxNQUFjLEVBQUUsUUFBc0I7UUFDMUQsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ2hDLElBQUksTUFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJO2dCQUFFLE9BQU8sS0FBSyxDQUFDO1lBRW5DLE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksTUFBTSxDQUFDO1lBQzdCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFFakQsT0FBTyxLQUFLLEdBQUcsT0FBTyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDcEMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1YsQ0FBQztJQUVPLFlBQVksQ0FBQyxHQUFXLEVBQUUsVUFBcUMsRUFBRTtRQUNyRSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FDOUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsSUFBSSxFQUNuQyxDQUFDLENBQ0osQ0FBQztRQUVGLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxHQUFHLFlBQVksQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFFTyxVQUFVLENBQUMsTUFBYyxFQUFFLEdBQXFCO1FBQ3BELElBQUksQ0FBQyxHQUFHLElBQUksTUFBTSxJQUFJLEdBQUcsQ0FBQyxTQUFTO1lBQUUsT0FBTyxDQUFDLENBQUM7UUFFOUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDN0MsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUNYLGFBQWEsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUN4QixHQUFHLENBQUMsZ0JBQWdCLENBQ3ZCLENBQUM7SUFDTixDQUFDO0lBRU8sU0FBUyxDQUFDLE1BQWMsRUFBRSxFQUFtQjtRQUNqRCxJQUFJLENBQUMsRUFBRTtZQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRWxCLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQzVCLE1BQU0sRUFDTixFQUFFLENBQUMsc0JBQXNCLENBQzVCLENBQUM7UUFFRixPQUFPLElBQUksQ0FBQyxHQUFHLENBQ1gsZUFBZSxHQUFHLEVBQUUsQ0FBQyxJQUFJLEVBQ3pCLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FDdEIsQ0FBQztJQUNOLENBQUM7Q0FDSjtBQXRFRCxnRUFzRUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDYW5hZGFJbmNvbWVUYXhTZXJ2aWNlIH0gZnJvbSBcIi4vQ2FuYWRhSW5jb21lVGF4U2VydmljZVwiO1xuaW1wb3J0IHtcbiAgICBDUFBDb250cmlidXRpb24sXG4gICAgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMsXG4gICAgRUlDb250cmlidXRpb24sXG4gICAgSW5jb21lVGF4UnVsZXMsXG4gICAgVGF4QnJhY2tldCxcbiAgICBUYXhDcmVkaXRcbn0gZnJvbSBcIi4vZG9tYWluL3R5cGVzXCI7XG5cblxuZXhwb3J0IGNsYXNzIENhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsIGltcGxlbWVudHMgQ2FuYWRhSW5jb21lVGF4U2VydmljZSB7XG4gICAgcHJpdmF0ZSBfaW5jb21lOiBudW1iZXI7XG4gICAgcHJpdmF0ZSBfcnVsZXM6IEluY29tZVRheFJ1bGVzO1xuXG4gICAgY29uc3RydWN0b3IoaW5jb21lOiBudW1iZXIsIHJ1bGVzOiBJbmNvbWVUYXhSdWxlcykge1xuICAgICAgICB0aGlzLl9pbmNvbWUgPSBpbmNvbWU7XG4gICAgICAgIHRoaXMuX3J1bGVzID0gcnVsZXM7XG4gICAgfVxuXG4gICAgcHVibGljIGNhbGN1bGF0ZU5ldEluY29tZSgpOiBDb21wdXRlZEluY29tZVRheFZhbHVlcyB7XG5cbiAgICAgICAgY29uc3QgZ3Jvc3NUYXggPSB0aGlzLmNvbXB1dGVHcm9zc1RheCh0aGlzLl9pbmNvbWUsIHRoaXMuX3J1bGVzLnRheF9icmFja2V0cyk7XG4gICAgICAgIGNvbnN0IG5ldFRheCA9IHRoaXMuYXBwbHlDcmVkaXRzKGdyb3NzVGF4LCB0aGlzLl9ydWxlcy5jcmVkaXRzKTtcbiAgICAgICAgY29uc3QgY3BwID0gdGhpcy5jb21wdXRlQ1BQKHRoaXMuX2luY29tZSwgdGhpcy5fcnVsZXMuY29udHJpYnV0aW9ucz8uY3BwKTtcbiAgICAgICAgY29uc3QgZWkgPSB0aGlzLmNvbXB1dGVFSSh0aGlzLl9pbmNvbWUsIHRoaXMuX3J1bGVzLmNvbnRyaWJ1dGlvbnM/LmVpKTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgZ3Jvc3NJbmNvbWU6IHRoaXMuX2luY29tZSxcbiAgICAgICAgICAgIGluY29tZVRheDogbmV0VGF4LFxuICAgICAgICAgICAgY3BwLFxuICAgICAgICAgICAgZWksXG4gICAgICAgICAgICB0b3RhbERlZHVjdGlvbnM6IG5ldFRheCArIGNwcCArIGVpLFxuICAgICAgICAgICAgbmV0SW5jb21lOiB0aGlzLl9pbmNvbWUgLSBuZXRUYXggLSBjcHAgLSBlaSxcbiAgICAgICAgICAgIGVmZmVjdGl2ZVRheFJhdGU6IG5ldFRheCAvIHRoaXMuX2luY29tZSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbXB1dGVHcm9zc1RheChpbmNvbWU6IG51bWJlciwgYnJhY2tldHM6IFRheEJyYWNrZXRbXSk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBicmFja2V0cy5yZWR1Y2UoKHRvdGFsLCBiKSA9PiB7XG4gICAgICAgICAgICBpZiAoaW5jb21lIDw9IGIuZnJvbSkgcmV0dXJuIHRvdGFsO1xuICAgIFxuICAgICAgICAgICAgY29uc3QgdXBwZXIgPSBiLnRvID8/IGluY29tZTtcbiAgICAgICAgICAgIGNvbnN0IHRheGFibGUgPSBNYXRoLm1pbihpbmNvbWUsIHVwcGVyKSAtIGIuZnJvbTtcbiAgICBcbiAgICAgICAgICAgIHJldHVybiB0b3RhbCArIHRheGFibGUgKiBiLnJhdGU7XG4gICAgICAgIH0sIDApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXBwbHlDcmVkaXRzKHRheDogbnVtYmVyLCBjcmVkaXRzOiBSZWNvcmQ8c3RyaW5nLCBUYXhDcmVkaXQ+ID0ge30pOiBudW1iZXIge1xuICAgICAgICBjb25zdCB0b3RhbENyZWRpdHMgPSBPYmplY3QudmFsdWVzKGNyZWRpdHMpLnJlZHVjZShcbiAgICAgICAgICAgIChzdW0sIGMpID0+IHN1bSArIGMuYW1vdW50ICogYy5yYXRlLFxuICAgICAgICAgICAgMCxcbiAgICAgICAgKTtcbiAgICBcbiAgICAgICAgcmV0dXJuIE1hdGgubWF4KDAsIHRheCAtIHRvdGFsQ3JlZGl0cyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjb21wdXRlQ1BQKGluY29tZTogbnVtYmVyLCBjcHA/OiBDUFBDb250cmlidXRpb24pOiBudW1iZXIge1xuICAgICAgICBpZiAoIWNwcCB8fCBpbmNvbWUgPD0gY3BwLmV4ZW1wdGlvbikgcmV0dXJuIDA7XG4gICAgXG4gICAgICAgIGNvbnN0IGNvbnRyaWJ1dGFibGUgPSBpbmNvbWUgLSBjcHAuZXhlbXB0aW9uO1xuICAgICAgICByZXR1cm4gTWF0aC5taW4oXG4gICAgICAgICAgICBjb250cmlidXRhYmxlICogY3BwLnJhdGUsXG4gICAgICAgICAgICBjcHAubWF4X2NvbnRyaWJ1dGlvbixcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbXB1dGVFSShpbmNvbWU6IG51bWJlciwgZWk/OiBFSUNvbnRyaWJ1dGlvbik6IG51bWJlciB7XG4gICAgICAgIGlmICghZWkpIHJldHVybiAwO1xuICAgIFxuICAgICAgICBjb25zdCBpbnN1cmFibGVJbmNvbWUgPSBNYXRoLm1pbihcbiAgICAgICAgICAgIGluY29tZSxcbiAgICAgICAgICAgIGVpLm1heF9pbnN1cmFibGVfZWFybmluZ3MsXG4gICAgICAgICk7XG4gICAgXG4gICAgICAgIHJldHVybiBNYXRoLm1pbihcbiAgICAgICAgICAgIGluc3VyYWJsZUluY29tZSAqIGVpLnJhdGUsXG4gICAgICAgICAgICBlaS5tYXhfY29udHJpYnV0aW9uLFxuICAgICAgICApO1xuICAgIH1cbn0iXX0=
51
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBV0EsTUFBYSwwQkFBMEI7SUFJbkMsWUFBWSxNQUFjLEVBQUUsS0FBcUI7UUFDN0MsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUM7UUFDdEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFDeEIsQ0FBQztJQUVNLGtCQUFrQjtRQUVyQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUM3RSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2hFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMxRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFdkUsT0FBTztZQUNILFdBQVcsRUFBRSxJQUFJLENBQUMsT0FBTztZQUN6QixTQUFTLEVBQUUsTUFBTTtZQUNqQixHQUFHO1lBQ0gsRUFBRTtZQUNGLGVBQWUsRUFBRSxNQUFNLEdBQUcsR0FBRyxHQUFHLEVBQUU7WUFDbEMsU0FBUyxFQUFFLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxHQUFHLEdBQUcsR0FBRyxFQUFFO1lBQzNDLGdCQUFnQixFQUFFLE1BQU0sR0FBRyxJQUFJLENBQUMsT0FBTztTQUMxQyxDQUFDO0lBQ04sQ0FBQztJQUVPLGVBQWUsQ0FBQyxNQUFjLEVBQUUsUUFBc0I7UUFDMUQsT0FBTyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ2hDLElBQUksTUFBTSxJQUFJLENBQUMsQ0FBQyxJQUFJO2dCQUFFLE9BQU8sS0FBSyxDQUFDO1lBRW5DLE1BQU0sS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksTUFBTSxDQUFDO1lBQzdCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFFakQsT0FBTyxLQUFLLEdBQUcsT0FBTyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDcEMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1YsQ0FBQztJQUVPLFlBQVksQ0FBQyxHQUFXLEVBQUUsVUFBcUMsRUFBRTtRQUNyRSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FDOUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsSUFBSSxFQUNuQyxDQUFDLENBQ0osQ0FBQztRQUVGLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxHQUFHLFlBQVksQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFFTyxVQUFVLENBQUMsTUFBYyxFQUFFLEdBQXFCO1FBQ3BELElBQUksQ0FBQyxHQUFHLElBQUksTUFBTSxJQUFJLEdBQUcsQ0FBQyxTQUFTO1lBQUUsT0FBTyxDQUFDLENBQUM7UUFFOUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxHQUFHLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDN0MsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUNYLGFBQWEsR0FBRyxHQUFHLENBQUMsSUFBSSxFQUN4QixHQUFHLENBQUMsZUFBZSxDQUN0QixDQUFDO0lBQ04sQ0FBQztJQUVPLFNBQVMsQ0FBQyxNQUFjLEVBQUUsRUFBbUI7UUFDakQsSUFBSSxDQUFDLEVBQUU7WUFBRSxPQUFPLENBQUMsQ0FBQztRQUVsQixNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUM1QixNQUFNLEVBQ04sRUFBRSxDQUFDLG9CQUFvQixDQUMxQixDQUFDO1FBRUYsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUNYLGVBQWUsR0FBRyxFQUFFLENBQUMsSUFBSSxFQUN6QixFQUFFLENBQUMsZUFBZSxDQUNyQixDQUFDO0lBQ04sQ0FBQztDQUNKO0FBdEVELGdFQXNFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFRheEJyYWNrZXQgfSBmcm9tIFwiLi4vLi4vZG9tYWluL3R5cGVzXCI7XG5pbXBvcnQgeyBDYW5hZGFJbmNvbWVUYXhTZXJ2aWNlIH0gZnJvbSBcIi4vQ2FuYWRhSW5jb21lVGF4U2VydmljZVwiO1xuaW1wb3J0IHtcbiAgICBDUFBDb250cmlidXRpb24sXG4gICAgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMsXG4gICAgRUlDb250cmlidXRpb24sXG4gICAgSW5jb21lVGF4UnVsZXMsXG4gICAgVGF4Q3JlZGl0XG59IGZyb20gXCIuL2RvbWFpbi90eXBlc1wiO1xuXG5cbmV4cG9ydCBjbGFzcyBDYW5hZGFJbmNvbWVUYXhTZXJ2aWNlSW1wbCBpbXBsZW1lbnRzIENhbmFkYUluY29tZVRheFNlcnZpY2Uge1xuICAgIHByaXZhdGUgX2luY29tZTogbnVtYmVyO1xuICAgIHByaXZhdGUgX3J1bGVzOiBJbmNvbWVUYXhSdWxlcztcblxuICAgIGNvbnN0cnVjdG9yKGluY29tZTogbnVtYmVyLCBydWxlczogSW5jb21lVGF4UnVsZXMpIHtcbiAgICAgICAgdGhpcy5faW5jb21lID0gaW5jb21lO1xuICAgICAgICB0aGlzLl9ydWxlcyA9IHJ1bGVzO1xuICAgIH1cblxuICAgIHB1YmxpYyBjYWxjdWxhdGVOZXRJbmNvbWUoKTogQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMge1xuXG4gICAgICAgIGNvbnN0IGdyb3NzVGF4ID0gdGhpcy5jb21wdXRlR3Jvc3NUYXgodGhpcy5faW5jb21lLCB0aGlzLl9ydWxlcy50YXhCcmFja2V0cyk7XG4gICAgICAgIGNvbnN0IG5ldFRheCA9IHRoaXMuYXBwbHlDcmVkaXRzKGdyb3NzVGF4LCB0aGlzLl9ydWxlcy5jcmVkaXRzKTtcbiAgICAgICAgY29uc3QgY3BwID0gdGhpcy5jb21wdXRlQ1BQKHRoaXMuX2luY29tZSwgdGhpcy5fcnVsZXMuY29udHJpYnV0aW9ucz8uY3BwKTtcbiAgICAgICAgY29uc3QgZWkgPSB0aGlzLmNvbXB1dGVFSSh0aGlzLl9pbmNvbWUsIHRoaXMuX3J1bGVzLmNvbnRyaWJ1dGlvbnM/LmVpKTtcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgZ3Jvc3NJbmNvbWU6IHRoaXMuX2luY29tZSxcbiAgICAgICAgICAgIGluY29tZVRheDogbmV0VGF4LFxuICAgICAgICAgICAgY3BwLFxuICAgICAgICAgICAgZWksXG4gICAgICAgICAgICB0b3RhbERlZHVjdGlvbnM6IG5ldFRheCArIGNwcCArIGVpLFxuICAgICAgICAgICAgbmV0SW5jb21lOiB0aGlzLl9pbmNvbWUgLSBuZXRUYXggLSBjcHAgLSBlaSxcbiAgICAgICAgICAgIGVmZmVjdGl2ZVRheFJhdGU6IG5ldFRheCAvIHRoaXMuX2luY29tZSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbXB1dGVHcm9zc1RheChpbmNvbWU6IG51bWJlciwgYnJhY2tldHM6IFRheEJyYWNrZXRbXSk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiBicmFja2V0cy5yZWR1Y2UoKHRvdGFsLCBiKSA9PiB7XG4gICAgICAgICAgICBpZiAoaW5jb21lIDw9IGIuZnJvbSkgcmV0dXJuIHRvdGFsO1xuICAgIFxuICAgICAgICAgICAgY29uc3QgdXBwZXIgPSBiLnRvID8/IGluY29tZTtcbiAgICAgICAgICAgIGNvbnN0IHRheGFibGUgPSBNYXRoLm1pbihpbmNvbWUsIHVwcGVyKSAtIGIuZnJvbTtcbiAgICBcbiAgICAgICAgICAgIHJldHVybiB0b3RhbCArIHRheGFibGUgKiBiLnJhdGU7XG4gICAgICAgIH0sIDApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXBwbHlDcmVkaXRzKHRheDogbnVtYmVyLCBjcmVkaXRzOiBSZWNvcmQ8c3RyaW5nLCBUYXhDcmVkaXQ+ID0ge30pOiBudW1iZXIge1xuICAgICAgICBjb25zdCB0b3RhbENyZWRpdHMgPSBPYmplY3QudmFsdWVzKGNyZWRpdHMpLnJlZHVjZShcbiAgICAgICAgICAgIChzdW0sIGMpID0+IHN1bSArIGMuYW1vdW50ICogYy5yYXRlLFxuICAgICAgICAgICAgMCxcbiAgICAgICAgKTtcbiAgICBcbiAgICAgICAgcmV0dXJuIE1hdGgubWF4KDAsIHRheCAtIHRvdGFsQ3JlZGl0cyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjb21wdXRlQ1BQKGluY29tZTogbnVtYmVyLCBjcHA/OiBDUFBDb250cmlidXRpb24pOiBudW1iZXIge1xuICAgICAgICBpZiAoIWNwcCB8fCBpbmNvbWUgPD0gY3BwLmV4ZW1wdGlvbikgcmV0dXJuIDA7XG4gICAgXG4gICAgICAgIGNvbnN0IGNvbnRyaWJ1dGFibGUgPSBpbmNvbWUgLSBjcHAuZXhlbXB0aW9uO1xuICAgICAgICByZXR1cm4gTWF0aC5taW4oXG4gICAgICAgICAgICBjb250cmlidXRhYmxlICogY3BwLnJhdGUsXG4gICAgICAgICAgICBjcHAubWF4Q29udHJpYnV0aW9uLFxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgY29tcHV0ZUVJKGluY29tZTogbnVtYmVyLCBlaT86IEVJQ29udHJpYnV0aW9uKTogbnVtYmVyIHtcbiAgICAgICAgaWYgKCFlaSkgcmV0dXJuIDA7XG4gICAgXG4gICAgICAgIGNvbnN0IGluc3VyYWJsZUluY29tZSA9IE1hdGgubWluKFxuICAgICAgICAgICAgaW5jb21lLFxuICAgICAgICAgICAgZWkubWF4SW5zdXJhYmxlRWFybmluZ3MsXG4gICAgICAgICk7XG4gICAgXG4gICAgICAgIHJldHVybiBNYXRoLm1pbihcbiAgICAgICAgICAgIGluc3VyYWJsZUluY29tZSAqIGVpLnJhdGUsXG4gICAgICAgICAgICBlaS5tYXhDb250cmlidXRpb24sXG4gICAgICAgICk7XG4gICAgfVxufSJdfQ==
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgaW50ZXJmYWNlIEluY29tZVRheFJ1bGVzIHtcbiAgICB0YXhfYnJhY2tldHM6IFRheEJyYWNrZXRbXTtcbiAgICBjcmVkaXRzPzogUmVjb3JkPHN0cmluZywgVGF4Q3JlZGl0PjtcbiAgICBjb250cmlidXRpb25zPzogQ29udHJpYnV0aW9ucztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUYXhCcmFja2V0IHtcbiAgICBmcm9tOiBudW1iZXI7XG4gICAgdG86IG51bWJlciB8IG51bGw7XG4gICAgcmF0ZTogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRheENyZWRpdCB7XG4gICAgYW1vdW50OiBudW1iZXI7XG4gICAgdHlwZTogJ25vblJlZnVuZGFibGUnIHwgJ3JlZnVuZGFibGUnO1xuICAgIHJhdGU6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250cmlidXRpb25zIHtcbiAgICBjcHA/OiBDUFBDb250cmlidXRpb247XG4gICAgZWk/OiBFSUNvbnRyaWJ1dGlvbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDUFBDb250cmlidXRpb24ge1xuICAgIHJhdGU6IG51bWJlcjtcbiAgICBtYXhfY29udHJpYnV0aW9uOiBudW1iZXI7XG4gICAgZXhlbXB0aW9uOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRUlDb250cmlidXRpb24ge1xuICAgIHJhdGU6IG51bWJlcjtcbiAgICBtYXhfaW5zdXJhYmxlX2Vhcm5pbmdzOiBudW1iZXI7XG4gICAgbWF4X2NvbnRyaWJ1dGlvbjogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENvbXB1dGVkSW5jb21lVGF4VmFsdWVzIHtcbiAgICBncm9zc0luY29tZTogbnVtYmVyO1xuICAgIGluY29tZVRheDogbnVtYmVyO1xuICAgIGNwcDogbnVtYmVyO1xuICAgIGVpOiBudW1iZXI7XG4gICAgdG90YWxEZWR1Y3Rpb25zOiBudW1iZXI7XG4gICAgbmV0SW5jb21lOiBudW1iZXI7XG4gICAgZWZmZWN0aXZlVGF4UmF0ZTogbnVtYmVyO1xufVxuIl19
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaW5jb21lLXRheC9jYW5hZGEvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSdWxlSW5wdXQsIFJ1bGVNZXRhLCBSdWxlT3V0cHV0LCBUYXhCcmFja2V0IH0gZnJvbSBcIi4uLy4uLy4uL2RvbWFpbi90eXBlc1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIEluY29tZVRheFJ1bGVzIHtcbiAgICB0YXhCcmFja2V0czogVGF4QnJhY2tldFtdO1xuICAgIGNyZWRpdHM/OiBSZWNvcmQ8c3RyaW5nLCBUYXhDcmVkaXQ+O1xuICAgIGNvbnRyaWJ1dGlvbnM/OiBDb250cmlidXRpb25zO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFRheENyZWRpdCB7XG4gICAgYW1vdW50OiBudW1iZXI7XG4gICAgdHlwZTogJ25vblJlZnVuZGFibGUnIHwgJ3JlZnVuZGFibGUnO1xuICAgIHJhdGU6IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250cmlidXRpb25zIHtcbiAgICBjcHA/OiBDUFBDb250cmlidXRpb247XG4gICAgZWk/OiBFSUNvbnRyaWJ1dGlvbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDUFBDb250cmlidXRpb24ge1xuICAgIHJhdGU6IG51bWJlcjtcbiAgICBtYXhDb250cmlidXRpb246IG51bWJlcjtcbiAgICBleGVtcHRpb246IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBFSUNvbnRyaWJ1dGlvbiB7XG4gICAgcmF0ZTogbnVtYmVyO1xuICAgIG1heEluc3VyYWJsZUVhcm5pbmdzOiBudW1iZXI7XG4gICAgbWF4Q29udHJpYnV0aW9uOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMge1xuICAgIGdyb3NzSW5jb21lOiBudW1iZXI7XG4gICAgaW5jb21lVGF4OiBudW1iZXI7XG4gICAgY3BwOiBudW1iZXI7XG4gICAgZWk6IG51bWJlcjtcbiAgICB0b3RhbERlZHVjdGlvbnM6IG51bWJlcjtcbiAgICBuZXRJbmNvbWU6IG51bWJlcjtcbiAgICBlZmZlY3RpdmVUYXhSYXRlOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSW5jb21lVGF4Q2FsY3VsYXRvclNjaGVtYSB7XG5cdG1ldGE6IFJ1bGVNZXRhO1xuXHRpbnB1dHM6IFJ1bGVJbnB1dFtdO1xuXHRvdXRwdXRzOiBSdWxlT3V0cHV0W107XG5cdHJ1bGVzOiBJbmNvbWVUYXhSdWxlcztcbn1cbiJdfQ==
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRnJhbmNlSW5jb21lVGF4U2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9pbmNvbWUtdGF4L2ZyYW5jZS9GcmFuY2VJbmNvbWVUYXhTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wdXRlZEluY29tZVRheFZhbHVlcyB9IGZyb20gXCIuL2RvbWFpbi90eXBlc1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIEZyYW5jZUluY29tZVRheFNlcnZpY2Uge1xuICAgIGNhbGN1bGF0ZU5ldEluY29tZShpbmNvbWU6IG51bWJlcik6IENvbXB1dGVkSW5jb21lVGF4VmFsdWVzO1xufSJdfQ==
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FranceIncomeTaxServiceImpl = void 0;
4
+ class FranceIncomeTaxServiceImpl {
5
+ constructor(income, rules, familyParts) {
6
+ this._income = income;
7
+ this._rules = rules;
8
+ this._familyParts = familyParts;
9
+ }
10
+ calculateNetIncome() {
11
+ const taxablePerPart = this._familyParts * this._income / this._familyParts;
12
+ const { tax: taxPerPart, marginalRate } = this.calculateProgressiveTax(taxablePerPart);
13
+ const incomeTax = taxPerPart * this._familyParts;
14
+ const socialContributions = this._income * this._rules.socialContributions.employee.rate;
15
+ const totalDeductions = incomeTax + socialContributions;
16
+ const netIncome = this._income - totalDeductions;
17
+ return {
18
+ grossIncome: this._income,
19
+ incomeTax: this.round(incomeTax),
20
+ socialContributions: this.round(socialContributions),
21
+ totalDeductions: this.round(totalDeductions),
22
+ netIncome: this.round(netIncome),
23
+ averageTaxRate: this.round(incomeTax / this._income),
24
+ marginalTaxRate: marginalRate,
25
+ };
26
+ }
27
+ calculateProgressiveTax(income) {
28
+ let tax = 0;
29
+ let marginalRate = 0;
30
+ for (const bracket of this._rules.taxBrackets) {
31
+ const upperBound = bracket.to ?? income;
32
+ if (income <= bracket.from) {
33
+ break;
34
+ }
35
+ const taxableAmount = Math.min(upperBound, income) - bracket.from;
36
+ if (taxableAmount > 0) {
37
+ tax += taxableAmount * bracket.rate;
38
+ marginalRate = bracket.rate;
39
+ }
40
+ }
41
+ return { tax, marginalRate };
42
+ }
43
+ round(value) {
44
+ return Math.round(value * 100) / 100;
45
+ }
46
+ }
47
+ exports.FranceIncomeTaxServiceImpl = FranceIncomeTaxServiceImpl;
48
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRnJhbmNlSW5jb21lVGF4U2VydmljZUltcGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvaW5jb21lLXRheC9mcmFuY2UvRnJhbmNlSW5jb21lVGF4U2VydmljZUltcGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR0EsTUFBYSwwQkFBMEI7SUFLbkMsWUFBWSxNQUFjLEVBQUUsS0FBcUIsRUFBRSxXQUFtQjtRQUNsRSxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztRQUN0QixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixJQUFJLENBQUMsWUFBWSxHQUFHLFdBQVcsQ0FBQztJQUNwQyxDQUFDO0lBRU0sa0JBQWtCO1FBRTNCLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBQzVFLE1BQU0sRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxHQUFHLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxjQUFjLENBQUMsQ0FBQztRQUN2RixNQUFNLFNBQVMsR0FBRyxVQUFVLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztRQUNqRCxNQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDO1FBQ3pGLE1BQU0sZUFBZSxHQUFHLFNBQVMsR0FBRyxtQkFBbUIsQ0FBQztRQUN4RCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxHQUFHLGVBQWUsQ0FBQztRQUVqRCxPQUFPO1lBQ04sV0FBVyxFQUFFLElBQUksQ0FBQyxPQUFPO1lBQ3pCLFNBQVMsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztZQUNoQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLG1CQUFtQixDQUFDO1lBQ3BELGVBQWUsRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQztZQUM1QyxTQUFTLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUM7WUFDaEMsY0FBYyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7WUFDcEQsZUFBZSxFQUFFLFlBQVk7U0FDN0IsQ0FBQztJQUNILENBQUM7SUFHVSx1QkFBdUIsQ0FBQyxNQUFjO1FBSWhELElBQUksR0FBRyxHQUFHLENBQUMsQ0FBQztRQUNaLElBQUksWUFBWSxHQUFHLENBQUMsQ0FBQztRQUVyQixLQUFLLE1BQU0sT0FBTyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDL0MsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLEVBQUUsSUFBSSxNQUFNLENBQUM7WUFFeEMsSUFBSSxNQUFNLElBQUksT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUM1QixNQUFNO1lBQ1AsQ0FBQztZQUVELE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFFbEUsSUFBSSxhQUFhLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZCLEdBQUcsSUFBSSxhQUFhLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQztnQkFDcEMsWUFBWSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDN0IsQ0FBQztRQUNGLENBQUM7UUFFRCxPQUFPLEVBQUUsR0FBRyxFQUFFLFlBQVksRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFVSxLQUFLLENBQUMsS0FBYTtRQUM3QixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLEdBQUcsQ0FBQztJQUN0QyxDQUFDO0NBQ0Q7QUE1REQsZ0VBNERDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMsIEluY29tZVRheFJ1bGVzIH0gZnJvbSBcIi4vZG9tYWluL3R5cGVzXCI7XG5pbXBvcnQgeyBGcmFuY2VJbmNvbWVUYXhTZXJ2aWNlIH0gZnJvbSBcIi4vRnJhbmNlSW5jb21lVGF4U2VydmljZVwiO1xuXG5leHBvcnQgY2xhc3MgRnJhbmNlSW5jb21lVGF4U2VydmljZUltcGwgaW1wbGVtZW50cyBGcmFuY2VJbmNvbWVUYXhTZXJ2aWNlIHtcbiAgICBwcml2YXRlIF9pbmNvbWU6IG51bWJlcjtcbiAgICBwcml2YXRlIF9ydWxlczogSW5jb21lVGF4UnVsZXM7XG4gICAgcHJpdmF0ZSBfZmFtaWx5UGFydHM6IG51bWJlcjtcblxuICAgIGNvbnN0cnVjdG9yKGluY29tZTogbnVtYmVyLCBydWxlczogSW5jb21lVGF4UnVsZXMsIGZhbWlseVBhcnRzOiBudW1iZXIpIHtcbiAgICAgICAgdGhpcy5faW5jb21lID0gaW5jb21lO1xuICAgICAgICB0aGlzLl9ydWxlcyA9IHJ1bGVzO1xuICAgICAgICB0aGlzLl9mYW1pbHlQYXJ0cyA9IGZhbWlseVBhcnRzO1xuICAgIH1cblxuICAgIHB1YmxpYyBjYWxjdWxhdGVOZXRJbmNvbWUoKTogQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMge1xuXG5cdFx0Y29uc3QgdGF4YWJsZVBlclBhcnQgPSB0aGlzLl9mYW1pbHlQYXJ0cyAqIHRoaXMuX2luY29tZSAvIHRoaXMuX2ZhbWlseVBhcnRzO1xuXHRcdGNvbnN0IHsgdGF4OiB0YXhQZXJQYXJ0LCBtYXJnaW5hbFJhdGUgfSA9IHRoaXMuY2FsY3VsYXRlUHJvZ3Jlc3NpdmVUYXgodGF4YWJsZVBlclBhcnQpO1xuXHRcdGNvbnN0IGluY29tZVRheCA9IHRheFBlclBhcnQgKiB0aGlzLl9mYW1pbHlQYXJ0cztcblx0XHRjb25zdCBzb2NpYWxDb250cmlidXRpb25zID0gdGhpcy5faW5jb21lICogdGhpcy5fcnVsZXMuc29jaWFsQ29udHJpYnV0aW9ucy5lbXBsb3llZS5yYXRlO1xuXHRcdGNvbnN0IHRvdGFsRGVkdWN0aW9ucyA9IGluY29tZVRheCArIHNvY2lhbENvbnRyaWJ1dGlvbnM7XG5cdFx0Y29uc3QgbmV0SW5jb21lID0gdGhpcy5faW5jb21lIC0gdG90YWxEZWR1Y3Rpb25zO1xuXG5cdFx0cmV0dXJuIHtcblx0XHRcdGdyb3NzSW5jb21lOiB0aGlzLl9pbmNvbWUsXG5cdFx0XHRpbmNvbWVUYXg6IHRoaXMucm91bmQoaW5jb21lVGF4KSxcblx0XHRcdHNvY2lhbENvbnRyaWJ1dGlvbnM6IHRoaXMucm91bmQoc29jaWFsQ29udHJpYnV0aW9ucyksXG5cdFx0XHR0b3RhbERlZHVjdGlvbnM6IHRoaXMucm91bmQodG90YWxEZWR1Y3Rpb25zKSxcblx0XHRcdG5ldEluY29tZTogdGhpcy5yb3VuZChuZXRJbmNvbWUpLFxuXHRcdFx0YXZlcmFnZVRheFJhdGU6IHRoaXMucm91bmQoaW5jb21lVGF4IC8gdGhpcy5faW5jb21lKSxcblx0XHRcdG1hcmdpbmFsVGF4UmF0ZTogbWFyZ2luYWxSYXRlLFxuXHRcdH07XG5cdH1cblxuXG4gICAgcHJpdmF0ZSBjYWxjdWxhdGVQcm9ncmVzc2l2ZVRheChpbmNvbWU6IG51bWJlcik6IHtcbiAgICAgICAgdGF4OiBudW1iZXI7XG4gICAgICAgIG1hcmdpbmFsUmF0ZTogbnVtYmVyXG4gICAgfSB7XG5cdFx0bGV0IHRheCA9IDA7XG5cdFx0bGV0IG1hcmdpbmFsUmF0ZSA9IDA7XG5cblx0XHRmb3IgKGNvbnN0IGJyYWNrZXQgb2YgdGhpcy5fcnVsZXMudGF4QnJhY2tldHMpIHtcblx0XHRcdGNvbnN0IHVwcGVyQm91bmQgPSBicmFja2V0LnRvID8/IGluY29tZTtcblxuXHRcdFx0aWYgKGluY29tZSA8PSBicmFja2V0LmZyb20pIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cblx0XHRcdGNvbnN0IHRheGFibGVBbW91bnQgPSBNYXRoLm1pbih1cHBlckJvdW5kLCBpbmNvbWUpIC0gYnJhY2tldC5mcm9tO1xuXG5cdFx0XHRpZiAodGF4YWJsZUFtb3VudCA+IDApIHtcblx0XHRcdFx0dGF4ICs9IHRheGFibGVBbW91bnQgKiBicmFja2V0LnJhdGU7XG5cdFx0XHRcdG1hcmdpbmFsUmF0ZSA9IGJyYWNrZXQucmF0ZTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4geyB0YXgsIG1hcmdpbmFsUmF0ZSB9O1xuXHR9XG5cbiAgICBwcml2YXRlIHJvdW5kKHZhbHVlOiBudW1iZXIpOiBudW1iZXIge1xuXHRcdHJldHVybiBNYXRoLnJvdW5kKHZhbHVlICogMTAwKSAvIDEwMDtcblx0fVxufVxuIl19
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvaW5jb21lLXRheC9mcmFuY2UvZG9tYWluL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSdWxlSW5wdXQsIFJ1bGVNZXRhLCBSdWxlT3V0cHV0LCBUYXhCcmFja2V0IH0gZnJvbSBcIi4uLy4uLy4uL2RvbWFpbi90eXBlc1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIEluY29tZVRheFJ1bGVzIHtcbiAgICB0YXhCcmFja2V0czogVGF4QnJhY2tldFtdO1xuICAgIHF1b3RpZW50RmFtaWxpYWw6IFF1b3RpZW50RmFtaWxpYWw7XG4gICAgc29jaWFsQ29udHJpYnV0aW9uczogQ29udHJpYnV0aW9ucztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBDb250cmlidXRpb25zIHtcbiAgICBlbXBsb3llZTogeyByYXRlOiBudW1iZXI7IH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUXVvdGllbnRGYW1pbGlhbCB7XG4gICAgZW5hYmxlZDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJbmNvbWVUYXhDYWxjdWxhdG9yU2NoZW1hIHtcbiAgICBtZXRhOiBSdWxlTWV0YTtcbiAgICBpbnB1dHM6IFJ1bGVJbnB1dFtdO1xuICAgIG91dHB1dHM6IFJ1bGVPdXRwdXRbXTtcbiAgICBydWxlczogSW5jb21lVGF4UnVsZXM7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMge1xuICAgIGdyb3NzSW5jb21lOiBudW1iZXI7XG5cdGluY29tZVRheDogbnVtYmVyO1xuXHRzb2NpYWxDb250cmlidXRpb25zOiBudW1iZXI7XG5cdHRvdGFsRGVkdWN0aW9uczogbnVtYmVyO1xuXHRuZXRJbmNvbWU6IG51bWJlcjtcblx0YXZlcmFnZVRheFJhdGU6IG51bWJlcjtcblx0bWFyZ2luYWxUYXhSYXRlOiBudW1iZXI7XG59Il19
package/dist/index.js CHANGED
@@ -1,6 +1,23 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CanadaIncomeTaxService = void 0;
17
+ exports.FranceIncomeTaxService = exports.CanadaIncomeTaxService = void 0;
4
18
  var CanadaIncomeTaxServiceImpl_1 = require("./income-tax/canada/CanadaIncomeTaxServiceImpl");
5
19
  Object.defineProperty(exports, "CanadaIncomeTaxService", { enumerable: true, get: function () { return CanadaIncomeTaxServiceImpl_1.CanadaIncomeTaxServiceImpl; } });
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkZBQXNIO0FBQTdHLG9JQUFBLDBCQUEwQixPQUEwQiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IENhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsIGFzIENhbmFkYUluY29tZVRheFNlcnZpY2UgfSBmcm9tICcuL2luY29tZS10YXgvY2FuYWRhL0NhbmFkYUluY29tZVRheFNlcnZpY2VJbXBsJztcbmV4cG9ydCB7IENvbXB1dGVkSW5jb21lVGF4VmFsdWVzLCBJbmNvbWVUYXhSdWxlcyB9IGZyb20gJy4vaW5jb21lLXRheC9jYW5hZGEvZG9tYWluL3R5cGVzJzsiXX0=
20
+ var FranceIncomeTaxServiceImpl_1 = require("./income-tax/france/FranceIncomeTaxServiceImpl");
21
+ Object.defineProperty(exports, "FranceIncomeTaxService", { enumerable: true, get: function () { return FranceIncomeTaxServiceImpl_1.FranceIncomeTaxServiceImpl; } });
22
+ __exportStar(require("./domain/types"), exports);
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSw2RkFBc0g7QUFBN0csb0lBQUEsMEJBQTBCLE9BQTBCO0FBQzdELDZGQUFzSDtBQUE3RyxvSUFBQSwwQkFBMEIsT0FBMEI7QUFXN0QsaURBQStCIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwgYXMgQ2FuYWRhSW5jb21lVGF4U2VydmljZSB9IGZyb20gJy4vaW5jb21lLXRheC9jYW5hZGEvQ2FuYWRhSW5jb21lVGF4U2VydmljZUltcGwnO1xuZXhwb3J0IHsgRnJhbmNlSW5jb21lVGF4U2VydmljZUltcGwgYXMgRnJhbmNlSW5jb21lVGF4U2VydmljZSB9IGZyb20gJy4vaW5jb21lLXRheC9mcmFuY2UvRnJhbmNlSW5jb21lVGF4U2VydmljZUltcGwnO1xuZXhwb3J0IHtcbiAgICBDb21wdXRlZEluY29tZVRheFZhbHVlcyBhcyBDYW5hZGFDb21wdXRlZEluY29tZVRheFZhbHVlcyxcbiAgICBJbmNvbWVUYXhSdWxlcyBhcyBDYW5hZGFJbmNvbWVUYXhSdWxlcyxcbiAgICBJbmNvbWVUYXhDYWxjdWxhdG9yU2NoZW1hIGFzIENhbmFkYUluY29tZVRheENhbGN1bGF0b3JTY2hlbWFcbn0gZnJvbSAnLi9pbmNvbWUtdGF4L2NhbmFkYS9kb21haW4vdHlwZXMnO1xuZXhwb3J0IHsgXG4gICAgQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMgYXMgRnJhbmNlQ29tcHV0ZWRJbmNvbWVUYXhWYWx1ZXMsXG4gICAgSW5jb21lVGF4UnVsZXMgYXMgRnJhbmNlSW5jb21lVGF4UnVsZXMsXG4gICAgSW5jb21lVGF4Q2FsY3VsYXRvclNjaGVtYSBhcyBGcmFuY2VJbmNvbWVUYXhDYWxjdWxhdG9yU2NoZW1hXG59IGZyb20gJy4vaW5jb21lLXRheC9mcmFuY2UvZG9tYWluL3R5cGVzJztcbmV4cG9ydCAqIGZyb20gJy4vZG9tYWluL3R5cGVzJzsiXX0=
@@ -9,8 +9,8 @@ export interface RuleMeta {
9
9
  region: string;
10
10
  calculator: CalculatorType;
11
11
  version: string;
12
- effective_from: string;
13
- effective_to: string | null;
12
+ effectivefrom: string;
13
+ effectiveto: string | null;
14
14
  source?: RuleSource[];
15
15
  }
16
16
  export interface RuleSource {
@@ -28,3 +28,8 @@ export interface RuleOutput {
28
28
  type: 'number' | 'string';
29
29
  unit?: string;
30
30
  }
31
+ export interface TaxBracket {
32
+ from: number;
33
+ to: number | null;
34
+ rate: number;
35
+ }
@@ -1,13 +1,9 @@
1
+ import { RuleInput, RuleMeta, RuleOutput, TaxBracket } from "../../../domain/types";
1
2
  export interface IncomeTaxRules {
2
- tax_brackets: TaxBracket[];
3
+ taxBrackets: TaxBracket[];
3
4
  credits?: Record<string, TaxCredit>;
4
5
  contributions?: Contributions;
5
6
  }
6
- export interface TaxBracket {
7
- from: number;
8
- to: number | null;
9
- rate: number;
10
- }
11
7
  export interface TaxCredit {
12
8
  amount: number;
13
9
  type: 'nonRefundable' | 'refundable';
@@ -19,13 +15,13 @@ export interface Contributions {
19
15
  }
20
16
  export interface CPPContribution {
21
17
  rate: number;
22
- max_contribution: number;
18
+ maxContribution: number;
23
19
  exemption: number;
24
20
  }
25
21
  export interface EIContribution {
26
22
  rate: number;
27
- max_insurable_earnings: number;
28
- max_contribution: number;
23
+ maxInsurableEarnings: number;
24
+ maxContribution: number;
29
25
  }
30
26
  export interface ComputedIncomeTaxValues {
31
27
  grossIncome: number;
@@ -36,3 +32,9 @@ export interface ComputedIncomeTaxValues {
36
32
  netIncome: number;
37
33
  effectiveTaxRate: number;
38
34
  }
35
+ export interface IncomeTaxCalculatorSchema {
36
+ meta: RuleMeta;
37
+ inputs: RuleInput[];
38
+ outputs: RuleOutput[];
39
+ rules: IncomeTaxRules;
40
+ }
@@ -0,0 +1,4 @@
1
+ import { ComputedIncomeTaxValues } from "./domain/types";
2
+ export interface FranceIncomeTaxService {
3
+ calculateNetIncome(income: number): ComputedIncomeTaxValues;
4
+ }
@@ -0,0 +1,11 @@
1
+ import { ComputedIncomeTaxValues, IncomeTaxRules } from "./domain/types";
2
+ import { FranceIncomeTaxService } from "./FranceIncomeTaxService";
3
+ export declare class FranceIncomeTaxServiceImpl implements FranceIncomeTaxService {
4
+ private _income;
5
+ private _rules;
6
+ private _familyParts;
7
+ constructor(income: number, rules: IncomeTaxRules, familyParts: number);
8
+ calculateNetIncome(): ComputedIncomeTaxValues;
9
+ private calculateProgressiveTax;
10
+ private round;
11
+ }
@@ -0,0 +1,29 @@
1
+ import { RuleInput, RuleMeta, RuleOutput, TaxBracket } from "../../../domain/types";
2
+ export interface IncomeTaxRules {
3
+ taxBrackets: TaxBracket[];
4
+ quotientFamilial: QuotientFamilial;
5
+ socialContributions: Contributions;
6
+ }
7
+ export interface Contributions {
8
+ employee: {
9
+ rate: number;
10
+ };
11
+ }
12
+ export interface QuotientFamilial {
13
+ enabled: boolean;
14
+ }
15
+ export interface IncomeTaxCalculatorSchema {
16
+ meta: RuleMeta;
17
+ inputs: RuleInput[];
18
+ outputs: RuleOutput[];
19
+ rules: IncomeTaxRules;
20
+ }
21
+ export interface ComputedIncomeTaxValues {
22
+ grossIncome: number;
23
+ incomeTax: number;
24
+ socialContributions: number;
25
+ totalDeductions: number;
26
+ netIncome: number;
27
+ averageTaxRate: number;
28
+ marginalTaxRate: number;
29
+ }
@@ -1,2 +1,5 @@
1
1
  export { CanadaIncomeTaxServiceImpl as CanadaIncomeTaxService } from './income-tax/canada/CanadaIncomeTaxServiceImpl';
2
- export { ComputedIncomeTaxValues, IncomeTaxRules } from './income-tax/canada/domain/types';
2
+ export { FranceIncomeTaxServiceImpl as FranceIncomeTaxService } from './income-tax/france/FranceIncomeTaxServiceImpl';
3
+ export { ComputedIncomeTaxValues as CanadaComputedIncomeTaxValues, IncomeTaxRules as CanadaIncomeTaxRules, IncomeTaxCalculatorSchema as CanadaIncomeTaxCalculatorSchema } from './income-tax/canada/domain/types';
4
+ export { ComputedIncomeTaxValues as FranceComputedIncomeTaxValues, IncomeTaxRules as FranceIncomeTaxRules, IncomeTaxCalculatorSchema as FranceIncomeTaxCalculatorSchema } from './income-tax/france/domain/types';
5
+ export * from './domain/types';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@novha/calc-engines",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/types/index.d.ts",
6
6
  "scripts": {
@@ -10,8 +10,8 @@ export interface RuleMeta {
10
10
  region: string;
11
11
  calculator: CalculatorType;
12
12
  version: string;
13
- effective_from: string;
14
- effective_to: string | null;
13
+ effectivefrom: string;
14
+ effectiveto: string | null;
15
15
  source?: RuleSource[];
16
16
  }
17
17
 
@@ -31,4 +31,10 @@ export interface RuleOutput {
31
31
  name: string;
32
32
  type: 'number' | 'string';
33
33
  unit?: string;
34
+ }
35
+
36
+ export interface TaxBracket {
37
+ from: number;
38
+ to: number | null;
39
+ rate: number;
34
40
  }
@@ -1,10 +1,10 @@
1
+ import { TaxBracket } from "../../domain/types";
1
2
  import { CanadaIncomeTaxService } from "./CanadaIncomeTaxService";
2
3
  import {
3
4
  CPPContribution,
4
5
  ComputedIncomeTaxValues,
5
6
  EIContribution,
6
7
  IncomeTaxRules,
7
- TaxBracket,
8
8
  TaxCredit
9
9
  } from "./domain/types";
10
10
 
@@ -20,7 +20,7 @@ export class CanadaIncomeTaxServiceImpl implements CanadaIncomeTaxService {
20
20
 
21
21
  public calculateNetIncome(): ComputedIncomeTaxValues {
22
22
 
23
- const grossTax = this.computeGrossTax(this._income, this._rules.tax_brackets);
23
+ const grossTax = this.computeGrossTax(this._income, this._rules.taxBrackets);
24
24
  const netTax = this.applyCredits(grossTax, this._rules.credits);
25
25
  const cpp = this.computeCPP(this._income, this._rules.contributions?.cpp);
26
26
  const ei = this.computeEI(this._income, this._rules.contributions?.ei);
@@ -62,7 +62,7 @@ export class CanadaIncomeTaxServiceImpl implements CanadaIncomeTaxService {
62
62
  const contributable = income - cpp.exemption;
63
63
  return Math.min(
64
64
  contributable * cpp.rate,
65
- cpp.max_contribution,
65
+ cpp.maxContribution,
66
66
  );
67
67
  }
68
68
 
@@ -71,12 +71,12 @@ export class CanadaIncomeTaxServiceImpl implements CanadaIncomeTaxService {
71
71
 
72
72
  const insurableIncome = Math.min(
73
73
  income,
74
- ei.max_insurable_earnings,
74
+ ei.maxInsurableEarnings,
75
75
  );
76
76
 
77
77
  return Math.min(
78
78
  insurableIncome * ei.rate,
79
- ei.max_contribution,
79
+ ei.maxContribution,
80
80
  );
81
81
  }
82
82
  }
@@ -1,15 +1,11 @@
1
+ import { RuleInput, RuleMeta, RuleOutput, TaxBracket } from "../../../domain/types";
2
+
1
3
  export interface IncomeTaxRules {
2
- tax_brackets: TaxBracket[];
4
+ taxBrackets: TaxBracket[];
3
5
  credits?: Record<string, TaxCredit>;
4
6
  contributions?: Contributions;
5
7
  }
6
8
 
7
- export interface TaxBracket {
8
- from: number;
9
- to: number | null;
10
- rate: number;
11
- }
12
-
13
9
  export interface TaxCredit {
14
10
  amount: number;
15
11
  type: 'nonRefundable' | 'refundable';
@@ -23,14 +19,14 @@ export interface Contributions {
23
19
 
24
20
  export interface CPPContribution {
25
21
  rate: number;
26
- max_contribution: number;
22
+ maxContribution: number;
27
23
  exemption: number;
28
24
  }
29
25
 
30
26
  export interface EIContribution {
31
27
  rate: number;
32
- max_insurable_earnings: number;
33
- max_contribution: number;
28
+ maxInsurableEarnings: number;
29
+ maxContribution: number;
34
30
  }
35
31
 
36
32
  export interface ComputedIncomeTaxValues {
@@ -42,3 +38,10 @@ export interface ComputedIncomeTaxValues {
42
38
  netIncome: number;
43
39
  effectiveTaxRate: number;
44
40
  }
41
+
42
+ export interface IncomeTaxCalculatorSchema {
43
+ meta: RuleMeta;
44
+ inputs: RuleInput[];
45
+ outputs: RuleOutput[];
46
+ rules: IncomeTaxRules;
47
+ }
@@ -0,0 +1,5 @@
1
+ import { ComputedIncomeTaxValues } from "./domain/types";
2
+
3
+ export interface FranceIncomeTaxService {
4
+ calculateNetIncome(income: number): ComputedIncomeTaxValues;
5
+ }
@@ -0,0 +1,64 @@
1
+ import { ComputedIncomeTaxValues, IncomeTaxRules } from "./domain/types";
2
+ import { FranceIncomeTaxService } from "./FranceIncomeTaxService";
3
+
4
+ export class FranceIncomeTaxServiceImpl implements FranceIncomeTaxService {
5
+ private _income: number;
6
+ private _rules: IncomeTaxRules;
7
+ private _familyParts: number;
8
+
9
+ constructor(income: number, rules: IncomeTaxRules, familyParts: number) {
10
+ this._income = income;
11
+ this._rules = rules;
12
+ this._familyParts = familyParts;
13
+ }
14
+
15
+ public calculateNetIncome(): ComputedIncomeTaxValues {
16
+
17
+ const taxablePerPart = this._familyParts * this._income / this._familyParts;
18
+ const { tax: taxPerPart, marginalRate } = this.calculateProgressiveTax(taxablePerPart);
19
+ const incomeTax = taxPerPart * this._familyParts;
20
+ const socialContributions = this._income * this._rules.socialContributions.employee.rate;
21
+ const totalDeductions = incomeTax + socialContributions;
22
+ const netIncome = this._income - totalDeductions;
23
+
24
+ return {
25
+ grossIncome: this._income,
26
+ incomeTax: this.round(incomeTax),
27
+ socialContributions: this.round(socialContributions),
28
+ totalDeductions: this.round(totalDeductions),
29
+ netIncome: this.round(netIncome),
30
+ averageTaxRate: this.round(incomeTax / this._income),
31
+ marginalTaxRate: marginalRate,
32
+ };
33
+ }
34
+
35
+
36
+ private calculateProgressiveTax(income: number): {
37
+ tax: number;
38
+ marginalRate: number
39
+ } {
40
+ let tax = 0;
41
+ let marginalRate = 0;
42
+
43
+ for (const bracket of this._rules.taxBrackets) {
44
+ const upperBound = bracket.to ?? income;
45
+
46
+ if (income <= bracket.from) {
47
+ break;
48
+ }
49
+
50
+ const taxableAmount = Math.min(upperBound, income) - bracket.from;
51
+
52
+ if (taxableAmount > 0) {
53
+ tax += taxableAmount * bracket.rate;
54
+ marginalRate = bracket.rate;
55
+ }
56
+ }
57
+
58
+ return { tax, marginalRate };
59
+ }
60
+
61
+ private round(value: number): number {
62
+ return Math.round(value * 100) / 100;
63
+ }
64
+ }
@@ -0,0 +1,32 @@
1
+ import { RuleInput, RuleMeta, RuleOutput, TaxBracket } from "../../../domain/types";
2
+
3
+ export interface IncomeTaxRules {
4
+ taxBrackets: TaxBracket[];
5
+ quotientFamilial: QuotientFamilial;
6
+ socialContributions: Contributions;
7
+ }
8
+
9
+ export interface Contributions {
10
+ employee: { rate: number; };
11
+ }
12
+
13
+ export interface QuotientFamilial {
14
+ enabled: boolean;
15
+ }
16
+
17
+ export interface IncomeTaxCalculatorSchema {
18
+ meta: RuleMeta;
19
+ inputs: RuleInput[];
20
+ outputs: RuleOutput[];
21
+ rules: IncomeTaxRules;
22
+ }
23
+
24
+ export interface ComputedIncomeTaxValues {
25
+ grossIncome: number;
26
+ incomeTax: number;
27
+ socialContributions: number;
28
+ totalDeductions: number;
29
+ netIncome: number;
30
+ averageTaxRate: number;
31
+ marginalTaxRate: number;
32
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,13 @@
1
1
  export { CanadaIncomeTaxServiceImpl as CanadaIncomeTaxService } from './income-tax/canada/CanadaIncomeTaxServiceImpl';
2
- export { ComputedIncomeTaxValues, IncomeTaxRules } from './income-tax/canada/domain/types';
2
+ export { FranceIncomeTaxServiceImpl as FranceIncomeTaxService } from './income-tax/france/FranceIncomeTaxServiceImpl';
3
+ export {
4
+ ComputedIncomeTaxValues as CanadaComputedIncomeTaxValues,
5
+ IncomeTaxRules as CanadaIncomeTaxRules,
6
+ IncomeTaxCalculatorSchema as CanadaIncomeTaxCalculatorSchema
7
+ } from './income-tax/canada/domain/types';
8
+ export {
9
+ ComputedIncomeTaxValues as FranceComputedIncomeTaxValues,
10
+ IncomeTaxRules as FranceIncomeTaxRules,
11
+ IncomeTaxCalculatorSchema as FranceIncomeTaxCalculatorSchema
12
+ } from './income-tax/france/domain/types';
13
+ export * from './domain/types';