@novha/calc-engines 6.3.0 → 7.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.
Files changed (86) hide show
  1. package/dist/capital-gains/canada/CanadaCapitalGainsServiceImpl.js +29 -17
  2. package/dist/index.js +3 -2
  3. package/dist/inheritance-tax/australia/AustraliaInheritanceTaxService.js +3 -0
  4. package/dist/inheritance-tax/australia/AustraliaInheritanceTaxServiceImpl.js +19 -0
  5. package/dist/inheritance-tax/australia/domain/types.js +3 -0
  6. package/dist/inheritance-tax/canada/CanadaInheritanceTaxService.js +3 -0
  7. package/dist/inheritance-tax/canada/CanadaInheritanceTaxServiceImpl.js +51 -0
  8. package/dist/inheritance-tax/canada/domain/types.js +3 -0
  9. package/dist/inheritance-tax/domain/types.js +3 -0
  10. package/dist/inheritance-tax/france/FranceInheritanceTaxService.js +3 -0
  11. package/dist/inheritance-tax/france/FranceInheritanceTaxServiceImpl.js +52 -0
  12. package/dist/inheritance-tax/france/domain/types.js +3 -0
  13. package/dist/inheritance-tax/germany/GermanyInheritanceTaxService.js +3 -0
  14. package/dist/inheritance-tax/germany/GermanyInheritanceTaxServiceImpl.js +52 -0
  15. package/dist/inheritance-tax/germany/domain/types.js +3 -0
  16. package/dist/inheritance-tax/index.js +25 -0
  17. package/dist/inheritance-tax/south-africa/SouthAfricaInheritanceTaxService.js +3 -0
  18. package/dist/inheritance-tax/south-africa/SouthAfricaInheritanceTaxServiceImpl.js +52 -0
  19. package/dist/inheritance-tax/south-africa/domain/types.js +3 -0
  20. package/dist/inheritance-tax/uk/UKInheritanceTaxService.js +3 -0
  21. package/dist/inheritance-tax/uk/UKInheritanceTaxServiceImpl.js +39 -0
  22. package/dist/inheritance-tax/uk/domain/types.js +3 -0
  23. package/dist/inheritance-tax/usa/USAInheritanceTaxService.js +3 -0
  24. package/dist/inheritance-tax/usa/USAInheritanceTaxServiceImpl.js +51 -0
  25. package/dist/inheritance-tax/usa/domain/types.js +3 -0
  26. package/dist/shared/domain/types.js +2 -1
  27. package/dist/types/capital-gains/canada/CanadaCapitalGainsServiceImpl.d.ts +1 -1
  28. package/dist/types/index.d.ts +1 -0
  29. package/dist/types/inheritance-tax/australia/AustraliaInheritanceTaxService.d.ts +4 -0
  30. package/dist/types/inheritance-tax/australia/AustraliaInheritanceTaxServiceImpl.d.ts +8 -0
  31. package/dist/types/inheritance-tax/australia/domain/types.d.ts +13 -0
  32. package/dist/types/inheritance-tax/canada/CanadaInheritanceTaxService.d.ts +4 -0
  33. package/dist/types/inheritance-tax/canada/CanadaInheritanceTaxServiceImpl.d.ts +9 -0
  34. package/dist/types/inheritance-tax/canada/domain/types.d.ts +20 -0
  35. package/dist/types/inheritance-tax/domain/types.d.ts +17 -0
  36. package/dist/types/inheritance-tax/france/FranceInheritanceTaxService.d.ts +4 -0
  37. package/dist/types/inheritance-tax/france/FranceInheritanceTaxServiceImpl.d.ts +9 -0
  38. package/dist/types/inheritance-tax/france/domain/types.d.ts +29 -0
  39. package/dist/types/inheritance-tax/germany/GermanyInheritanceTaxService.d.ts +4 -0
  40. package/dist/types/inheritance-tax/germany/GermanyInheritanceTaxServiceImpl.d.ts +9 -0
  41. package/dist/types/inheritance-tax/germany/domain/types.d.ts +28 -0
  42. package/dist/types/inheritance-tax/index.d.ts +15 -0
  43. package/dist/types/inheritance-tax/south-africa/SouthAfricaInheritanceTaxService.d.ts +4 -0
  44. package/dist/types/inheritance-tax/south-africa/SouthAfricaInheritanceTaxServiceImpl.d.ts +9 -0
  45. package/dist/types/inheritance-tax/south-africa/domain/types.d.ts +20 -0
  46. package/dist/types/inheritance-tax/uk/UKInheritanceTaxService.d.ts +4 -0
  47. package/dist/types/inheritance-tax/uk/UKInheritanceTaxServiceImpl.d.ts +8 -0
  48. package/dist/types/inheritance-tax/uk/domain/types.d.ts +17 -0
  49. package/dist/types/inheritance-tax/usa/USAInheritanceTaxService.d.ts +4 -0
  50. package/dist/types/inheritance-tax/usa/USAInheritanceTaxServiceImpl.d.ts +9 -0
  51. package/dist/types/inheritance-tax/usa/domain/types.d.ts +19 -0
  52. package/dist/types/shared/domain/types.d.ts +2 -1
  53. package/package.json +1 -1
  54. package/src/capital-gains/canada/CanadaCapitalGainsServiceImpl.ts +34 -21
  55. package/src/index.ts +1 -0
  56. package/src/inheritance-tax/australia/AustraliaInheritanceTaxService.ts +5 -0
  57. package/src/inheritance-tax/australia/AustraliaInheritanceTaxServiceImpl.ts +21 -0
  58. package/src/inheritance-tax/australia/domain/types.ts +16 -0
  59. package/src/inheritance-tax/canada/CanadaInheritanceTaxService.ts +5 -0
  60. package/src/inheritance-tax/canada/CanadaInheritanceTaxServiceImpl.ts +65 -0
  61. package/src/inheritance-tax/canada/domain/types.ts +24 -0
  62. package/src/inheritance-tax/domain/types.ts +19 -0
  63. package/src/inheritance-tax/france/FranceInheritanceTaxService.ts +5 -0
  64. package/src/inheritance-tax/france/FranceInheritanceTaxServiceImpl.ts +66 -0
  65. package/src/inheritance-tax/france/domain/types.ts +35 -0
  66. package/src/inheritance-tax/germany/GermanyInheritanceTaxService.ts +5 -0
  67. package/src/inheritance-tax/germany/GermanyInheritanceTaxServiceImpl.ts +66 -0
  68. package/src/inheritance-tax/germany/domain/types.ts +34 -0
  69. package/src/inheritance-tax/index.ts +86 -0
  70. package/src/inheritance-tax/south-africa/SouthAfricaInheritanceTaxService.ts +5 -0
  71. package/src/inheritance-tax/south-africa/SouthAfricaInheritanceTaxServiceImpl.ts +66 -0
  72. package/src/inheritance-tax/south-africa/domain/types.ts +24 -0
  73. package/src/inheritance-tax/uk/UKInheritanceTaxService.ts +5 -0
  74. package/src/inheritance-tax/uk/UKInheritanceTaxServiceImpl.ts +46 -0
  75. package/src/inheritance-tax/uk/domain/types.ts +20 -0
  76. package/src/inheritance-tax/usa/USAInheritanceTaxService.ts +5 -0
  77. package/src/inheritance-tax/usa/USAInheritanceTaxServiceImpl.ts +65 -0
  78. package/src/inheritance-tax/usa/domain/types.ts +23 -0
  79. package/src/shared/domain/types.ts +1 -0
  80. package/test/australia-inheritance-tax.test.ts +35 -0
  81. package/test/canada-inheritance-tax.test.ts +62 -0
  82. package/test/france-inheritance-tax.test.ts +101 -0
  83. package/test/germany-inheritance-tax.test.ts +100 -0
  84. package/test/south-africa-inheritance-tax.test.ts +81 -0
  85. package/test/uk-inheritance-tax.test.ts +72 -0
  86. package/test/usa-inheritance-tax.test.ts +90 -0
@@ -0,0 +1,66 @@
1
+ import { Breakdown } from "../domain/types";
2
+ import { FranceInheritanceTaxService } from "./FranceInheritanceTaxService";
3
+ import { Input, Result, Rules, TaxBracket } from "./domain/types";
4
+
5
+ export class FranceInheritanceTaxServiceImpl implements FranceInheritanceTaxService {
6
+ private _input: Input;
7
+ private _rules: Rules;
8
+
9
+ constructor(input: Input, rules: Rules) {
10
+ this._input = input;
11
+ this._rules = rules;
12
+ }
13
+
14
+ calculate(): Result {
15
+ const estate = this._input.estateValue;
16
+ const relationship = this._rules.relationships[this._input.relationship];
17
+
18
+ if (estate <= 0 || estate <= relationship.exemption) {
19
+ return {
20
+ taxableEstate: 0,
21
+ inheritanceTax: 0,
22
+ effectiveRate: 0,
23
+ breakdowns: [],
24
+ };
25
+ }
26
+
27
+ const taxableEstate = estate - relationship.exemption;
28
+ const { tax, breakdowns } = this.applyBrackets(taxableEstate, relationship.brackets);
29
+
30
+ return {
31
+ taxableEstate,
32
+ inheritanceTax: tax,
33
+ effectiveRate: estate > 0 ? (tax / estate) * 100 : 0,
34
+ breakdowns,
35
+ };
36
+ }
37
+
38
+ private applyBrackets(
39
+ taxableAmount: number,
40
+ brackets: TaxBracket[],
41
+ ): { tax: number; breakdowns: Breakdown[] } {
42
+ let tax = 0;
43
+ const breakdowns: Breakdown[] = [];
44
+
45
+ for (const bracket of brackets) {
46
+ if (taxableAmount <= bracket.from) break;
47
+
48
+ const upper = bracket.to ?? taxableAmount;
49
+ const taxable = Math.min(upper, taxableAmount) - bracket.from;
50
+
51
+ if (taxable > 0) {
52
+ const bracketTax = taxable * bracket.rate;
53
+ tax += bracketTax;
54
+
55
+ breakdowns.push({
56
+ from: `${bracket.from}`,
57
+ to: `${bracket.to ?? 'Above'}`,
58
+ rate: bracket.rate,
59
+ amount: bracketTax,
60
+ });
61
+ }
62
+ }
63
+
64
+ return { tax, breakdowns };
65
+ }
66
+ }
@@ -0,0 +1,35 @@
1
+ import { Breakdown } from "../../domain/types";
2
+
3
+ export interface TaxBracket {
4
+ from: number;
5
+ to: number | null;
6
+ rate: number;
7
+ }
8
+
9
+ export interface RelationshipClass {
10
+ exemption: number;
11
+ brackets: TaxBracket[];
12
+ }
13
+
14
+ export interface Rules {
15
+ relationships: {
16
+ spouse: RelationshipClass;
17
+ child: RelationshipClass;
18
+ sibling: RelationshipClass;
19
+ other: RelationshipClass;
20
+ };
21
+ }
22
+
23
+ export type RelationshipType = 'spouse' | 'child' | 'sibling' | 'other';
24
+
25
+ export interface Input {
26
+ estateValue: number;
27
+ relationship: RelationshipType;
28
+ }
29
+
30
+ export interface Result {
31
+ taxableEstate: number;
32
+ inheritanceTax: number;
33
+ effectiveRate: number;
34
+ breakdowns: Breakdown[];
35
+ }
@@ -0,0 +1,5 @@
1
+ import { Result } from "./domain/types";
2
+
3
+ export interface GermanyInheritanceTaxService {
4
+ calculate(): Result;
5
+ }
@@ -0,0 +1,66 @@
1
+ import { Breakdown } from "../domain/types";
2
+ import { GermanyInheritanceTaxService } from "./GermanyInheritanceTaxService";
3
+ import { Input, Result, Rules, TaxBracket } from "./domain/types";
4
+
5
+ export class GermanyInheritanceTaxServiceImpl implements GermanyInheritanceTaxService {
6
+ private _input: Input;
7
+ private _rules: Rules;
8
+
9
+ constructor(input: Input, rules: Rules) {
10
+ this._input = input;
11
+ this._rules = rules;
12
+ }
13
+
14
+ calculate(): Result {
15
+ const estate = this._input.estateValue;
16
+ const taxClass = this._rules.taxClasses[this._input.taxClass];
17
+
18
+ if (estate <= 0 || estate <= taxClass.exemption) {
19
+ return {
20
+ taxableEstate: 0,
21
+ inheritanceTax: 0,
22
+ effectiveRate: 0,
23
+ breakdowns: [],
24
+ };
25
+ }
26
+
27
+ const taxableEstate = estate - taxClass.exemption;
28
+ const { tax, breakdowns } = this.applyBrackets(taxableEstate, taxClass.brackets);
29
+
30
+ return {
31
+ taxableEstate,
32
+ inheritanceTax: tax,
33
+ effectiveRate: estate > 0 ? (tax / estate) * 100 : 0,
34
+ breakdowns,
35
+ };
36
+ }
37
+
38
+ private applyBrackets(
39
+ taxableAmount: number,
40
+ brackets: TaxBracket[],
41
+ ): { tax: number; breakdowns: Breakdown[] } {
42
+ let tax = 0;
43
+ const breakdowns: Breakdown[] = [];
44
+
45
+ for (const bracket of brackets) {
46
+ if (taxableAmount <= bracket.from) break;
47
+
48
+ const upper = bracket.to ?? taxableAmount;
49
+ const taxable = Math.min(upper, taxableAmount) - bracket.from;
50
+
51
+ if (taxable > 0) {
52
+ const bracketTax = taxable * bracket.rate;
53
+ tax += bracketTax;
54
+
55
+ breakdowns.push({
56
+ from: `${bracket.from}`,
57
+ to: `${bracket.to ?? 'Above'}`,
58
+ rate: bracket.rate,
59
+ amount: bracketTax,
60
+ });
61
+ }
62
+ }
63
+
64
+ return { tax, breakdowns };
65
+ }
66
+ }
@@ -0,0 +1,34 @@
1
+ import { Breakdown } from "../../domain/types";
2
+
3
+ export interface TaxBracket {
4
+ from: number;
5
+ to: number | null;
6
+ rate: number;
7
+ }
8
+
9
+ export interface TaxClass {
10
+ exemption: number;
11
+ brackets: TaxBracket[];
12
+ }
13
+
14
+ export interface Rules {
15
+ taxClasses: {
16
+ I: TaxClass;
17
+ II: TaxClass;
18
+ III: TaxClass;
19
+ };
20
+ }
21
+
22
+ export type TaxClassName = 'I' | 'II' | 'III';
23
+
24
+ export interface Input {
25
+ estateValue: number;
26
+ taxClass: TaxClassName;
27
+ }
28
+
29
+ export interface Result {
30
+ taxableEstate: number;
31
+ inheritanceTax: number;
32
+ effectiveRate: number;
33
+ breakdowns: Breakdown[];
34
+ }
@@ -0,0 +1,86 @@
1
+ // Canada
2
+ import { CanadaInheritanceTaxServiceImpl as CanadaInheritanceTaxService } from './canada/CanadaInheritanceTaxServiceImpl';
3
+ import {
4
+ Input as CanadaInheritanceTaxInput,
5
+ Rules as CanadaInheritanceTaxRules,
6
+ Result as CanadaInheritanceTaxResult,
7
+ } from './canada/domain/types';
8
+
9
+ // France
10
+ import { FranceInheritanceTaxServiceImpl as FranceInheritanceTaxService } from './france/FranceInheritanceTaxServiceImpl';
11
+ import {
12
+ Input as FranceInheritanceTaxInput,
13
+ Rules as FranceInheritanceTaxRules,
14
+ Result as FranceInheritanceTaxResult,
15
+ } from './france/domain/types';
16
+
17
+ // South Africa
18
+ import { SouthAfricaInheritanceTaxServiceImpl as SouthAfricaInheritanceTaxService } from './south-africa/SouthAfricaInheritanceTaxServiceImpl';
19
+ import {
20
+ Input as SouthAfricaInheritanceTaxInput,
21
+ Rules as SouthAfricaInheritanceTaxRules,
22
+ Result as SouthAfricaInheritanceTaxResult,
23
+ } from './south-africa/domain/types';
24
+
25
+ // Australia
26
+ import { AustraliaInheritanceTaxServiceImpl as AustraliaInheritanceTaxService } from './australia/AustraliaInheritanceTaxServiceImpl';
27
+ import {
28
+ Input as AustraliaInheritanceTaxInput,
29
+ Rules as AustraliaInheritanceTaxRules,
30
+ Result as AustraliaInheritanceTaxResult,
31
+ } from './australia/domain/types';
32
+
33
+ // UK
34
+ import { UKInheritanceTaxServiceImpl as UKInheritanceTaxService } from './uk/UKInheritanceTaxServiceImpl';
35
+ import {
36
+ Input as UKInheritanceTaxInput,
37
+ Rules as UKInheritanceTaxRules,
38
+ Result as UKInheritanceTaxResult,
39
+ } from './uk/domain/types';
40
+
41
+ // USA
42
+ import { USAInheritanceTaxServiceImpl as USAInheritanceTaxService } from './usa/USAInheritanceTaxServiceImpl';
43
+ import {
44
+ Input as USAInheritanceTaxInput,
45
+ Rules as USAInheritanceTaxRules,
46
+ Result as USAInheritanceTaxResult,
47
+ } from './usa/domain/types';
48
+
49
+ // Germany
50
+ import { GermanyInheritanceTaxServiceImpl as GermanyInheritanceTaxService } from './germany/GermanyInheritanceTaxServiceImpl';
51
+ import {
52
+ Input as GermanyInheritanceTaxInput,
53
+ Rules as GermanyInheritanceTaxRules,
54
+ Result as GermanyInheritanceTaxResult,
55
+ } from './germany/domain/types';
56
+
57
+ export {
58
+ CanadaInheritanceTaxService,
59
+ CanadaInheritanceTaxInput,
60
+ CanadaInheritanceTaxRules,
61
+ CanadaInheritanceTaxResult,
62
+ FranceInheritanceTaxService,
63
+ FranceInheritanceTaxInput,
64
+ FranceInheritanceTaxRules,
65
+ FranceInheritanceTaxResult,
66
+ SouthAfricaInheritanceTaxService,
67
+ SouthAfricaInheritanceTaxInput,
68
+ SouthAfricaInheritanceTaxRules,
69
+ SouthAfricaInheritanceTaxResult,
70
+ AustraliaInheritanceTaxService,
71
+ AustraliaInheritanceTaxInput,
72
+ AustraliaInheritanceTaxRules,
73
+ AustraliaInheritanceTaxResult,
74
+ UKInheritanceTaxService,
75
+ UKInheritanceTaxInput,
76
+ UKInheritanceTaxRules,
77
+ UKInheritanceTaxResult,
78
+ USAInheritanceTaxService,
79
+ USAInheritanceTaxInput,
80
+ USAInheritanceTaxRules,
81
+ USAInheritanceTaxResult,
82
+ GermanyInheritanceTaxService,
83
+ GermanyInheritanceTaxInput,
84
+ GermanyInheritanceTaxRules,
85
+ GermanyInheritanceTaxResult,
86
+ };
@@ -0,0 +1,5 @@
1
+ import { Result } from "./domain/types";
2
+
3
+ export interface SouthAfricaInheritanceTaxService {
4
+ calculate(): Result;
5
+ }
@@ -0,0 +1,66 @@
1
+ import { Breakdown } from "../domain/types";
2
+ import { SouthAfricaInheritanceTaxService } from "./SouthAfricaInheritanceTaxService";
3
+ import { Input, Result, Rules, TaxBracket } from "./domain/types";
4
+
5
+ export class SouthAfricaInheritanceTaxServiceImpl implements SouthAfricaInheritanceTaxService {
6
+ private _input: Input;
7
+ private _rules: Rules;
8
+
9
+ constructor(input: Input, rules: Rules) {
10
+ this._input = input;
11
+ this._rules = rules;
12
+ }
13
+
14
+ calculate(): Result {
15
+ const estate = this._input.estateValue;
16
+ const netEstate = estate - this._input.deductions;
17
+
18
+ if (netEstate <= 0 || netEstate <= this._rules.primaryAbatement) {
19
+ return {
20
+ taxableEstate: 0,
21
+ inheritanceTax: 0,
22
+ effectiveRate: 0,
23
+ breakdowns: [],
24
+ };
25
+ }
26
+
27
+ const taxableEstate = netEstate - this._rules.primaryAbatement;
28
+ const { tax, breakdowns } = this.applyBrackets(taxableEstate, this._rules.taxBrackets);
29
+
30
+ return {
31
+ taxableEstate,
32
+ inheritanceTax: tax,
33
+ effectiveRate: estate > 0 ? (tax / estate) * 100 : 0,
34
+ breakdowns,
35
+ };
36
+ }
37
+
38
+ private applyBrackets(
39
+ taxableAmount: number,
40
+ brackets: TaxBracket[],
41
+ ): { tax: number; breakdowns: Breakdown[] } {
42
+ let tax = 0;
43
+ const breakdowns: Breakdown[] = [];
44
+
45
+ for (const bracket of brackets) {
46
+ if (taxableAmount <= bracket.from) break;
47
+
48
+ const upper = bracket.to ?? taxableAmount;
49
+ const taxable = Math.min(upper, taxableAmount) - bracket.from;
50
+
51
+ if (taxable > 0) {
52
+ const bracketTax = taxable * bracket.rate;
53
+ tax += bracketTax;
54
+
55
+ breakdowns.push({
56
+ from: `${bracket.from}`,
57
+ to: `${bracket.to ?? 'Above'}`,
58
+ rate: bracket.rate,
59
+ amount: bracketTax,
60
+ });
61
+ }
62
+ }
63
+
64
+ return { tax, breakdowns };
65
+ }
66
+ }
@@ -0,0 +1,24 @@
1
+ import { Breakdown } from "../../domain/types";
2
+
3
+ export interface TaxBracket {
4
+ from: number;
5
+ to: number | null;
6
+ rate: number;
7
+ }
8
+
9
+ export interface Rules {
10
+ primaryAbatement: number;
11
+ taxBrackets: TaxBracket[];
12
+ }
13
+
14
+ export interface Input {
15
+ estateValue: number;
16
+ deductions: number;
17
+ }
18
+
19
+ export interface Result {
20
+ taxableEstate: number;
21
+ inheritanceTax: number;
22
+ effectiveRate: number;
23
+ breakdowns: Breakdown[];
24
+ }
@@ -0,0 +1,5 @@
1
+ import { Result } from "./domain/types";
2
+
3
+ export interface UKInheritanceTaxService {
4
+ calculate(): Result;
5
+ }
@@ -0,0 +1,46 @@
1
+ import { Breakdown } from "../domain/types";
2
+ import { UKInheritanceTaxService } from "./UKInheritanceTaxService";
3
+ import { Input, Result, Rules } from "./domain/types";
4
+
5
+ export class UKInheritanceTaxServiceImpl implements UKInheritanceTaxService {
6
+ private _input: Input;
7
+ private _rules: Rules;
8
+
9
+ constructor(input: Input, rules: Rules) {
10
+ this._input = input;
11
+ this._rules = rules;
12
+ }
13
+
14
+ calculate(): Result {
15
+ const estate = this._input.estateValue;
16
+
17
+ if (estate <= 0 || estate <= this._rules.nilRateBand) {
18
+ return {
19
+ taxableEstate: 0,
20
+ inheritanceTax: 0,
21
+ effectiveRate: 0,
22
+ breakdowns: [],
23
+ };
24
+ }
25
+
26
+ const taxableEstate = estate - this._rules.nilRateBand;
27
+ const rate = this._input.charitableGivingPercent >= this._rules.charityThreshold
28
+ ? this._rules.charityRate
29
+ : this._rules.standardRate;
30
+ const tax = taxableEstate * rate;
31
+
32
+ const breakdowns: Breakdown[] = [{
33
+ from: `${this._rules.nilRateBand}`,
34
+ to: 'Above',
35
+ rate,
36
+ amount: tax,
37
+ }];
38
+
39
+ return {
40
+ taxableEstate,
41
+ inheritanceTax: tax,
42
+ effectiveRate: estate > 0 ? (tax / estate) * 100 : 0,
43
+ breakdowns,
44
+ };
45
+ }
46
+ }
@@ -0,0 +1,20 @@
1
+ import { Breakdown } from "../../domain/types";
2
+
3
+ export interface Rules {
4
+ nilRateBand: number;
5
+ standardRate: number;
6
+ charityRate: number;
7
+ charityThreshold: number;
8
+ }
9
+
10
+ export interface Input {
11
+ estateValue: number;
12
+ charitableGivingPercent: number;
13
+ }
14
+
15
+ export interface Result {
16
+ taxableEstate: number;
17
+ inheritanceTax: number;
18
+ effectiveRate: number;
19
+ breakdowns: Breakdown[];
20
+ }
@@ -0,0 +1,5 @@
1
+ import { Result } from "./domain/types";
2
+
3
+ export interface USAInheritanceTaxService {
4
+ calculate(): Result;
5
+ }
@@ -0,0 +1,65 @@
1
+ import { Breakdown } from "../domain/types";
2
+ import { USAInheritanceTaxService } from "./USAInheritanceTaxService";
3
+ import { Input, Result, Rules, TaxBracket } from "./domain/types";
4
+
5
+ export class USAInheritanceTaxServiceImpl implements USAInheritanceTaxService {
6
+ private _input: Input;
7
+ private _rules: Rules;
8
+
9
+ constructor(input: Input, rules: Rules) {
10
+ this._input = input;
11
+ this._rules = rules;
12
+ }
13
+
14
+ calculate(): Result {
15
+ const estate = this._input.estateValue;
16
+
17
+ if (estate <= 0 || estate <= this._rules.exemption) {
18
+ return {
19
+ taxableEstate: 0,
20
+ inheritanceTax: 0,
21
+ effectiveRate: 0,
22
+ breakdowns: [],
23
+ };
24
+ }
25
+
26
+ const taxableEstate = estate - this._rules.exemption;
27
+ const { tax, breakdowns } = this.applyBrackets(taxableEstate, this._rules.taxBrackets);
28
+
29
+ return {
30
+ taxableEstate,
31
+ inheritanceTax: tax,
32
+ effectiveRate: estate > 0 ? (tax / estate) * 100 : 0,
33
+ breakdowns,
34
+ };
35
+ }
36
+
37
+ private applyBrackets(
38
+ taxableAmount: number,
39
+ brackets: TaxBracket[],
40
+ ): { tax: number; breakdowns: Breakdown[] } {
41
+ let tax = 0;
42
+ const breakdowns: Breakdown[] = [];
43
+
44
+ for (const bracket of brackets) {
45
+ if (taxableAmount <= bracket.from) break;
46
+
47
+ const upper = bracket.to ?? taxableAmount;
48
+ const taxable = Math.min(upper, taxableAmount) - bracket.from;
49
+
50
+ if (taxable > 0) {
51
+ const bracketTax = taxable * bracket.rate;
52
+ tax += bracketTax;
53
+
54
+ breakdowns.push({
55
+ from: `${bracket.from}`,
56
+ to: `${bracket.to ?? 'Above'}`,
57
+ rate: bracket.rate,
58
+ amount: bracketTax,
59
+ });
60
+ }
61
+ }
62
+
63
+ return { tax, breakdowns };
64
+ }
65
+ }
@@ -0,0 +1,23 @@
1
+ import { Breakdown } from "../../domain/types";
2
+
3
+ export interface TaxBracket {
4
+ from: number;
5
+ to: number | null;
6
+ rate: number;
7
+ }
8
+
9
+ export interface Rules {
10
+ exemption: number;
11
+ taxBrackets: TaxBracket[];
12
+ }
13
+
14
+ export interface Input {
15
+ estateValue: number;
16
+ }
17
+
18
+ export interface Result {
19
+ taxableEstate: number;
20
+ inheritanceTax: number;
21
+ effectiveRate: number;
22
+ breakdowns: Breakdown[];
23
+ }
@@ -4,6 +4,7 @@ export enum CalculatorType {
4
4
  LOAN = 'LOAN',
5
5
  CORPORATE_TAX = 'CORPORATE_TAX',
6
6
  CAPITAL_GAINS = 'CAPITAL_GAINS',
7
+ INHERITANCE_TAX = 'INHERITANCE_TAX',
7
8
  }
8
9
 
9
10
  export interface RuleMeta {
@@ -0,0 +1,35 @@
1
+ import { AustraliaInheritanceTaxServiceImpl } from '../src/inheritance-tax/australia/AustraliaInheritanceTaxServiceImpl';
2
+ import { Input, Rules } from '../src/inheritance-tax/australia/domain/types';
3
+
4
+ const australiaRules: Rules = {
5
+ applicable: false,
6
+ };
7
+
8
+ describe('AustraliaInheritanceTaxServiceImpl', () => {
9
+ it('returns zero tax for any estate value', () => {
10
+ const input: Input = { estateValue: 1000000 };
11
+ const service = new AustraliaInheritanceTaxServiceImpl(input, australiaRules);
12
+ const result = service.calculate();
13
+
14
+ expect(result.inheritanceTax).toBe(0);
15
+ expect(result.taxableEstate).toBe(0);
16
+ expect(result.effectiveRate).toBe(0);
17
+ expect(result.breakdowns).toHaveLength(0);
18
+ });
19
+
20
+ it('returns zero tax for very large estate', () => {
21
+ const input: Input = { estateValue: 100000000 };
22
+ const service = new AustraliaInheritanceTaxServiceImpl(input, australiaRules);
23
+ const result = service.calculate();
24
+
25
+ expect(result.inheritanceTax).toBe(0);
26
+ });
27
+
28
+ it('returns zero tax for zero estate', () => {
29
+ const input: Input = { estateValue: 0 };
30
+ const service = new AustraliaInheritanceTaxServiceImpl(input, australiaRules);
31
+ const result = service.calculate();
32
+
33
+ expect(result.inheritanceTax).toBe(0);
34
+ });
35
+ });
@@ -0,0 +1,62 @@
1
+ import { CanadaInheritanceTaxServiceImpl } from '../src/inheritance-tax/canada/CanadaInheritanceTaxServiceImpl';
2
+ import { Input, Rules } from '../src/inheritance-tax/canada/domain/types';
3
+
4
+ const canadaRules: Rules = {
5
+ inclusionRate: 0.50,
6
+ taxBrackets: [
7
+ { from: 0, to: 55867, rate: 0.15 },
8
+ { from: 55867, to: 111733, rate: 0.205 },
9
+ { from: 111733, to: 154906, rate: 0.26 },
10
+ { from: 154906, to: 220000, rate: 0.29 },
11
+ { from: 220000, to: null, rate: 0.33 },
12
+ ],
13
+ };
14
+
15
+ describe('CanadaInheritanceTaxServiceImpl', () => {
16
+ it('returns zero tax when no capital gain (estate = cost base)', () => {
17
+ const input: Input = { estateValue: 500000, adjustedCostBase: 500000 };
18
+ const service = new CanadaInheritanceTaxServiceImpl(input, canadaRules);
19
+ const result = service.calculate();
20
+
21
+ expect(result.inheritanceTax).toBe(0);
22
+ expect(result.taxableEstate).toBe(0);
23
+ });
24
+
25
+ it('returns zero tax when estate is below cost base', () => {
26
+ const input: Input = { estateValue: 300000, adjustedCostBase: 500000 };
27
+ const service = new CanadaInheritanceTaxServiceImpl(input, canadaRules);
28
+ const result = service.calculate();
29
+
30
+ expect(result.inheritanceTax).toBe(0);
31
+ });
32
+
33
+ it('correctly calculates tax on capital gain at death', () => {
34
+ const input: Input = { estateValue: 600000, adjustedCostBase: 400000 };
35
+ const service = new CanadaInheritanceTaxServiceImpl(input, canadaRules);
36
+ const result = service.calculate();
37
+
38
+ // Gain = 200000, taxable gain = 200000 * 0.5 = 100000
39
+ // 55867 * 0.15 = 8380.05
40
+ // (100000 - 55867) * 0.205 = 44133 * 0.205 = 9047.265
41
+ // Total ≈ 17427.315
42
+ expect(result.taxableEstate).toBe(100000);
43
+ expect(result.inheritanceTax).toBeCloseTo(17427.315, 2);
44
+ });
45
+
46
+ it('returns zero for zero estate', () => {
47
+ const input: Input = { estateValue: 0, adjustedCostBase: 0 };
48
+ const service = new CanadaInheritanceTaxServiceImpl(input, canadaRules);
49
+ const result = service.calculate();
50
+
51
+ expect(result.inheritanceTax).toBe(0);
52
+ });
53
+
54
+ it('calculates effective rate based on total gain', () => {
55
+ const input: Input = { estateValue: 600000, adjustedCostBase: 400000 };
56
+ const service = new CanadaInheritanceTaxServiceImpl(input, canadaRules);
57
+ const result = service.calculate();
58
+
59
+ // Effective rate is relative to the gain (200000), not the included portion
60
+ expect(result.effectiveRate).toBeCloseTo((result.inheritanceTax / 200000) * 100, 2);
61
+ });
62
+ });