Search code examples
javascriptfunctiongoogle-apps-script

Debt Snowball Function


function DEBT_CALC() {
    let debts = [
        { title: "AMEX", interestRate: 0.14, minimumPayment: 52.55, balance: 510 },
        { title: "Chase", interestRate: 0.19, minimumPayment: 30, balance: 900 },
        { title: "US Bank", interestRate: 0.15, minimumPayment: 70, balance: 600 }
    ];

    let debtStrategy = "avalanche";
    let additionalPayment = 0;
    let months = 0;
    let totalInterestPaid = 0;
    let rollingMinimumPayment = 0;
    let overpaymentApplied = false; // Flag to track if overpayment has been applied in the current month

    function sortDebts() {
        if (debtStrategy === 'snowball') {
            debts.sort((a, b) => a.balance - b.balance);
        } else if (debtStrategy === 'avalanche') {
            debts.sort((a, b) => b.interestRate - a.interestRate);
        }
    }

    sortDebts();
    console.log("Initial Debt Order: " + debts.map(debt => debt.title).join(", "));

    while (debts.some(debt => debt.balance > 0)) {
        months++;
        let overpayment = 0;

        for (let i = 0; i < debts.length; i++) {
            let debt = debts[i];

            if (debt.balance > 0) {
                let interest = debt.balance * (debt.interestRate / 12);
                totalInterestPaid += interest;
                debt.balance += interest;

                let payment = Math.min(debt.balance, debt.minimumPayment + additionalPayment + rollingMinimumPayment + overpayment);
                debt.balance -= payment;
                console.log(`\t Payment of $${payment.toFixed(2)} made to ${debt.title}`);

                if (debt.balance <= 0) {
                    console.log(`\t\t ${debt.title} is paid off`);
                    overpayment = (debt.minimumPayment + additionalPayment + overpayment) - payment;
                    rollingMinimumPayment += debt.minimumPayment;
                    debt.balance = 0;

                    if (overpayment > 0 && !overpaymentApplied) {
                        for (let j = 0; j < debts.length; j++) {
                            if (debts[j].balance > 0 && debts[j].title !== debt.title) {
                                console.log(`\t\t Overpayment of $${overpayment.toFixed(2)} from ${debt.title} applied to ${debts[j].title}`);
                                debts[j].balance = Math.max(debts[j].balance - overpayment, 0);
                                overpaymentApplied = true;
                                overpayment = 0;
                                break;
                            }
                        }
                    }
                } else {
                    overpayment = (debt.minimumPayment + additionalPayment + overpayment) - payment;
                }
            }

            if (i == debts.length - 1) {
                overpaymentApplied = false; // Reset the flag for the next month
            }
        }

        console.log(`End of Month ${months}: ` + debts.map(debt => `${debt.title} balance = $${Math.max(debt.balance, 0).toFixed(2)}`).join(", "));
    }

    console.log(`Total Months to pay off: ${months}`);
    console.log(`Total Interest Paid: $${totalInterestPaid.toFixed(2)}`);
}

I doubt anyone is going to help with this, but I can't get this function right no matter how hard I try. The problem correctly is that in month 10 AMEX gets a higher payment for some reason because US bank gets paid off in the same month - so frustrating! I also haven't coded the additional payments yet..

Here's what I have and I'm so close but also so close to giving up... To explain, snowball means lowest balance debts get paid off first, and avalanche means the highest interest gets paid off first. After a debt is paid off, that minimum payment will get applied to the next debt in the sorted list (sorted by snowball or avalanche). An overpayment is simply when a debt gets paid too much and that overpayment will happen directly in that month, where a minimum payment gets applied into the next month!


Solution

  • I rewrote it like this:

    function sortDebts(debtStrategy,debts) {
      if (debtStrategy === 'snowball') {
        debts.sort((a, b) => a.balance - b.balance);
      } else if (debtStrategy === 'avalanche') {
        debts.sort((a, b) => b.interestRate - a.interestRate);
      }
    }
    
    function DEBT_CALC() {
      let debts = [
        { title: "AMEX", interestRate: 0.14, minimumPayment: 52.55, balance: 510 },
        { title: "Chase", interestRate: 0.19, minimumPayment: 30, balance: 900 },
        { title: "US Bank", interestRate: 0.15, minimumPayment: 70, balance: 600 }
      ];
      let debtStrategy = "avalanche";
      let additionalPayment = 0;
      let months = 0;
      let totalInterestPaid = 0;
      let rollingMinimumPayment = 0;
      let overpaymentApplied = false; // Flag to track if overpayment has been applied in the current month
      sortDebts(debtStrategy,debts);
      Logger.log("Initial Debt Order: " + debts.map(debt => debt.title).join(", "));
    
      while (debts.some(e => e.balance > 0)) {
        months++;
        let overpayment = 0;
    
        for (let i = 0; i < debts.length; i++) {
          let d = debts[i];
    
          if (d.balance > 0) {
            let interest = d.balance * (d.interestRate / 12);
            totalInterestPaid += interest;
            d.balance += interest;
    
            let payment = Math.min(d.balance, d.minimumPayment + additionalPayment + rollingMinimumPayment + overpayment);
            d.balance -= payment;
            Logger.log(`\t Payment of $${payment.toFixed(2)} made to ${d.title}`);
    
            if (d.balance <= 0) {
              Logger.log(`\t\t ${d.title} is paid off`);
              overpayment = (d.minimumPayment + additionalPayment + overpayment) - payment;
              rollingMinimumPayment += d.minimumPayment;
              d.balance = 0;
    
              if (overpayment > 0 && !overpaymentApplied) {
                for (let j = 0; j < debts.length; j++) {
                  if (debts[j].balance > 0 && debts[j].title !== d.title) {
                    Logger.log(`\t\t Overpayment of $${overpayment.toFixed(2)} from ${d.title} applied to ${debts[j].title}`);
                    debts[j].balance = Math.max(debts[j].balance - overpayment, 0);
                    overpaymentApplied = true;
                    overpayment = 0;
                    break;
                  }
                }
              }
            } else {
              overpayment = (d.minimumPayment + additionalPayment + overpayment) - payment;
            }
          }
    
          if (i == debts.length - 1) {
            overpaymentApplied = false; // Reset the flag for the next month
          }
        }
    
        Logger.log(`End of Month ${months}: ` + debts.map(debt => `${debt.title} balance = $${Math.max(debt.balance, 0).toFixed(2)}`).join(", "));
      }
    
      Logger.log(`Total Months to pay off: ${months}`);
      Logger.log(`Total Interest Paid: $${totalInterestPaid.toFixed(2)}`);
    }
    

    The Output:

    Execution log
    2:59:14 PM  Notice  Execution started
    2:59:13 PM  Info    Initial Debt Order: Chase, US Bank, AMEX
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 1: Chase balance = $884.25, US Bank balance = $537.50, AMEX balance = $463.40
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 2: Chase balance = $868.25, US Bank balance = $474.22, AMEX balance = $416.26
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 3: Chase balance = $852.00, US Bank balance = $410.15, AMEX balance = $368.56
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 4: Chase balance = $835.49, US Bank balance = $345.27, AMEX balance = $320.31
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 5: Chase balance = $818.72, US Bank balance = $279.59, AMEX balance = $271.50
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 6: Chase balance = $801.68, US Bank balance = $213.08, AMEX balance = $222.12
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 7: Chase balance = $784.37, US Bank balance = $145.75, AMEX balance = $172.16
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 8: Chase balance = $766.79, US Bank balance = $77.57, AMEX balance = $121.62
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $70.00 made to US Bank
    2:59:13 PM  Info         Payment of $52.55 made to AMEX
    2:59:13 PM  Info    End of Month 9: Chase balance = $748.93, US Bank balance = $8.54, AMEX balance = $70.49
    2:59:13 PM  Info         Payment of $30.00 made to Chase
    2:59:13 PM  Info         Payment of $8.65 made to US Bank
    2:59:13 PM  Info             US Bank is paid off
    2:59:13 PM  Info             Overpayment of $61.35 from US Bank applied to Chase
    2:59:13 PM  Info         Payment of $71.31 made to AMEX
    2:59:13 PM  Info             AMEX is paid off
    2:59:13 PM  Info    End of Month 10: Chase balance = $669.44, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info         Payment of $152.55 made to Chase
    2:59:13 PM  Info    End of Month 11: Chase balance = $527.49, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info         Payment of $152.55 made to Chase
    2:59:13 PM  Info    End of Month 12: Chase balance = $383.29, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info         Payment of $152.55 made to Chase
    2:59:13 PM  Info    End of Month 13: Chase balance = $236.81, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info         Payment of $152.55 made to Chase
    2:59:13 PM  Info    End of Month 14: Chase balance = $88.01, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info         Payment of $89.40 made to Chase
    2:59:13 PM  Info             Chase is paid off
    2:59:13 PM  Info    End of Month 15: Chase balance = $0.00, US Bank balance = $0.00, AMEX balance = $0.00
    2:59:13 PM  Info    Total Months to pay off: 15
    2:59:13 PM  Info    Total Interest Paid: $233.86
    2:59:16 PM  Notice  Execution completed