+12
@@ -56,6 +56,14 @@ public enum ChargeCalculationType {
|
||||
return this.value.equals(ChargeCalculationType.PERCENT_OF_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isPercentageOfAmountAndInterest() {
|
||||
return this.value.equals(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST.getValue());
|
||||
}
|
||||
|
||||
public boolean isPercentageOfInterest() {
|
||||
return this.value.equals(ChargeCalculationType.PERCENT_OF_INTEREST.getValue());
|
||||
}
|
||||
|
||||
public boolean isFlat() {
|
||||
return this.value.equals(ChargeCalculationType.FLAT.getValue());
|
||||
}
|
||||
@@ -63,4 +71,8 @@ public enum ChargeCalculationType {
|
||||
public boolean isAllowedSavingsChargeCalculationType() {
|
||||
return isFlat() || isPercentageOfAmount();
|
||||
}
|
||||
|
||||
public boolean isPercentageBased(){
|
||||
return isPercentageOfAmount() || isPercentageOfAmountAndInterest() || isPercentageOfInterest();
|
||||
}
|
||||
}
|
||||
+38
-29
@@ -16,7 +16,8 @@ public enum ChargeTimeType {
|
||||
SAVINGS_CLOSURE(4, "chargeTimeType.savingsClosure"), // only for savings
|
||||
WITHDRAWAL_FEE(5, "chargeTimeType.withdrawalFee"), // only for savings
|
||||
ANNUAL_FEE(6, "chargeTimeType.annualFee"), // only for savings
|
||||
MONTHLY_FEE(7, "chargeTimeType.monthlyFee"); // only for savings
|
||||
MONTHLY_FEE(7, "chargeTimeType.monthlyFee"), // only for savings
|
||||
INSTALMENT_FEE(8,"chargeTimeType.instalmentFee");//only for loan charges
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -47,34 +48,37 @@ public enum ChargeTimeType {
|
||||
public static ChargeTimeType fromInt(final Integer chargeTime) {
|
||||
ChargeTimeType chargeTimeType = ChargeTimeType.INVALID;
|
||||
if (chargeTime != null) {
|
||||
switch (chargeTime) {
|
||||
case 1:
|
||||
chargeTimeType = DISBURSEMENT;
|
||||
break;
|
||||
case 2:
|
||||
chargeTimeType = SPECIFIED_DUE_DATE;
|
||||
break;
|
||||
case 3:
|
||||
chargeTimeType = SAVINGS_ACTIVATION;
|
||||
break;
|
||||
case 4:
|
||||
chargeTimeType = SAVINGS_CLOSURE;
|
||||
break;
|
||||
case 5:
|
||||
chargeTimeType = WITHDRAWAL_FEE;
|
||||
break;
|
||||
case 6:
|
||||
chargeTimeType = ANNUAL_FEE;
|
||||
break;
|
||||
case 7:
|
||||
chargeTimeType = MONTHLY_FEE;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
}
|
||||
switch (chargeTime) {
|
||||
case 1:
|
||||
chargeTimeType = DISBURSEMENT;
|
||||
break;
|
||||
case 2:
|
||||
chargeTimeType = SPECIFIED_DUE_DATE;
|
||||
break;
|
||||
case 3:
|
||||
chargeTimeType = SAVINGS_ACTIVATION;
|
||||
break;
|
||||
case 4:
|
||||
chargeTimeType = SAVINGS_CLOSURE;
|
||||
break;
|
||||
case 5:
|
||||
chargeTimeType = WITHDRAWAL_FEE;
|
||||
break;
|
||||
case 6:
|
||||
chargeTimeType = ANNUAL_FEE;
|
||||
break;
|
||||
case 7:
|
||||
chargeTimeType = MONTHLY_FEE;
|
||||
break;
|
||||
case 8:
|
||||
chargeTimeType = INSTALMENT_FEE;
|
||||
break;
|
||||
default:
|
||||
chargeTimeType = INVALID;
|
||||
break;
|
||||
}
|
||||
return chargeTimeType;
|
||||
}
|
||||
return chargeTimeType;
|
||||
}
|
||||
|
||||
public boolean isTimeOfDisbursement() {
|
||||
@@ -105,8 +109,13 @@ public enum ChargeTimeType {
|
||||
return this.value.equals(ChargeTimeType.MONTHLY_FEE.getValue());
|
||||
}
|
||||
|
||||
|
||||
public boolean isInstalmentFee() {
|
||||
return this.value.equals(ChargeTimeType.INSTALMENT_FEE.getValue());
|
||||
}
|
||||
|
||||
public boolean isAllowedLoanChargeTime() {
|
||||
return isTimeOfDisbursement() || isOnSpecifiedDueDate();
|
||||
return isTimeOfDisbursement() || isOnSpecifiedDueDate() || isInstalmentFee();
|
||||
}
|
||||
|
||||
public boolean isAllowedSavingsChargeTime() {
|
||||
|
||||
+11
-3
@@ -11,7 +11,7 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule
|
||||
|
||||
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||
public static enum LOAN_CHARGE_CANNOT_BE_PAYED_REASON {
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, CHARGE_NOT_ACCOUNT_TRANSFER;
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, CHARGE_NOT_ACCOUNT_TRANSFER,CHARGE_NOT_PAYABLE;
|
||||
|
||||
public String errorMessage() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
@@ -20,7 +20,11 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule
|
||||
return "This loan charge has already been waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "This loan charge can be payed as the loan associated with it is currently inactive";
|
||||
} else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { return "This loan charge can be payed as the charge payment mode is not account transfer"; }
|
||||
} else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) {
|
||||
return "This loan charge can be payed as the charge payment mode is not account transfer";
|
||||
}else if (name().toString().equalsIgnoreCase("CHARGE_NOT_PAYABLE")) {
|
||||
return "This loan charge is not Payable through account transfer";
|
||||
}
|
||||
|
||||
return name().toString();
|
||||
}
|
||||
@@ -32,7 +36,11 @@ public class LoanChargeCannotBePayedException extends AbstractPlatformDomainRule
|
||||
return "error.msg.loan.charge.already.waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "error.msg.loan.charge.associated.loan.inactive";
|
||||
} else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) { return "error.msg.loan.charge.payment.mode.not.account.transfer"; }
|
||||
} else if (name().toString().equalsIgnoreCase("CHARGE_NOT_ACCOUNT_TRANSFER")) {
|
||||
return "error.msg.loan.charge.payment.mode.not.account.transfer";
|
||||
}else if (name().toString().equalsIgnoreCase("CHARGE_NOT_PAYABLE")) {
|
||||
return "error.msg.loan.charge.payment.not.allowed.account.transfer";
|
||||
}
|
||||
return name().toString();
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -11,14 +11,19 @@ public class LoanChargeCannotBeWaivedException extends AbstractPlatformDomainRul
|
||||
|
||||
/*** enum of reasons of why Loan Charge cannot be waived **/
|
||||
public static enum LOAN_CHARGE_CANNOT_BE_WAIVED_REASON {
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE;
|
||||
ALREADY_PAID, ALREADY_WAIVED, LOAN_INACTIVE, WAIVE_NOT_ALLOWED_FOR_CHARGE;
|
||||
|
||||
public String errorMessage() {
|
||||
if (name().toString().equalsIgnoreCase("ALREADY_PAID")) {
|
||||
return "This loan charge has been completely paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "This loan charge has already been waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { return "This loan charge can be waived as the loan associated with it is currently inactive"; }
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "This loan charge can be waived as the loan associated with it is currently inactive";
|
||||
} else if(name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")){
|
||||
return "This loan charge can be waived";
|
||||
}
|
||||
|
||||
return name().toString();
|
||||
}
|
||||
|
||||
@@ -27,7 +32,11 @@ public class LoanChargeCannotBeWaivedException extends AbstractPlatformDomainRul
|
||||
return "error.msg.loan.charge.already.paid";
|
||||
} else if (name().toString().equalsIgnoreCase("ALREADY_WAIVED")) {
|
||||
return "error.msg.loan.charge.already.waived";
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) { return "error.msg.loan.charge.associated.loan.inactive"; }
|
||||
} else if (name().toString().equalsIgnoreCase("LOAN_INACTIVE")) {
|
||||
return "error.msg.loan.charge.associated.loan.inactive";
|
||||
} else if(name().toString().equalsIgnoreCase("WAIVE_NOT_ALLOWED_FOR_CHARGE")) {
|
||||
return "error.msg.loan.charge.waive.not.allowed";
|
||||
}
|
||||
return name().toString();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -27,9 +27,9 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
public List<EnumOptionData> retrieveCalculationTypes() {
|
||||
|
||||
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT)
|
||||
// chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST),
|
||||
// chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST)
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST),
|
||||
chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,12 +64,12 @@ public class ChargeDropdownReadPlatformServiceImpl implements ChargeDropdownRead
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveLoanCalculationTypes() {
|
||||
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT));
|
||||
return Arrays.asList(chargeCalculationType(ChargeCalculationType.FLAT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT), chargeCalculationType(ChargeCalculationType.PERCENT_OF_AMOUNT_AND_INTEREST), chargeCalculationType(ChargeCalculationType.PERCENT_OF_INTEREST));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveLoanCollectionTimeTypes() {
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE));
|
||||
return Arrays.asList(chargeTimeType(ChargeTimeType.DISBURSEMENT), chargeTimeType(ChargeTimeType.SPECIFIED_DUE_DATE),chargeTimeType(ChargeTimeType.INSTALMENT_FEE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
@@ -43,6 +43,9 @@ public class ChargeEnumerations {
|
||||
case MONTHLY_FEE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.MONTHLY_FEE.getValue().longValue(), ChargeTimeType.MONTHLY_FEE.getCode(), "Monthly Fee");
|
||||
break;
|
||||
case INSTALMENT_FEE:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INSTALMENT_FEE.getValue().longValue(), ChargeTimeType.INSTALMENT_FEE.getCode(), "Instalment Fee");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(ChargeTimeType.INVALID.getValue().longValue(), ChargeTimeType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
|
||||
+76
-6
@@ -427,9 +427,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
|
||||
loanCharge.update(this);
|
||||
setOfLoanCharges().add(loanCharge);
|
||||
|
||||
this.summary = updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
@@ -452,6 +449,19 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
loanCharge.update(this);
|
||||
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||
if(loanCharge.getChargeCalculation().isPercentageBased()){
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
}else{
|
||||
chargeAmt = loanCharge.amount();
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments());
|
||||
setOfLoanCharges().add(loanCharge);
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
@@ -624,9 +634,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(3);
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
|
||||
if (setOfLoanCharges().contains(loanCharge)) {
|
||||
final Map<String, Object> loanChargeChanges = loanCharge.update(command, getPrincpal().getAmount());
|
||||
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
final Map<String, Object> loanChargeChanges = loanCharge.update(command, amount);
|
||||
actualChanges.putAll(loanChargeChanges);
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
@@ -656,6 +666,30 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loanCharge
|
||||
* @return
|
||||
*/
|
||||
private BigDecimal calculateAmountPercentageAppliedTo(final LoanCharge loanCharge) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
switch (loanCharge.getChargeCalculation()) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
amount = getPrincpal().getAmount();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
final BigDecimal totalInterestCharged = this.loanSummaryWrapper.calculateTotalInterestCharged(
|
||||
repaymentScheduleInstallments, getCurrency()).getAmount();
|
||||
amount = getPrincpal().getAmount().add(totalInterestCharged);
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
amount = getSummary().getTotalInterestCharged();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
@@ -765,7 +799,21 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public void updateLoanCharges(final Set<LoanCharge> loanCharges) {
|
||||
setOfLoanCharges().clear();
|
||||
setOfLoanCharges().addAll(associateChargesWithThisLoan(loanCharges));
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
loanCharge.update(this);
|
||||
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||
if(loanCharge.getChargeCalculation().isPercentageBased()){
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
}else{
|
||||
chargeAmt = loanCharge.amount();
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments());
|
||||
}
|
||||
setOfLoanCharges().addAll(loanCharges);
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
@@ -1012,6 +1060,17 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
|
||||
for (final LoanCharge loanCharge : possiblyModifedLoanCharges) {
|
||||
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||
if(loanCharge.getChargeCalculation().isPercentageBased()){
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
}else{
|
||||
chargeAmt = loanCharge.amount();
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments());
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
}
|
||||
}
|
||||
@@ -1143,6 +1202,17 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
// charges are optional
|
||||
for (final LoanCharge loanCharge : setOfLoanCharges()) {
|
||||
BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||
if(loanCharge.getChargeCalculation().isPercentageBased()){
|
||||
chargeAmt = loanCharge.getPercentage();
|
||||
}else{
|
||||
chargeAmt = loanCharge.amount();
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(this.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, this.repaymentScheduleDetail().getNumberOfRepayments());
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
}
|
||||
|
||||
|
||||
+80
-60
@@ -103,9 +103,23 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
final ChargeTimeType chargeTime = null;
|
||||
final ChargeCalculationType chargeCalculation = null;
|
||||
final ChargePaymentMode chargePaymentMode = null;
|
||||
BigDecimal amountPercentageAppliedTo = BigDecimal.ZERO;
|
||||
switch (ChargeCalculationType.fromInt(chargeDefinition.getChargeCalculation())) {
|
||||
case PERCENT_OF_AMOUNT:
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getSummary().getTotalInterestCharged());
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
amountPercentageAppliedTo = loan.getSummary().getTotalInterestCharged();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new LoanCharge(loan, chargeDefinition, loan.getPrincpal().getAmount(), amount, chargeTime, chargeCalculation, dueDate,
|
||||
chargePaymentMode);
|
||||
return new LoanCharge(loan, chargeDefinition, amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate,
|
||||
chargePaymentMode, null);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -113,8 +127,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
*/
|
||||
public static LoanCharge createNewWithoutLoan(final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate,
|
||||
final ChargePaymentMode chargePaymentMode) {
|
||||
return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode);
|
||||
final ChargePaymentMode chargePaymentMode,final Integer numberOfRepayments) {
|
||||
return new LoanCharge(null, chargeDefinition, loanPrincipal, amount, chargeTime, chargeCalculation, dueDate, chargePaymentMode, numberOfRepayments);
|
||||
}
|
||||
|
||||
protected LoanCharge() {
|
||||
@@ -123,7 +137,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate,
|
||||
final ChargePaymentMode chargePaymentMode) {
|
||||
final ChargePaymentMode chargePaymentMode, Integer numberOfRepayments) {
|
||||
this.loan = loan;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
@@ -162,12 +176,11 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
if (chargePaymentMode != null) {
|
||||
this.chargePaymentMode = chargePaymentMode.getValue();
|
||||
}
|
||||
|
||||
populateDerivedFields(loanPrincipal,chargeAmount);
|
||||
populateDerivedFields(loanPrincipal, chargeAmount,numberOfRepayments);
|
||||
this.paid = determineIfFullyPaid();
|
||||
}
|
||||
|
||||
private void populateDerivedFields(final BigDecimal loanPrincipal, final BigDecimal chargeAmount) {
|
||||
private void populateDerivedFields(final BigDecimal amountPercentageAppliedTo, final BigDecimal chargeAmount,Integer numberOfRepayments) {
|
||||
|
||||
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
|
||||
case INVALID:
|
||||
@@ -181,40 +194,31 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
break;
|
||||
case FLAT:
|
||||
this.percentage = null;
|
||||
this.amount = chargeAmount;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = chargeAmount;
|
||||
if(this.isInstalmentFee()){
|
||||
if(numberOfRepayments == null){
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
}
|
||||
this.amount = chargeAmount.multiply(BigDecimal.valueOf(numberOfRepayments));
|
||||
}else{
|
||||
this.amount = chargeAmount;
|
||||
}
|
||||
this.amountOutstanding = this.amount;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = chargeAmount;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
this.amountPercentageAppliedTo = amountPercentageAppliedTo;
|
||||
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage));
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = null;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
this.amountOutstanding = BigDecimal.ZERO;
|
||||
this.amountWaived = null;
|
||||
this.amountWrittenOff = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +259,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.loan = loan;
|
||||
}
|
||||
|
||||
public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal) {
|
||||
public void update(final BigDecimal amount, final LocalDate dueDate, final BigDecimal loanPrincipal, Integer numberOfRepayments) {
|
||||
if (dueDate != null) {
|
||||
this.dueDate = dueDate.toDate();
|
||||
}
|
||||
@@ -265,31 +269,28 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
this.amount = amount;
|
||||
if(this.isInstalmentFee()){
|
||||
if(numberOfRepayments == null){
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
}
|
||||
this.amount = amount.multiply(BigDecimal.valueOf(numberOfRepayments));
|
||||
}else{
|
||||
this.amount = amount;
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage));
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = amount;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command, final BigDecimal loanPrincipal) {
|
||||
public Map<String, Object> update(final JsonCommand command, final BigDecimal amount) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(7);
|
||||
|
||||
@@ -316,25 +317,19 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
this.amount = newValue;
|
||||
if(this.isInstalmentFee()){
|
||||
this.amount = newValue.multiply(BigDecimal.valueOf(this.loan.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}else{
|
||||
this.amount = newValue;
|
||||
}
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT:
|
||||
this.percentage = newValue;
|
||||
this.amountPercentageAppliedTo = loanPrincipal;
|
||||
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage));
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||
this.percentage = newValue;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
break;
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = newValue;
|
||||
this.amount = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountOutstanding = null;
|
||||
this.amountPercentageAppliedTo = amount;
|
||||
this.amount = minimumAndMaximumCap(percentageOf(this.amountPercentageAppliedTo, this.percentage));
|
||||
this.amountOutstanding = calculateOutstanding();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -349,6 +344,10 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public boolean isSpecifiedDueDate() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE);
|
||||
}
|
||||
|
||||
public boolean isInstalmentFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE);
|
||||
}
|
||||
|
||||
private boolean isGreaterThanZero(final BigDecimal value) {
|
||||
return value.compareTo(BigDecimal.ZERO) == 1;
|
||||
@@ -367,11 +366,16 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private boolean determineIfFullyPaid() {
|
||||
if(this.amount == null){
|
||||
return true;
|
||||
}
|
||||
return BigDecimal.ZERO.compareTo(calculateOutstanding()) == 0;
|
||||
}
|
||||
|
||||
private BigDecimal calculateOutstanding() {
|
||||
|
||||
if(this.amount == null){
|
||||
return null;
|
||||
}
|
||||
BigDecimal amountPaidLocal = BigDecimal.ZERO;
|
||||
if (this.amountPaid != null) {
|
||||
amountPaidLocal = this.amountPaid;
|
||||
@@ -573,4 +577,20 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public ChargePaymentMode getChargePaymentMode() {
|
||||
return ChargePaymentMode.fromInt(this.chargePaymentMode);
|
||||
}
|
||||
|
||||
|
||||
public ChargeCalculationType getChargeCalculation() {
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation);
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getPercentage() {
|
||||
return this.percentage;
|
||||
}
|
||||
|
||||
|
||||
public void updateAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
calculateOutstanding();
|
||||
}
|
||||
}
|
||||
+67
-8
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -25,14 +26,14 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||
|
||||
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
||||
currency);
|
||||
currency,period,repaymentPeriods.size());
|
||||
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
||||
currency);
|
||||
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency);
|
||||
|
||||
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency);
|
||||
loanCharges, currency,period,repaymentPeriods.size());
|
||||
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency);
|
||||
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
||||
@@ -47,13 +48,42 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
}
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
final MonetaryCurrency monetaryCurrency,LoanRepaymentScheduleInstallment period,int numberOfRepayments) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(period.getPrincipal(monetaryCurrency).getAmount()).add(
|
||||
period.getInterestCharged(monetaryCurrency).getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(period.getInterestCharged(monetaryCurrency).getAmount());
|
||||
} else {
|
||||
amount = amount.add(period.getPrincipal(monetaryCurrency).getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||
} else {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,13 +119,42 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
}
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency) {
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency,LoanRepaymentScheduleInstallment period,int numberOfRepayments) {
|
||||
|
||||
Money cumulative = Money.zero(currency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
if (loanCharge.isPenaltyCharge()) {
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(period.getPrincipal(currency).getAmount())
|
||||
.add(period.getInterestCharged(currency).getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(period.getInterestCharged(currency).getAmount());
|
||||
} else {
|
||||
amount = amount.add(period.getPrincipal(currency).getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed()).add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalInterestCharged());
|
||||
} else {
|
||||
amount = amount.add(period.getLoan().getSummary().getTotalPrincipalDisbursed());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -274,4 +274,9 @@ public final class LoanSummary {
|
||||
.plus(this.totalFeeChargesOutstanding).plus(this.totalPenaltyChargesOutstanding);
|
||||
this.totalOutstanding = totalOutstanding.getAmount();
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getTotalPrincipalDisbursed() {
|
||||
return this.totalPrincipalDisbursed;
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -257,14 +257,21 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
|
||||
private LoanCharge findEarliestUnpaidChargeFromUnOrderedSet(final Set<LoanCharge> charges) {
|
||||
LoanCharge earliestUnpaidCharge = null;
|
||||
|
||||
LoanCharge installemntCharge = null;
|
||||
for (final LoanCharge loanCharge : charges) {
|
||||
if (loanCharge.isNotFullyPaid() && !loanCharge.isDueAtDisbursement()) {
|
||||
if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) {
|
||||
if(loanCharge.isInstalmentFee()){
|
||||
if(installemntCharge == null){
|
||||
installemntCharge = loanCharge;
|
||||
}
|
||||
}else if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) {
|
||||
earliestUnpaidCharge = loanCharge;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(earliestUnpaidCharge == null || earliestUnpaidCharge.getDueLocalDate().isAfter(new LocalDate())){
|
||||
earliestUnpaidCharge = installemntCharge;
|
||||
}
|
||||
|
||||
return earliestUnpaidCharge;
|
||||
}
|
||||
|
||||
+70
-8
@@ -108,9 +108,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
outstandingBalance = outstandingBalance.minus(principalInterestForThisPeriod.principal());
|
||||
|
||||
// 7. determine fees and penalties
|
||||
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency);
|
||||
final Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges, currency,
|
||||
principalInterestForThisPeriod, principalDisbursed, totalInterestChargedForFullLoanTerm, numberOfRepayments);
|
||||
final Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate, scheduledDueDate, loanCharges,
|
||||
currency);
|
||||
currency, principalInterestForThisPeriod, principalDisbursed, totalInterestChargedForFullLoanTerm, numberOfRepayments);
|
||||
|
||||
// 8. sum up real totalInstallmentDue from components
|
||||
final Money totalInstallmentDue = principalInterestForThisPeriod.principal() //
|
||||
@@ -174,13 +175,43 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||
final MonetaryCurrency monetaryCurrency) {
|
||||
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
||||
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isFeeCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
if (loanCharge.isFeeCharge()) {
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(principalInterestForThisPeriod.principal().getAmount()).add(
|
||||
principalInterestForThisPeriod.interest().getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(principalInterestForThisPeriod.interest().getAmount());
|
||||
} else {
|
||||
amount = amount.add(principalInterestForThisPeriod.principal().getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||
} else {
|
||||
amount = amount.add(principalDisbursed.getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,13 +219,44 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency) {
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency,
|
||||
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
||||
final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
for (final LoanCharge loanCharge : loanCharges) {
|
||||
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd) && loanCharge.isPenaltyCharge()) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
if (loanCharge.isPenaltyCharge()) {
|
||||
if (loanCharge.isInstalmentFee()) {
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(principalInterestForThisPeriod.principal().getAmount()).add(
|
||||
principalInterestForThisPeriod.interest().getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(principalInterestForThisPeriod.interest().getAmount());
|
||||
} else {
|
||||
amount = amount.add(principalInterestForThisPeriod.principal().getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else {
|
||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||
}
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||
amount = amount.add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||
} else {
|
||||
amount = amount.add(principalDisbursed.getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||
cumulative = cumulative.plus(loanCharge.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -325,12 +325,6 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
existingLoanApplication.updateTransactionProcessingStrategy(strategy);
|
||||
}
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
|
||||
existingLoanApplication.updateLoanCharges(loanCharges);
|
||||
}
|
||||
|
||||
final String collateralParamName = "collateral";
|
||||
if (changes.containsKey(collateralParamName)) {
|
||||
final Set<LoanCollateral> loanCollateral = this.loanCollateralAssembler.fromParsedJson(command.parsedJson());
|
||||
@@ -346,6 +340,12 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query);
|
||||
existingLoanApplication.updateLoanSchedule(loanSchedule);
|
||||
}
|
||||
|
||||
final String chargesParamName = "charges";
|
||||
if (changes.containsKey(chargesParamName)) {
|
||||
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(command.parsedJson());
|
||||
existingLoanApplication.updateLoanCharges(loanCharges);
|
||||
}
|
||||
|
||||
this.loanRepository.saveAndFlush(existingLoanApplication);
|
||||
|
||||
|
||||
+3
-2
@@ -47,6 +47,7 @@ public class LoanChargeAssembler {
|
||||
final Set<LoanCharge> loanCharges = new HashSet<LoanCharge>();
|
||||
|
||||
final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
|
||||
final Integer numberOfRepayments = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("numberOfRepayments", element);
|
||||
|
||||
if (element.isJsonObject()) {
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
@@ -83,14 +84,14 @@ public class LoanChargeAssembler {
|
||||
chargePaymentModeEnum = ChargePaymentMode.fromInt(chargePaymentMode);
|
||||
}
|
||||
final LoanCharge loanCharge = LoanCharge.createNewWithoutLoan(chargeDefinition, principal, amount, chargeTime,
|
||||
chargeCalculation, dueDate, chargePaymentModeEnum);
|
||||
chargeCalculation, dueDate, chargePaymentModeEnum,numberOfRepayments);
|
||||
loanCharges.add(loanCharge);
|
||||
} else {
|
||||
final Long loanChargeId = id;
|
||||
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
|
||||
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
|
||||
|
||||
loanCharge.update(amount, dueDate, principal);
|
||||
loanCharge.update(amount, dueDate, principal, numberOfRepayments);
|
||||
|
||||
loanCharges.add(loanCharge);
|
||||
}
|
||||
|
||||
+10
-2
@@ -925,7 +925,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loanCharge.isWaived()) {
|
||||
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.ALREADY_PAID,
|
||||
loanCharge.getId()); }
|
||||
loanCharge.getId());
|
||||
} else if (loanCharge.isInstalmentFee()){
|
||||
throw new LoanChargeCannotBeWaivedException(LOAN_CHARGE_CANNOT_BE_WAIVED_REASON.WAIVE_NOT_ALLOWED_FOR_CHARGE,
|
||||
loanCharge.getId());
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>(3);
|
||||
|
||||
@@ -1001,7 +1005,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loanCharge.isWaived()) {
|
||||
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_WAIVED, loanCharge.getId());
|
||||
} else if (loanCharge.isPaid()) { throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.ALREADY_PAID,
|
||||
loanCharge.getId()); }
|
||||
loanCharge.getId());
|
||||
}else if (loanCharge.isInstalmentFee()){
|
||||
throw new LoanChargeCannotBePayedException(LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_PAYABLE,
|
||||
loanCharge.getId());
|
||||
}
|
||||
|
||||
if (!loanCharge.getChargePaymentMode().isPaymentModeAccountTransfer()) { throw new LoanChargeCannotBePayedException(
|
||||
LOAN_CHARGE_CANNOT_BE_PAYED_REASON.CHARGE_NOT_ACCOUNT_TRANSFER, loanCharge.getId()); }
|
||||
|
||||
Reference in New Issue
Block a user