moneyfunx 0.0.30 → 0.0.33

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.
@@ -0,0 +1,6 @@
1
+ export { PaymentTooLowError } from "./lib/errors";
2
+ export { calculateMinPayment, numPaymentsToZero, principalRemaining, } from "./lib/helperFunctions";
3
+ export { ILoan, Loan } from "./lib/loan";
4
+ export { determineExtraPayment, amortizePayments, payLoans, } from "./lib/payments";
5
+ export { AmortizationRecord, LoansPaymentSummary, PaymentSummary, } from "./lib/paymentTypes";
6
+ export { snowball, avalanche, sortLoans } from "./lib/sorting";
package/build/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { PaymentTooLowError } from "./lib/errors";
2
+ export { calculateMinPayment, numPaymentsToZero, principalRemaining, } from "./lib/helperFunctions";
3
+ export { Loan } from "./lib/loan";
4
+ export { determineExtraPayment, amortizePayments, payLoans, } from "./lib/payments";
5
+ export { snowball, avalanche, sortLoans } from "./lib/sorting";
@@ -0,0 +1,6 @@
1
+ export declare class NegativeBalanceError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class PaymentTooLowError extends Error {
5
+ constructor(message: string);
6
+ }
@@ -0,0 +1,14 @@
1
+ export class NegativeBalanceError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ Object.setPrototypeOf(this, NegativeBalanceError.prototype);
5
+ this.name = "NegativeBalanceError";
6
+ }
7
+ }
8
+ export class PaymentTooLowError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ Object.setPrototypeOf(this, PaymentTooLowError.prototype);
12
+ this.name = "PaymentTooLowError";
13
+ }
14
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Helper Functions
3
+ *
4
+ * These functions are inner primitives used in lifecycle calculations for Loans
5
+ */
6
+ /**
7
+ * Calculates the minimum payment to pay the principal back in the number of periods at the periodic rate
8
+ *
9
+ * balance = principal + interest
10
+ *
11
+ * @param {number} principal The amount borrowed
12
+ * @param {number} periodicRate The rate the balance accrues interest at per period
13
+ * @param {number [int]} periods The number of periods the principal is repaid over
14
+ * @returns {number} The minimum payment
15
+ */
16
+ export declare function calculateMinPayment(principal: number, periodicRate: number, periods: number): number;
17
+ /**
18
+ * Calculates the principal remaining after a certain number of payments from a beginning principal at a periodic rate
19
+ *
20
+ * balance = principal + interest
21
+ *
22
+ * @param {number} principal The amount borrowed
23
+ * @param {number} payment The amount paid at each period
24
+ * @param {number} periodicRate The rate the balance accrues interest at per period
25
+ * @param {number [int]} periods The number of periods paid to compute the desired principal remaining
26
+ * @returns {number} The remaining principal
27
+ */
28
+ export declare function principalRemaining(principal: number, payment: number, periodicRate: number, periods: number): number;
29
+ /**
30
+ * Calculates the number of payments required to pay off a principal
31
+ *
32
+ * balance = principal + interest
33
+ *
34
+ * @param {number} principal The amount borrowed
35
+ * @param {number} payment The amount paid at each period
36
+ * @param {number} periodicRate The rate the balance accrues interest at per period
37
+ * @returns The number of payments needed to pay off the principal
38
+ */
39
+ export declare function numPaymentsToZero(principal: number, payment: number, periodicRate: number): number;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Helper Functions
3
+ *
4
+ * These functions are inner primitives used in lifecycle calculations for Loans
5
+ */
6
+ /**
7
+ * Calculates the minimum payment to pay the principal back in the number of periods at the periodic rate
8
+ *
9
+ * balance = principal + interest
10
+ *
11
+ * @param {number} principal The amount borrowed
12
+ * @param {number} periodicRate The rate the balance accrues interest at per period
13
+ * @param {number [int]} periods The number of periods the principal is repaid over
14
+ * @returns {number} The minimum payment
15
+ */
16
+ export function calculateMinPayment(principal, periodicRate, periods) {
17
+ return periodicRate > 0
18
+ ? principal *
19
+ ((periodicRate * Math.pow((1 + periodicRate), periods)) /
20
+ (Math.pow((1 + periodicRate), periods) - 1))
21
+ : principal / periods;
22
+ }
23
+ /**
24
+ * Calculates the principal remaining after a certain number of payments from a beginning principal at a periodic rate
25
+ *
26
+ * balance = principal + interest
27
+ *
28
+ * @param {number} principal The amount borrowed
29
+ * @param {number} payment The amount paid at each period
30
+ * @param {number} periodicRate The rate the balance accrues interest at per period
31
+ * @param {number [int]} periods The number of periods paid to compute the desired principal remaining
32
+ * @returns {number} The remaining principal
33
+ */
34
+ export function principalRemaining(principal, payment, periodicRate, periods) {
35
+ return Math.max(principal * Math.pow((1 + periodicRate), periods) -
36
+ payment * ((Math.pow((1 + periodicRate), periods) - 1) / periodicRate), 0);
37
+ }
38
+ /**
39
+ * Calculates the number of payments required to pay off a principal
40
+ *
41
+ * balance = principal + interest
42
+ *
43
+ * @param {number} principal The amount borrowed
44
+ * @param {number} payment The amount paid at each period
45
+ * @param {number} periodicRate The rate the balance accrues interest at per period
46
+ * @returns The number of payments needed to pay off the principal
47
+ */
48
+ export function numPaymentsToZero(principal, payment, periodicRate) {
49
+ return Math.ceil(Math.log(payment / (payment - principal * periodicRate)) /
50
+ Math.log(periodicRate + 1));
51
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ *
3
+ * *****************
4
+ * *** MoneyFunx ***
5
+ * *****************
6
+ *
7
+ * mek it funx up
8
+ *
9
+ * This library contains functions used to in personal financial analysis
10
+ *
11
+ */
12
+ /**
13
+ * Represents a financial loan
14
+ */
15
+ export interface ILoan {
16
+ id: string;
17
+ principal: number;
18
+ annualRate: number;
19
+ periodsPerYear: number;
20
+ termInYears: number;
21
+ periodicRate: number;
22
+ periods: number;
23
+ minPayment: number;
24
+ totalInterest: number;
25
+ }
26
+ export declare class Loan implements ILoan {
27
+ id: string;
28
+ principal: number;
29
+ annualRate: number;
30
+ periodsPerYear: number;
31
+ termInYears: number;
32
+ periodicRate: number;
33
+ periods: number;
34
+ minPayment: number;
35
+ totalInterest: number;
36
+ /**
37
+ * @constructor
38
+ * @param {number} principal The amount borrowed
39
+ * @param {number} annualRate The yearly rate the loan accrues interest at
40
+ * @param {number} periodsPerYear The number of times the interest is accrued in a year
41
+ * @param {number} termInYears The number of years the loan is repaid over
42
+ */
43
+ constructor(principal: number, annualRate: number, periodsPerYear: number, termInYears: number);
44
+ /**
45
+ * Verifies a payment amount is valid
46
+ * Throws a PaymentTooLowError if the payment amount is less than the loan's minimum payment
47
+ *
48
+ * @param {number} payment The amount to pay the loan with
49
+ * @returns {number} The validated payment amount
50
+ */
51
+ validatePayment(payment?: number): number;
52
+ /**
53
+ * Calculates the minimum payment to pay off the loan in the required number of periods
54
+ * @returns {number} The minimum amount to pay off the loan in the required number of periods
55
+ */
56
+ calculateMinPayment(): number;
57
+ /**
58
+ * Calculates the amount of interest accrued in a period on a provided balance
59
+ * @param {number} balance The amunt of money owed on a loan
60
+ * @returns {number} The amount of interest accrued in one period
61
+ */
62
+ accrueInterest(balance?: number): number;
63
+ /**
64
+ * Calculates the number of payments needed to pay off a balance at a provided payemnt amount
65
+ * @param {number} payment The amount to pay the loan with
66
+ * @param {number} balance The amout of money owed on a loan
67
+ * @returns {number} The number of payments neede to pay the loan off
68
+ */
69
+ numPaymentsToZero(payment?: number, balance?: number): number;
70
+ /**
71
+ * Calculates the amout of pricipal remaining after paying a starting balance with a payment for a number of periods
72
+ * @param {number} periods The number of payemnts to make
73
+ * @param {number} payment The amount to pay the loan with
74
+ * @param {number} balance The amount of money owed on a loan
75
+ * @returns {number} The share of the amount borrowed left to pay
76
+ */
77
+ principalRemaining(periods: number, payment?: number, balance?: number): number;
78
+ /**
79
+ * Calculates the amount of interest paid after paying a starting balance with a payment for a number of periods
80
+ * @param {number} periods The number of payments to make
81
+ * @param {number} payment The amount to pay the loan with
82
+ * @param {number} balance The amount of money owed on a loan
83
+ * @returns The total amount of interest paid
84
+ */
85
+ interestPaid(periods: number, payment?: number, balance?: number): number;
86
+ }
@@ -9,74 +9,8 @@
9
9
  * This library contains functions used to in personal financial analysis
10
10
  *
11
11
  */
12
-
13
12
  import * as errors from "./errors";
14
-
15
- /**
16
- * Calculates the minimum payment to pay the principal back in the number of periods at the periodic rate
17
- *
18
- * balance = principal + interest
19
- *
20
- * @param {number} principal The amount borrowed
21
- * @param {number} periodicRate The rate the balance accrues interest at per period
22
- * @param {number [int]} periods The number of periods the principal is repaid over
23
- * @returns {number} The minimum payment
24
- */
25
- export function calculateMinPayment(principal, periodicRate, periods) {
26
- return periodicRate > 0 ?
27
- principal * (
28
- (
29
- periodicRate * (1 + periodicRate) ** periods
30
- ) / (
31
- (1 + periodicRate) ** periods - 1
32
- )
33
- ) :
34
- principal / periods;
35
- }
36
-
37
- /**
38
- * Calculates the principal remaining after a certain number of payments from a beginning principal at a periodic rate
39
- *
40
- * balance = principal + interest
41
- *
42
- * @param {number} principal The amount borrowed
43
- * @param {number} payment The amount paid at each period
44
- * @param {number} periodicRate The rate the balance accrues interest at per period
45
- * @param {number [int]} periods The number of periods paid to compute the desired principal remaining
46
- * @returns {number} The remaining principal
47
- */
48
- export function principalRemaining(principal, payment, periodicRate, periods) {
49
- return Math.max(
50
- (principal * (1 + periodicRate) ** periods) - (
51
- payment * (
52
- ((1 + periodicRate) ** periods - 1) / (periodicRate)
53
- )
54
- ),
55
- 0
56
- );
57
- }
58
-
59
- /**
60
- * Calculates the number of payments required to pay off a principal
61
- *
62
- * balance = principal + interest
63
- *
64
- * @param {number} principal The amount borrowed
65
- * @param {number} payment The amount paid at each period
66
- * @param {number} periodicRate The rate the balance accrues interest at per period
67
- * @returns The number of payments needed to pay off the principal
68
- */
69
- export function numPaymentsToZero(principal, payment, periodicRate) {
70
- return Math.ceil(
71
- Math.log(
72
- (payment / (payment - principal * periodicRate))
73
- ) / Math.log(periodicRate + 1)
74
- );
75
- }
76
-
77
- /**
78
- * Represents a financial loan
79
- */
13
+ import * as helpers from "./helperFunctions";
80
14
  export class Loan {
81
15
  /**
82
16
  * @constructor
@@ -94,9 +28,8 @@ export class Loan {
94
28
  this.periodicRate = this.annualRate / this.periodsPerYear;
95
29
  this.periods = this.periodsPerYear * this.termInYears;
96
30
  this.minPayment = this.calculateMinPayment();
97
- this.totalInterest = (this.minPayment * (this.periods)) - this.principal;
31
+ this.totalInterest = this.minPayment * this.periods - this.principal;
98
32
  }
99
-
100
33
  /**
101
34
  * Verifies a payment amount is valid
102
35
  * Throws a PaymentTooLowError if the payment amount is less than the loan's minimum payment
@@ -107,19 +40,18 @@ export class Loan {
107
40
  validatePayment(payment = this.minPayment) {
108
41
  if (payment < this.minPayment) {
109
42
  throw new errors.PaymentTooLowError(`payment of ${payment} cannot be less than ${this.minPayment}`);
110
- } else {
43
+ }
44
+ else {
111
45
  return payment;
112
46
  }
113
47
  }
114
-
115
48
  /**
116
49
  * Calculates the minimum payment to pay off the loan in the required number of periods
117
50
  * @returns {number} The minimum amount to pay off the loan in the required number of periods
118
51
  */
119
52
  calculateMinPayment() {
120
- return calculateMinPayment(this.principal, this.periodicRate, this.periods);
53
+ return helpers.calculateMinPayment(this.principal, this.periodicRate, this.periods);
121
54
  }
122
-
123
55
  /**
124
56
  * Calculates the amount of interest accrued in a period on a provided balance
125
57
  * @param {number} balance The amunt of money owed on a loan
@@ -128,7 +60,6 @@ export class Loan {
128
60
  accrueInterest(balance = this.principal) {
129
61
  return balance * this.periodicRate;
130
62
  }
131
-
132
63
  /**
133
64
  * Calculates the number of payments needed to pay off a balance at a provided payemnt amount
134
65
  * @param {number} payment The amount to pay the loan with
@@ -136,14 +67,9 @@ export class Loan {
136
67
  * @returns {number} The number of payments neede to pay the loan off
137
68
  */
138
69
  numPaymentsToZero(payment = this.minPayment, balance = this.principal) {
139
- payment = this.validatePayment(payment);
140
- return numPaymentsToZero(
141
- balance,
142
- payment,
143
- this.periodicRate
144
- );
70
+ this.validatePayment(payment);
71
+ return helpers.numPaymentsToZero(balance, payment, this.periodicRate);
145
72
  }
146
-
147
73
  /**
148
74
  * Calculates the amout of pricipal remaining after paying a starting balance with a payment for a number of periods
149
75
  * @param {number} periods The number of payemnts to make
@@ -152,17 +78,11 @@ export class Loan {
152
78
  * @returns {number} The share of the amount borrowed left to pay
153
79
  */
154
80
  principalRemaining(periods, payment = this.minPayment, balance = this.principal) {
155
- payment = this.validatePayment(payment);
156
- return periods < this.numPaymentsToZero(payment, balance) ?
157
- principalRemaining(
158
- balance,
159
- payment,
160
- this.periodicRate,
161
- periods
162
- ) :
163
- 0;
81
+ this.validatePayment(payment);
82
+ return periods < this.numPaymentsToZero(payment, balance)
83
+ ? helpers.principalRemaining(balance, payment, this.periodicRate, periods)
84
+ : 0;
164
85
  }
165
-
166
86
  /**
167
87
  * Calculates the amount of interest paid after paying a starting balance with a payment for a number of periods
168
88
  * @param {number} periods The number of payments to make
@@ -171,26 +91,13 @@ export class Loan {
171
91
  * @returns The total amount of interest paid
172
92
  */
173
93
  interestPaid(periods, payment = this.minPayment, balance = this.principal) {
174
- payment = this.validatePayment(payment);
175
- return periods < this.numPaymentsToZero(payment, balance) ?
176
- (payment * periods) - (balance - this.principalRemaining(periods, payment, balance)) :
177
- Math.max(
178
- payment * (this.numPaymentsToZero(payment, balance) - 1) - (
179
- balance - this.principalRemaining(
180
- this.numPaymentsToZero(payment) - 1,
181
- payment,
182
- balance
183
- )
184
- ) + (
185
- this.accrueInterest(
186
- this.principalRemaining(
187
- this.numPaymentsToZero(payment) - 1,
188
- payment,
189
- balance
190
- )
191
- )
192
- ),
193
- 0
194
- );
94
+ this.validatePayment(payment);
95
+ return periods < this.numPaymentsToZero(payment, balance)
96
+ ? payment * periods -
97
+ (balance - this.principalRemaining(periods, payment, balance))
98
+ : Math.max(payment * (this.numPaymentsToZero(payment, balance) - 1) -
99
+ (balance -
100
+ this.principalRemaining(this.numPaymentsToZero(payment) - 1, payment, balance)) +
101
+ this.accrueInterest(this.principalRemaining(this.numPaymentsToZero(payment) - 1, payment, balance)), 0);
195
102
  }
196
103
  }
@@ -0,0 +1,13 @@
1
+ export interface AmortizationRecord {
2
+ period: number;
3
+ principal: number;
4
+ interest: number;
5
+ principalRemaining: number;
6
+ }
7
+ export interface PaymentSummary {
8
+ lifetimeInterest: number;
9
+ amortizationSchedule: Array<AmortizationRecord>;
10
+ }
11
+ export interface LoansPaymentSummary {
12
+ [id: string]: PaymentSummary;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ *
3
+ * This file contains functions for computing detailed information on paying loans
4
+ *
5
+ */
6
+ import { ILoan, Loan } from "./loan";
7
+ import { AmortizationRecord, LoansPaymentSummary } from "./paymentTypes";
8
+ /**
9
+ *
10
+ * Calculates the extra amount in a payment after all loans' minimum payments are met
11
+ * Throws an exception if the payment provided is less than the collective minimum payments for all loans
12
+ *
13
+ * @param {Array<Loan>} loans The loans to allocate minimum payments
14
+ * @param {number} payment The amount to pay across all loans
15
+ * @returns {number} The extra amount of payment
16
+ */
17
+ export declare function determineExtraPayment(loans: Array<ILoan>, payment: number): number;
18
+ /**
19
+ *
20
+ * Calculates the amortization schedule for a loan paid with a payment
21
+ *
22
+ * @param {Loan} loan The loan to amortize payments for
23
+ * @param {number} payment The amount to pay to the loan's balance each period
24
+ * @param {number} numPayments The number of periods to make payments to the loan
25
+ * @param {number} startPeriod An initial offset of periods to "fast-forward" the state of the loan to prior to calculation of each period
26
+ * @returns {Array<AmortizationRecord>} The amortization schdule for the number of payments of payment made to the loan from the provided start period
27
+ */
28
+ export declare function amortizePayments(loan: Loan, payment: number, numPayments: number, startPeriod?: number): Array<AmortizationRecord>;
29
+ /**
30
+ *
31
+ * Calculates a wealth of information about paying of a set of loans with a total payment amount
32
+ *
33
+ * @param {Array<Loans>} loans The loans to pay off
34
+ * @param {number} payment The total amount of money budgeted to pay all loans each period
35
+ * @param {boolean} reduceMinimum Flag to reduce the total payment amount by a loan's minimum when that loan is paid off
36
+ * @returns {LoansPaymentSummary} Various totals and series of data regarding paying off the loans at the payment amount
37
+ */
38
+ export declare function payLoans(loans: Array<Loan>, payment: number, reduceMinimum?: boolean): LoansPaymentSummary;
@@ -3,10 +3,8 @@
3
3
  * This file contains functions for computing detailed information on paying loans
4
4
  *
5
5
  */
6
-
7
6
  import * as errors from "./errors";
8
- import * as loanLib from "./loan";
9
-
7
+ import * as helpers from "./helperFunctions";
10
8
  /**
11
9
  *
12
10
  * Calculates the extra amount in a payment after all loans' minimum payments are met
@@ -17,16 +15,12 @@ import * as loanLib from "./loan";
17
15
  * @returns {number} The extra amount of payment
18
16
  */
19
17
  export function determineExtraPayment(loans, payment) {
20
- const totalMinPayment = loans.reduce(
21
- (previousValue, currentValue) => previousValue + currentValue.minPayment,
22
- 0
23
- );
18
+ const totalMinPayment = loans.reduce((previousValue, currentValue) => previousValue + currentValue.minPayment, 0);
24
19
  if (totalMinPayment > payment) {
25
20
  throw new errors.PaymentTooLowError(`Payment amount of ${payment} must be greater than ${totalMinPayment}`);
26
21
  }
27
22
  return payment - totalMinPayment;
28
23
  }
29
-
30
24
  /**
31
25
  *
32
26
  * Calculates the amortization schedule for a loan paid with a payment
@@ -35,56 +29,30 @@ export function determineExtraPayment(loans, payment) {
35
29
  * @param {number} payment The amount to pay to the loan's balance each period
36
30
  * @param {number} numPayments The number of periods to make payments to the loan
37
31
  * @param {number} startPeriod An initial offset of periods to "fast-forward" the state of the loan to prior to calculation of each period
38
- * @returns {Array<JSON[number, number, number, number]>} The amortization schdule for the number of payments of payment made to the loan from the provided start period
32
+ * @returns {Array<AmortizationRecord>} The amortization schdule for the number of payments of payment made to the loan from the provided start period
39
33
  */
40
- export function amortizePayments(loan, payment = null, numPayments = null, startPeriod = 0) {
34
+ export function amortizePayments(loan, payment, numPayments, startPeriod = 0) {
41
35
  if (payment === null) {
42
36
  payment = loan.minPayment;
43
37
  }
44
38
  payment = loan.validatePayment(payment);
45
-
46
39
  if (numPayments === null) {
47
40
  numPayments = loan.numPaymentsToZero(payment);
48
41
  }
49
-
50
42
  let amortizationSchedule = [];
51
- for (
52
- let period = 0;
53
- period < numPayments;
54
- period++
55
- ) {
56
- let interestThisPeriod = loan.accrueInterest(
57
- loan.principalRemaining(
58
- period,
59
- payment,
60
- loan.principalRemaining(startPeriod)
61
- )
62
- );
63
- let principalThisPeriod = Math.min(
64
- payment - interestThisPeriod,
65
- loan.principalRemaining(
66
- period,
67
- payment,
68
- loan.principalRemaining(startPeriod)
69
- )
70
- );
71
- let principalRemaining = loan.principalRemaining(
72
- period + 1,
73
- payment,
74
- loan.principalRemaining(startPeriod)
75
- );
76
- amortizationSchedule.push(
77
- {
78
- period: startPeriod + period + 1,
79
- principal: principalThisPeriod,
80
- interest: interestThisPeriod,
81
- principalRemaining: principalRemaining,
82
- }
83
- );
43
+ for (let period = 0; period < numPayments; period++) {
44
+ let interestThisPeriod = loan.accrueInterest(loan.principalRemaining(period, payment, loan.principalRemaining(startPeriod)));
45
+ let principalThisPeriod = Math.min(payment - interestThisPeriod, loan.principalRemaining(period, payment, loan.principalRemaining(startPeriod)));
46
+ let principalRemaining = loan.principalRemaining(period + 1, payment, loan.principalRemaining(startPeriod));
47
+ amortizationSchedule.push({
48
+ period: startPeriod + period + 1,
49
+ principal: principalThisPeriod,
50
+ interest: interestThisPeriod,
51
+ principalRemaining: principalRemaining,
52
+ });
84
53
  }
85
54
  return amortizationSchedule;
86
55
  }
87
-
88
56
  /**
89
57
  *
90
58
  * Calculates a wealth of information about paying of a set of loans with a total payment amount
@@ -97,84 +65,61 @@ export function amortizePayments(loan, payment = null, numPayments = null, start
97
65
  export function payLoans(loans, payment, reduceMinimum = false) {
98
66
  let monthlyPayment = payment;
99
67
  let paymentData = {};
100
- loans.map(
101
- (loan) => {
102
- paymentData[loan.id] = { lifetimeInterest: 0, amortizationSchedule: [] };
103
- }
104
- );
105
-
68
+ loans.map((loan) => {
69
+ paymentData[loan.id] = { lifetimeInterest: 0, amortizationSchedule: [] };
70
+ });
106
71
  let periodsElapsed = 0;
107
72
  let paidLoans = 0;
108
73
  let lifetimeInterest = 0;
109
74
  let totalAmortizationSchedule = [];
110
-
111
75
  while (paidLoans < loans.length) {
112
76
  let firstLoan = loans.slice(paidLoans)[0];
113
- let firstLoanPayment = firstLoan.minPayment + determineExtraPayment(loans.slice(paidLoans), monthlyPayment);
114
- let periodsToPay = loanLib.numPaymentsToZero(
115
- firstLoan.principalRemaining(periodsElapsed),
116
- firstLoanPayment,
117
- firstLoan.periodicRate
118
- );
119
- let firstLoanInterestPaid = firstLoan.interestPaid(
120
- periodsToPay,
121
- firstLoanPayment,
122
- firstLoan.principalRemaining(periodsElapsed)
123
- );
77
+ let firstLoanPayment = firstLoan.minPayment +
78
+ determineExtraPayment(loans.slice(paidLoans), monthlyPayment);
79
+ let periodsToPay = helpers.numPaymentsToZero(firstLoan.principalRemaining(periodsElapsed), firstLoanPayment, firstLoan.periodicRate);
80
+ let firstLoanInterestPaid = firstLoan.interestPaid(periodsToPay, firstLoanPayment, firstLoan.principalRemaining(periodsElapsed));
124
81
  let firstLoanPaidPeriods = amortizePayments(firstLoan, firstLoanPayment, periodsToPay, periodsElapsed);
125
-
126
82
  paymentData[firstLoan.id].lifetimeInterest += firstLoanInterestPaid;
127
83
  paymentData[firstLoan.id].amortizationSchedule = [
128
84
  ...paymentData[firstLoan.id].amortizationSchedule,
129
- ...firstLoanPaidPeriods
85
+ ...firstLoanPaidPeriods,
130
86
  ];
131
-
132
87
  totalAmortizationSchedule = [
133
88
  ...totalAmortizationSchedule,
134
- ...firstLoanPaidPeriods
89
+ ...firstLoanPaidPeriods,
135
90
  ];
136
-
137
91
  // the first loan is paid off, handle totals
138
92
  paidLoans += 1;
139
93
  lifetimeInterest += paymentData[firstLoan.id].lifetimeInterest;
140
94
  if (reduceMinimum) {
141
95
  monthlyPayment -= firstLoan.minPayment;
142
96
  }
143
-
144
97
  // handle calculating information for the rest of the loans
145
98
  loans.slice(paidLoans).map((loan) => {
146
- paymentData[loan.id].lifetimeInterest += loan.interestPaid(
147
- periodsToPay,
148
- loan.minPayment,
149
- loan.principalRemaining(periodsElapsed)
150
- );
99
+ paymentData[loan.id].lifetimeInterest += loan.interestPaid(periodsToPay, loan.minPayment, loan.principalRemaining(periodsElapsed));
151
100
  let paidPeriods = amortizePayments(loan, loan.minPayment, periodsToPay, periodsElapsed);
152
101
  paymentData[loan.id].amortizationSchedule = [
153
102
  ...paymentData[loan.id].amortizationSchedule,
154
- ...paidPeriods
103
+ ...paidPeriods,
155
104
  ];
156
-
157
105
  totalAmortizationSchedule = totalAmortizationSchedule.map((element) => {
158
106
  const matchedInnerElement = paidPeriods.find((innerElement) => innerElement.period === element.period);
159
- return matchedInnerElement ?
160
- {
107
+ return matchedInnerElement
108
+ ? {
161
109
  period: element.period,
162
110
  principal: element.principal + matchedInnerElement.principal,
163
111
  interest: element.interest + matchedInnerElement.interest,
164
- principalRemaining: element.principalRemaining + matchedInnerElement.principalRemaining
165
- } :
166
- element;
112
+ principalRemaining: element.principalRemaining +
113
+ matchedInnerElement.principalRemaining,
114
+ }
115
+ : element;
167
116
  });
168
117
  });
169
118
  periodsElapsed += periodsToPay;
170
119
  }
171
-
172
- if (loans.length) {
173
- paymentData["totals"] = {
174
- lifetimeInterest: lifetimeInterest,
175
- amortizationSchedule: totalAmortizationSchedule
176
- };
177
- }
178
-
120
+ paymentData["totals"] = {
121
+ lifetimeInterest: lifetimeInterest,
122
+ amortizationSchedule: totalAmortizationSchedule,
123
+ };
179
124
  return paymentData;
180
125
  }