* Various repairs to TestAccountingInteractionInLoanWorkflow
* Fixed accrual accounting of interest (was calculating double interest).
diff --git a/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java b/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
index 5400c74..091f0d4 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AccountingFixture.java
@@ -580,6 +580,8 @@
final Action action) {
final Set<Debtor> filteredDebtors = debtors.stream().filter(x -> BigDecimal.valueOf(Double.valueOf(x.getAmount())).compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toSet());
final Set<Creditor> filteredCreditors = creditors.stream().filter(x -> BigDecimal.valueOf(Double.valueOf(x.getAmount())).compareTo(BigDecimal.ZERO) != 0).collect(Collectors.toSet());
+ if (filteredCreditors.size() == 0 && filteredDebtors.size() == 0)
+ return;
final JournalEntryMatcher specifiesCorrectJournalEntry = new JournalEntryMatcher(filteredDebtors, filteredCreditors, productIdentifier, caseIdentifier, action);
Mockito.verify(ledgerManager, Mockito.atLeastOnce()).createJournalEntry(AdditionalMatchers.and(argThat(isValid()), argThat(specifiesCorrectJournalEntry)));
diff --git a/component-test/src/main/java/io/mifos/portfolio/Fixture.java b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
index 2623acf..3a77a72 100644
--- a/component-test/src/main/java/io/mifos/portfolio/Fixture.java
+++ b/component-test/src/main/java/io/mifos/portfolio/Fixture.java
@@ -24,7 +24,6 @@
import io.mifos.portfolio.api.v1.domain.*;
import java.math.BigDecimal;
-import java.math.RoundingMode;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Consumer;
@@ -40,7 +39,7 @@
@SuppressWarnings({"WeakerAccess", "unused"})
public class Fixture {
static final int MINOR_CURRENCY_UNIT_DIGITS = 2;
- static final BigDecimal INTEREST_RATE = BigDecimal.valueOf(0.10).setScale(4, RoundingMode.HALF_EVEN);
+ static final BigDecimal INTEREST_RATE = BigDecimal.valueOf(10_00, 2);
static final BigDecimal ACCRUAL_PERIODS = BigDecimal.valueOf(365.2425);
public static final String CUSTOMER_IDENTIFIER = "alice";
@@ -124,7 +123,7 @@
final Set<AccountAssignment> accountAssignments = new HashSet<>();
ret.setAccountAssignments(accountAssignments);
ret.setCurrentState(Case.State.CREATED.name());
- ret.setInterest(BigDecimal.valueOf(10_00, 2));
+ ret.setInterest(INTEREST_RATE);
final CaseParameters caseParameters = getTestCaseParameters();
final Gson gson = new Gson();
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
index 6bf90ff..da4af76 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -72,7 +72,7 @@
private String customerLoanInterestIdentifier = null;
private String customerLoanFeeIdentifier = null;
- private BigDecimal expectedCurrentBalance = null;
+ private BigDecimal expectedCurrentPrincipal = null;
private BigDecimal interestAccrued = BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, RoundingMode.HALF_EVEN);
private BigDecimal nonLateFees = BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, RoundingMode.HALF_EVEN);
@@ -101,8 +101,12 @@
step5Disburse(
BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
- step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), null);
- step7PaybackPartialAmount(expectedCurrentBalance, today, 0, BigDecimal.ZERO);
+ step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
+ step7PaybackPartialAmount(
+ expectedCurrentPrincipal.add(nonLateFees).add(interestAccrued),
+ today,
+ 0,
+ BigDecimal.ZERO);
step8Close();
}
@@ -120,8 +124,12 @@
step5Disburse(
BigDecimal.valueOf(1_500_00, MINOR_CURRENCY_UNIT_DIGITS),
UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(15_00, MINOR_CURRENCY_UNIT_DIGITS));
- step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), null);
- step7PaybackPartialAmount(expectedCurrentBalance, today, 0, BigDecimal.ZERO);
+ step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
+ step7PaybackPartialAmount(
+ expectedCurrentPrincipal.add(nonLateFees).add(interestAccrued),
+ today,
+ 0,
+ BigDecimal.ZERO);
step8Close();
}
@@ -136,13 +144,13 @@
step5Disburse(
BigDecimal.valueOf(2_000_00, MINOR_CURRENCY_UNIT_DIGITS),
UPPER_RANGE_DISBURSEMENT_FEE_ID, BigDecimal.valueOf(20_00, MINOR_CURRENCY_UNIT_DIGITS));
- step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), null);
- final BigDecimal repayment1 = expectedCurrentBalance.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_EVEN);
+ step6CalculateInterestAccrualAndCheckForLateness(midnightToday(), BigDecimal.ZERO);
+ final BigDecimal repayment1 = expectedCurrentPrincipal.divide(BigDecimal.valueOf(2), BigDecimal.ROUND_HALF_EVEN);
step7PaybackPartialAmount(
repayment1.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN),
today,
0, BigDecimal.ZERO);
- step7PaybackPartialAmount(expectedCurrentBalance, today, 0, BigDecimal.ZERO);
+ step7PaybackPartialAmount(expectedCurrentPrincipal, today, 0, BigDecimal.ZERO);
step8Close();
}
@@ -174,10 +182,10 @@
int week = 0;
final List<BigDecimal> repayments = new ArrayList<>();
- while (expectedCurrentBalance.compareTo(BigDecimal.ZERO) > 0) {
- logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentBalance);
+ while (expectedCurrentPrincipal.compareTo(BigDecimal.ZERO) > 0) {
+ logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentPrincipal);
step6CalculateInterestAndCheckForLatenessForWeek(today, week);
- final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week+1)*7);
+ final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week+1)*7, BigDecimal.ZERO);
repayments.add(nextRepaymentAmount);
step7PaybackPartialAmount(nextRepaymentAmount, today, (week+1)*7, BigDecimal.ZERO);
week++;
@@ -190,8 +198,11 @@
}
private void checkRepaymentVariance(List<BigDecimal> repayments) {
- final BigDecimal minPayment = repayments.stream().min(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
- final BigDecimal maxPayment = repayments.stream().max(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
+ final Set<BigDecimal> setOfRepayments = new HashSet<>(repayments);
+ Assert.assertTrue("Too many payment sizes. Payments are " + repayments,
+ + setOfRepayments.size() <= 2); //There should be a standard payment size with only one slightly off.
+ final BigDecimal minPayment = setOfRepayments.stream().min(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
+ final BigDecimal maxPayment = setOfRepayments.stream().max(BigDecimal::compareTo).orElseThrow(IllegalStateException::new);
final BigDecimal delta = maxPayment.subtract(minPayment).abs();
Assert.assertTrue("Payments vary too much. Payments are " + repayments,
delta.divide(maxPayment, BigDecimal.ROUND_HALF_EVEN).compareTo(BigDecimal.valueOf(0.01)) <= 0);
@@ -212,8 +223,8 @@
int week = 0;
final int weekOfLateRepayment = 3;
final List<BigDecimal> repayments = new ArrayList<>();
- while (expectedCurrentBalance.compareTo(BigDecimal.ZERO) > 0) {
- logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentBalance);
+ while (expectedCurrentPrincipal.compareTo(BigDecimal.ZERO) > 0) {
+ logger.info("Simulating week {}. Expected current balance {}.", week, expectedCurrentPrincipal);
if (week == weekOfLateRepayment) {
final BigDecimal lateFee = BigDecimal.valueOf(31_18, MINOR_CURRENCY_UNIT_DIGITS);
step6CalculateInterestAndCheckForLatenessForRangeOfDays(
@@ -222,13 +233,13 @@
(week + 1) * 7 + 2,
8,
lateFee);
- final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7 + 2);
+ final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7 + 2, lateFee);
repayments.add(nextRepaymentAmount);
step7PaybackPartialAmount(nextRepaymentAmount, today, (week + 1) * 7 + 2, lateFee);
}
else {
step6CalculateInterestAndCheckForLatenessForWeek(today, week);
- final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7);
+ final BigDecimal nextRepaymentAmount = findNextRepaymentAmount(today, (week + 1) * 7, BigDecimal.ZERO);
repayments.add(nextRepaymentAmount);
step7PaybackPartialAmount(nextRepaymentAmount, today, (week + 1) * 7, BigDecimal.ZERO);
}
@@ -244,8 +255,13 @@
private BigDecimal findNextRepaymentAmount(
final LocalDateTime referenceDate,
- final int dayNumber) {
- AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentBalance);
+ final int dayNumber,
+ final BigDecimal lateFee) {
+ AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentPrincipal);
+ AccountingFixture.mockBalance(customerLoanInterestIdentifier, interestAccrued);
+ AccountingFixture.mockBalance(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued);
+ AccountingFixture.mockBalance(AccountingFixture.LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER, lateFee);
+ AccountingFixture.mockBalance(customerLoanFeeIdentifier, nonLateFees.add(lateFee));
final Payment nextPayment = portfolioManager.getCostComponentsForAction(
product.getIdentifier(),
@@ -397,7 +413,9 @@
customerLoanFeeIdentifier =
AccountingFixture.verifyAccountCreationMatchingDesignator(ledgerManager, customerLoanLedgerIdentifier, AccountDesignators.CUSTOMER_LOAN_FEES, AccountType.ASSET);
- expectedCurrentBalance = BigDecimal.ZERO;
+ expectedCurrentPrincipal = BigDecimal.ZERO;
+ interestAccrued = BigDecimal.ZERO;
+ nonLateFees = BigDecimal.ZERO;
}
//Approve the case, accept a loan origination fee, and prepare to disburse the loan by earmarking the funds.
@@ -405,7 +423,7 @@
final BigDecimal amount,
final String whichDisbursementFee,
final BigDecimal disbursementFeeAmount) throws InterruptedException {
- logger.info("step5Disburse");
+ logger.info("step5Disburse '{}'", amount);
checkCostComponentForActionCorrect(
product.getIdentifier(),
customerCase.getIdentifier(),
@@ -442,7 +460,8 @@
creditors.add(new Creditor(AccountingFixture.LOAN_ORIGINATION_FEES_ACCOUNT_IDENTIFIER, LOAN_ORIGINATION_FEE_AMOUNT.toPlainString()));
AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors, product.getIdentifier(), customerCase.getIdentifier(), Action.DISBURSE);
- expectedCurrentBalance = expectedCurrentBalance.add(amount);
+ expectedCurrentPrincipal = expectedCurrentPrincipal.add(amount);
+ interestAccrued = BigDecimal.ZERO;
nonLateFees = nonLateFees.add(disbursementFeeAmount);
}
@@ -472,7 +491,7 @@
step6CalculateInterestAccrualAndCheckForLateness(day, calculatedLateFee);
}
else {
- step6CalculateInterestAccrualAndCheckForLateness(day, null);
+ step6CalculateInterestAccrualAndCheckForLateness(day, BigDecimal.ZERO);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
@@ -492,15 +511,31 @@
private void step6CalculateInterestAccrualAndCheckForLateness(
final LocalDateTime forTime,
final BigDecimal calculatedLateFee) throws InterruptedException {
- logger.info("step6CalculateInterestAccrualAndCheckForLateness");
+ logger.info("step6CalculateInterestAccrualAndCheckForLateness '{}', '{}'", forTime, calculatedLateFee);
final String beatIdentifier = "alignment0";
final String midnightTimeStamp = DateConverter.toIsoString(forTime);
- AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentBalance);
+ final BigDecimal allFees = nonLateFees.add(calculatedLateFee);
+ AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentPrincipal);
+ AccountingFixture.mockBalance(customerLoanInterestIdentifier, interestAccrued);
+ AccountingFixture.mockBalance(customerLoanFeeIdentifier, allFees);
- final BigDecimal calculatedInterest = expectedCurrentBalance.multiply(Fixture.INTEREST_RATE.divide(Fixture.ACCRUAL_PERIODS, 8, BigDecimal.ROUND_HALF_EVEN))
+ final BigDecimal dailyInterestRate = Fixture.INTEREST_RATE
+ .divide(BigDecimal.valueOf(100), 8, BigDecimal.ROUND_HALF_EVEN)
+ .divide(Fixture.ACCRUAL_PERIODS, 8, BigDecimal.ROUND_HALF_EVEN);
+
+ final BigDecimal calculatedInterest = expectedCurrentPrincipal
+ .add(interestAccrued)
+ .add(allFees)
+ .multiply(dailyInterestRate)
.setScale(MINOR_CURRENCY_UNIT_DIGITS, BigDecimal.ROUND_HALF_EVEN);
+ logger.info("currentPrincipal '{}'", expectedCurrentPrincipal);
+ logger.info("interestAccrued '{}'", interestAccrued);
+ logger.info("allFees '{}'", allFees);
+ logger.info("INTEREST_RATE '{}'", Fixture.INTEREST_RATE);
+ logger.info("calculatedInterest '{}'", calculatedInterest);
+
checkCostComponentForActionCorrect(
product.getIdentifier(),
@@ -510,7 +545,7 @@
null,
new CostComponent(ChargeIdentifiers.INTEREST_ID, calculatedInterest));
- if (calculatedLateFee != null) {
+ if (calculatedLateFee.compareTo(BigDecimal.ZERO) != 0) {
checkCostComponentForActionCorrect(
product.getIdentifier(),
customerCase.getIdentifier(),
@@ -534,9 +569,6 @@
final Case customerCaseAfterStateChange = portfolioManager.getCase(product.getIdentifier(), customerCase.getIdentifier());
Assert.assertEquals(customerCaseAfterStateChange.getCurrentState(), Case.State.ACTIVE.name());
-
- interestAccrued = interestAccrued.add(calculatedInterest);
-
final Set<Debtor> debtors = new HashSet<>();
debtors.add(new Debtor(
customerLoanInterestIdentifier,
@@ -548,7 +580,22 @@
calculatedInterest.toPlainString()));
AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors, product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST);
- expectedCurrentBalance = expectedCurrentBalance.add(calculatedInterest);
+
+ if (calculatedLateFee.compareTo(BigDecimal.ZERO) != 0) {
+ final Set<Debtor> lateFeeDebtors = new HashSet<>();
+ lateFeeDebtors.add(new Debtor(
+ customerLoanFeeIdentifier,
+ calculatedLateFee.toPlainString()));
+
+ final Set<Creditor> lateFeeCreditors = new HashSet<>();
+ lateFeeCreditors.add(new Creditor(
+ AccountingFixture.LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER,
+ calculatedLateFee.toPlainString()));
+ AccountingFixture.verifyTransfer(ledgerManager, lateFeeDebtors, lateFeeCreditors, product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST);
+ }
+ interestAccrued = interestAccrued.add(calculatedInterest);
+ logger.info("Completed step6CalculateInterestAccrualAndCheckForLateness");
+ logger.info("interestAccrued '{}'", interestAccrued);
}
private void step7PaybackPartialAmount(
@@ -558,13 +605,17 @@
final BigDecimal lateFee) throws InterruptedException {
logger.info("step7PaybackPartialAmount '{}'", amount);
- final BigDecimal principal = amount.subtract(interestAccrued).subtract(lateFee).subtract(nonLateFees);
final BigDecimal allFees = lateFee.add(nonLateFees);
- AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, principal);
+ final BigDecimal principal = amount.subtract(interestAccrued).subtract(allFees);
+ AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentPrincipal);
AccountingFixture.mockBalance(customerLoanFeeIdentifier, allFees);
AccountingFixture.mockBalance(customerLoanInterestIdentifier, interestAccrued);
AccountingFixture.mockBalance(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued);
AccountingFixture.mockBalance(AccountingFixture.LATE_FEE_ACCRUAL_ACCOUNT_IDENTIFIER, lateFee);
+ logger.info("currentPrincipal '{}'", expectedCurrentPrincipal);
+ logger.info("interestAccrued '{}'", interestAccrued);
+ logger.info("allFees '{}'", allFees);
+ logger.info("lateFee '{}'", lateFee);
checkCostComponentForActionCorrect(
product.getIdentifier(),
@@ -618,15 +669,23 @@
AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors, product.getIdentifier(), customerCase.getIdentifier(), Action.ACCEPT_PAYMENT);
- expectedCurrentBalance = expectedCurrentBalance.subtract(amount).add(lateFee);
+ expectedCurrentPrincipal = expectedCurrentPrincipal.subtract(principal);
interestAccrued = BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, RoundingMode.HALF_EVEN);
nonLateFees = BigDecimal.ZERO.setScale(MINOR_CURRENCY_UNIT_DIGITS, RoundingMode.HALF_EVEN);
+ logger.info("Completed step7PaybackPartialAmount");
+ logger.info("currentPrincipal '{}'", expectedCurrentPrincipal);
+ logger.info("interestAccrued '{}'", interestAccrued);
+ logger.info("nonLateFees '{}'", nonLateFees);
}
private void step8Close() throws InterruptedException {
logger.info("step8Close");
-
- AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentBalance);
+ logger.info("currentPrincipal '{}'", expectedCurrentPrincipal);
+ logger.info("interestAccrued '{}'", interestAccrued);
+ logger.info("nonLateFees '{}'", nonLateFees);
+ AccountingFixture.mockBalance(customerLoanPrincipalIdentifier, expectedCurrentPrincipal);
+ AccountingFixture.mockBalance(customerLoanInterestIdentifier, interestAccrued);
+ AccountingFixture.mockBalance(customerLoanFeeIdentifier, nonLateFees);
checkCostComponentForActionCorrect(
product.getIdentifier(),
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java b/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
index a358757..a5468af 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestChargeDefinitions.java
@@ -56,7 +56,7 @@
.collect(Collectors.toSet());
final Set<String> expectedReadOnlyChargeDefinitionIdentifiers = Stream.of(
- ChargeIdentifiers.ALLOW_FOR_WRITE_OFF_ID,
+ //ChargeIdentifiers.ALLOW_FOR_WRITE_OFF_ID,
ChargeIdentifiers.DISBURSE_PAYMENT_ID,
ChargeIdentifiers.INTEREST_ID,
ChargeIdentifiers.REPAY_PRINCIPAL_ID,
@@ -107,7 +107,7 @@
public void shouldNotDeleteReadOnlyChargeDefinition() throws InterruptedException {
final Product product = createProduct();
- portfolioManager.deleteChargeDefinition(product.getIdentifier(), ChargeIdentifiers.ALLOW_FOR_WRITE_OFF_ID);
+ portfolioManager.deleteChargeDefinition(product.getIdentifier(), ChargeIdentifiers.INTEREST_ID);
}
@Test
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 16ddbe7..c4fe59d 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -198,14 +198,14 @@
disbursePayment.setReadOnly(true);
//TODO: Make multiple write off allowance charges.
- final ChargeDefinition writeOffAllowanceCharge = charge(
+ /*final ChargeDefinition writeOffAllowanceCharge = charge(
ALLOW_FOR_WRITE_OFF_NAME,
Action.MARK_LATE,
BigDecimal.valueOf(30),
AccountDesignators.LOAN_FUNDS_SOURCE,
AccountDesignators.ARREARS_ALLOWANCE);
writeOffAllowanceCharge.setProportionalTo(ChargeProportionalDesignator.RUNNING_BALANCE_DESIGNATOR.getValue());
- writeOffAllowanceCharge.setReadOnly(true);
+ writeOffAllowanceCharge.setReadOnly(true);*/
final ChargeDefinition interestCharge = charge(
INTEREST_NAME,
@@ -257,7 +257,7 @@
customerPrincipalRepaymentCharge.setReadOnly(true);
ret.add(disbursePayment);
- ret.add(writeOffAllowanceCharge);
+ //ret.add(writeOffAllowanceCharge);
ret.add(interestCharge);
ret.add(customerPrincipalRepaymentCharge);
ret.add(customerInterestRepaymentCharge);
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java b/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
index 2bd5675..744e82e 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/IndividualLoanService.java
@@ -153,6 +153,7 @@
final SortedSet<ScheduledCharge> scheduledChargesInPeriod = orderedScheduledChargesGroupedByPeriod.get(repaymentPeriod);
final PaymentBuilder paymentBuilder =
CostComponentService.getCostComponentsForScheduledCharges(
+ null, //Action doesn't matter because not using accrual accounting.
Collections.emptyMap(),
scheduledChargesInPeriod,
initialBalance,
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
index 3846c37..2b989e9 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderService.java
@@ -110,6 +110,7 @@
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.ACCEPT_PAYMENT,
accruedCostComponents,
chargesSplitIntoScheduledAndAccrued.get(false),
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
index 7fef484..5125b49 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApplyInterestPaymentBuilderService.java
@@ -78,6 +78,7 @@
chargeDefinition)));
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.APPLY_INTEREST,
accruedCostComponents,
chargesSplitIntoScheduledAndAccrued.get(false),
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
index 61649c7..5e06b98 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ApprovePaymentBuilderService.java
@@ -58,6 +58,7 @@
productIdentifier, scheduledActions);
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.APPROVE,
Collections.emptyMap(),
scheduledCharges,
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
index 4c5bdfb..f0fbcdf 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/ClosePaymentBuilderService.java
@@ -84,6 +84,7 @@
chargeDefinition)));
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.CLOSE,
accruedCostComponents,
chargesSplitIntoScheduledAndAccrued.get(false),
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/CostComponentService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/CostComponentService.java
index 8ed2536..a494427 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/CostComponentService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/CostComponentService.java
@@ -43,6 +43,7 @@
private static final int RUNNING_CALCULATION_PRECISION = 8;
public static PaymentBuilder getCostComponentsForScheduledCharges(
+ final Action action,
final Map<ChargeDefinition, CostComponent> accruedCostComponents,
final Collection<ScheduledCharge> scheduledCharges,
final BigDecimal maximumBalance,
@@ -59,11 +60,7 @@
final ChargeDefinition chargeDefinition = entry.getKey();
final BigDecimal chargeAmount = entry.getValue().getAmount();
- //TODO: This should adjust differently depending on accrual accounting.
- // It can't be fixed until getAmountProportionalTo is fixed.
- paymentBuilder.addToBalance(chargeDefinition.getFromAccountDesignator(), chargeAmount.negate());
- paymentBuilder.addToBalance(chargeDefinition.getToAccountDesignator(), chargeAmount);
- paymentBuilder.addToCostComponent(chargeDefinition, chargeAmount);
+ paymentBuilder.adjustBalances(action, chargeDefinition, chargeAmount);
}
@@ -196,6 +193,7 @@
.filter(x -> x.getScheduledAction().getAction().equals(Action.DISBURSE))
.collect(Collectors.toList());
final PaymentBuilder paymentBuilder = getCostComponentsForScheduledCharges(
+ null, //Action doesn't matter since there's nothing accrued.
Collections.emptyMap(),
disbursementFees,
maximumBalanceSize,
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
index 99e90c0..3b3c826 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DenyPaymentBuilderService.java
@@ -57,6 +57,7 @@
productIdentifier, scheduledActions);
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.DENY,
Collections.emptyMap(),
scheduledCharges,
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
index 97a47eb..a88c37f 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/DisbursePaymentBuilderService.java
@@ -109,6 +109,7 @@
chargeDefinition)))).orElse(Collections.emptyMap());
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.DISBURSE,
accruedCostComponents,
chargesSplitIntoScheduledAndAccrued.get(false),
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
index 5b77f93..51aa3fb 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/MarkLatePaymentBuilderService.java
@@ -82,6 +82,7 @@
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.MARK_LATE,
accruedCostComponents,
chargesSplitIntoScheduledAndAccrued.get(false),
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
index d4da18c..ed3d3ab 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/costcomponent/OpenPaymentBuilderService.java
@@ -56,6 +56,7 @@
productIdentifier, scheduledActions);
return CostComponentService.getCostComponentsForScheduledCharges(
+ Action.OPEN,
Collections.emptyMap(),
scheduledCharges,
caseParameters.getBalanceRangeMaximum(),
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
index 217f1eb..63083d5 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/util/AccountingAdapter.java
@@ -167,7 +167,7 @@
public BigDecimal getCurrentAccountBalance(final String accountIdentifier) {
try {
final Account account = ledgerManager.findAccount(accountIdentifier);
- if (account == null)
+ if (account == null || account.getBalance() == null)
throw ServiceException.internalError("Could not find the account with identifier ''{0}''", accountIdentifier);
return BigDecimal.valueOf(account.getBalance());
}
diff --git a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
index b3f1424..528270c 100644
--- a/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
+++ b/service/src/test/java/io/mifos/individuallending/internal/service/costcomponent/AcceptPaymentBuilderServiceTest.java
@@ -17,6 +17,7 @@
public void getPaymentBuilder() throws Exception {
final PaymentBuilderServiceTestCase testCase = new PaymentBuilderServiceTestCase("simple case");
testCase.runningBalances.adjustBalance(AccountDesignators.CUSTOMER_LOAN_PRINCIPAL, testCase.balance.negate());
+ testCase.runningBalances.adjustBalance(AccountDesignators.CUSTOMER_LOAN_INTEREST, testCase.accruedInterest.negate());
testCase.runningBalances.adjustBalance(AccountDesignators.INTEREST_ACCRUAL, testCase.accruedInterest);
final PaymentBuilder paymentBuilder = PaymentBuilderServiceTestHarness.constructCallToPaymentBuilder(