Repayment of full amount now works.
diff --git a/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java b/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
index c0e0c56..9270c92 100644
--- a/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
+++ b/api/src/main/java/io/mifos/individuallending/api/v1/domain/product/ChargeIdentifiers.java
@@ -48,6 +48,7 @@
String TRACK_RETURN_PRINCIPAL_ID = "track-return-principal";
String MAXIMUM_BALANCE_DESIGNATOR = "{maximumbalance}";
String RUNNING_BALANCE_DESIGNATOR = "{runningbalance}";
+ String REPAYMENT_DESIGNATOR = "{repayment}";
String PRINCIPAL_ADJUSTMENT_DESIGNATOR = "{principaladjustment}";
static String nameToIdentifier(String name) {
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 472fc5d..8eab0e9 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestAccountingInteractionInLoanWorkflow.java
@@ -246,12 +246,12 @@
final Set<Debtor> debtors = new HashSet<>();
debtors.add(new Debtor(
- AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER,
+ customerLoanAccountIdentifier,
calculatedInterest.toPlainString()));
final Set<Creditor> creditors = new HashSet<>();
creditors.add(new Creditor(
- customerLoanAccountIdentifier,
+ AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER,
calculatedInterest.toPlainString()));
AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors);
@@ -274,15 +274,17 @@
checkNextActionsCorrect(product.getIdentifier(), customerCase.getIdentifier(), Action.APPLY_INTEREST,
Action.APPLY_INTEREST, Action.MARK_LATE, Action.ACCEPT_PAYMENT, Action.DISBURSE, Action.WRITE_OFF, Action.CLOSE);
+ final BigDecimal principal = expectedCurrentBalance.subtract(interestAccrued);
+
final Set<Debtor> debtors = new HashSet<>();
- debtors.add(new Debtor(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
- debtors.add(new Debtor(AccountingFixture.LOANS_PAYABLE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.subtract(interestAccrued).toPlainString()));
debtors.add(new Debtor(customerLoanAccountIdentifier, expectedCurrentBalance.toPlainString()));
+ debtors.add(new Debtor(AccountingFixture.LOAN_FUNDS_SOURCE_ACCOUNT_IDENTIFIER, principal.toPlainString()));
+ debtors.add(new Debtor(AccountingFixture.LOAN_INTEREST_ACCRUAL_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
final Set<Creditor> creditors = new HashSet<>();
- creditors.add(new Creditor(AccountingFixture.CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
- creditors.add(new Creditor(AccountingFixture.LOAN_FUNDS_SOURCE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.subtract(interestAccrued).toPlainString()));
creditors.add(new Creditor(AccountingFixture.TELLER_ONE_ACCOUNT_IDENTIFIER, expectedCurrentBalance.toPlainString()));
+ creditors.add(new Creditor(AccountingFixture.LOANS_PAYABLE_ACCOUNT_IDENTIFIER, principal.toPlainString()));
+ creditors.add(new Creditor(AccountingFixture.CONSUMER_LOAN_INTEREST_ACCOUNT_IDENTIFIER, interestAccrued.toPlainString()));
AccountingFixture.verifyTransfer(ledgerManager, debtors, creditors);
diff --git a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
index 6a30102..2bbea7c 100644
--- a/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
+++ b/service/src/main/java/io/mifos/individuallending/IndividualLendingPatternFactory.java
@@ -173,11 +173,11 @@
INTEREST_NAME,
Action.ACCEPT_PAYMENT,
BigDecimal.valueOf(0.05),
- INTEREST_ACCRUAL,
+ CUSTOMER_LOAN,
INTEREST_INCOME);
interestCharge.setForCycleSizeUnit(ChronoUnit.YEARS);
interestCharge.setAccrueAction(Action.APPLY_INTEREST.name());
- interestCharge.setAccrualAccountDesignator(CUSTOMER_LOAN);
+ interestCharge.setAccrualAccountDesignator(INTEREST_ACCRUAL);
interestCharge.setProportionalTo(ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR);
final ChargeDefinition customerRepaymentCharge = new ChargeDefinition();
@@ -187,20 +187,20 @@
customerRepaymentCharge.setDescription(REPAYMENT_NAME);
customerRepaymentCharge.setFromAccountDesignator(CUSTOMER_LOAN);
customerRepaymentCharge.setToAccountDesignator(ENTRY);
- customerRepaymentCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+ customerRepaymentCharge.setProportionalTo(ChargeIdentifiers.REPAYMENT_DESIGNATOR);
customerRepaymentCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
customerRepaymentCharge.setAmount(BigDecimal.ONE);
- final ChargeDefinition trackPrincipalRepaymentCharge = new ChargeDefinition();
- trackPrincipalRepaymentCharge.setChargeAction(Action.ACCEPT_PAYMENT.name());
- trackPrincipalRepaymentCharge.setIdentifier(TRACK_RETURN_PRINCIPAL_ID);
- trackPrincipalRepaymentCharge.setName(TRACK_RETURN_PRINCIPAL_NAME);
- trackPrincipalRepaymentCharge.setDescription(TRACK_RETURN_PRINCIPAL_NAME);
- trackPrincipalRepaymentCharge.setFromAccountDesignator(LOANS_PAYABLE);
- trackPrincipalRepaymentCharge.setToAccountDesignator(LOAN_FUNDS_SOURCE);
- trackPrincipalRepaymentCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
- trackPrincipalRepaymentCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
- trackPrincipalRepaymentCharge.setAmount(BigDecimal.ONE);
+ final ChargeDefinition trackReturnPrincipalCharge = new ChargeDefinition();
+ trackReturnPrincipalCharge.setChargeAction(Action.ACCEPT_PAYMENT.name());
+ trackReturnPrincipalCharge.setIdentifier(TRACK_RETURN_PRINCIPAL_ID);
+ trackReturnPrincipalCharge.setName(TRACK_RETURN_PRINCIPAL_NAME);
+ trackReturnPrincipalCharge.setDescription(TRACK_RETURN_PRINCIPAL_NAME);
+ trackReturnPrincipalCharge.setFromAccountDesignator(LOAN_FUNDS_SOURCE);
+ trackReturnPrincipalCharge.setToAccountDesignator(LOANS_PAYABLE);
+ trackReturnPrincipalCharge.setProportionalTo(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+ trackReturnPrincipalCharge.setChargeMethod(ChargeDefinition.ChargeMethod.PROPORTIONAL);
+ trackReturnPrincipalCharge.setAmount(BigDecimal.ONE);
final ChargeDefinition disbursementReturnCharge = charge(
RETURN_DISBURSEMENT_NAME,
@@ -220,7 +220,7 @@
ret.add(writeOffAllowanceCharge);
ret.add(interestCharge);
ret.add(customerRepaymentCharge);
- ret.add(trackPrincipalRepaymentCharge);
+ ret.add(trackReturnPrincipalCharge);
ret.add(disbursementReturnCharge);
return ret;
diff --git a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
index 14ef0f9..e62327e 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/command/handler/IndividualLoanCommandHandler.java
@@ -52,6 +52,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -103,6 +104,8 @@
Action.OPEN,
entry,
designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
.collect(Collectors.toList());
accountingAdapter.bookCharges(charges,
@@ -142,6 +145,8 @@
Action.DENY,
entry,
designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
.collect(Collectors.toList());
final CaseEntity customerCase = dataContextOfAction.getCustomerCase();
@@ -187,6 +192,8 @@
Action.APPROVE,
entry,
designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
.collect(Collectors.toList());
accountingAdapter.bookCharges(charges,
@@ -226,7 +233,9 @@
.map(entry -> mapCostComponentEntryToChargeInstance(
Action.DISBURSE,
entry,
- designatorToAccountIdentifierMapper)),
+ designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get),
Stream.of(getDisbursalChargeInstance(disbursalAmount, designatorToAccountIdentifierMapper)))
.collect(Collectors.toList());
@@ -275,6 +284,8 @@
Action.APPLY_INTEREST,
entry,
designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
.collect(Collectors.toList());
accountingAdapter.bookCharges(charges,
@@ -319,6 +330,8 @@
Action.ACCEPT_PAYMENT,
entry,
designatorToAccountIdentifierMapper))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
.collect(Collectors.toList());
@@ -384,30 +397,38 @@
return new IndividualLoanCommandEvent(command.getProductIdentifier(), command.getCaseIdentifier());
}
- private static ChargeInstance mapCostComponentEntryToChargeInstance(
+ private static Optional<ChargeInstance> mapCostComponentEntryToChargeInstance(
final Action action,
final Map.Entry<ChargeDefinition, CostComponent> costComponentEntry,
final DesignatorToAccountIdentifierMapper designatorToAccountIdentifierMapper) {
final ChargeDefinition chargeDefinition = costComponentEntry.getKey();
final BigDecimal chargeAmount = costComponentEntry.getValue().getAmount();
- if (chargeDefinition.getAccrualAccountDesignator() != null) {
+ if (chargeIsAccrued(chargeDefinition)) {
if (Action.valueOf(chargeDefinition.getAccrueAction()) == action)
- return new ChargeInstance(
+ return Optional.of(new ChargeInstance(
designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
- chargeAmount);
- else
- return new ChargeInstance(
- designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
+ chargeAmount));
+ else if (Action.valueOf(chargeDefinition.getChargeAction()) == action)
+ return Optional.of(new ChargeInstance(
designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getAccrualAccountDesignator()),
- chargeAmount);
+ designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
+ chargeAmount));
+ else
+ return Optional.empty();
}
- else
- return new ChargeInstance(
+ else if (Action.valueOf(chargeDefinition.getChargeAction()) == action)
+ return Optional.of(new ChargeInstance(
designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getFromAccountDesignator()),
designatorToAccountIdentifierMapper.mapOrThrow(chargeDefinition.getToAccountDesignator()),
- chargeAmount);
+ chargeAmount));
+ else
+ return Optional.empty();
+ }
+
+ private static boolean chargeIsAccrued(final ChargeDefinition chargeDefinition) {
+ return chargeDefinition.getAccrualAccountDesignator() != null;
}
private static ChargeInstance getDisbursalChargeInstance(
diff --git a/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java b/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
index 5c871ba..7a1a6e5 100644
--- a/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
+++ b/service/src/main/java/io/mifos/individuallending/internal/service/CostComponentService.java
@@ -42,7 +42,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
-import java.util.function.BiFunction;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -52,6 +52,7 @@
public class CostComponentService {
private static final int EXTRA_PRECISION = 4;
private static final int RUNNING_CALCULATION_PRECISION = 8;
+ private static final String NOT_PROPORTIONAL = "null";
private final ProductRepository productRepository;
private final CaseRepository caseRepository;
@@ -195,7 +196,7 @@
final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledCharges.stream()
- .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.DISBURSE)));
+ .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.DISBURSE)));
final Map<ChargeDefinition, CostComponent> accruedCostComponents =
optionalStartOfTerm.map(startOfTerm ->
@@ -239,7 +240,7 @@
Collections.singletonList(interestAction));
final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledCharges.stream()
- .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.APPLY_INTEREST)));
+ .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.APPLY_INTEREST)));
final Map<ChargeDefinition, CostComponent> accruedCostComponents = chargesSplitIntoScheduledAndAccrued.get(true)
.stream()
@@ -295,7 +296,7 @@
Collections.singletonList(scheduledAction));
final Map<Boolean, List<ScheduledCharge>> chargesSplitIntoScheduledAndAccrued = scheduledChargesForThisAction.stream()
- .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x, Action.ACCEPT_PAYMENT)));
+ .collect(Collectors.partitioningBy(x -> isAccruedChargeForAction(x.getChargeDefinition(), Action.ACCEPT_PAYMENT)));
final Map<ChargeDefinition, CostComponent> accruedCostComponents = chargesSplitIntoScheduledAndAccrued.get(true)
.stream()
@@ -314,9 +315,19 @@
minorCurrencyUnitDigits);
}
- private static boolean isAccruedChargeForAction(final ScheduledCharge scheduledCharge, final Action action) {
- return scheduledCharge.getChargeDefinition().getAccrueAction() != null &&
- scheduledCharge.getChargeDefinition().getChargeAction().equals(action.name());
+ private static boolean isAccruedChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+ return chargeDefinition.getAccrueAction() != null &&
+ chargeDefinition.getChargeAction().equals(action.name());
+ }
+
+ private static boolean isAccrualChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+ return chargeDefinition.getAccrueAction() != null &&
+ chargeDefinition.getAccrueAction().equals(action.name());
+ }
+
+ private static boolean isOneOffChargeForAction(final ChargeDefinition chargeDefinition, final Action action) {
+ return chargeDefinition.getChargeAction() != null &&
+ chargeDefinition.getChargeAction().equals(action.name());
}
private CostComponent getAccruedCostComponentToApply(final DataContextOfAction dataContextOfAction,
@@ -385,35 +396,86 @@
balanceAdjustment = balanceAdjustment.add(entry.getValue().getAmount());
}
- final Map<Boolean, List<ScheduledCharge>> partitionedCharges = scheduledCharges.stream()
- .collect(Collectors.partitioningBy(CostComponentService::proportionalToPrincipalAdjustment));
+ final Map<String, List<ScheduledCharge>> partitionedCharges = scheduledCharges.stream()
+ .collect(Collectors.groupingBy(CostComponentService::proportionalToDesignator));
- for (final ScheduledCharge scheduledCharge : partitionedCharges.get(false))
+ final List<String> orderOfChargesByDesignatorFirstSet = Arrays.asList(
+ NOT_PROPORTIONAL,
+ ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR,
+ ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR);
+
+ for (final String chargeProportionalTo : orderOfChargesByDesignatorFirstSet)
{
- final CostComponent costComponent = costComponentMap
- .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+ final BigDecimal amountProportionalTo;
+ switch (chargeProportionalTo) {
+ case NOT_PROPORTIONAL:
+ amountProportionalTo = BigDecimal.ZERO;
+ break;
+ case ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR:
+ amountProportionalTo = maximumBalance;
+ break;
+ case ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR:
+ amountProportionalTo = runningBalance;
+ break;
+ default:
+ amountProportionalTo = BigDecimal.ZERO;
+ break;
+ }
+//TODO: correctly implement charges which are proportionate to other charges.
- final BigDecimal chargeAmount = howToApplyScheduledChargeToBalance(scheduledCharge)
- .apply(maximumBalance, currentRunningBalance)
- .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
- if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
- balanceAdjustment = balanceAdjustment.add(chargeAmount);
- costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
- currentRunningBalance = currentRunningBalance.add(chargeAmount);
+ final List<ScheduledCharge> partition = partitionedCharges.get(chargeProportionalTo);
+ if (partition != null) {
+ for (final ScheduledCharge scheduledCharge : partition) {
+ final CostComponent costComponent = costComponentMap
+ .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+
+ final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge)
+ .apply(amountProportionalTo)
+ .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
+ if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
+ balanceAdjustment = balanceAdjustment.add(chargeAmount);
+ costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
+ currentRunningBalance = currentRunningBalance.add(chargeAmount);
+ }
+ }
}
- final BigDecimal principalAdjustment = loanPaymentSize.subtract(balanceAdjustment);
- for (final ScheduledCharge scheduledCharge : partitionedCharges.get(true))
- {
- final CostComponent costComponent = costComponentMap
- .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+ final List<String> orderOfChargesByDesignatorSecondSet = Arrays.asList(
+ ChargeIdentifiers.REPAYMENT_DESIGNATOR,
+ ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
- final BigDecimal chargeAmount = applyPrincipalAdjustmentCharge(scheduledCharge, principalAdjustment)
- .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
- if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
- balanceAdjustment = balanceAdjustment.add(chargeAmount);
- costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
- currentRunningBalance = currentRunningBalance.add(chargeAmount);
+
+ final BigDecimal principalAdjustment = loanPaymentSize.subtract(balanceAdjustment);
+ for (final String chargeProportionalTo : orderOfChargesByDesignatorSecondSet)
+ {
+ final BigDecimal amountProportionalTo;
+ switch (chargeProportionalTo) {
+ case ChargeIdentifiers.REPAYMENT_DESIGNATOR:
+ amountProportionalTo = loanPaymentSize;
+ break;
+ case ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR:
+ amountProportionalTo = principalAdjustment;
+ break;
+ default:
+ amountProportionalTo = BigDecimal.ZERO;
+ break;
+ }
+
+ final List<ScheduledCharge> partition = partitionedCharges.get(chargeProportionalTo);
+ if (partition != null) {
+ for (final ScheduledCharge scheduledCharge : partition) {
+ final CostComponent costComponent = costComponentMap
+ .computeIfAbsent(scheduledCharge.getChargeDefinition(), CostComponentService::constructEmptyCostComponent);
+
+ final BigDecimal chargeAmount = howToApplyScheduledChargeToAmount(scheduledCharge)
+ .apply(amountProportionalTo)
+ .setScale(minorCurrencyUnitDigits, BigDecimal.ROUND_HALF_EVEN);
+ if (chargeDefinitionTouchesAccount(scheduledCharge.getChargeDefinition(), AccountDesignators.CUSTOMER_LOAN))
+ balanceAdjustment = balanceAdjustment.add(chargeAmount);
+ costComponent.setAmount(costComponent.getAmount().add(chargeAmount));
+ currentRunningBalance = currentRunningBalance.add(chargeAmount);
+ }
+ }
}
return new CostComponentsForRepaymentPeriod(
@@ -422,12 +484,6 @@
balanceAdjustment.negate());
}
- private static BigDecimal applyPrincipalAdjustmentCharge(
- final ScheduledCharge scheduledCharge,
- final BigDecimal principalAdjustment) {
- return scheduledCharge.getChargeDefinition().getAmount().multiply(principalAdjustment);
- }
-
private static CostComponent constructEmptyCostComponent(final ChargeDefinition chargeDefinition) {
final CostComponent ret = new CostComponent();
ret.setChargeIdentifier(chargeDefinition.getIdentifier());
@@ -435,42 +491,33 @@
return ret;
}
- private static boolean proportionalToPrincipalAdjustment(final ScheduledCharge scheduledCharge) {
+ private static String proportionalToDesignator(final ScheduledCharge scheduledCharge) {
if (!scheduledCharge.getChargeDefinition().getChargeMethod().equals(ChargeDefinition.ChargeMethod.PROPORTIONAL))
- return false;
- final String proportionalTo = scheduledCharge.getChargeDefinition().getProportionalTo();
- return proportionalTo != null && proportionalTo.equals(ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR);
+ return NOT_PROPORTIONAL;
+
+ return scheduledCharge.getChargeDefinition().getProportionalTo();
}
- private static BiFunction<BigDecimal, BigDecimal, BigDecimal> howToApplyScheduledChargeToBalance(
+ private static Function<BigDecimal, BigDecimal> howToApplyScheduledChargeToAmount(
final ScheduledCharge scheduledCharge)
{
-
+ final ChargeDefinition chargeDefinition = scheduledCharge.getChargeDefinition();
+ final Action action = scheduledCharge.getScheduledAction().action;
switch (scheduledCharge.getChargeDefinition().getChargeMethod())
{
case FIXED:
- return (maximumBalance, runningBalance) -> scheduledCharge.getChargeDefinition().getAmount();
- case PROPORTIONAL: {
- switch (scheduledCharge.getChargeDefinition().getProportionalTo()) {
- case ChargeIdentifiers.RUNNING_BALANCE_DESIGNATOR:
- return (maximumBalance, runningBalance) ->
- PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
- .multiply(runningBalance);
- case ChargeIdentifiers.MAXIMUM_BALANCE_DESIGNATOR:
- return (maximumBalance, runningBalance) ->
- PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
- .multiply(maximumBalance);
- case ChargeIdentifiers.PRINCIPAL_ADJUSTMENT_DESIGNATOR: //This is handled elsewhere.
- throw new IllegalStateException("A principal adjustment charge should not be passed to the same application function as the other charges.");
- default:
-//TODO: correctly implement charges which are proportionate to other charges.
- return (maximumBalance, runningBalance) ->
- PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
- .multiply(maximumBalance);
- }
- }
+ return (amountProportionalTo) -> scheduledCharge.getChargeDefinition().getAmount();
+ case PROPORTIONAL:
+ if (isAccrualChargeForAction(chargeDefinition, action))
+ return (amountProportionalTo) ->
+ PeriodChargeCalculator.chargeAmountPerPeriod(scheduledCharge, RUNNING_CALCULATION_PRECISION)
+ .multiply(amountProportionalTo);
+ else if (isOneOffChargeForAction(chargeDefinition, action))
+ return (amountProportionalTo) ->
+ scheduledCharge.getChargeDefinition().getAmount()
+ .multiply(amountProportionalTo);
default:
- return (maximumBalance, runningBalance) -> BigDecimal.ZERO;
+ return (amountProportionalTo) -> BigDecimal.ZERO;
}
}