@novha/calc-engines 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capital-gains/australia/AustraliaCapitalGainsService.js +3 -0
- package/dist/capital-gains/australia/AustraliaCapitalGainsServiceImpl.js +67 -0
- package/dist/capital-gains/australia/domain/types.js +3 -0
- package/dist/capital-gains/canada/CanadaCapitalGainsService.js +3 -0
- package/dist/capital-gains/canada/CanadaCapitalGainsServiceImpl.js +66 -0
- package/dist/capital-gains/canada/domain/types.js +3 -0
- package/dist/capital-gains/domain/types.js +3 -0
- package/dist/capital-gains/france/FranceCapitalGainsService.js +3 -0
- package/dist/capital-gains/france/FranceCapitalGainsServiceImpl.js +51 -0
- package/dist/capital-gains/france/domain/types.js +3 -0
- package/dist/capital-gains/germany/GermanyCapitalGainsService.js +3 -0
- package/dist/capital-gains/germany/GermanyCapitalGainsServiceImpl.js +68 -0
- package/dist/capital-gains/germany/domain/types.js +3 -0
- package/dist/capital-gains/index.js +25 -0
- package/dist/capital-gains/south-africa/SouthAfricaCapitalGainsService.js +3 -0
- package/dist/capital-gains/south-africa/SouthAfricaCapitalGainsServiceImpl.js +83 -0
- package/dist/capital-gains/south-africa/domain/types.js +3 -0
- package/dist/capital-gains/uk/UKCapitalGainsService.js +3 -0
- package/dist/capital-gains/uk/UKCapitalGainsServiceImpl.js +86 -0
- package/dist/capital-gains/uk/domain/types.js +3 -0
- package/dist/capital-gains/usa/USACapitalGainsService.js +3 -0
- package/dist/capital-gains/usa/USACapitalGainsServiceImpl.js +77 -0
- package/dist/capital-gains/usa/domain/types.js +3 -0
- package/dist/corporate/index.js +18 -0
- package/dist/income-tax/index.js +24 -0
- package/dist/index.js +18 -51
- package/dist/mortgage/france/FranceMortgageService.js +1 -1
- package/dist/mortgage/france/FranceMortgageServiceImpl.js +1 -1
- package/dist/mortgage/index.js +25 -0
- package/dist/shared/domain/types.js +2 -1
- package/dist/types/capital-gains/australia/AustraliaCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/australia/AustraliaCapitalGainsServiceImpl.d.ts +10 -0
- package/dist/types/capital-gains/australia/domain/types.d.ts +15 -0
- package/dist/types/capital-gains/canada/CanadaCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/canada/CanadaCapitalGainsServiceImpl.d.ts +10 -0
- package/dist/types/capital-gains/canada/domain/types.d.ts +13 -0
- package/dist/types/capital-gains/domain/types.d.ts +15 -0
- package/dist/types/capital-gains/france/FranceCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/france/FranceCapitalGainsServiceImpl.d.ts +9 -0
- package/dist/types/capital-gains/france/domain/types.d.ts +7 -0
- package/dist/types/capital-gains/germany/GermanyCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/germany/GermanyCapitalGainsServiceImpl.d.ts +9 -0
- package/dist/types/capital-gains/germany/domain/types.d.ts +8 -0
- package/dist/types/capital-gains/index.d.ts +15 -0
- package/dist/types/capital-gains/south-africa/SouthAfricaCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/south-africa/SouthAfricaCapitalGainsServiceImpl.d.ts +10 -0
- package/dist/types/capital-gains/south-africa/domain/types.d.ts +14 -0
- package/dist/types/capital-gains/uk/UKCapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/uk/UKCapitalGainsServiceImpl.d.ts +9 -0
- package/dist/types/capital-gains/uk/domain/types.d.ts +10 -0
- package/dist/types/capital-gains/usa/USACapitalGainsService.d.ts +4 -0
- package/dist/types/capital-gains/usa/USACapitalGainsServiceImpl.d.ts +10 -0
- package/dist/types/capital-gains/usa/domain/types.d.ts +26 -0
- package/dist/types/corporate/index.d.ts +15 -0
- package/dist/types/income-tax/index.d.ts +15 -0
- package/dist/types/index.d.ts +4 -42
- package/dist/types/mortgage/france/FranceMortgageService.d.ts +1 -1
- package/dist/types/mortgage/france/FranceMortgageServiceImpl.d.ts +1 -1
- package/dist/types/mortgage/index.d.ts +15 -0
- package/dist/types/shared/domain/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/capital-gains/australia/AustraliaCapitalGainsService.ts +5 -0
- package/src/capital-gains/australia/AustraliaCapitalGainsServiceImpl.ts +84 -0
- package/src/capital-gains/australia/domain/types.ts +17 -0
- package/src/capital-gains/canada/CanadaCapitalGainsService.ts +5 -0
- package/src/capital-gains/canada/CanadaCapitalGainsServiceImpl.ts +82 -0
- package/src/capital-gains/canada/domain/types.ts +15 -0
- package/src/capital-gains/domain/types.ts +15 -0
- package/src/capital-gains/france/FranceCapitalGainsService.ts +5 -0
- package/src/capital-gains/france/FranceCapitalGainsServiceImpl.ts +58 -0
- package/src/capital-gains/france/domain/types.ts +8 -0
- package/src/capital-gains/germany/GermanyCapitalGainsService.ts +5 -0
- package/src/capital-gains/germany/GermanyCapitalGainsServiceImpl.ts +77 -0
- package/src/capital-gains/germany/domain/types.ts +10 -0
- package/src/capital-gains/index.ts +72 -0
- package/src/capital-gains/south-africa/SouthAfricaCapitalGainsService.ts +5 -0
- package/src/capital-gains/south-africa/SouthAfricaCapitalGainsServiceImpl.ts +101 -0
- package/src/capital-gains/south-africa/domain/types.ts +16 -0
- package/src/capital-gains/uk/UKCapitalGainsService.ts +5 -0
- package/src/capital-gains/uk/UKCapitalGainsServiceImpl.ts +98 -0
- package/src/capital-gains/uk/domain/types.ts +11 -0
- package/src/capital-gains/usa/USACapitalGainsService.ts +5 -0
- package/src/capital-gains/usa/USACapitalGainsServiceImpl.ts +96 -0
- package/src/capital-gains/usa/domain/types.ts +27 -0
- package/src/corporate/index.ts +79 -0
- package/src/income-tax/index.ts +72 -0
- package/src/index.ts +6 -146
- package/src/mortgage/france/FranceMortgageService.ts +1 -1
- package/src/mortgage/france/FranceMortgageServiceImpl.ts +1 -1
- package/src/mortgage/index.ts +87 -0
- package/src/shared/domain/types.ts +1 -0
- package/test/australia-capital-gains.test.ts +80 -0
- package/test/canada-capital-gains.test.ts +71 -0
- package/test/france-capital-gains.test.ts +66 -0
- package/test/germany-capital-gains.test.ts +85 -0
- package/test/south-africa-capital-gains.test.ts +84 -0
- package/test/uk-capital-gains.test.ts +79 -0
- package/test/usa-capital-gains.test.ts +88 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { UKCapitalGainsServiceImpl } from '../src/capital-gains/uk/UKCapitalGainsServiceImpl';
|
|
2
|
+
import { Input, Rules } from '../src/capital-gains/uk/domain/types';
|
|
3
|
+
|
|
4
|
+
// UK 2024-25 CGT rules (non-residential assets)
|
|
5
|
+
const ukCapitalGainsRules: Rules = {
|
|
6
|
+
annualExemption: 3000,
|
|
7
|
+
basicRate: 0.10,
|
|
8
|
+
higherRate: 0.20,
|
|
9
|
+
basicRateLimit: 37700,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe('UKCapitalGainsServiceImpl', () => {
|
|
13
|
+
it('applies annual exemption', () => {
|
|
14
|
+
const input: Input = { capitalGain: 2000, totalTaxableIncome: 20000 };
|
|
15
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
16
|
+
const result = service.calculate();
|
|
17
|
+
|
|
18
|
+
expect(result.taxableGain).toBe(0);
|
|
19
|
+
expect(result.capitalGainTax).toBe(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('applies basic rate for basic-rate taxpayer', () => {
|
|
23
|
+
const input: Input = { capitalGain: 13000, totalTaxableIncome: 20000 };
|
|
24
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
25
|
+
const result = service.calculate();
|
|
26
|
+
|
|
27
|
+
// Taxable gain = 13000 - 3000 = 10000
|
|
28
|
+
// Basic rate remaining = 37700 - 20000 = 17700 (enough for all)
|
|
29
|
+
expect(result.taxableGain).toBe(10000);
|
|
30
|
+
expect(result.capitalGainTax).toBe(10000 * 0.10);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('applies higher rate for higher-rate taxpayer', () => {
|
|
34
|
+
const input: Input = { capitalGain: 53000, totalTaxableIncome: 50000 };
|
|
35
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
36
|
+
const result = service.calculate();
|
|
37
|
+
|
|
38
|
+
// Taxable gain = 53000 - 3000 = 50000, all at higher rate (income > basic rate limit)
|
|
39
|
+
expect(result.taxableGain).toBe(50000);
|
|
40
|
+
expect(result.capitalGainTax).toBe(50000 * 0.20);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('splits between basic and higher rate', () => {
|
|
44
|
+
const input: Input = { capitalGain: 23000, totalTaxableIncome: 30000 };
|
|
45
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
46
|
+
const result = service.calculate();
|
|
47
|
+
|
|
48
|
+
// Taxable gain = 23000 - 3000 = 20000
|
|
49
|
+
// Basic rate remaining = 37700 - 30000 = 7700
|
|
50
|
+
// 7700 at 10% = 770, 12300 at 20% = 2460
|
|
51
|
+
expect(result.taxableGain).toBe(20000);
|
|
52
|
+
expect(result.capitalGainTax).toBe(7700 * 0.10 + 12300 * 0.20);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('returns zero for zero gain', () => {
|
|
56
|
+
const input: Input = { capitalGain: 0, totalTaxableIncome: 50000 };
|
|
57
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
58
|
+
const result = service.calculate();
|
|
59
|
+
|
|
60
|
+
expect(result.capitalGainTax).toBe(0);
|
|
61
|
+
expect(result.breakdowns).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('returns zero for negative gain', () => {
|
|
65
|
+
const input: Input = { capitalGain: -5000, totalTaxableIncome: 50000 };
|
|
66
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
67
|
+
const result = service.calculate();
|
|
68
|
+
|
|
69
|
+
expect(result.capitalGainTax).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('calculates effective rate based on full gain', () => {
|
|
73
|
+
const input: Input = { capitalGain: 53000, totalTaxableIncome: 50000 };
|
|
74
|
+
const service = new UKCapitalGainsServiceImpl(input, ukCapitalGainsRules);
|
|
75
|
+
const result = service.calculate();
|
|
76
|
+
|
|
77
|
+
expect(result.effectiveRate).toBe((result.capitalGainTax / 53000) * 100);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { USACapitalGainsServiceImpl } from '../src/capital-gains/usa/USACapitalGainsServiceImpl';
|
|
2
|
+
import { Input, Rules } from '../src/capital-gains/usa/domain/types';
|
|
3
|
+
|
|
4
|
+
// USA 2024 long-term capital gains brackets (single filer)
|
|
5
|
+
const usaCapitalGainsRules: Rules = {
|
|
6
|
+
longTermBrackets: [
|
|
7
|
+
{ from: 0, to: 47025, rate: 0.00 },
|
|
8
|
+
{ from: 47025, to: 518900, rate: 0.15 },
|
|
9
|
+
{ from: 518900, to: null, rate: 0.20 },
|
|
10
|
+
],
|
|
11
|
+
shortTermBrackets: [
|
|
12
|
+
{ from: 0, to: 11600, rate: 0.10 },
|
|
13
|
+
{ from: 11600, to: 47150, rate: 0.12 },
|
|
14
|
+
{ from: 47150, to: 100525, rate: 0.22 },
|
|
15
|
+
{ from: 100525, to: 191950, rate: 0.24 },
|
|
16
|
+
{ from: 191950, to: 243725, rate: 0.32 },
|
|
17
|
+
{ from: 243725, to: 609350, rate: 0.35 },
|
|
18
|
+
{ from: 609350, to: null, rate: 0.37 },
|
|
19
|
+
],
|
|
20
|
+
netInvestmentIncomeTax: { rate: 0.038, threshold: 200000 },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('USACapitalGainsServiceImpl', () => {
|
|
24
|
+
it('applies 0% long-term rate for low income', () => {
|
|
25
|
+
const input: Input = { capitalGain: 30000, totalTaxableIncome: 30000, holdingPeriodMonths: 24 };
|
|
26
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
27
|
+
const result = service.calculate();
|
|
28
|
+
|
|
29
|
+
expect(result.capitalGainTax).toBe(0);
|
|
30
|
+
expect(result.netInvestmentIncomeTax).toBe(0);
|
|
31
|
+
expect(result.totalTax).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('applies 15% long-term rate for mid income', () => {
|
|
35
|
+
const input: Input = { capitalGain: 50000, totalTaxableIncome: 100000, holdingPeriodMonths: 13 };
|
|
36
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
37
|
+
const result = service.calculate();
|
|
38
|
+
|
|
39
|
+
// Other income = 50000, gain falls in 50000-100000 range, all at 15%
|
|
40
|
+
expect(result.capitalGainTax).toBe(50000 * 0.15);
|
|
41
|
+
expect(result.netInvestmentIncomeTax).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('applies NIIT for high-income earners', () => {
|
|
45
|
+
const input: Input = { capitalGain: 100000, totalTaxableIncome: 300000, holdingPeriodMonths: 24 };
|
|
46
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
47
|
+
const result = service.calculate();
|
|
48
|
+
|
|
49
|
+
expect(result.netInvestmentIncomeTax).toBe(100000 * 0.038);
|
|
50
|
+
expect(result.totalTax).toBe(result.capitalGainTax + result.netInvestmentIncomeTax);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('applies short-term rates for gains held <= 12 months', () => {
|
|
54
|
+
const input: Input = { capitalGain: 50000, totalTaxableIncome: 50000, holdingPeriodMonths: 6 };
|
|
55
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
56
|
+
const result = service.calculate();
|
|
57
|
+
|
|
58
|
+
// Short-term gain taxed as ordinary income from 0-50000
|
|
59
|
+
expect(result.capitalGainTax).toBeGreaterThan(0);
|
|
60
|
+
expect(result.breakdowns.length).toBeGreaterThan(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('returns zero for zero gain', () => {
|
|
64
|
+
const input: Input = { capitalGain: 0, totalTaxableIncome: 50000, holdingPeriodMonths: 24 };
|
|
65
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
66
|
+
const result = service.calculate();
|
|
67
|
+
|
|
68
|
+
expect(result.totalTax).toBe(0);
|
|
69
|
+
expect(result.effectiveRate).toBe(0);
|
|
70
|
+
expect(result.breakdowns).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns zero for negative gain', () => {
|
|
74
|
+
const input: Input = { capitalGain: -5000, totalTaxableIncome: 50000, holdingPeriodMonths: 24 };
|
|
75
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
76
|
+
const result = service.calculate();
|
|
77
|
+
|
|
78
|
+
expect(result.totalTax).toBe(0);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('calculates effective rate correctly', () => {
|
|
82
|
+
const input: Input = { capitalGain: 100000, totalTaxableIncome: 100000, holdingPeriodMonths: 24 };
|
|
83
|
+
const service = new USACapitalGainsServiceImpl(input, usaCapitalGainsRules);
|
|
84
|
+
const result = service.calculate();
|
|
85
|
+
|
|
86
|
+
expect(result.effectiveRate).toBe((result.totalTax / 100000) * 100);
|
|
87
|
+
});
|
|
88
|
+
});
|