From 3d303b442ab8d664c56fd8709d970f86c7eed83f Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Jul 2021 10:40:29 -0400 Subject: [PATCH 1/2] Loan code cleanup and refactoring --- MekHQ/src/mekhq/campaign/Campaign.java | 14 +- .../src/mekhq/campaign/finances/Finances.java | 22 +- MekHQ/src/mekhq/campaign/finances/Loan.java | 615 +++++++++--------- .../gui/adapter/LoanTableMouseAdapter.java | 9 +- MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java | 43 +- .../mekhq/gui/dialog/PayCollateralDialog.java | 6 +- MekHQ/src/mekhq/gui/model/LoanTableModel.java | 10 +- 7 files changed, 356 insertions(+), 363 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 261385ba0f..bf59593e2d 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -6196,27 +6196,27 @@ public PartInventory getPartInventory(Part part) { } public void addLoan(Loan loan) { - addReport("You have taken out loan " + loan.getDescription() + addReport("You have taken out loan " + loan + ". Your account has been credited " + loan.getPrincipal().toAmountAndSymbolString() + " for the principal amount."); finances.addLoan(loan); MekHQ.triggerEvent(new LoanNewEvent(loan)); finances.credit(loan.getPrincipal(), Transaction.C_LOAN_PRINCIPAL, - "loan principal for " + loan.getDescription(), getLocalDate()); + "loan principal for " + loan, getLocalDate()); } public void payOffLoan(Loan loan) { - if (finances.debit(loan.getRemainingValue(), - Transaction.C_LOAN_PAYMENT, "loan payoff for " + loan.getDescription(), getLocalDate())) { + if (finances.debit(loan.determineRemainingValue(), + Transaction.C_LOAN_PAYMENT, "loan payoff for " + loan, getLocalDate())) { addReport("You have paid off the remaining loan balance of " - + loan.getRemainingValue().toAmountAndSymbolString() - + "on " + loan.getDescription()); + + loan.determineRemainingValue().toAmountAndSymbolString() + + "on " + loan); finances.removeLoan(loan); MekHQ.triggerEvent(new LoanPaidEvent(loan)); } else { addReport("You do not have enough funds to pay off " - + loan.getDescription() + ""); + + loan + ""); } } diff --git a/MekHQ/src/mekhq/campaign/finances/Finances.java b/MekHQ/src/mekhq/campaign/finances/Finances.java index 8810b4214a..1dcb89650b 100644 --- a/MekHQ/src/mekhq/campaign/finances/Finances.java +++ b/MekHQ/src/mekhq/campaign/finances/Finances.java @@ -101,7 +101,7 @@ public Money getBalance() { public Money getLoanBalance() { Money balance = Money.zero(); - return balance.plus(loans.stream().map(Loan::getRemainingValue).collect(Collectors.toList())); + return balance.plus(loans.stream().map(Loan::determineRemainingValue).collect(Collectors.toList())); } public boolean isInDebt() { @@ -186,8 +186,8 @@ public void writeToXml(PrintWriter pw1, int indent) { for (Transaction trans : getAllTransactions()) { trans.writeToXml(pw1, indent + 1); } - for (Loan loan : getAllLoans()) { - loan.writeToXml(pw1, indent + 1); + for (final Loan loan : getAllLoans()) { + loan.writeToXML(pw1, indent + 1); } for (Asset asset : getAllAssets()) { asset.writeToXML(pw1, indent + 1); @@ -358,12 +358,11 @@ public void newDay(Campaign campaign) { for (Loan loan : loans) { if (loan.checkLoanPayment(campaign.getLocalDate())) { if (debit(loan.getPaymentAmount(), Transaction.C_LOAN_PAYMENT, - String.format(resourceMap.getString("Loan.title"), loan.getDescription()), + String.format(resourceMap.getString("Loan.title"), loan), campaign.getLocalDate())) { campaign.addReport(String.format( resourceMap.getString("Loan.text"), - loan.getPaymentAmount().toAmountAndSymbolString(), - loan.getDescription())); + loan.getPaymentAmount().toAmountAndSymbolString(), loan)); loan.paidLoan(); } else { campaign.addReport(String.format( @@ -375,7 +374,7 @@ public void newDay(Campaign campaign) { if (loan.getRemainingPayments() > 0) { newLoans.add(loan); } else { - campaign.addReport(String.format(resourceMap.getString("Loan.paid"), loan.getDescription())); + campaign.addReport(String.format(resourceMap.getString("Loan.paid"), loan)); } } if ((wentIntoDebt != null) && !isInDebt()) { @@ -424,12 +423,11 @@ public Money checkOverdueLoanPayments(Campaign campaign) { for (Loan loan : loans) { if (loan.isOverdue()) { if (debit(loan.getPaymentAmount(), Transaction.C_LOAN_PAYMENT, - String.format(resourceMap.getString("Loan.title"), loan.getDescription()), + String.format(resourceMap.getString("Loan.title"), loan), campaign.getLocalDate())) { campaign.addReport(String.format( resourceMap.getString("Loan.text"), - loan.getPaymentAmount().toAmountAndSymbolString(), - loan.getDescription())); + loan.getPaymentAmount().toAmountAndSymbolString(), loan)); loan.paidLoan(); } else { overdueAmount = overdueAmount.plus(loan.getPaymentAmount()); @@ -438,7 +436,7 @@ public Money checkOverdueLoanPayments(Campaign campaign) { if (loan.getRemainingPayments() > 0) { newLoans.add(loan); } else { - campaign.addReport(String.format(resourceMap.getString("Loan.paid"), loan.getDescription())); + campaign.addReport(String.format(resourceMap.getString("Loan.paid"), loan)); } } loans = newLoans; @@ -474,7 +472,7 @@ public int getFailedCollateral() { public Money getTotalLoanCollateral() { Money amount = Money.zero(); - return amount.plus(loans.stream().map(Loan::getCollateralAmount).collect(Collectors.toList())); + return amount.plus(loans.stream().map(Loan::determineCollateralAmount).collect(Collectors.toList())); } public Money getTotalAssetValue() { diff --git a/MekHQ/src/mekhq/campaign/finances/Loan.java b/MekHQ/src/mekhq/campaign/finances/Loan.java index e1cfd56c22..f8792ae5b2 100644 --- a/MekHQ/src/mekhq/campaign/finances/Loan.java +++ b/MekHQ/src/mekhq/campaign/finances/Loan.java @@ -1,8 +1,8 @@ /* * Loan.java * - * Copyright (c) 2009 - Jay Lawson . All rights reserved. - * Copyright (c) 2020 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2009 - Jay Lawson . All Rights Reserved. + * Copyright (c) 2020-2021 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,7 +21,15 @@ */ package mekhq.campaign.finances; +import megamek.common.Compute; +import mekhq.MekHQ; +import mekhq.MekHqXmlUtil; +import mekhq.Utilities; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + import java.io.PrintWriter; +import java.io.Serializable; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; @@ -29,380 +37,311 @@ import java.util.List; import java.util.Objects; -import megamek.common.Compute; -import mekhq.MekHqXmlSerializable; -import mekhq.MekHqXmlUtil; -import mekhq.Utilities; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - /** + * TODO : Update loan baseline based on latest Campaign Operations Rules + * TODO : Move MADE_UP_INSTITUTIONS to data + * TODO : Move determineFirstPaymentDate to FinancialTerm Enum * @author Jay Lawson */ -public class Loan implements MekHqXmlSerializable { - // If you add more Canon institutions, please add them at the beginning and change the next line. - // The first four of these are Canon, the rest are made up. - private static final List madeUpInstitutions = Arrays.asList("Southern Bank and Trust" /* Canon */, "The Alliance Reserve Bank" /* Canon */, - "Capellan Commonality Bank" /* Canon */, "Potwin Bank and Trust" /* Canon */, "ComStar Reserve", "Federated Employees Union", - "Bank of Oriente", "New Avalon Interstellar Bank", "Federated Boeing Credit Union", "First Commonwealth Bank", - "Donegal Bank and Trust", "Defiance Industries Credit Union", "Superior Bank of Sarna", "St. Ives Bank and Trust", - "Luthien Bank of the Dragon", "Golden Bank of Sian", "Rasalhague National Bank", "Canopus Federal Reserve", - "Concordat Bank and Trust", "Outworlds Alliance National Bank", "Hegemony Bank and Trust", - "Andurien First National Bank"); +public class Loan implements Serializable { + //region Variable Declarations + private static final long serialVersionUID = -1120267466243022054L; private String institution; - private String refNumber; + private String referenceNumber; private Money principal; private int rate; - private LocalDate nextPayment; private int years; - private int schedule; + private int financialTerm; private int collateral; - private int nPayments; - private Money payAmount; - private Money collateralValue; + private int remainingPayments; + private Money paymentAmount; + private LocalDate nextPayment; private boolean overdue; + // TODO : I shouldn't be inline but instead part of a data file + private static final List MADE_UP_INSTITUTIONS = Arrays.asList( + "Southern Bank and Trust" /* Canon */, "The Alliance Reserve Bank" /* Canon */, + "Capellan Commonality Bank" /* Canon */, "Potwin Bank and Trust" /* Canon */, + "ComStar Reserve", "Federated Employees Union", "Bank of Oriente", + "New Avalon Interstellar Bank", "Federated Boeing Credit Union", + "First Commonwealth Bank", "Donegal Bank and Trust", "Defiance Industries Credit Union", + "Superior Bank of Sarna", "St. Ives Bank and Trust", "Luthien Bank of the Dragon", + "Golden Bank of Sian", "Rasalhague National Bank", "Canopus Federal Reserve", + "Concordat Bank and Trust", "Outworlds Alliance National Bank", + "Hegemony Bank and Trust", "Andurien First National Bank"); + //endregion Variable Declarations + + //region Constructors public Loan() { //don't do anything, this is for loading } - public Loan(Money p, int r, int c, int y, int s, LocalDate today) { - this(p, r, c, y, s, today, Utilities.getRandomItem(madeUpInstitutions), randomRefNumber()); + public Loan(final Money principal, final int rate, final int years, final int financialTerm, + final int collateral, final LocalDate today) { + this(Utilities.getRandomItem(MADE_UP_INSTITUTIONS), randomReferenceNumber(), principal, rate, + years, financialTerm, collateral, today); } - public Loan(Money p, int r, int c, int y, int s, LocalDate today, String i, String ref) { - this.principal = p; - this.rate = r; - this.collateral = c; - this.years = y; - this.schedule = s; - nextPayment = today; - setFirstPaymentDate(); - calculateAmortization(); - institution = i; - refNumber = ref; - overdue = false; - } - - public void setFirstPaymentDate() { - //We are going to assume a standard grace period, so you have to go - //through the first full time length (not partial) before your first - //payment - - // First, we need to increase the number of days by one - nextPayment = nextPayment.plusDays(1); - - // Finally, we use that and the schedule type to determine the length including the grace period - switch (schedule) { - case Finances.SCHEDULE_BIWEEKLY: - nextPayment = nextPayment.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)).plusWeeks(2); - break; - case Finances.SCHEDULE_MONTHLY: - if (nextPayment.getDayOfMonth() != 1) { - nextPayment = nextPayment.with(TemporalAdjusters.firstDayOfNextMonth()); - } - nextPayment = nextPayment.plusMonths(1); - break; - case Finances.SCHEDULE_QUARTERLY: - if (nextPayment.getDayOfMonth() != 1) { - nextPayment = nextPayment.with(TemporalAdjusters.firstDayOfNextMonth()); - } - nextPayment = nextPayment.plusMonths(3); - break; - case Finances.SCHEDULE_YEARLY: - if (nextPayment.getDayOfYear() != 1) { - nextPayment = nextPayment.with(TemporalAdjusters.firstDayOfNextYear()); - } - nextPayment = nextPayment.plusYears(1); - break; - } - } + public Loan(final String institution, final String referenceNumber, final Money principal, + final int rate, final int years, final int financialTerm, final int collateral, + final LocalDate today) { + setInstitution(institution); + setReferenceNumber(referenceNumber); + setPrincipal(principal); + setRate(rate); + setYears(years); + setFinancialTerm(financialTerm); + setCollateral(collateral); + setNextPayment(determineFirstPaymentDate(today)); + setOverdue(false); - public void calculateAmortization() { - //figure out actual rate from APR - int denom = 1; - switch (schedule) { - case Finances.SCHEDULE_BIWEEKLY: - denom = 26; - break; - case Finances.SCHEDULE_MONTHLY: - denom = 12; - break; - case Finances.SCHEDULE_QUARTERLY: - denom = 4; - break; - case Finances.SCHEDULE_YEARLY: - denom = 1; - break; - } - double r = ((double) rate / 100.0) / denom; - nPayments = years * denom; - payAmount = principal.multipliedBy(r * Math.pow(1 + r, nPayments)).dividedBy(Math.pow(1 + r, nPayments) - 1); - collateralValue = principal.multipliedBy(collateral).dividedBy(100); - } - - public Money getPrincipal() { - return principal; - } - - public void setPrincipal(Money principal) { - this.principal = principal; + calculateAmortization(); } + //endregion Constructors + //region Getters/Setters public String getInstitution() { return institution; } - public void setInstitution(String s) { - this.institution = s; + public void setInstitution(final String institution) { + this.institution = institution; } - public String getRefNumber() { - return refNumber; + public String getReferenceNumber() { + return referenceNumber; } - public void setRefNumber(String s) { - this.refNumber = s; + public void setReferenceNumber(final String referenceNumber) { + this.referenceNumber = referenceNumber; } - public boolean isOverdue() { - return overdue; + public Money getPrincipal() { + return principal; } - public void setOverdue(boolean b) { - overdue = b; + public void setPrincipal(final Money principal) { + this.principal = principal; } public int getRate() { return rate; } - public void setRate(int rate) { + public void setRate(final int rate) { this.rate = rate; } - public int getInterestRate() { - return rate; - } - - public boolean checkLoanPayment(LocalDate today) { - return (today.equals(nextPayment) || (today.isAfter(nextPayment)) && (nPayments > 0)); + public int getYears() { + return years; } - public Money getPaymentAmount() { - return payAmount; + public void setYears(final int years) { + this.years = years; } - public Money getRemainingValue() { - return payAmount.multipliedBy(nPayments); + public int getFinancialTerm() { + return financialTerm; } - public void paidLoan() { - switch (schedule) { - case Finances.SCHEDULE_BIWEEKLY: - setNextPayment(getNextPayment().plusWeeks(2)); - break; - case Finances.SCHEDULE_MONTHLY: - setNextPayment(getNextPayment().plusMonths(1)); - break; - case Finances.SCHEDULE_QUARTERLY: - setNextPayment(getNextPayment().plusMonths(3)); - break; - case Finances.SCHEDULE_YEARLY: - setNextPayment(getNextPayment().plusYears(1)); - break; - } - nPayments--; - overdue = false; + public void setFinancialTerm(final int financialTerm) { + this.financialTerm = financialTerm; } - public int getRemainingPayments() { - return nPayments; + public int getCollateral() { + return collateral; } - public String getDescription() { - return institution + " " + refNumber; + public void setCollateral(final int collateral) { + this.collateral = collateral; } - public LocalDate getNextPayment() { - return nextPayment; + public int getRemainingPayments() { + return remainingPayments; } - public void setNextPayment(LocalDate nextPayment) { - this.nextPayment = nextPayment; + public void setRemainingPayments(final int remainingPayments) { + this.remainingPayments = remainingPayments; } - public int getPaymentSchedule() { - return schedule; + public Money getPaymentAmount() { + return paymentAmount; } - public int getSchedule() { - return schedule; + public void setPaymentAmount(final Money paymentAmount) { + this.paymentAmount = paymentAmount; } - public void setSchedule(int schedule) { - this.schedule = schedule; + public LocalDate getNextPayment() { + return nextPayment; } - public int getCollateralPercent() { - return collateral; + public void setNextPayment(final LocalDate nextPayment) { + this.nextPayment = nextPayment; } - public int getCollateral() { - return collateral; + public boolean isOverdue() { + return overdue; } - public void setCollateral(int collateral) { - this.collateral = collateral; + public void setOverdue(final boolean overdue) { + this.overdue = overdue; } + //endregion Getters/Setters - public int getnPayments() { - return nPayments; + //region Determination Methods + public Money determineCollateralAmount() { + return getPrincipal().multipliedBy(getCollateral()).dividedBy(100); } - public void setnPayments(int nPayments) { - this.nPayments = nPayments; + public Money determineRemainingValue() { + return getPaymentAmount().multipliedBy(getRemainingPayments()); } - public Money getPayAmount() { - return payAmount; - } + public LocalDate determineFirstPaymentDate(final LocalDate today) { + // TODO : Move to FinancialTerm Enum + // We are going to assume a standard grace period, so you have to go + // through the first full time length (not partial) before your first + // payment - public void setPayAmount(Money payAmount) { - this.payAmount = payAmount; - } + // First, we need to increase the number of days by one + LocalDate date = today.plusDays(1); - public Money getCollateralAmount() { - return collateralValue; - } + // Finally, we use that and the schedule type to determine the length including the grace period + switch (getFinancialTerm()) { + case Finances.SCHEDULE_BIWEEKLY: + date = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)).plusWeeks(2); + break; + case Finances.SCHEDULE_MONTHLY: + if (date.getDayOfMonth() != 1) { + date = date.with(TemporalAdjusters.firstDayOfNextMonth()); + } + date = date.plusMonths(1); + break; + case Finances.SCHEDULE_QUARTERLY: + if (date.getDayOfMonth() != 1) { + date = date.with(TemporalAdjusters.firstDayOfNextMonth()); + } + date = date.plusMonths(3); + break; + case Finances.SCHEDULE_YEARLY: + if (date.getDayOfYear() != 1) { + date = date.with(TemporalAdjusters.firstDayOfNextYear()); + } + date = date.plusYears(1); + break; + } - public Money getCollateralValue() { - return collateralValue; + return date; } + //endregion Determination Methods - public void setCollateralValue(Money collateralValue) { - this.collateralValue = collateralValue; - } + public void calculateAmortization() { + // figure out actual rate from APR + // TODO : FinancialTerm Enum + final int paymentsPerYear; + switch (getFinancialTerm()) { + case Finances.SCHEDULE_BIWEEKLY: + paymentsPerYear = 26; + break; + case Finances.SCHEDULE_MONTHLY: + paymentsPerYear = 12; + break; + case Finances.SCHEDULE_QUARTERLY: + paymentsPerYear = 4; + break; + case Finances.SCHEDULE_YEARLY: + default: + paymentsPerYear = 1; + break; + } - public static List getMadeupInstitutions() { - return madeUpInstitutions; - } + final int numberOfPayments = getYears() * paymentsPerYear; + final double periodicRate = (getRate() / 100.0) / paymentsPerYear; - public int getYears() { - return years; + setRemainingPayments(numberOfPayments); + setPaymentAmount(getPrincipal() + .multipliedBy(periodicRate * Math.pow(1 + periodicRate, numberOfPayments)) + .dividedBy(Math.pow(1 + periodicRate, numberOfPayments) - 1)); } - public void setYears(int years) { - this.years = years; + public void paidLoan() { + switch (getFinancialTerm()) { + case Finances.SCHEDULE_BIWEEKLY: + setNextPayment(getNextPayment().plusWeeks(2)); + break; + case Finances.SCHEDULE_MONTHLY: + setNextPayment(getNextPayment().plusMonths(1)); + break; + case Finances.SCHEDULE_QUARTERLY: + setNextPayment(getNextPayment().plusMonths(3)); + break; + case Finances.SCHEDULE_YEARLY: + setNextPayment(getNextPayment().plusYears(1)); + break; + } + setRemainingPayments(getRemainingPayments() - 1); + setOverdue(false); } - @Override - public void writeToXml(PrintWriter pw1, int indent) { - MekHqXmlUtil.writeSimpleXMLOpenIndentedLine(pw1, indent, "loan"); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "institution", institution); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "refNumber", refNumber); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "principal", principal.toXmlString()); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "rate", rate); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "years", years); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "schedule", schedule); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "collateral", collateral); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "nPayments", nPayments); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "payAmount", payAmount.toXmlString()); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "collateralValue", collateralValue.toXmlString()); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "overdue", overdue); - MekHqXmlUtil.writeSimpleXmlTag(pw1, indent + 1, "nextPayment", - MekHqXmlUtil.saveFormattedDate(getNextPayment())); - MekHqXmlUtil.writeSimpleXMLCloseIndentedLine(pw1, indent, "loan"); - } - - public static Loan generateInstanceFromXML(Node wn) { - Loan retVal = new Loan(); - - NodeList nl = wn.getChildNodes(); - for (int x=0; x 0); } - public static Loan getBaseLoanFor(int rating, LocalDate date) { - //we are going to treat the score from StellarOps the same as dragoons score - //TODO: pirates and government forces + public static Loan getBaseLoan(final int rating, final LocalDate date) { + // we are going to treat the score from StellarOps the same as dragoons score + // TODO: pirates and government forces if (rating <= 0) { - return new Loan(Money.of(10000000), 35, 80, 1, Finances.SCHEDULE_MONTHLY, date); + return new Loan(Money.of(10000000), 35, 1, Finances.SCHEDULE_MONTHLY, 80, date); } else if (rating < 5) { - return new Loan(Money.of(10000000), 20, 60, 1, Finances.SCHEDULE_MONTHLY, date); + return new Loan(Money.of(10000000), 20, 1, Finances.SCHEDULE_MONTHLY, 60, date); } else if (rating < 10) { - return new Loan(Money.of(10000000), 15, 40, 2, Finances.SCHEDULE_MONTHLY, date); + return new Loan(Money.of(10000000), 15, 2, Finances.SCHEDULE_MONTHLY, 40, date); } else if (rating < 14) { - return new Loan(Money.of(10000000), 10, 25, 3, Finances.SCHEDULE_MONTHLY, date); + return new Loan(Money.of(10000000), 10, 3, Finances.SCHEDULE_MONTHLY, 25, date); } else { - return new Loan(Money.of(10000000), 7, 15, 5, Finances.SCHEDULE_MONTHLY, date); + return new Loan(Money.of(10000000), 7, 5, Finances.SCHEDULE_MONTHLY, 15, date); } } - /* These two bracket methods below return a 3=length integer array + /* + * These two bracket methods below return a 3=length integer array * of (minimum, starting, maximum). They are based on the StellarOps beta, * but since that document doesn't have minimum collateral amounts, we * just make up some numbers there (note that the minimum collateral also * determines the maximum interest) */ - public static int[] getInterestBracket(int rating) { + public static int[] getInterestBracket(final int rating) { if (rating <= 0) { - return new int[]{15,35,75}; + return new int[]{15, 35, 75}; } else if (rating < 5) { - return new int[]{10,20,60}; + return new int[]{10, 20, 60}; } else if (rating < 10) { - return new int[]{5,15,35}; + return new int[]{5, 15, 35}; } else if (rating < 14) { - return new int[]{5,10,25}; + return new int[]{5, 10, 25}; } else { - return new int[]{4,7,17}; + return new int[]{4, 7, 17}; } } - public static int[] getCollateralBracket(int rating) { + public static int[] getCollateralBracket(final int rating) { if (rating <= 0) { - return new int[]{60,80,380}; + return new int[]{60, 80, 380}; } else if (rating < 5) { - return new int[]{40,60,210}; + return new int[]{40, 60, 210}; } else if (rating < 10) { - return new int[]{20,40,140}; + return new int[]{20, 40, 140}; } else if (rating < 14) { - return new int[]{10,25,75}; + return new int[]{10, 25, 75}; } else { - return new int[]{5,15,35}; + return new int[]{5, 15, 35}; } } - public static int getMaxYears(int rating) { + public static int getMaxYears(final int rating) { if (rating < 5) { return 1; } else if (rating < 9) { @@ -414,91 +353,149 @@ public static int getMaxYears(int rating) { } } - public static int getCollateralIncrement(boolean interestPositive, int rating) { + public static int getCollateralIncrement(final int rating, final boolean interestPositive) { if (rating < 5) { - if (interestPositive) { - return 2; - } else { - return 15; - } + return interestPositive ? 2 : 15; } else { - if (interestPositive) { - return 1; - } else { - return 10; - } + return interestPositive ? 1 : 10; } } - public static int recalculateCollateralFromInterest(int interest, int rating) { - int interestDiff = interest - getInterestBracket(rating)[1]; + public static int recalculateCollateralFromInterest(final int rating, final int interest) { + final int interestDiff = interest - getInterestBracket(rating)[1]; if (interestDiff < 0) { - return getCollateralBracket(rating)[1] + (Math.abs(interestDiff) * getCollateralIncrement(false, rating)); + return getCollateralBracket(rating)[1] + (Math.abs(interestDiff) * getCollateralIncrement(rating, false)); } else { - return getCollateralBracket(rating)[1] - (interestDiff/getCollateralIncrement(true, rating)); + return getCollateralBracket(rating)[1] - (interestDiff / getCollateralIncrement(rating, true)); } } - public static int recalculateInterestFromCollateral(int collateral, int rating) { - int collateralDiff = collateral - getCollateralBracket(rating)[1]; + public static int recalculateInterestFromCollateral(final int rating, final int collateral) { + final int collateralDiff = collateral - getCollateralBracket(rating)[1]; if (collateralDiff < 0) { - return getInterestBracket(rating)[1] + (getCollateralIncrement(true, rating) * Math.abs(collateralDiff)); + return getInterestBracket(rating)[1] + (getCollateralIncrement(rating, true) * Math.abs(collateralDiff)); } else { - return getInterestBracket(rating)[1] - (collateralDiff/getCollateralIncrement(false, rating)); + return getInterestBracket(rating)[1] - (collateralDiff / getCollateralIncrement(rating, false)); } } - public static String randomRefNumber() { + public static String randomReferenceNumber() { int length = Compute.randomInt(5) + 6; - StringBuilder buffer = new StringBuilder(); + final StringBuilder stringBuilder = new StringBuilder(); int nSinceSlash = 2; while (length > 0) { if (Compute.randomInt(9) < 3) { - buffer.append((char) (Compute.randomInt(26) + 'A')); + stringBuilder.append((char) (Compute.randomInt(26) + 'A')); } else { - buffer.append(Compute.randomInt(9)); + stringBuilder.append(Compute.randomInt(9)); } length--; nSinceSlash++; //Check for a random slash if ((length > 0) && (Compute.randomInt(9) < 3) && (nSinceSlash >= 3)) { - buffer.append("-"); + stringBuilder.append("-"); nSinceSlash = 0; } } - return buffer.toString(); + return stringBuilder.toString(); + } + + //region File I/O + public void writeToXML(final PrintWriter pw, int indent) { + MekHqXmlUtil.writeSimpleXMLOpenIndentedLine(pw, indent++, "loan"); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "institution", getInstitution()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "referenceNumber", getReferenceNumber()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "principal", getPrincipal().toXmlString()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "rate", getRate()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "years", getYears()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "financialTerm", getFinancialTerm()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "collateral", getCollateral()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "remainingPayments", getRemainingPayments()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "paymentAmount", getPaymentAmount().toXmlString()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "nextPayment", getNextPayment()); + MekHqXmlUtil.writeSimpleXmlTag(pw, indent, "overdue", isOverdue()); + MekHqXmlUtil.writeSimpleXMLCloseIndentedLine(pw, --indent, "loan"); + } + + public static Loan generateInstanceFromXML(final Node wn) { + final Loan loan = new Loan(); + final NodeList nl = wn.getChildNodes(); + for (int x = 0; x < nl.getLength(); x++) { + final Node wn2 = nl.item(x); + try { + if (wn2.getNodeName().equalsIgnoreCase("institution")) { + loan.setInstitution(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("referenceNumber")) { + loan.setReferenceNumber(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("principal")) { + loan.setPrincipal(Money.fromXmlString(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("rate")) { + loan.setRate(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("years")) { + loan.setYears(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("financialTerm")) { + loan.setFinancialTerm(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("collateral")) { + loan.setCollateral(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("remainingPayments")) { + loan.setRemainingPayments(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("paymentAmount")) { + loan.setPaymentAmount(Money.fromXmlString(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("nextPayment")) { + loan.setNextPayment(MekHqXmlUtil.parseDate(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("overdue")) { + loan.setOverdue(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("payAmount")) { // Legacy - 0.49.3 Removal + loan.setPaymentAmount(Money.fromXmlString(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("refNumber")) { // Legacy - 0.49.3 Removal + loan.setReferenceNumber(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("schedule")) { // Legacy - 0.49.3 Removal + loan.setFinancialTerm(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("nPayments")) { // Legacy - 0.49.3 Removal + loan.setRemainingPayments(Integer.parseInt(wn2.getTextContent().trim())); + } + } catch (Exception e) { + MekHQ.getLogger().error(e); + } + } + return loan; } + //endregion File I/O @Override - public boolean equals(Object obj) { - if (!(obj instanceof Loan)) { + public String toString() { + return getInstitution() + " " + getReferenceNumber(); + } + + @Override + public boolean equals(final Object other) { + if (other == null) { + return false; + } else if (this == other) { + return true; + } else if (other instanceof Loan) { + final Loan loan = (Loan) other; + return getInstitution().equals(loan.getInstitution()) + && getReferenceNumber().equals(loan.getReferenceNumber()) + && getPrincipal().equals(loan.getPrincipal()) + && (getRate() == loan.getRate()) + && (getYears() == loan.getYears()) + && (getFinancialTerm() == loan.getFinancialTerm()) + && (getCollateral() == loan.getCollateral()) + && (getRemainingPayments() == loan.getRemainingPayments()) + && getPaymentAmount().equals(loan.getPaymentAmount()) + && getNextPayment().isEqual(loan.getNextPayment()) + && (isOverdue() == loan.isOverdue()); + } else { return false; } - Loan loan = (Loan) obj; - return this.getDescription().equals(loan.getDescription()) - && this.getInterestRate() == loan.getInterestRate() - && this.getCollateralAmount().equals(loan.getCollateralAmount()) - && this.getCollateralPercent() == loan.getCollateralPercent() - && this.getNextPayment().equals(loan.getNextPayment()) - && this.getYears() == loan.getYears() - && this.getPrincipal().equals(loan.getPrincipal()) - && this.getPaymentSchedule() == loan.getPaymentSchedule() - && this.getRemainingPayments() == loan.getRemainingPayments() - && this.getRemainingValue().equals(loan.getRemainingValue()); } @Override public int hashCode() { - return Objects.hash(getDescription(), - getInterestRate(), - getCollateralAmount(), - getCollateralPercent(), - getNextPayment(), - getYears(), - getPrincipal(), - getPaymentSchedule(), - getRemainingPayments(), - getRemainingValue()); + return Objects.hash(getInstitution(), getReferenceNumber(), getPrincipal(), getRate(), + getYears(), getFinancialTerm(), getCollateral(), getRemainingPayments(), + getPaymentAmount(), getNextPayment(), isOverdue()); } } diff --git a/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java index 0405513e0d..c6413491c8 100644 --- a/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/LoanTableMouseAdapter.java @@ -29,7 +29,7 @@ protected LoanTableMouseAdapter(CampaignGUI gui, JTable loanTable, LoanTableMode this.loanTable = loanTable; this.loanModel = loanModel; } - + public static void connect(CampaignGUI gui, JTable loanTable, LoanTableModel loanModel) { new LoanTableMouseAdapter(gui, loanTable, loanModel) .connect(loanTable); @@ -51,8 +51,7 @@ public void actionPerformed(ActionEvent action) { .showConfirmDialog( null, "Defaulting on this loan will affect your unit rating the same as a contract breach.\nDo you wish to proceed?", - "Default on " + selectedLoan.getDescription() - + "?", JOptionPane.YES_NO_OPTION)) { + "Default on " + selectedLoan + "?", JOptionPane.YES_NO_OPTION)) { PayCollateralDialog pcd = new PayCollateralDialog( gui.getFrame(), true, gui.getCampaign(), selectedLoan); pcd.setVisible(true); @@ -101,9 +100,9 @@ protected Optional createPopupMenu() { JMenu menu = null; // **lets fill the pop up menu**// menuItem = new JMenuItem("Pay Off Full Balance (" - + loan.getRemainingValue().toAmountAndSymbolString() + ")"); + + loan.determineRemainingValue().toAmountAndSymbolString() + ")"); menuItem.setActionCommand("PAY_BALANCE"); - menuItem.setEnabled(gui.getCampaign().getFunds().isGreaterOrEqualThan(loan.getRemainingValue())); + menuItem.setEnabled(gui.getCampaign().getFunds().isGreaterOrEqualThan(loan.determineRemainingValue())); menuItem.addActionListener(this); popup.add(menuItem); menuItem = new JMenuItem("Default on This Loan"); diff --git a/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java b/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java index 9dfc8e3bb4..5bf3b08f14 100644 --- a/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java @@ -110,7 +110,7 @@ public NewLoanDialog(java.awt.Frame parent, boolean modal, Campaign c) { this.numberFormatter = new NumberFormatter(NumberFormat.getInstance()); IUnitRating unitRating = c.getUnitRating(); rating = unitRating.getModifier(); - loan = Loan.getBaseLoanFor(rating, campaign.getLocalDate()); + loan = Loan.getBaseLoan(rating, campaign.getLocalDate()); maxCollateralValue = campaign.getFinances().getMaxCollateral(campaign); initComponents(); setLocationRelativeTo(parent); @@ -161,7 +161,7 @@ public void changeInstitution() { loan.setInstitution(txtName.getText()); } }); - txtNumber = new javax.swing.JTextField(loan.getRefNumber()); + txtNumber = new javax.swing.JTextField(loan.getReferenceNumber()); txtNumber.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { changeRefNumber(); @@ -176,7 +176,7 @@ public void insertUpdate(DocumentEvent e) { } public void changeRefNumber() { - loan.setRefNumber(txtNumber.getText()); + loan.setReferenceNumber(txtNumber.getText()); } }); @@ -262,7 +262,7 @@ public void changeRefNumber() { scheduleModel.addElement(Finances.getScheduleName(i)); } choiceSchedule = new JComboBox<>(scheduleModel); - choiceSchedule.setSelectedIndex(loan.getPaymentSchedule()); + choiceSchedule.setSelectedIndex(loan.getFinancialTerm()); choiceSchedule.addActionListener(this); @@ -524,17 +524,16 @@ private void setUpInfo() { private void refreshLoan(Money principal) { // Modify loan settings + loan.setInstitution(txtName.getText()); + loan.setReferenceNumber(txtNumber.getText()); loan.setPrincipal(principal); loan.setRate(sldInterest.getValue()); + loan.setYears(sldLength.getValue()); + loan.setFinancialTerm(choiceSchedule.getSelectedIndex()); loan.setCollateral(sldCollateral.getValue()); - loan.setYears(sldLength.getValue()); - loan.setSchedule(choiceSchedule.getSelectedIndex()); - loan.setInstitution(txtName.getText()); - loan.setRefNumber(txtNumber.getText()); + loan.setNextPayment(loan.determineFirstPaymentDate(campaign.getLocalDate())); // Recalculate information based on settings - loan.setNextPayment(campaign.getLocalDate()); - loan.setFirstPaymentDate(); loan.calculateAmortization(); // Refresh dialog values @@ -544,23 +543,23 @@ private void refreshLoan(Money principal) { private void refreshValues() { try { txtPrincipal.setText(loan.getPrincipal().toAmountAndSymbolString()); - lblAPR.setText(loan.getInterestRate() + "%"); - lblCollateralPct.setText(loan.getCollateralPercent() + "%"); + lblAPR.setText(loan.getRate() + "%"); + lblCollateralPct.setText(loan.getCollateral() + "%"); lblYears.setText(loan.getYears() + " years"); - lblSchedule.setText(Finances.getScheduleName(loan.getPaymentSchedule())); + lblSchedule.setText(Finances.getScheduleName(loan.getFinancialTerm())); lblPrincipal.setText(loan.getPrincipal().toAmountAndSymbolString()); lblFirstPayment.setText(MekHQ.getMekHQOptions().getDisplayFormattedDate(loan.getNextPayment())); lblPayAmount.setText(loan.getPaymentAmount().toAmountAndSymbolString()); lblNPayment.setText(numberFormatter.valueToString(loan.getRemainingPayments())); - lblTotalPayment.setText(loan.getRemainingValue().toAmountAndSymbolString()); - lblCollateralAmount.setText(loan.getCollateralAmount().toAmountAndSymbolString()); + lblTotalPayment.setText(loan.determineRemainingValue().toAmountAndSymbolString()); + lblCollateralAmount.setText(loan.determineCollateralAmount().toAmountAndSymbolString()); } catch (Exception ex) { MekHQ.getLogger().error(ex); } } private void addLoan() { - if (maxCollateralValue.isLessThan(loan.getCollateralAmount())) { + if (maxCollateralValue.isLessThan(loan.determineCollateralAmount())) { JOptionPane.showMessageDialog( frame, resourceMap.getString("addLoanErrorMessage.text"), @@ -579,7 +578,7 @@ private void cancel() { private void setSliders() { if (campaign.getCampaignOptions().useLoanLimits()) { int[] interest = Loan.getInterestBracket(rating); - sldInterest = new JSlider(interest[0], interest[2], loan.getInterestRate()); + sldInterest = new JSlider(interest[0], interest[2], loan.getRate()); if (interest[2] - interest[0] > 30) { sldInterest.setMajorTickSpacing(10); } else { @@ -589,7 +588,7 @@ private void setSliders() { sldInterest.setPaintLabels(true); int[] collateral = Loan.getCollateralBracket(rating); - sldCollateral = new JSlider(collateral[0], collateral[2], loan.getCollateralPercent()); + sldCollateral = new JSlider(collateral[0], collateral[2], loan.getCollateral()); if (collateral[2] - collateral[0] > 50) { sldCollateral.setMajorTickSpacing(20); } else { @@ -603,12 +602,12 @@ private void setSliders() { sldLength.setPaintTicks(true); sldLength.setPaintLabels(true); } else { - sldInterest = new JSlider(0, 100, loan.getInterestRate()); + sldInterest = new JSlider(0, 100, loan.getRate()); sldInterest.setMajorTickSpacing(10); sldInterest.setPaintTicks(true); sldInterest.setPaintLabels(true); - sldCollateral = new JSlider(0, 300, loan.getCollateralPercent()); + sldCollateral = new JSlider(0, 300, loan.getCollateral()); sldCollateral.setMajorTickSpacing(50); sldCollateral.setPaintTicks(true); sldCollateral.setPaintLabels(true); @@ -630,11 +629,11 @@ public void stateChanged(ChangeEvent e) { if (campaign.getCampaignOptions().useLoanLimits()) { if (e.getSource() == sldInterest) { sldCollateral.removeChangeListener(this); - sldCollateral.setValue(Loan.recalculateCollateralFromInterest(sldInterest.getValue(), rating)); + sldCollateral.setValue(Loan.recalculateCollateralFromInterest(rating, sldInterest.getValue())); sldCollateral.addChangeListener(this); } else if (e.getSource() == sldCollateral) { sldInterest.removeChangeListener(this); - sldInterest.setValue(Loan.recalculateInterestFromCollateral(sldCollateral.getValue(), rating)); + sldInterest.setValue(Loan.recalculateInterestFromCollateral(rating, sldCollateral.getValue())); sldInterest.addChangeListener(this); } } diff --git a/MekHQ/src/mekhq/gui/dialog/PayCollateralDialog.java b/MekHQ/src/mekhq/gui/dialog/PayCollateralDialog.java index 2c7b8cdd86..a6232e2c3a 100644 --- a/MekHQ/src/mekhq/gui/dialog/PayCollateralDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PayCollateralDialog.java @@ -303,10 +303,10 @@ private void updateAmount() { } int percent = 0; - if (loan.getCollateralAmount().isPositive()) { + if (loan.determineCollateralAmount().isPositive()) { percent = amount .multipliedBy(100) - .dividedBy(loan.getCollateralAmount()) + .dividedBy(loan.determineCollateralAmount()) .getAmount().intValue(); } @@ -316,7 +316,7 @@ private void updateAmount() { btnPay.setEnabled(true); } barAmount.setValue(percent); - barAmount.setString(amount.toAmountString() + "/" + loan.getCollateralAmount().toAmountString()); + barAmount.setString(amount.toAmountString() + "/" + loan.determineCollateralAmount().toAmountString()); } public ArrayList getUnits() { diff --git a/MekHQ/src/mekhq/gui/model/LoanTableModel.java b/MekHQ/src/mekhq/gui/model/LoanTableModel.java index ef1f757aaf..63c5343099 100644 --- a/MekHQ/src/mekhq/gui/model/LoanTableModel.java +++ b/MekHQ/src/mekhq/gui/model/LoanTableModel.java @@ -91,19 +91,19 @@ public String getColumnName(int column) { public Object getValueAt(int row, int col) { Loan loan = getLoan(row); if (col == COL_DESC) { - return loan.getDescription(); + return loan.toString(); } else if (col == COL_COLLATERAL) { - return loan.getCollateralAmount().toAmountAndSymbolString(); + return loan.determineCollateralAmount().toAmountAndSymbolString(); } else if (col == COL_VALUE) { - return loan.getRemainingValue().toAmountAndSymbolString(); + return loan.determineRemainingValue().toAmountAndSymbolString(); } else if (col == COL_PAYMENT) { return loan.getPaymentAmount().toAmountAndSymbolString(); } else if (col == COL_PRINCIPAL) { return loan.getPrincipal().toAmountAndSymbolString(); } else if (col == COL_SCHEDULE) { - return Finances.getScheduleName(loan.getPaymentSchedule()); + return Finances.getScheduleName(loan.getFinancialTerm()); } else if (col == COL_RATE) { - return loan.getInterestRate() + "%"; + return loan.getRate() + "%"; } else if (col == COL_NLEFT) { return loan.getRemainingPayments(); } else if (col == COL_NEXT_PAY) { From 05fddddc7a7f7453fc7dc6b1cbf75f5683f7429e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 30 Jul 2021 10:45:02 -0400 Subject: [PATCH 2/2] Fixing tab spacing in NewLoanDialog --- MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java b/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java index 5bf3b08f14..df60863109 100644 --- a/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/NewLoanDialog.java @@ -523,20 +523,20 @@ private void setUpInfo() { } private void refreshLoan(Money principal) { - // Modify loan settings + // Modify loan settings loan.setInstitution(txtName.getText()); loan.setReferenceNumber(txtNumber.getText()); - loan.setPrincipal(principal); - loan.setRate(sldInterest.getValue()); + loan.setPrincipal(principal); + loan.setRate(sldInterest.getValue()); loan.setYears(sldLength.getValue()); loan.setFinancialTerm(choiceSchedule.getSelectedIndex()); - loan.setCollateral(sldCollateral.getValue()); + loan.setCollateral(sldCollateral.getValue()); loan.setNextPayment(loan.determineFirstPaymentDate(campaign.getLocalDate())); - // Recalculate information based on settings - loan.calculateAmortization(); + // Recalculate information based on settings + loan.calculateAmortization(); - // Refresh dialog values + // Refresh dialog values refreshValues(); }