@novha/calc-engines 7.0.0 → 8.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 (196) hide show
  1. package/dist/capital-gains/brazil/BrazilCapitalGainsService.js +3 -0
  2. package/dist/capital-gains/brazil/BrazilCapitalGainsServiceImpl.js +56 -0
  3. package/dist/capital-gains/brazil/domain/types.js +3 -0
  4. package/dist/capital-gains/index.js +14 -2
  5. package/dist/capital-gains/india/IndiaCapitalGainsService.js +3 -0
  6. package/dist/capital-gains/india/IndiaCapitalGainsServiceImpl.js +58 -0
  7. package/dist/capital-gains/india/domain/types.js +3 -0
  8. package/dist/capital-gains/japan/JapanCapitalGainsService.js +3 -0
  9. package/dist/capital-gains/japan/JapanCapitalGainsServiceImpl.js +41 -0
  10. package/dist/capital-gains/japan/domain/types.js +3 -0
  11. package/dist/capital-gains/spain/SpainCapitalGainsService.js +3 -0
  12. package/dist/capital-gains/spain/SpainCapitalGainsServiceImpl.js +56 -0
  13. package/dist/capital-gains/spain/domain/types.js +3 -0
  14. package/dist/corporate/brazil/BrazilCorporateTaxService.js +3 -0
  15. package/dist/corporate/brazil/BrazilCorporateTaxServiceImpl.js +33 -0
  16. package/dist/corporate/brazil/domain/types.js +3 -0
  17. package/dist/corporate/index.js +10 -2
  18. package/dist/corporate/india/IndiaCorporateTaxService.js +3 -0
  19. package/dist/corporate/india/IndiaCorporateTaxServiceImpl.js +29 -0
  20. package/dist/corporate/india/domain/types.js +3 -0
  21. package/dist/corporate/japan/JapanCorporateTaxService.js +3 -0
  22. package/dist/corporate/japan/JapanCorporateTaxServiceImpl.js +29 -0
  23. package/dist/corporate/japan/domain/types.js +3 -0
  24. package/dist/corporate/spain/SpainCorporateTaxService.js +3 -0
  25. package/dist/corporate/spain/SpainCorporateTaxServiceImpl.js +29 -0
  26. package/dist/corporate/spain/domain/types.js +3 -0
  27. package/dist/income-tax/brazil/BrazilIncomeTaxService.js +3 -0
  28. package/dist/income-tax/brazil/BrazilIncomeTaxServiceImpl.js +78 -0
  29. package/dist/income-tax/brazil/domain/types.js +3 -0
  30. package/dist/income-tax/index.js +14 -2
  31. package/dist/income-tax/india/IndiaIncomeTaxService.js +3 -0
  32. package/dist/income-tax/india/IndiaIncomeTaxServiceImpl.js +75 -0
  33. package/dist/income-tax/india/domain/types.js +3 -0
  34. package/dist/income-tax/japan/JapanIncomeTaxService.js +3 -0
  35. package/dist/income-tax/japan/JapanIncomeTaxServiceImpl.js +75 -0
  36. package/dist/income-tax/japan/domain/types.js +3 -0
  37. package/dist/income-tax/spain/SpainIncomeTaxService.js +3 -0
  38. package/dist/income-tax/spain/SpainIncomeTaxServiceImpl.js +62 -0
  39. package/dist/income-tax/spain/domain/types.js +3 -0
  40. package/dist/inheritance-tax/index.js +8 -2
  41. package/dist/inheritance-tax/japan/JapanInheritanceTaxService.js +3 -0
  42. package/dist/inheritance-tax/japan/JapanInheritanceTaxServiceImpl.js +53 -0
  43. package/dist/inheritance-tax/japan/domain/types.js +3 -0
  44. package/dist/inheritance-tax/spain/SpainInheritanceTaxService.js +3 -0
  45. package/dist/inheritance-tax/spain/SpainInheritanceTaxServiceImpl.js +51 -0
  46. package/dist/inheritance-tax/spain/domain/types.js +3 -0
  47. package/dist/mortgage/brazil/BrazilMortgageService.js +3 -0
  48. package/dist/mortgage/brazil/BrazilMortgageServiceImpl.js +76 -0
  49. package/dist/mortgage/brazil/domain/types.js +3 -0
  50. package/dist/mortgage/index.js +14 -2
  51. package/dist/mortgage/india/IndiaMortgageService.js +3 -0
  52. package/dist/mortgage/india/IndiaMortgageServiceImpl.js +76 -0
  53. package/dist/mortgage/india/domain/types.js +3 -0
  54. package/dist/mortgage/japan/JapanMortgageService.js +3 -0
  55. package/dist/mortgage/japan/JapanMortgageServiceImpl.js +76 -0
  56. package/dist/mortgage/japan/domain/types.js +3 -0
  57. package/dist/mortgage/spain/SpainMortgageService.js +3 -0
  58. package/dist/mortgage/spain/SpainMortgageServiceImpl.js +96 -0
  59. package/dist/mortgage/spain/domain/types.js +3 -0
  60. package/dist/types/capital-gains/brazil/BrazilCapitalGainsService.d.ts +4 -0
  61. package/dist/types/capital-gains/brazil/BrazilCapitalGainsServiceImpl.d.ts +10 -0
  62. package/dist/types/capital-gains/brazil/domain/types.d.ts +11 -0
  63. package/dist/types/capital-gains/index.d.ts +9 -1
  64. package/dist/types/capital-gains/india/IndiaCapitalGainsService.d.ts +4 -0
  65. package/dist/types/capital-gains/india/IndiaCapitalGainsServiceImpl.d.ts +9 -0
  66. package/dist/types/capital-gains/india/domain/types.d.ts +9 -0
  67. package/dist/types/capital-gains/japan/JapanCapitalGainsService.d.ts +4 -0
  68. package/dist/types/capital-gains/japan/JapanCapitalGainsServiceImpl.d.ts +9 -0
  69. package/dist/types/capital-gains/japan/domain/types.d.ts +6 -0
  70. package/dist/types/capital-gains/spain/SpainCapitalGainsService.d.ts +4 -0
  71. package/dist/types/capital-gains/spain/SpainCapitalGainsServiceImpl.d.ts +10 -0
  72. package/dist/types/capital-gains/spain/domain/types.d.ts +11 -0
  73. package/dist/types/corporate/brazil/BrazilCorporateTaxService.d.ts +4 -0
  74. package/dist/types/corporate/brazil/BrazilCorporateTaxServiceImpl.d.ts +8 -0
  75. package/dist/types/corporate/brazil/domain/types.d.ts +19 -0
  76. package/dist/types/corporate/index.d.ts +9 -1
  77. package/dist/types/corporate/india/IndiaCorporateTaxService.d.ts +4 -0
  78. package/dist/types/corporate/india/IndiaCorporateTaxServiceImpl.d.ts +8 -0
  79. package/dist/types/corporate/india/domain/types.d.ts +15 -0
  80. package/dist/types/corporate/japan/JapanCorporateTaxService.d.ts +4 -0
  81. package/dist/types/corporate/japan/JapanCorporateTaxServiceImpl.d.ts +8 -0
  82. package/dist/types/corporate/japan/domain/types.d.ts +15 -0
  83. package/dist/types/corporate/spain/SpainCorporateTaxService.d.ts +4 -0
  84. package/dist/types/corporate/spain/SpainCorporateTaxServiceImpl.d.ts +8 -0
  85. package/dist/types/corporate/spain/domain/types.d.ts +15 -0
  86. package/dist/types/income-tax/brazil/BrazilIncomeTaxService.d.ts +4 -0
  87. package/dist/types/income-tax/brazil/BrazilIncomeTaxServiceImpl.d.ts +11 -0
  88. package/dist/types/income-tax/brazil/domain/types.d.ts +18 -0
  89. package/dist/types/income-tax/index.d.ts +9 -1
  90. package/dist/types/income-tax/india/IndiaIncomeTaxService.d.ts +4 -0
  91. package/dist/types/income-tax/india/IndiaIncomeTaxServiceImpl.d.ts +10 -0
  92. package/dist/types/income-tax/india/domain/types.d.ts +16 -0
  93. package/dist/types/income-tax/japan/JapanIncomeTaxService.d.ts +4 -0
  94. package/dist/types/income-tax/japan/JapanIncomeTaxServiceImpl.d.ts +10 -0
  95. package/dist/types/income-tax/japan/domain/types.d.ts +16 -0
  96. package/dist/types/income-tax/spain/SpainIncomeTaxService.d.ts +4 -0
  97. package/dist/types/income-tax/spain/SpainIncomeTaxServiceImpl.d.ts +10 -0
  98. package/dist/types/income-tax/spain/domain/types.d.ts +16 -0
  99. package/dist/types/inheritance-tax/index.d.ts +5 -1
  100. package/dist/types/inheritance-tax/japan/JapanInheritanceTaxService.d.ts +4 -0
  101. package/dist/types/inheritance-tax/japan/JapanInheritanceTaxServiceImpl.d.ts +9 -0
  102. package/dist/types/inheritance-tax/japan/domain/types.d.ts +21 -0
  103. package/dist/types/inheritance-tax/spain/SpainInheritanceTaxService.d.ts +4 -0
  104. package/dist/types/inheritance-tax/spain/SpainInheritanceTaxServiceImpl.d.ts +9 -0
  105. package/dist/types/inheritance-tax/spain/domain/types.d.ts +19 -0
  106. package/dist/types/mortgage/brazil/BrazilMortgageService.d.ts +4 -0
  107. package/dist/types/mortgage/brazil/BrazilMortgageServiceImpl.d.ts +7 -0
  108. package/dist/types/mortgage/brazil/domain/types.d.ts +39 -0
  109. package/dist/types/mortgage/index.d.ts +9 -1
  110. package/dist/types/mortgage/india/IndiaMortgageService.d.ts +4 -0
  111. package/dist/types/mortgage/india/IndiaMortgageServiceImpl.d.ts +7 -0
  112. package/dist/types/mortgage/india/domain/types.d.ts +39 -0
  113. package/dist/types/mortgage/japan/JapanMortgageService.d.ts +4 -0
  114. package/dist/types/mortgage/japan/JapanMortgageServiceImpl.d.ts +7 -0
  115. package/dist/types/mortgage/japan/domain/types.d.ts +39 -0
  116. package/dist/types/mortgage/spain/SpainMortgageService.d.ts +4 -0
  117. package/dist/types/mortgage/spain/SpainMortgageServiceImpl.d.ts +8 -0
  118. package/dist/types/mortgage/spain/domain/types.d.ts +44 -0
  119. package/package.json +1 -1
  120. package/src/capital-gains/brazil/BrazilCapitalGainsService.ts +5 -0
  121. package/src/capital-gains/brazil/BrazilCapitalGainsServiceImpl.ts +69 -0
  122. package/src/capital-gains/brazil/domain/types.ts +15 -0
  123. package/src/capital-gains/index.ts +40 -0
  124. package/src/capital-gains/india/IndiaCapitalGainsService.ts +5 -0
  125. package/src/capital-gains/india/IndiaCapitalGainsServiceImpl.ts +64 -0
  126. package/src/capital-gains/india/domain/types.ts +12 -0
  127. package/src/capital-gains/japan/JapanCapitalGainsService.ts +5 -0
  128. package/src/capital-gains/japan/JapanCapitalGainsServiceImpl.ts +47 -0
  129. package/src/capital-gains/japan/domain/types.ts +9 -0
  130. package/src/capital-gains/spain/SpainCapitalGainsService.ts +5 -0
  131. package/src/capital-gains/spain/SpainCapitalGainsServiceImpl.ts +69 -0
  132. package/src/capital-gains/spain/domain/types.ts +15 -0
  133. package/src/corporate/brazil/BrazilCorporateTaxService.ts +5 -0
  134. package/src/corporate/brazil/BrazilCorporateTaxServiceImpl.ts +40 -0
  135. package/src/corporate/brazil/domain/types.ts +16 -0
  136. package/src/corporate/index.ts +45 -1
  137. package/src/corporate/india/IndiaCorporateTaxService.ts +5 -0
  138. package/src/corporate/india/IndiaCorporateTaxServiceImpl.ts +35 -0
  139. package/src/corporate/india/domain/types.ts +15 -0
  140. package/src/corporate/japan/JapanCorporateTaxService.ts +5 -0
  141. package/src/corporate/japan/JapanCorporateTaxServiceImpl.ts +35 -0
  142. package/src/corporate/japan/domain/types.ts +15 -0
  143. package/src/corporate/spain/SpainCorporateTaxService.ts +5 -0
  144. package/src/corporate/spain/SpainCorporateTaxServiceImpl.ts +35 -0
  145. package/src/corporate/spain/domain/types.ts +15 -0
  146. package/src/income-tax/brazil/BrazilIncomeTaxService.ts +5 -0
  147. package/src/income-tax/brazil/BrazilIncomeTaxServiceImpl.ts +91 -0
  148. package/src/income-tax/brazil/domain/types.ts +21 -0
  149. package/src/income-tax/index.ts +40 -0
  150. package/src/income-tax/india/IndiaIncomeTaxService.ts +5 -0
  151. package/src/income-tax/india/IndiaIncomeTaxServiceImpl.ts +87 -0
  152. package/src/income-tax/india/domain/types.ts +16 -0
  153. package/src/income-tax/japan/JapanIncomeTaxService.ts +5 -0
  154. package/src/income-tax/japan/JapanIncomeTaxServiceImpl.ts +87 -0
  155. package/src/income-tax/japan/domain/types.ts +16 -0
  156. package/src/income-tax/spain/SpainIncomeTaxService.ts +5 -0
  157. package/src/income-tax/spain/SpainIncomeTaxServiceImpl.ts +75 -0
  158. package/src/income-tax/spain/domain/types.ts +16 -0
  159. package/src/inheritance-tax/index.ts +24 -0
  160. package/src/inheritance-tax/japan/JapanInheritanceTaxService.ts +5 -0
  161. package/src/inheritance-tax/japan/JapanInheritanceTaxServiceImpl.ts +67 -0
  162. package/src/inheritance-tax/japan/domain/types.ts +25 -0
  163. package/src/inheritance-tax/spain/SpainInheritanceTaxService.ts +5 -0
  164. package/src/inheritance-tax/spain/SpainInheritanceTaxServiceImpl.ts +65 -0
  165. package/src/inheritance-tax/spain/domain/types.ts +23 -0
  166. package/src/mortgage/brazil/BrazilMortgageService.ts +5 -0
  167. package/src/mortgage/brazil/BrazilMortgageServiceImpl.ts +101 -0
  168. package/src/mortgage/brazil/domain/types.ts +46 -0
  169. package/src/mortgage/index.ts +49 -1
  170. package/src/mortgage/india/IndiaMortgageService.ts +5 -0
  171. package/src/mortgage/india/IndiaMortgageServiceImpl.ts +101 -0
  172. package/src/mortgage/india/domain/types.ts +46 -0
  173. package/src/mortgage/japan/JapanMortgageService.ts +5 -0
  174. package/src/mortgage/japan/JapanMortgageServiceImpl.ts +101 -0
  175. package/src/mortgage/japan/domain/types.ts +46 -0
  176. package/src/mortgage/spain/SpainMortgageService.ts +5 -0
  177. package/src/mortgage/spain/SpainMortgageServiceImpl.ts +130 -0
  178. package/src/mortgage/spain/domain/types.ts +52 -0
  179. package/test/brazil-capital-gains.test.ts +65 -0
  180. package/test/brazil-corporate-tax.test.ts +63 -0
  181. package/test/brazil-income-tax.test.ts +85 -0
  182. package/test/brazil-mortgage.test.ts +70 -0
  183. package/test/india-capital-gains.test.ts +71 -0
  184. package/test/india-corporate-tax.test.ts +54 -0
  185. package/test/india-income-tax.test.ts +78 -0
  186. package/test/india-mortgage.test.ts +70 -0
  187. package/test/japan-capital-gains.test.ts +55 -0
  188. package/test/japan-corporate-tax.test.ts +54 -0
  189. package/test/japan-income-tax.test.ts +76 -0
  190. package/test/japan-inheritance-tax.test.ts +74 -0
  191. package/test/japan-mortgage.test.ts +69 -0
  192. package/test/spain-capital-gains.test.ts +66 -0
  193. package/test/spain-corporate-tax.test.ts +54 -0
  194. package/test/spain-income-tax.test.ts +84 -0
  195. package/test/spain-inheritance-tax.test.ts +78 -0
  196. package/test/spain-mortgage.test.ts +70 -0
@@ -0,0 +1,84 @@
1
+ import { SpainIncomeTaxServiceImpl } from '../src/income-tax/spain/SpainIncomeTaxServiceImpl';
2
+ import { IncomeTaxRules } from '../src/income-tax/spain/domain/types';
3
+
4
+ const spainRules: IncomeTaxRules = {
5
+ taxBrackets: [
6
+ { from: 0, to: 12450, rate: 0.19 },
7
+ { from: 12450, to: 20200, rate: 0.24 },
8
+ { from: 20200, to: 35200, rate: 0.30 },
9
+ { from: 35200, to: 60000, rate: 0.37 },
10
+ { from: 60000, to: 300000, rate: 0.45 },
11
+ { from: 300000, to: null, rate: 0.47 },
12
+ ],
13
+ socialContributions: { rate: 0.0635 },
14
+ };
15
+
16
+ describe('SpainIncomeTaxServiceImpl', () => {
17
+ it('returns zero tax for zero income', () => {
18
+ const service = new SpainIncomeTaxServiceImpl(0, spainRules);
19
+ const result = service.calculateNetIncome();
20
+
21
+ expect(result.incomeTax).toBe(0);
22
+ expect(result.socialContributions).toBe(0);
23
+ expect(result.totalDeductions).toBe(0);
24
+ expect(result.effectiveTaxRate).toBe(0);
25
+ });
26
+
27
+ it('correctly calculates income tax for income in first bracket', () => {
28
+ const service = new SpainIncomeTaxServiceImpl(10000, spainRules);
29
+ const result = service.calculateNetIncome();
30
+
31
+ // 0-10000: 10000 * 0.19 = 1900
32
+ expect(result.incomeTax).toBeCloseTo(1900, 2);
33
+ expect(result.grossIncome).toBe(10000);
34
+ });
35
+
36
+ it('correctly calculates social contributions', () => {
37
+ const service = new SpainIncomeTaxServiceImpl(10000, spainRules);
38
+ const result = service.calculateNetIncome();
39
+
40
+ // 10000 * 0.0635 = 635
41
+ expect(result.socialContributions).toBeCloseTo(635, 2);
42
+ });
43
+
44
+ it('correctly calculates income tax spanning multiple brackets', () => {
45
+ const service = new SpainIncomeTaxServiceImpl(25000, spainRules);
46
+ const result = service.calculateNetIncome();
47
+
48
+ // 0-12450: 12450 * 0.19 = 2365.5
49
+ // 12450-20200: 7750 * 0.24 = 1860
50
+ // 20200-25000: 4800 * 0.30 = 1440
51
+ // Total = 5665.5
52
+ expect(result.incomeTax).toBeCloseTo(5665.5, 1);
53
+ });
54
+
55
+ it('calculates effective tax rate as income tax divided by gross income', () => {
56
+ const service = new SpainIncomeTaxServiceImpl(25000, spainRules);
57
+ const result = service.calculateNetIncome();
58
+
59
+ expect(result.effectiveTaxRate).toBeCloseTo(result.incomeTax / result.grossIncome, 4);
60
+ });
61
+
62
+ it('net income equals gross income minus total deductions', () => {
63
+ const service = new SpainIncomeTaxServiceImpl(25000, spainRules);
64
+ const result = service.calculateNetIncome();
65
+
66
+ expect(result.netIncome).toBeCloseTo(result.grossIncome - result.totalDeductions, 2);
67
+ });
68
+
69
+ it('total deductions equals income tax plus social contributions', () => {
70
+ const service = new SpainIncomeTaxServiceImpl(25000, spainRules);
71
+ const result = service.calculateNetIncome();
72
+
73
+ expect(result.totalDeductions).toBeCloseTo(result.incomeTax + result.socialContributions, 2);
74
+ });
75
+
76
+ it('applies highest bracket rate to income above 300000', () => {
77
+ const service = new SpainIncomeTaxServiceImpl(350000, spainRules);
78
+ const result = service.calculateNetIncome();
79
+
80
+ expect(result.incomeTax).toBeGreaterThan(0);
81
+ const lastBreakdown = result.taxBracketBreakdown[result.taxBracketBreakdown.length - 1];
82
+ expect(lastBreakdown.rate).toBe(0.47);
83
+ });
84
+ });
@@ -0,0 +1,78 @@
1
+ import { SpainInheritanceTaxServiceImpl } from '../src/inheritance-tax/spain/SpainInheritanceTaxServiceImpl';
2
+ import { Input, Rules } from '../src/inheritance-tax/spain/domain/types';
3
+
4
+ const spainRules: Rules = {
5
+ exemption: 0,
6
+ taxBrackets: [
7
+ { from: 0, to: 7993.46, rate: 0.0765 },
8
+ { from: 7993.46, to: 31956.34, rate: 0.102 },
9
+ { from: 31956.34, to: 79881.68, rate: 0.153 },
10
+ { from: 79881.68, to: 239389.13, rate: 0.2125 },
11
+ { from: 239389.13, to: 398777.54, rate: 0.255 },
12
+ { from: 398777.54, to: 797555.08, rate: 0.2975 },
13
+ { from: 797555.08, to: null, rate: 0.34 },
14
+ ],
15
+ };
16
+
17
+ describe('SpainInheritanceTaxServiceImpl', () => {
18
+ it('returns zero tax for zero estate', () => {
19
+ const input: Input = { estateValue: 0 };
20
+ const service = new SpainInheritanceTaxServiceImpl(input, spainRules);
21
+ const result = service.calculate();
22
+
23
+ expect(result.inheritanceTax).toBe(0);
24
+ expect(result.taxableEstate).toBe(0);
25
+ expect(result.effectiveRate).toBe(0);
26
+ });
27
+
28
+ it('returns zero tax when estate is at or below the exemption', () => {
29
+ const rulesWithExemption: Rules = { ...spainRules, exemption: 100000 };
30
+ const input: Input = { estateValue: 80000 };
31
+ const service = new SpainInheritanceTaxServiceImpl(input, rulesWithExemption);
32
+ const result = service.calculate();
33
+
34
+ expect(result.inheritanceTax).toBe(0);
35
+ expect(result.taxableEstate).toBe(0);
36
+ });
37
+
38
+ it('correctly calculates tax for estate in first bracket', () => {
39
+ const input: Input = { estateValue: 5000 };
40
+ const service = new SpainInheritanceTaxServiceImpl(input, spainRules);
41
+ const result = service.calculate();
42
+
43
+ // 0-5000: 5000 * 0.0765 = 382.5
44
+ expect(result.taxableEstate).toBe(5000);
45
+ expect(result.inheritanceTax).toBeCloseTo(382.5, 2);
46
+ });
47
+
48
+ it('correctly calculates tax for estate spanning multiple brackets', () => {
49
+ const input: Input = { estateValue: 100000 };
50
+ const service = new SpainInheritanceTaxServiceImpl(input, spainRules);
51
+ const result = service.calculate();
52
+
53
+ // 0-7993.46: 7993.46 * 0.0765 ≈ 611.50
54
+ // 7993.46-31956.34: 23962.88 * 0.102 ≈ 2444.21
55
+ // 31956.34-79881.68: 47925.34 * 0.153 ≈ 7332.58
56
+ // 79881.68-100000: 20118.32 * 0.2125 ≈ 4275.14
57
+ // Total ≈ 14663.43
58
+ expect(result.taxableEstate).toBe(100000);
59
+ expect(result.inheritanceTax).toBeCloseTo(14663.43, 0);
60
+ });
61
+
62
+ it('effective rate equals inheritance tax divided by estate times 100', () => {
63
+ const input: Input = { estateValue: 100000 };
64
+ const service = new SpainInheritanceTaxServiceImpl(input, spainRules);
65
+ const result = service.calculate();
66
+
67
+ expect(result.effectiveRate).toBeCloseTo((result.inheritanceTax / 100000) * 100, 2);
68
+ });
69
+
70
+ it('taxable estate is reduced by the exemption amount', () => {
71
+ const rulesWithExemption: Rules = { ...spainRules, exemption: 20000 };
72
+ const input: Input = { estateValue: 50000 };
73
+ const service = new SpainInheritanceTaxServiceImpl(input, rulesWithExemption);
74
+ const result = service.calculate();
75
+
76
+ expect(result.taxableEstate).toBe(30000);
77
+ });
78
+ });
@@ -0,0 +1,70 @@
1
+ import { SpainMortgageServiceImpl } from '../src/mortgage/spain/SpainMortgageServiceImpl';
2
+ import { MortgageInput, MortgageRules } from '../src/mortgage/spain/domain/types';
3
+
4
+ const spainRules: MortgageRules = {
5
+ loanConstraints: { maxLtvPercent: 0.80, maxAmortizationYears: 30 },
6
+ interest: { compounding: 'MONTHLY' },
7
+ transferTax: { brackets: [{ above: 0, rate: 0.07 }] },
8
+ };
9
+
10
+ const input: MortgageInput = {
11
+ propertyPrice: 300000,
12
+ downPayment: 60000,
13
+ annualInterestRate: 3.5,
14
+ amortizationYears: 25,
15
+ isFirstTimeBuyer: false,
16
+ };
17
+
18
+ describe('SpainMortgageServiceImpl', () => {
19
+ it('correctly calculates the loan amount', () => {
20
+ const service = new SpainMortgageServiceImpl();
21
+ const result = service.calculate(input, spainRules);
22
+
23
+ // loanAmount = 300000 - 60000 = 240000
24
+ expect(result.loanAmount).toBe(240000);
25
+ });
26
+
27
+ it('correctly calculates transfer tax', () => {
28
+ const service = new SpainMortgageServiceImpl();
29
+ const result = service.calculate(input, spainRules);
30
+
31
+ // transferTax = (300000 - 0) * 0.07 = 21000
32
+ expect(result.transferTax).toBeCloseTo(21000, 2);
33
+ });
34
+
35
+ it('calculates a positive monthly payment', () => {
36
+ const service = new SpainMortgageServiceImpl();
37
+ const result = service.calculate(input, spainRules);
38
+
39
+ expect(result.monthlyPayment).toBeGreaterThan(0);
40
+ });
41
+
42
+ it('total paid is greater than the loan amount', () => {
43
+ const service = new SpainMortgageServiceImpl();
44
+ const result = service.calculate(input, spainRules);
45
+
46
+ expect(result.totalPaid).toBeGreaterThan(result.loanAmount);
47
+ });
48
+
49
+ it('total paid equals monthly payment multiplied by total payments', () => {
50
+ const service = new SpainMortgageServiceImpl();
51
+ const result = service.calculate(input, spainRules);
52
+
53
+ const expectedTotal = result.monthlyPayment * (input.amortizationYears * 12);
54
+ expect(result.totalPaid).toBeCloseTo(expectedTotal, 2);
55
+ });
56
+
57
+ it('amortization schedule has the correct number of years', () => {
58
+ const service = new SpainMortgageServiceImpl();
59
+ const result = service.calculate(input, spainRules);
60
+
61
+ expect(result.amortizationSchedule).toHaveLength(25);
62
+ });
63
+
64
+ it('throws an error when down payment equals or exceeds property price', () => {
65
+ const service = new SpainMortgageServiceImpl();
66
+ const badInput: MortgageInput = { ...input, downPayment: 300000 };
67
+
68
+ expect(() => service.calculate(badInput, spainRules)).toThrow();
69
+ });
70
+ });