diff --git a/app/models/Household.ts b/app/models/Household.ts index f91f50fb..f290199c 100644 --- a/app/models/Household.ts +++ b/app/models/Household.ts @@ -88,6 +88,7 @@ export class Household { affordability: marketPurchase.affordability, landPriceOrRent: this.property.landPrice, }), + marketPurchase: marketPurchase }); const fairholdLandRent = new FairholdLandRent({ @@ -103,6 +104,8 @@ export class Household { affordability: marketRent.affordability, landPriceOrRent: averageRentYearly, }), // fairhold object + + marketPurchase: marketPurchase }); return { diff --git a/app/models/Mortgage.ts b/app/models/Mortgage.ts index 63e7e4db..b5524e3e 100644 --- a/app/models/Mortgage.ts +++ b/app/models/Mortgage.ts @@ -35,6 +35,7 @@ export class Mortgage { monthlyPayment: number; totalMortgageCost: number; yearlyPaymentBreakdown: MortgageBreakdown; + totalInterest: number; constructor(params: MortgageParams) { this.propertyValue = params.propertyValue; @@ -51,6 +52,7 @@ export class Mortgage { this.totalMortgageCost = totalMortgageCost; this.yearlyPaymentBreakdown = this.calculateYearlyPaymentBreakdown(); + this.totalInterest = this.calculateTotalInterest(); } private calculateMortgagePrinciple() { @@ -110,4 +112,8 @@ export class Mortgage { return yearlyPaymentBreakdown; } + private calculateTotalInterest() { + const totalInterest = parseFloat((this.principal * this.interestRate * this.termYears).toFixed(2)) + return totalInterest + } } diff --git a/app/models/tenure/FairholdLandPurchase.test.ts b/app/models/tenure/FairholdLandPurchase.test.ts index 58372edc..f3d78a64 100644 --- a/app/models/tenure/FairholdLandPurchase.test.ts +++ b/app/models/tenure/FairholdLandPurchase.test.ts @@ -1,10 +1,16 @@ import { Fairhold } from "../Fairhold"; import { DEFAULT_FORECAST_PARAMETERS, ForecastParameters } from "../ForecastParameters"; import { FairholdLandPurchase } from "./FairholdLandPurchase"; +import { MarketPurchase } from "./MarketPurchase" let tenureFairholdLandPurchase: FairholdLandPurchase; beforeEach(() => { + // partial MarketPurchase object with only necessary properties for test (instead of mocking a whole MarketPurchase object) + const partialMarketPurchase: Pick = { + interestPaid: 200000, + }; + const forecastParameters: ForecastParameters = { ...DEFAULT_FORECAST_PARAMETERS, }; @@ -20,7 +26,8 @@ beforeEach(() => { affordability: 0.2, landPriceOrRent: 31531.579, }), - forecastParameters: forecastParameters, + forecastParameters: forecastParameters, + marketPurchase: partialMarketPurchase as MarketPurchase }); }); diff --git a/app/models/tenure/FairholdLandPurchase.ts b/app/models/tenure/FairholdLandPurchase.ts index 44ffce48..b6e41005 100644 --- a/app/models/tenure/FairholdLandPurchase.ts +++ b/app/models/tenure/FairholdLandPurchase.ts @@ -1,6 +1,7 @@ import { Fairhold } from "../Fairhold"; import { Mortgage } from "../Mortgage"; import { ForecastParameters } from '../ForecastParameters'; +import { MarketPurchase } from "./MarketPurchase"; interface FairholdLandPurchaseParams { newBuildPrice: number; @@ -8,6 +9,7 @@ interface FairholdLandPurchaseParams { affordability: number; fairhold: Fairhold; forecastParameters: ForecastParameters; + marketPurchase: MarketPurchase; } export class FairholdLandPurchase { @@ -15,6 +17,9 @@ export class FairholdLandPurchase { discountedLandPrice: number; discountedLandMortgage: Mortgage; depreciatedHouseMortgage: Mortgage; + interestPaid: number; + /** interest saved relative to market purchase, pounds */ + interestSaved: number; constructor(params: FairholdLandPurchaseParams) { this.params = params; @@ -27,6 +32,20 @@ export class FairholdLandPurchase { this.depreciatedHouseMortgage = new Mortgage({ propertyValue: params.depreciatedBuildPrice, }); + + this.interestPaid = + this.calculateInterestPaid(); + + this.interestSaved = + this.calculateInterestSaved(params.marketPurchase); + } + + private calculateInterestPaid() { + return this.depreciatedHouseMortgage.totalInterest + this.discountedLandMortgage.totalInterest; + } + + private calculateInterestSaved(marketPurchase: MarketPurchase): number { + return marketPurchase.interestPaid - this.interestPaid; } } diff --git a/app/models/tenure/FairholdLandRent.test.ts b/app/models/tenure/FairholdLandRent.test.ts index be335eee..a65af4dc 100644 --- a/app/models/tenure/FairholdLandRent.test.ts +++ b/app/models/tenure/FairholdLandRent.test.ts @@ -1,10 +1,16 @@ import { Fairhold } from "../Fairhold"; import { DEFAULT_FORECAST_PARAMETERS, ForecastParameters } from "../ForecastParameters"; import { FairholdLandRent } from "./FairholdLandRent"; +import { MarketPurchase } from "./MarketPurchase" let tenureFairholdLandRent: FairholdLandRent; beforeEach(() => { + // partial MarketPurchase object with only necessary properties for test (instead of mocking a whole MarketPurchase object) + const partialMarketPurchase: Pick = { + interestPaid: 200000, + }; + const forecastParameters: ForecastParameters = { ...DEFAULT_FORECAST_PARAMETERS, }; @@ -22,7 +28,8 @@ beforeEach(() => { landPriceOrRent: 20000, }), forecastParameters: forecastParameters, - }); + marketPurchase: partialMarketPurchase as MarketPurchase + }) }); it("can be instantiated", () => { diff --git a/app/models/tenure/FairholdLandRent.ts b/app/models/tenure/FairholdLandRent.ts index 6fd05837..985ce515 100644 --- a/app/models/tenure/FairholdLandRent.ts +++ b/app/models/tenure/FairholdLandRent.ts @@ -2,6 +2,7 @@ import { MONTHS_PER_YEAR } from "../constants"; import { Fairhold } from "../Fairhold"; import { Mortgage } from "../Mortgage"; import { ForecastParameters } from '../ForecastParameters'; +import { MarketPurchase } from "./MarketPurchase"; interface FairholdLandRentParams { averageRentYearly: number; @@ -12,6 +13,7 @@ interface FairholdLandRentParams { incomeYearly: number; fairhold: Fairhold; forecastParameters: ForecastParameters; + marketPurchase: MarketPurchase; } export class FairholdLandRent { @@ -20,6 +22,9 @@ export class FairholdLandRent { depreciatedHouseMortgage: Mortgage; /** discounted value of the monthly land rent according to fairhold */ discountedLandRentMonthly: number; + interestPaid: number; + /** interest saved relative to market purchase, pounds */ + interestSaved: number; constructor(params: FairholdLandRentParams) { this.params = params; @@ -29,6 +34,12 @@ export class FairholdLandRent { this.discountedLandRentMonthly = this.calculateDiscountedLandRentMonthly(params); + + this.interestPaid = + this.calculateInterestPaid(); + + this.interestSaved = + this.calculateInterestSaved(params.marketPurchase); } private calculateDiscountedLandRentMonthly({ @@ -51,4 +62,12 @@ export class FairholdLandRent { return discountedLandRentMonthly; } + + private calculateInterestPaid() { + return this.depreciatedHouseMortgage.totalInterest; + } + + private calculateInterestSaved(marketPurchase: MarketPurchase): number { + return marketPurchase.interestPaid - this.interestPaid; + } } diff --git a/app/models/tenure/MarketPurchase.ts b/app/models/tenure/MarketPurchase.ts index e5a0864c..828c4414 100644 --- a/app/models/tenure/MarketPurchase.ts +++ b/app/models/tenure/MarketPurchase.ts @@ -16,6 +16,7 @@ export class MarketPurchase { public affordability: number; public houseMortgage: Mortgage; public landMortgage: Mortgage; + public interestPaid: number; constructor(params: MarketPurchaseParams) { this.params = params; @@ -32,6 +33,8 @@ export class MarketPurchase { }); this.affordability = this.calculateAffordability(params); + + this.interestPaid = this.calculateInterestPaid(); } private calculateAffordability({ incomeYearly }: MarketPurchaseParams) { @@ -42,4 +45,8 @@ export class MarketPurchase { return affordability; } + private calculateInterestPaid() { + return this.houseMortgage.totalInterest + this.landMortgage.totalInterest + } + }