MIFOSX-1586 : corrected overdue charges with interest calculation
This commit is contained in:
+148
-18
@@ -2444,11 +2444,15 @@ public class ClientLoanIntegrationTest {
|
|||||||
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
||||||
validateCharge(flatInstallmentFee, loanCharges, "50", "100.00", "50.0", "50.0");
|
validateCharge(flatInstallmentFee, loanCharges, "50", "100.00", "50.0", "50.0");
|
||||||
|
|
||||||
/*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("50.0"),
|
/*
|
||||||
JournalEntry.TransactionType.CREDIT));
|
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||||
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
* "20 September 2011", new JournalEntry(Float.valueOf("50.0"),
|
||||||
new JournalEntry(Float.valueOf("50.0"), JournalEntry.TransactionType.DEBIT));
|
* JournalEntry.TransactionType.CREDIT));
|
||||||
*/
|
* this.journalEntryHelper.checkJournalEntryForExpenseAccount
|
||||||
|
* (expenseAccount, "20 September 2011", new
|
||||||
|
* JournalEntry(Float.valueOf("50.0"),
|
||||||
|
* JournalEntry.TransactionType.DEBIT));
|
||||||
|
*/
|
||||||
final String jobName = "Add Accrual Transactions";
|
final String jobName = "Add Accrual Transactions";
|
||||||
try {
|
try {
|
||||||
this.schedulerJobHelper.excuteJob(jobName);
|
this.schedulerJobHelper.excuteJob(jobName);
|
||||||
@@ -2645,10 +2649,15 @@ public class ClientLoanIntegrationTest {
|
|||||||
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
||||||
validateCharge(percentageInstallmentFee, loanCharges, "1", "61.19", "29.11", "29.70");
|
validateCharge(percentageInstallmentFee, loanCharges, "1", "61.19", "29.11", "29.70");
|
||||||
|
|
||||||
/*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(Float.valueOf("29.7"),
|
/*
|
||||||
JournalEntry.TransactionType.CREDIT));
|
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||||
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
* "20 September 2011", new JournalEntry(Float.valueOf("29.7"),
|
||||||
new JournalEntry(Float.valueOf("29.7"), JournalEntry.TransactionType.DEBIT));*/
|
* JournalEntry.TransactionType.CREDIT));
|
||||||
|
* this.journalEntryHelper.checkJournalEntryForExpenseAccount
|
||||||
|
* (expenseAccount, "20 September 2011", new
|
||||||
|
* JournalEntry(Float.valueOf("29.7"),
|
||||||
|
* JournalEntry.TransactionType.DEBIT));
|
||||||
|
*/
|
||||||
|
|
||||||
final String jobName = "Add Accrual Transactions";
|
final String jobName = "Add Accrual Transactions";
|
||||||
try {
|
try {
|
||||||
@@ -2850,10 +2859,15 @@ public class ClientLoanIntegrationTest {
|
|||||||
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
||||||
validateCharge(amountPlusInterestPercentageInstallmentFee, loanCharges, "1", "63.02", "31.51", "31.51");
|
validateCharge(amountPlusInterestPercentageInstallmentFee, loanCharges, "1", "63.02", "31.51", "31.51");
|
||||||
|
|
||||||
/*this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, "20 September 2011", new JournalEntry(
|
/*
|
||||||
Float.valueOf("31.51"), JournalEntry.TransactionType.CREDIT));
|
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||||
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
* "20 September 2011", new JournalEntry( Float.valueOf("31.51"),
|
||||||
new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.DEBIT));*/
|
* JournalEntry.TransactionType.CREDIT));
|
||||||
|
* this.journalEntryHelper.checkJournalEntryForExpenseAccount
|
||||||
|
* (expenseAccount, "20 September 2011", new
|
||||||
|
* JournalEntry(Float.valueOf("31.51"),
|
||||||
|
* JournalEntry.TransactionType.DEBIT));
|
||||||
|
*/
|
||||||
|
|
||||||
final String jobName = "Add Accrual Transactions";
|
final String jobName = "Add Accrual Transactions";
|
||||||
try {
|
try {
|
||||||
@@ -3141,7 +3155,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
||||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
||||||
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN,
|
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_EMI_AMOUN,
|
||||||
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null,null);
|
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD, "0", null, null);
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, null,
|
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE, null,
|
||||||
LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList<HashMap>(0));
|
LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList<HashMap>(0));
|
||||||
@@ -3231,7 +3245,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
|
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
|
||||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST,
|
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST,
|
||||||
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
|
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
|
||||||
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE,null);
|
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", LOAN_DISBURSEMENT_DATE, null);
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE,
|
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE,
|
||||||
LOAN_DISBURSEMENT_DATE, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList<HashMap>(0));
|
LOAN_DISBURSEMENT_DATE, LoanApplicationTestBuilder.RBI_INDIA_STRATEGY, new ArrayList<HashMap>(0));
|
||||||
@@ -3356,7 +3370,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
||||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE,
|
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE,
|
||||||
LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS,
|
LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS,
|
||||||
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_WEEKLY, "1", REST_START_DATE,null);
|
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_WEEKLY, "1", REST_START_DATE, null);
|
||||||
|
|
||||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE,
|
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE,
|
||||||
REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, charges);
|
REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, charges);
|
||||||
@@ -3433,6 +3447,113 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanScheduleWithInterestRecalculation_WITH_REST_DAILY_INTEREST_COMPOUND_INTEREST_FEE_STRATEGY_WITH_OVERDUE_CHARGE()
|
||||||
|
throws InterruptedException {
|
||||||
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
|
this.schedulerJobHelper = new SchedulerJobHelper(this.requestSpec, this.responseSpec);
|
||||||
|
|
||||||
|
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
|
||||||
|
|
||||||
|
Calendar todaysDate = Calendar.getInstance();
|
||||||
|
todaysDate.add(Calendar.DAY_OF_MONTH, -7 * 3);
|
||||||
|
final String LOAN_DISBURSEMENT_DATE = dateFormat.format(todaysDate.getTime());
|
||||||
|
todaysDate.add(Calendar.DAY_OF_MONTH, -2);
|
||||||
|
final String REST_START_DATE = dateFormat.format(todaysDate.getTime());
|
||||||
|
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
|
||||||
|
Integer overdueFeeChargeId = ChargesHelper.createCharges(this.requestSpec, this.responseSpec,
|
||||||
|
ChargesHelper.getLoanOverdueFeeJSONWithCalculattionTypePercentage());
|
||||||
|
Assert.assertNotNull(overdueFeeChargeId);
|
||||||
|
|
||||||
|
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||||
|
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||||
|
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_STRATEGY_RESCHEDULE_NEXT_REPAYMENTS,
|
||||||
|
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY, "1", REST_START_DATE, null, overdueFeeChargeId.toString());
|
||||||
|
|
||||||
|
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, LOAN_DISBURSEMENT_DATE,
|
||||||
|
REST_START_DATE, LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, null);
|
||||||
|
|
||||||
|
Assert.assertNotNull(loanID);
|
||||||
|
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||||
|
|
||||||
|
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
List<Map<String, Object>> expectedvalues = new ArrayList<>();
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2505.73", "23.18", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2517.29", "11.62", "0.0", "0.0");
|
||||||
|
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||||
|
|
||||||
|
System.out.println("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.approveLoan(LOAN_DISBURSEMENT_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||||
|
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||||
|
|
||||||
|
System.out.println("-------------------------------DISBURSE LOAN-------------------------------------------");
|
||||||
|
loanStatusHashMap = this.loanTransactionHelper.disburseLoan(LOAN_DISBURSEMENT_DATE, loanID);
|
||||||
|
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||||
|
|
||||||
|
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
expectedvalues = new ArrayList<>();
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2482.55", "46.36", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2482.33", "46.58", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2552.36", "11.78", "0.0", "0.0");
|
||||||
|
|
||||||
|
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||||
|
|
||||||
|
String JobName = "Apply penalty to overdue loans";
|
||||||
|
this.schedulerJobHelper.excuteJob(JobName);
|
||||||
|
|
||||||
|
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
expectedvalues = new ArrayList<>();
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "252.89");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2481.38", "47.53", "0.0", "252.89");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2479.99", "48.92", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2555.87", "11.8", "0.0", "0.0");
|
||||||
|
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||||
|
|
||||||
|
Calendar repaymentDate = Calendar.getInstance();
|
||||||
|
repaymentDate.add(Calendar.DAY_OF_MONTH, -7 * 2);
|
||||||
|
final String LOAN_FIRST_REPAYMENT_DATE = dateFormat.format(repaymentDate.getTime());
|
||||||
|
Float totalDueForCurrentPeriod = (Float) loanSchedule.get(1).get("totalDueForPeriod");
|
||||||
|
totalDueForCurrentPeriod = totalDueForCurrentPeriod - new Float("252.89");
|
||||||
|
this.loanTransactionHelper.makeRepayment(LOAN_FIRST_REPAYMENT_DATE, totalDueForCurrentPeriod, loanID);
|
||||||
|
|
||||||
|
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
expectedvalues = new ArrayList<>();
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "252.89");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2492.89", "36.02", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2530.13", "11.68", "0.0", "0.0");
|
||||||
|
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||||
|
|
||||||
|
repaymentDate = Calendar.getInstance();
|
||||||
|
repaymentDate.add(Calendar.DAY_OF_MONTH, -3);
|
||||||
|
final String LOAN_SECOND_REPAYMENT_DATE = dateFormat.format(repaymentDate.getTime());
|
||||||
|
totalDueForCurrentPeriod = (Float) loanSchedule.get(2).get("totalDueForPeriod");
|
||||||
|
this.loanTransactionHelper.makeRepayment(LOAN_SECOND_REPAYMENT_DATE, totalDueForCurrentPeriod, loanID);
|
||||||
|
|
||||||
|
loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||||
|
expectedvalues = new ArrayList<>();
|
||||||
|
todaysDate = Calendar.getInstance();
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, -2, "2482.76", "46.15", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2494.22", "34.69", "0.0", "252.89");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2498.39", "30.52", "0.0", "0.0");
|
||||||
|
addRepaymentValues(expectedvalues, todaysDate, 1, "2524.63", "11.65", "0.0", "0.0");
|
||||||
|
verifyLoanRepaymentSchedule(loanSchedule, expectedvalues);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoanScheduleWithInterestRecalculation_WITH_PERIODIC_ACCOUNTING() {
|
public void testLoanScheduleWithInterestRecalculation_WITH_PERIODIC_ACCOUNTING() {
|
||||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||||
@@ -3533,7 +3654,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRepaymentValues(List<Map<String, Object>> expectedvalues, Calendar todaysDate, int addMonths, String principalDue,
|
private void addRepaymentValues(List<Map<String, Object>> expectedvalues, Calendar todaysDate, int addMonths, String principalDue,
|
||||||
String interestDue, String feeChargesDue, String penaltyChargesDue) {
|
String interestDue, String feeChargesDue, String penaltyChargesDue) {
|
||||||
Map<String, Object> values = new HashMap<>(3);
|
Map<String, Object> values = new HashMap<>(3);
|
||||||
values.put("dueDate", getDateAsArray(todaysDate, addMonths));
|
values.put("dueDate", getDateAsArray(todaysDate, addMonths));
|
||||||
@@ -3560,6 +3681,15 @@ public class ClientLoanIntegrationTest {
|
|||||||
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||||
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
||||||
final String recalculationRestFrequencyDate, final Account[] accounts) {
|
final String recalculationRestFrequencyDate, final Account[] accounts) {
|
||||||
|
return createLoanProductWithInterestRecalculation(repaymentStrategy, interestRecalculationCompoundingMethod,
|
||||||
|
rescheduleStrategyMethod, recalculationRestFrequencyType, recalculationRestFrequencyInterval,
|
||||||
|
recalculationRestFrequencyDate, accounts, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy,
|
||||||
|
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||||
|
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
||||||
|
final String recalculationRestFrequencyDate, final Account[] accounts, final String chargeId) {
|
||||||
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
System.out.println("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||||
LoanProductTestBuilder builder = new LoanProductTestBuilder()
|
LoanProductTestBuilder builder = new LoanProductTestBuilder()
|
||||||
.withPrincipal("10000000.00")
|
.withPrincipal("10000000.00")
|
||||||
@@ -3578,7 +3708,7 @@ public class ClientLoanIntegrationTest {
|
|||||||
builder = builder.withAccountingRulePeriodicAccrual(accounts);
|
builder = builder.withAccountingRulePeriodicAccrual(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String loanProductJSON = builder.build(null);
|
final String loanProductJSON = builder.build(chargeId);
|
||||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
@@ -222,6 +222,18 @@ public class ChargesHelper {
|
|||||||
System.out.println(chargesCreateJson);
|
System.out.println(chargesCreateJson);
|
||||||
return chargesCreateJson;
|
return chargesCreateJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getLoanOverdueFeeJSONWithCalculattionTypePercentage() {
|
||||||
|
final HashMap<String, Object> map = populateDefaultsForLoan();
|
||||||
|
map.put("penalty", ChargesHelper.penalty);
|
||||||
|
map.put("amount", "10");
|
||||||
|
map.put("chargePaymentMode", ChargesHelper.CHARGE_PAYMENT_MODE_REGULAR);
|
||||||
|
map.put("chargeTimeType", CHARGE_OVERDUE_INSTALLMENT_FEE);
|
||||||
|
map.put("chargeCalculationType", ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT_AND_INTEREST);
|
||||||
|
String chargesCreateJson = new Gson().toJson(map);
|
||||||
|
System.out.println(chargesCreateJson);
|
||||||
|
return chargesCreateJson;
|
||||||
|
}
|
||||||
|
|
||||||
public static HashMap<String, Object> populateDefaultsForLoan() {
|
public static HashMap<String, Object> populateDefaultsForLoan() {
|
||||||
final HashMap<String, Object> map = new HashMap<>();
|
final HashMap<String, Object> map = new HashMap<>();
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
|
|||||||
|
|
||||||
// accounting related data validation
|
// accounting related data validation
|
||||||
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
|
||||||
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
|
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4);
|
||||||
|
|
||||||
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
|
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
|
||||||
|
|
||||||
|
|||||||
Executable
+52
@@ -0,0 +1,52 @@
|
|||||||
|
package org.mifosplatform.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||||
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||||
|
|
||||||
|
public class HolidayDetailDTO {
|
||||||
|
|
||||||
|
final boolean isHolidayEnabled;
|
||||||
|
final List<Holiday> holidays;
|
||||||
|
final WorkingDays workingDays;
|
||||||
|
final boolean allowTransactionsOnHoliday;
|
||||||
|
final boolean allowTransactionsOnNonWorkingDay;
|
||||||
|
|
||||||
|
public HolidayDetailDTO(final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||||
|
this.isHolidayEnabled = isHolidayEnabled;
|
||||||
|
this.holidays = holidays;
|
||||||
|
this.workingDays = workingDays;
|
||||||
|
this.allowTransactionsOnHoliday = false;
|
||||||
|
this.allowTransactionsOnNonWorkingDay = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HolidayDetailDTO(final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||||
|
final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay) {
|
||||||
|
this.isHolidayEnabled = isHolidayEnabled;
|
||||||
|
this.holidays = holidays;
|
||||||
|
this.workingDays = workingDays;
|
||||||
|
this.allowTransactionsOnHoliday = allowTransactionsOnHoliday;
|
||||||
|
this.allowTransactionsOnNonWorkingDay = allowTransactionsOnNonWorkingDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHolidayEnabled() {
|
||||||
|
return this.isHolidayEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Holiday> getHolidays() {
|
||||||
|
return this.holidays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorkingDays getWorkingDays() {
|
||||||
|
return this.workingDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowTransactionsOnHoliday() {
|
||||||
|
return this.allowTransactionsOnHoliday;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowTransactionsOnNonWorkingDay() {
|
||||||
|
return this.allowTransactionsOnNonWorkingDay;
|
||||||
|
}
|
||||||
|
}
|
||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
package org.mifosplatform.portfolio.loanaccount.data;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDate;
|
||||||
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
|
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||||
|
|
||||||
|
public class ScheduleGeneratorDTO {
|
||||||
|
|
||||||
|
final LoanScheduleGeneratorFactory loanScheduleFactory;
|
||||||
|
final ApplicationCurrency applicationCurrency;
|
||||||
|
final LocalDate calculatedRepaymentsStartingFromDate;
|
||||||
|
final HolidayDetailDTO holidayDetailDTO;
|
||||||
|
final CalendarInstance calendarInstanceForInterestRecalculation;
|
||||||
|
LocalDate recalculateFrom;
|
||||||
|
final Long overdurPenaltyWaitPeriod;
|
||||||
|
LocalDate lastTransactionDate;
|
||||||
|
|
||||||
|
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||||
|
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
|
||||||
|
this.loanScheduleFactory = loanScheduleFactory;
|
||||||
|
this.applicationCurrency = applicationCurrency;
|
||||||
|
this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate;
|
||||||
|
this.calendarInstanceForInterestRecalculation = null;
|
||||||
|
this.recalculateFrom = null;
|
||||||
|
this.overdurPenaltyWaitPeriod = null;
|
||||||
|
this.lastTransactionDate = null;
|
||||||
|
this.holidayDetailDTO = holidayDetailDTO;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||||
|
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||||
|
final CalendarInstance calendarInstanceForInterestRecalculation, final LocalDate recalculateFrom,
|
||||||
|
final Long overdurPenaltyWaitPeriod, final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
|
this.loanScheduleFactory = loanScheduleFactory;
|
||||||
|
this.applicationCurrency = applicationCurrency;
|
||||||
|
this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate;
|
||||||
|
this.calendarInstanceForInterestRecalculation = calendarInstanceForInterestRecalculation;
|
||||||
|
this.recalculateFrom = recalculateFrom;
|
||||||
|
this.overdurPenaltyWaitPeriod = overdurPenaltyWaitPeriod;
|
||||||
|
this.lastTransactionDate = lastTransactionDate;
|
||||||
|
this.holidayDetailDTO = holidayDetailDTO;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
||||||
|
return this.loanScheduleFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationCurrency getApplicationCurrency() {
|
||||||
|
return this.applicationCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getCalculatedRepaymentsStartingFromDate() {
|
||||||
|
return this.calculatedRepaymentsStartingFromDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CalendarInstance getCalendarInstanceForInterestRecalculation() {
|
||||||
|
return this.calendarInstanceForInterestRecalculation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getRecalculateFrom() {
|
||||||
|
return this.recalculateFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getOverdurPenaltyWaitPeriod() {
|
||||||
|
return this.overdurPenaltyWaitPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPenaltyWaitPeriod() {
|
||||||
|
int penaltyWaitPeriod = 0;
|
||||||
|
if (this.overdurPenaltyWaitPeriod != null) {
|
||||||
|
penaltyWaitPeriod = this.overdurPenaltyWaitPeriod.intValue();
|
||||||
|
}
|
||||||
|
return penaltyWaitPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getLastTransactionDate() {
|
||||||
|
return this.lastTransactionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HolidayDetailDTO getHolidayDetailDTO() {
|
||||||
|
return this.holidayDetailDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecalculateFrom(LocalDate recalculateFrom) {
|
||||||
|
this.recalculateFrom = recalculateFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastTransactionDate(LocalDate lastTransactionDate) {
|
||||||
|
this.lastTransactionDate = lastTransactionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+358
-210
@@ -75,7 +75,9 @@ import org.mifosplatform.portfolio.group.domain.Group;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
import org.mifosplatform.portfolio.loanaccount.command.LoanChargeCommand;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
import org.mifosplatform.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||||
@@ -466,10 +468,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge, final List<Long> existingTransactionIds,
|
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge) {
|
||||||
final List<Long> existingReversedTransactionIds, final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
|
|
||||||
validateLoanIsNotClosed(loanCharge);
|
validateLoanIsNotClosed(loanCharge);
|
||||||
@@ -526,22 +525,14 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
// store Id's of existing loan transactions and existing reversed loan
|
// store Id's of existing loan transactions and existing reversed loan
|
||||||
// transactions
|
// transactions
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
|
||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
|
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
getLastUserTransactionForChargeCalc());
|
||||||
&& (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate()))) {
|
|
||||||
LocalDate recalculateFrom = null;
|
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
} else {
|
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
|
||||||
}
|
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
if (!loanCharge.isDueAtDisbursement()) {
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||||
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
|
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
}
|
}
|
||||||
@@ -652,12 +643,18 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
|
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
|
||||||
final String defaultUserMessage = "This charge with specified due date cannot be added as the it is not in schedule range.";
|
final String defaultUserMessage = "This charge with specified due date cannot be added as the it is not in schedule range.";
|
||||||
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
|
throw new LoanChargeCannotBeAddedException("loanCharge", "specified.due.date.outside.range", defaultUserMessage,
|
||||||
getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
|
getDisbursementDate(), lastRepaymentPeriodDueDate, loanCharge.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate getLastRepaymentPeriodDueDate() {
|
private LocalDate getLastRepaymentPeriodDueDate() {
|
||||||
return this.repaymentScheduleInstallments.get(this.repaymentScheduleInstallments.size() - 1).getDueDate();
|
LocalDate lastRepaymentDate = getDisbursementDate();
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
|
if (!installment.isRecalculatedInterestComponent() && installment.getDueDate().isAfter(lastRepaymentDate)) {
|
||||||
|
lastRepaymentDate = installment.getDueDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastRepaymentDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLoanCharge(final LoanCharge loanCharge) {
|
public void removeLoanCharge(final LoanCharge loanCharge) {
|
||||||
@@ -674,7 +671,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
if (removed) {
|
if (removed) {
|
||||||
charge.setActive(false);
|
charge.setActive(false);
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +691,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
***/
|
***/
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
@@ -751,11 +749,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
***/
|
***/
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||||
} else {
|
} else {
|
||||||
// reprocess loan schedule based on charge been waived.
|
// reprocess loan schedule based on charge been waived.
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
@@ -838,10 +837,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final Integer loanInstallmentNumber, final List<Holiday> holidays, final WorkingDays workingDays,
|
final Integer loanInstallmentNumber, final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge) {
|
||||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation,
|
|
||||||
final Money accruedCharge) {
|
|
||||||
|
|
||||||
validateLoanIsNotClosed(loanCharge);
|
validateLoanIsNotClosed(loanCharge);
|
||||||
|
|
||||||
@@ -887,9 +883,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||||
&& (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate()))) {
|
&& (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate()))) {
|
||||||
LocalDate recalculateFrom = null;
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
// Waive of charges whose due date falls after latest 'repayment'
|
// Waive of charges whose due date falls after latest 'repayment'
|
||||||
// transaction dont require entire loan schedule to be reprocessed.
|
// transaction dont require entire loan schedule to be reprocessed.
|
||||||
@@ -906,11 +900,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
***/
|
***/
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||||
} else {
|
} else {
|
||||||
// reprocess loan schedule based on charge been waived.
|
// reprocess loan schedule based on charge been waived.
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
@@ -1032,34 +1027,74 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
updateLoanScheduleDependentDerivedFields();
|
updateLoanScheduleDependentDerivedFields();
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
resetAccruals();
|
applyAccurals();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method to reverse all the accrual entries
|
* method updates accrual derived fields on installments and reverse the
|
||||||
|
* unprocessed transactions
|
||||||
*/
|
*/
|
||||||
private void resetAccruals() {
|
private void applyAccurals() {
|
||||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return; }
|
Collection<LoanTransaction> accruals = retreiveListOfAccrualTransactions();
|
||||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
for (final LoanTransaction transaction : transactions) {
|
applyPeriodicAccruals(accruals);
|
||||||
if (transaction.isAccrual()) {
|
} else if (isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
transaction.reverse();
|
updateAccrualsForNonPeriodicAccruals(accruals);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAccrualsPresent() {
|
private void applyPeriodicAccruals(final Collection<LoanTransaction> accruals) {
|
||||||
boolean isAccrualsPresent = false;
|
|
||||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return isAccrualsPresent; }
|
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
Money interest = Money.zero(getCurrency());
|
||||||
for (final LoanTransaction transaction : transactions) {
|
Money fee = Money.zero(getCurrency());
|
||||||
if (transaction.isAccrual() && transaction.isNotReversed()) {
|
Money penality = Money.zero(getCurrency());
|
||||||
isAccrualsPresent = true;
|
for (LoanTransaction loanTransaction : accruals) {
|
||||||
break;
|
if (loanTransaction.getTransactionDate().isAfter(installment.getFromDate())
|
||||||
|
&& !loanTransaction.getTransactionDate().isAfter(installment.getDueDate())) {
|
||||||
|
interest = interest.plus(loanTransaction.getInterestPortion(getCurrency()));
|
||||||
|
fee = fee.plus(loanTransaction.getFeeChargesPortion(getCurrency()));
|
||||||
|
penality = penality.plus(loanTransaction.getPenaltyChargesPortion(getCurrency()));
|
||||||
|
if (installment.getFeeChargesCharged(getCurrency()).isLessThan(fee)
|
||||||
|
|| installment.getInterestCharged(getCurrency()).isLessThan(interest)
|
||||||
|
|| installment.getPenaltyChargesCharged(getCurrency()).isLessThan(penality)) {
|
||||||
|
interest = interest.minus(loanTransaction.getInterestPortion(getCurrency()));
|
||||||
|
fee = fee.minus(loanTransaction.getFeeChargesPortion(getCurrency()));
|
||||||
|
penality = penality.minus(loanTransaction.getPenaltyChargesPortion(getCurrency()));
|
||||||
|
loanTransaction.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
installment.updateAccrualPortion(interest, fee, penality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAccrualsForNonPeriodicAccruals(final Collection<LoanTransaction> accruals) {
|
||||||
|
|
||||||
|
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
|
||||||
|
for (LoanTransaction loanTransaction : accruals) {
|
||||||
|
if (loanTransaction.getInterestPortion(getCurrency()).isGreaterThanZero()) {
|
||||||
|
if (loanTransaction.getInterestPortion(getCurrency()).isNotEqualTo(interestApplied)) {
|
||||||
|
loanTransaction.reverse();
|
||||||
|
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
|
||||||
|
getDisbursementDate());
|
||||||
|
this.loanTransactions.add(interestAppliedTransaction);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Set<LoanChargePaidBy> chargePaidBies = loanTransaction.getLoanChargesPaid();
|
||||||
|
for (final LoanChargePaidBy chargePaidBy : chargePaidBies) {
|
||||||
|
LoanCharge loanCharge = chargePaidBy.getLoanCharge();
|
||||||
|
Money chargeAmount = loanCharge.getAmount(getCurrency());
|
||||||
|
if (chargeAmount.isNotEqualTo(loanTransaction.getAmount(getCurrency()))) {
|
||||||
|
loanTransaction.reverse();
|
||||||
|
handleChargeAppliedTransaction(loanCharge, loanTransaction.getTransactionDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isAccrualsPresent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLoanScheduleDependentDerivedFields() {
|
private void updateLoanScheduleDependentDerivedFields() {
|
||||||
@@ -1339,8 +1374,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public void recalculateAllCharges() {
|
public void recalculateAllCharges() {
|
||||||
Set<LoanCharge> charges = this.charges();
|
Set<LoanCharge> charges = this.charges();
|
||||||
|
int penaltyWaitPeriod = 0;
|
||||||
for (final LoanCharge loanCharge : charges) {
|
for (final LoanCharge loanCharge : charges) {
|
||||||
recalculateLoanCharge(loanCharge);
|
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1411,25 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateLoanCharge(final LoanCharge loanCharge) {
|
private void updateOverdueScheduleInstallment(final LoanCharge loanCharge) {
|
||||||
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
if (loanCharge.isOverdueInstallmentCharge() && loanCharge.isActive()) {
|
||||||
|
LoanOverdueInstallmentCharge overdueInstallmentCharge = loanCharge.getOverdueInstallmentCharge();
|
||||||
|
if (overdueInstallmentCharge != null) {
|
||||||
|
Integer installmentNumber = overdueInstallmentCharge.getInstallment().getInstallmentNumber();
|
||||||
|
LoanRepaymentScheduleInstallment installment = fetchRepaymentScheduleInstallment(installmentNumber);
|
||||||
|
overdueInstallmentCharge.updateLoanRepaymentScheduleInstallment(installment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateLoanCharge(final LoanCharge loanCharge, final int penaltyWaitPeriod) {
|
||||||
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
|
if (loanCharge.isOverdueInstallmentCharge()) {
|
||||||
|
amount = calculateOverdueAmountPercentageAppliedTo(loanCharge, penaltyWaitPeriod);
|
||||||
|
} else {
|
||||||
|
amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||||
|
}
|
||||||
|
|
||||||
BigDecimal chargeAmt = BigDecimal.ZERO;
|
BigDecimal chargeAmt = BigDecimal.ZERO;
|
||||||
BigDecimal totalChargeAmt = BigDecimal.ZERO;
|
BigDecimal totalChargeAmt = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
@@ -1390,9 +1443,46 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
if (loanCharge.isActive()) {
|
||||||
totalChargeAmt);
|
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
totalChargeAmt);
|
||||||
|
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateOverdueAmountPercentageAppliedTo(final LoanCharge loanCharge, final int penaltyWaitPeriod) {
|
||||||
|
LoanRepaymentScheduleInstallment installment = loanCharge.getOverdueInstallmentCharge().getInstallment();
|
||||||
|
LocalDate graceDate = LocalDate.now().minusDays(penaltyWaitPeriod);
|
||||||
|
Money amount = Money.zero(getCurrency());
|
||||||
|
if (graceDate.isAfter(installment.getDueDate())) {
|
||||||
|
amount = calculateOverdueAmountPercentageAppliedTo(installment, loanCharge.getChargeCalculation());
|
||||||
|
if (!amount.isGreaterThanZero()) {
|
||||||
|
loanCharge.setActive(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loanCharge.setActive(false);
|
||||||
|
}
|
||||||
|
return amount.getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money calculateOverdueAmountPercentageAppliedTo(LoanRepaymentScheduleInstallment installment,
|
||||||
|
ChargeCalculationType calculationType) {
|
||||||
|
Money amount = Money.zero(getCurrency());
|
||||||
|
switch (calculationType) {
|
||||||
|
case PERCENT_OF_AMOUNT:
|
||||||
|
amount = installment.getPrincipalOutstanding(getCurrency());
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||||
|
amount = installment.getPrincipalOutstanding(getCurrency()).plus(installment.getInterestOutstanding(getCurrency()));
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_INTEREST:
|
||||||
|
amount = installment.getInterestOutstanding(getCurrency());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) {
|
private void updateDisbursementDetails(final JsonCommand command, final Map<String, Object> actualChanges) {
|
||||||
@@ -1588,8 +1678,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// charges are optional
|
// charges are optional
|
||||||
|
int penaltyWaitPeriod = 0;
|
||||||
for (final LoanCharge loanCharge : charges()) {
|
for (final LoanCharge loanCharge : charges()) {
|
||||||
recalculateLoanCharge(loanCharge);
|
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
@@ -1815,11 +1906,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser,
|
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
||||||
final JsonCommand command, final ApplicationCurrency currency, final Map<String, Object> actualChanges,
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
|
||||||
final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay,
|
|
||||||
final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
||||||
LoanStatus.fromInt(this.loanStatus));
|
LoanStatus.fromInt(this.loanStatus));
|
||||||
@@ -1836,15 +1924,15 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
actualChanges.put("dateFormat", command.dateFormat());
|
actualChanges.put("dateFormat", command.dateFormat());
|
||||||
actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
|
actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||||
|
|
||||||
// validate if disbursement date is a holiday or a non-working day
|
// validate if disbursement date is a holiday or a non-working day
|
||||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
validateDisbursementDateIsOnNonWorkingDay(holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
||||||
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||||
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
||||||
LocalDate recalculateFrom = null;
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
@@ -1870,9 +1958,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void regenerateScheduleOnDisbursement(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
||||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule,
|
|
||||||
final LocalDate actualDisbursementDate, BigDecimal emiAmount) {
|
final LocalDate actualDisbursementDate, BigDecimal emiAmount) {
|
||||||
boolean isEmiAmountChanged = false;
|
boolean isEmiAmountChanged = false;
|
||||||
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) {
|
||||||
@@ -1883,8 +1969,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||||
workingDays);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1958,7 +2043,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||||
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
|
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
}
|
}
|
||||||
@@ -2068,25 +2154,20 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
* Ability to regenerate the repayment schedule based on the loans current
|
* Ability to regenerate the repayment schedule based on the loans current
|
||||||
* details/state.
|
* details/state.
|
||||||
*/
|
*/
|
||||||
private void regenerateRepaymentSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
private void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = regenerateScheduleModel(loanScheduleFactory, applicationCurrency,
|
final LoanScheduleModel loanSchedule = regenerateScheduleModel(scheduleGeneratorDTO);
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
|
||||||
|
|
||||||
updateLoanSchedule(loanSchedule);
|
updateLoanSchedule(loanSchedule);
|
||||||
Set<LoanCharge> charges = this.charges();
|
Set<LoanCharge> charges = this.charges();
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
recalculateLoanCharge(loanCharge);
|
recalculateLoanCharge(loanCharge, scheduleGeneratorDTO.getPenaltyWaitPeriod());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanScheduleModel regenerateScheduleModel(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
public LoanScheduleModel regenerateScheduleModel(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod);
|
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||||
|
|
||||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
@@ -2113,14 +2194,14 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
loanVariationTermsData.add(data);
|
loanVariationTermsData.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
|
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(),
|
||||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
loanTermFrequency, loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||||
loanVariationTermsData, getInterestChargedFromDate());
|
loanVariationTermsData, getInterestChargedFromDate());
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
|
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||||
isHolidayEnabled, holidays, workingDays);
|
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||||
return loanSchedule;
|
return loanSchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2220,10 +2301,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return chargesPayment;
|
return chargesPayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> undoDisbursal(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
public Map<String, Object> undoDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final List<Long> existingTransactionIds,
|
||||||
final ApplicationCurrency applicationCurrency, final List<Long> existingTransactionIds,
|
final List<Long> existingReversedTransactionIds) {
|
||||||
final List<Long> existingReversedTransactionIds, final LocalDate calculatedRepaymentsStartingFromDate,
|
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO);
|
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO);
|
||||||
|
|
||||||
@@ -2254,8 +2333,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
// clear off actual disbusrement date so schedule regeneration
|
// clear off actual disbusrement date so schedule regeneration
|
||||||
// uses expected date.
|
// uses expected date.
|
||||||
|
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||||
isHolidayEnabled, holidays, workingDays);
|
|
||||||
if (isDisbueseAmtChanged) {
|
if (isDisbueseAmtChanged) {
|
||||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||||
}
|
}
|
||||||
@@ -2298,38 +2376,35 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
this.loanTermVariations.clear();
|
this.loanTermVariations.clear();
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
|
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction,
|
public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||||
final List<Long> existingReversedTransactionIds, final List<Holiday> holidays, final WorkingDays workingDays,
|
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||||
|
|
||||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||||
waiveInterestTransaction.getTransactionDate());
|
waiveInterestTransaction.getTransactionDate());
|
||||||
|
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||||
|
waiveInterestTransaction.getTransactionDate());
|
||||||
|
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(waiveInterestTransaction,
|
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(waiveInterestTransaction,
|
||||||
loanLifecycleStateMachine, null, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
|
||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction,
|
public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||||
final List<Long> existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
|
final List<Long> existingReversedTransactionIds, boolean isRecoveryRepayment, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay, final boolean isHolidayEnabled,
|
|
||||||
boolean isRecoveryRepayment, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
LoanEvent event = null;
|
LoanEvent event = null;
|
||||||
if (isRecoveryRepayment) {
|
if (isRecoveryRepayment) {
|
||||||
@@ -2338,31 +2413,36 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
|
event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||||
validateAccountStatus(event);
|
validateAccountStatus(event);
|
||||||
validateActivityNotBeforeClientOrGroupTransferDate(event, repaymentTransaction.getTransactionDate());
|
validateActivityNotBeforeClientOrGroupTransferDate(event, repaymentTransaction.getTransactionDate());
|
||||||
|
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(event, repaymentTransaction.getTransactionDate());
|
||||||
validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||||
validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
holidayDetailDTO.getHolidays());
|
||||||
|
validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||||
|
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
|
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction,
|
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction,
|
||||||
loanLifecycleStateMachine, null, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
|
||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
final HolidayDetailDTO holidayDetailDTO, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||||
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_CHARGE_PAYMENT);
|
validateAccountStatus(LoanEvent.LOAN_CHARGE_PAYMENT);
|
||||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
||||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_CHARGE_PAYMENT,
|
||||||
validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
paymentTransaction.getTransactionDate());
|
||||||
|
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||||
|
holidayDetailDTO.getHolidays());
|
||||||
|
validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||||
|
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
|
|
||||||
if (paymentTransaction.getTransactionDate().isAfter(new LocalDate())) {
|
if (paymentTransaction.getTransactionDate().isAfter(new LocalDate())) {
|
||||||
final String errorMessage = "The date on which a loan charge paid cannot be in the future.";
|
final String errorMessage = "The date on which a loan charge paid cannot be in the future.";
|
||||||
@@ -2415,9 +2495,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
private ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(final LoanTransaction loanTransaction,
|
private ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(final LoanTransaction loanTransaction,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
||||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
|
||||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
|
|
||||||
@@ -2516,12 +2594,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
if (reprocess) {
|
if (reprocess) {
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||||
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
|
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
}
|
}
|
||||||
@@ -2607,6 +2685,18 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return repaymentsOrWaivers;
|
return repaymentsOrWaivers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<LoanTransaction> retreiveListOfAccrualTransactions() {
|
||||||
|
final List<LoanTransaction> transactions = new ArrayList<>();
|
||||||
|
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||||
|
if (transaction.isNotReversed() && transaction.isAccrual()) {
|
||||||
|
transactions.add(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
|
||||||
|
Collections.sort(transactions, transactionComparator);
|
||||||
|
return transactions;
|
||||||
|
}
|
||||||
|
|
||||||
private void doPostLoanTransactionChecks(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
private void doPostLoanTransactionChecks(final LocalDate transactionDate, final LoanLifecycleStateMachine loanLifecycleStateMachine) {
|
||||||
|
|
||||||
if (isOverPaid()) {
|
if (isOverPaid()) {
|
||||||
@@ -2767,13 +2857,13 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail,
|
public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail,
|
||||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
||||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final boolean isHolidayEnabled, final boolean allowTransactionsOnNonWorkingDay,
|
|
||||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||||
validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||||
|
holidayDetailDTO.getHolidays());
|
||||||
|
validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||||
|
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||||
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
|
|
||||||
@@ -2803,17 +2893,14 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
|
if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
|
||||||
changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine,
|
changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine,
|
||||||
transactionForAdjustment, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
transactionForAdjustment, scheduleGeneratorDTO);
|
||||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds,
|
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds,
|
||||||
final List<Long> existingReversedTransactionIds, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final WorkingDays workingDays, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
@@ -2825,13 +2912,11 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
LocalDate recalculateFrom = null;
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||||
charges());
|
charges(), getLastUserTransactionForChargeCalc());
|
||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
@@ -2893,15 +2978,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final AppUser currentUser, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
|
||||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
|
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor,
|
loanRepaymentScheduleTransactionProcessor);
|
||||||
calendarInstanceForInterestRecalculation);
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
|
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
|
||||||
|
|
||||||
@@ -2960,25 +3042,20 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return changedTransactionDetail;
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangedTransactionDetail closeDisbursements(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
private ChangedTransactionDetail closeDisbursements(final ScheduleGeneratorDTO scheduleGeneratorDTO,
|
||||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
|
||||||
final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = null;
|
ChangedTransactionDetail changedTransactionDetail = null;
|
||||||
if (isDisbursementAllowed() && atleastOnceDisbursed()) {
|
if (isDisbursementAllowed() && atleastOnceDisbursed()) {
|
||||||
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount());
|
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount());
|
||||||
removeDisbursementDetail();
|
removeDisbursementDetail();
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||||
workingDays);
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
LocalDate recalculateFrom = null;
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||||
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges());
|
allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments, charges(),
|
||||||
|
getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
this.loanTransactions.add(mapEntry.getValue());
|
this.loanTransactions.add(mapEntry.getValue());
|
||||||
@@ -3003,9 +3080,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
|
||||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_CLOSED);
|
validateAccountStatus(LoanEvent.LOAN_CLOSED);
|
||||||
|
|
||||||
@@ -3032,9 +3107,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
|
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor,
|
loanRepaymentScheduleTransactionProcessor);
|
||||||
calendarInstanceForInterestRecalculation);
|
|
||||||
|
|
||||||
LoanTransaction loanTransaction = null;
|
LoanTransaction loanTransaction = null;
|
||||||
if (isOpen()) {
|
if (isOpen()) {
|
||||||
@@ -3596,7 +3670,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
final List<Map<String, Object>> newLoanTransactions = new ArrayList<>();
|
final List<Map<String, Object>> newLoanTransactions = new ArrayList<>();
|
||||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||||
if (transaction.isReversed() && !existingReversedTransactionIds.contains(transaction.getId())) {
|
if (transaction.isReversed() && existingTransactionIds.contains(transaction.getId())
|
||||||
|
&& !existingReversedTransactionIds.contains(transaction.getId())) {
|
||||||
newLoanTransactions.add(transaction.toMapData(currencyData));
|
newLoanTransactions.add(transaction.toMapData(currencyData));
|
||||||
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
||||||
newLoanTransactions.add(transaction.toMapData(currencyData));
|
newLoanTransactions.add(transaction.toMapData(currencyData));
|
||||||
@@ -3621,10 +3696,13 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
if (receivableInterest.isLessThanZero()) {
|
if (receivableInterest.isLessThanZero()) {
|
||||||
receivableInterest = receivableInterest.zero();
|
receivableInterest = receivableInterest.zero();
|
||||||
}
|
}
|
||||||
/* if (transaction.getTransactionDate().isAfter(tillDate) && transaction.isAccrual()) {
|
/*
|
||||||
final String errorMessage = "The date on which a loan is interest waived cannot be in after accrual transactions.";
|
* if (transaction.getTransactionDate().isAfter(tillDate) &&
|
||||||
throw new InvalidLoanStateTransitionException("waive", "cannot.be.after.accrual.date", errorMessage, tillDate);
|
* transaction.isAccrual()) { final String errorMessage =
|
||||||
}*/
|
* "The date on which a loan is interest waived cannot be in after accrual transactions."
|
||||||
|
* ; throw new InvalidLoanStateTransitionException("waive",
|
||||||
|
* "cannot.be.after.accrual.date", errorMessage, tillDate); }
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
return receivableInterest;
|
return receivableInterest;
|
||||||
}
|
}
|
||||||
@@ -3902,6 +3980,57 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateActivityNotBeforeLastTransactionDateForInterestRecalculation(final LoanEvent event, final LocalDate activityDate) {
|
||||||
|
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return; }
|
||||||
|
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
||||||
|
final LocalDate clientOfficeJoiningDate = this.client.getOfficeJoiningLocalDate();
|
||||||
|
if (lastTransactionDate.isAfter(activityDate)) {
|
||||||
|
String errorMessage = null;
|
||||||
|
String action = null;
|
||||||
|
String postfix = null;
|
||||||
|
switch (event) {
|
||||||
|
case LOAN_REPAYMENT_OR_WAIVER:
|
||||||
|
errorMessage = "The date on which a repayment or waiver is made cannot be earlier than last transaction date";
|
||||||
|
action = "repayment.or.waiver";
|
||||||
|
postfix = "cannot.be.made.before.last.transaction.date";
|
||||||
|
break;
|
||||||
|
case WRITE_OFF_OUTSTANDING:
|
||||||
|
errorMessage = "The date on which a write off is made cannot be earlier than last transaction date";
|
||||||
|
action = "writeoff";
|
||||||
|
postfix = "cannot.be.made.before.last.transaction.date";
|
||||||
|
break;
|
||||||
|
case LOAN_CHARGE_PAYMENT:
|
||||||
|
errorMessage = "The date on which a charge payment is made cannot be earlier than last transaction date";
|
||||||
|
action = "charge.payment";
|
||||||
|
postfix = "cannot.be.made.before.last.transaction.date";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw new InvalidLoanStateTransitionException(action, postfix, errorMessage, clientOfficeJoiningDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDate getLastUserTransactionDate() {
|
||||||
|
LocalDate currentTransactionDate = getDisbursementDate();
|
||||||
|
for (final LoanTransaction previousTransaction : this.loanTransactions) {
|
||||||
|
if (!(previousTransaction.isReversed() || previousTransaction.isAccrual())) {
|
||||||
|
if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
|
||||||
|
currentTransactionDate = previousTransaction.getTransactionDate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentTransactionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDate getLastUserTransactionForChargeCalc() {
|
||||||
|
LocalDate lastTransaction = getDisbursementDate();
|
||||||
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
lastTransaction = getLastUserTransactionDate();
|
||||||
|
}
|
||||||
|
return lastTransaction;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<LoanCharge> charges() {
|
public Set<LoanCharge> charges() {
|
||||||
Set<LoanCharge> loanCharges = new HashSet<>();
|
Set<LoanCharge> loanCharges = new HashSet<>();
|
||||||
if (this.charges != null) {
|
if (this.charges != null) {
|
||||||
@@ -4081,9 +4210,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
|
|
||||||
public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
|
public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
|
||||||
final JsonCommand command, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
final JsonCommand command, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||||
final Map<String, Object> actualChanges, final LoanScheduleGeneratorFactory loanScheduleFactory,
|
final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
|
||||||
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
|
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
|
||||||
final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
|
final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
|
||||||
disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate());
|
disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate());
|
||||||
@@ -4095,12 +4222,9 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
|
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
LocalDate recalculateFrom = null;
|
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
|
||||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
} else {
|
} else {
|
||||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||||
workingDays);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
@@ -4108,7 +4232,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||||
charges());
|
charges(), getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
this.loanTransactions.add(mapEntry.getValue());
|
this.loanTransactions.add(mapEntry.getValue());
|
||||||
@@ -4178,31 +4302,40 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
return maturityDate;
|
return maturityDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final ScheduleGeneratorDTO generatorDTO,
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final CalendarInstance calendarInstanceForInterestRecalculation, final List<Long> existingTransactionIds,
|
|
||||||
final List<Long> existingReversedTransactionIds) {
|
|
||||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||||
LocalDate recalculateFrom = null;
|
/*
|
||||||
List<LoanTransaction> loanTransactions = this.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
|
* LocalDate recalculateFrom = null; List<LoanTransaction>
|
||||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
* loanTransactions =
|
||||||
if (recalculateFrom == null || loanTransaction.getTransactionDate().isAfter(recalculateFrom)) {
|
* this.retreiveListOfTransactionsPostDisbursementExcludeAccruals(); for
|
||||||
recalculateFrom = loanTransaction.getTransactionDate();
|
* (LoanTransaction loanTransaction : loanTransactions) { if
|
||||||
}
|
* (recalculateFrom == null ||
|
||||||
}
|
* loanTransaction.getTransactionDate().isAfter(recalculateFrom)) {
|
||||||
|
* recalculateFrom = loanTransaction.getTransactionDate(); } }
|
||||||
|
* generatorDTO.setRecalculateFrom(recalculateFrom);
|
||||||
|
*/
|
||||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, applicationCurrency,
|
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays,
|
|
||||||
calendarInstanceForInterestRecalculation, recalculateFrom);
|
|
||||||
}
|
}
|
||||||
|
return processTransactions();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
|
||||||
|
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
|
||||||
|
return processTransactions();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChangedTransactionDetail processTransactions() {
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||||
charges());
|
charges(), getLastUserTransactionForChargeCalc());
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
mapEntry.getValue().updateLoan(this);
|
mapEntry.getValue().updateLoan(this);
|
||||||
}
|
}
|
||||||
@@ -4216,28 +4349,38 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
|
|
||||||
this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values());
|
this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||||
return changedTransactionDetail;
|
|
||||||
|
|
||||||
|
return changedTransactionDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateRepaymentScheduleWithInterestRecalculation(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
private void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final CalendarInstance calendarInstanceForInterestRecalculation, LocalDate recalculateFrom) {
|
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = getRecalculatedSchedule(loanScheduleFactory, applicationCurrency,
|
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation,
|
generatorDTO.setLastTransactionDate(lastTransactionDate);
|
||||||
recalculateFrom);
|
final LoanScheduleModel loanSchedule = getRecalculatedSchedule(generatorDTO);
|
||||||
if (loanSchedule == null) { return; }
|
if (loanSchedule == null) { return; }
|
||||||
|
|
||||||
updateLoanSchedule(loanSchedule);
|
updateLoanSchedule(loanSchedule);
|
||||||
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate();
|
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate();
|
||||||
|
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
|
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||||
|
final List<LoanTransaction> copyTransactions = new ArrayList<>();
|
||||||
|
for (LoanTransaction loanTransaction : allNonContraTransactionsPostDisbursement) {
|
||||||
|
copyTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
||||||
|
}
|
||||||
|
loanRepaymentScheduleTransactionProcessor.populateDerivedFeildsWithoutReprocess(getDisbursementDate(), copyTransactions,
|
||||||
|
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||||
|
|
||||||
Set<LoanCharge> charges = this.charges();
|
Set<LoanCharge> charges = this.charges();
|
||||||
for (LoanCharge loanCharge : charges) {
|
for (LoanCharge loanCharge : charges) {
|
||||||
if (!loanCharge.isDueAtDisbursement()) {
|
if (!loanCharge.isDueAtDisbursement()) {
|
||||||
if (loanCharge.getDueLocalDate() == null || !lastRepaymentDate.isBefore(loanCharge.getDueLocalDate())) {
|
updateOverdueScheduleInstallment(loanCharge);
|
||||||
recalculateLoanCharge(loanCharge);
|
if (loanCharge.getDueLocalDate() == null || (!lastRepaymentDate.isBefore(loanCharge.getDueLocalDate()))) {
|
||||||
loanCharge.updateWaivedAmount(getCurrency());
|
if (!loanCharge.isWaived() && !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
|
recalculateLoanCharge(loanCharge, generatorDTO.getPenaltyWaitPeriod());
|
||||||
|
loanCharge.updateWaivedAmount(getCurrency());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loanCharge.setActive(false);
|
loanCharge.setActive(false);
|
||||||
}
|
}
|
||||||
@@ -4246,28 +4389,25 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel getRecalculatedSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
private LoanScheduleModel getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final CalendarInstance calendarInstanceForInterestRecalculation, LocalDate recalculateFrom) {
|
|
||||||
|
|
||||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa) { return null; }
|
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || !isOpen()) { return null; }
|
||||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod);
|
final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||||
|
|
||||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
|
|
||||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(applicationCurrency,
|
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO.getApplicationCurrency(),
|
||||||
calculatedRepaymentsStartingFromDate, calendarInstanceForInterestRecalculation);
|
generatorDTO.getCalculatedRepaymentsStartingFromDate(), generatorDTO.getCalendarInstanceForInterestRecalculation());
|
||||||
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||||
.determineProcessor(this.transactionProcessingStrategy);
|
.determineProcessor(this.transactionProcessingStrategy);
|
||||||
|
|
||||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, applicationCurrency,
|
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(),
|
||||||
loanApplicationTerms, charges(), isHolidayEnabled, holidays, workingDays,
|
generatorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||||
retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
loanRepaymentScheduleTransactionProcessor, this.repaymentScheduleInstallments, generatorDTO.getRecalculateFrom(),
|
||||||
this.repaymentScheduleInstallments, recalculateFrom);
|
generatorDTO.getLastTransactionDate(), generatorDTO.getPenaltyWaitPeriod());
|
||||||
return loanSchedule;
|
return loanSchedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4576,4 +4716,12 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
this.termFrequency = termFrequency;
|
this.termFrequency = termFrequency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFeeCompoundingEnabledForInterestRecalculation() {
|
||||||
|
boolean isEnabled = false;
|
||||||
|
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
isEnabled = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod().isFeeCompoundingEnabled();
|
||||||
|
}
|
||||||
|
return isEnabled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+34
-16
@@ -20,6 +20,7 @@ import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomain
|
|||||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||||
|
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||||
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
|
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
|
||||||
@@ -43,7 +44,9 @@ import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
|
|||||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
import org.mifosplatform.portfolio.group.domain.Group;
|
import org.mifosplatform.portfolio.group.domain.Group;
|
||||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualWritePlatformService;
|
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualWritePlatformService;
|
||||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
||||||
@@ -149,6 +152,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
ApplicationCurrency applicationCurrency = null;
|
ApplicationCurrency applicationCurrency = null;
|
||||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate recalculateDueDateChargesFrom = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
@@ -161,12 +167,17 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
calendarInstance);
|
calendarInstance);
|
||||||
|
|
||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
recalculateDueDateChargesFrom);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
|
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
|
||||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday,
|
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
|
||||||
holidays, workingDays, allowTransactionsOnNonWorkingDay, isHolidayEnabled, isRecoveryRepayment, this.loanScheduleFactory,
|
scheduleGeneratorDTO);
|
||||||
applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
|
||||||
|
|
||||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
||||||
|
|
||||||
@@ -259,17 +270,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
|
|
||||||
final LoanTransaction newPaymentTransaction = LoanTransaction.loanPayment(null, loan.getOffice(), paymentAmout, paymentDetail,
|
final LoanTransaction newPaymentTransaction = LoanTransaction.loanPayment(null, loan.getOffice(), paymentAmout, paymentDetail,
|
||||||
transactionDate, txnExternalId, loanTransactionType);
|
transactionDate, txnExternalId, loanTransactionType);
|
||||||
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
|
||||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
|
||||||
transactionDate.toDate(), HolidayStatusType.ACTIVE.getValue());
|
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
|
||||||
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
|
||||||
if (loanTransactionType.isRepaymentAtDisbursement()) {
|
if (loanTransactionType.isRepaymentAtDisbursement()) {
|
||||||
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||||
} else {
|
} else {
|
||||||
|
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
|
||||||
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||||
|
transactionDate.toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||||
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
|
||||||
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
|
||||||
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
loan.makeChargePayment(chargeId, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
|
||||||
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay, newPaymentTransaction,
|
holidayDetailDTO, newPaymentTransaction, installmentNumber);
|
||||||
installmentNumber);
|
|
||||||
}
|
}
|
||||||
saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -422,7 +437,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
@Override
|
@Override
|
||||||
public void recalculateAccruals(Loan loan) {
|
public void recalculateAccruals(Loan loan) {
|
||||||
LocalDate accruedTill = loan.getAccruedTill();
|
LocalDate accruedTill = loan.getAccruedTill();
|
||||||
if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || accruedTill == null || loan.isAccrualsPresent()) { return; }
|
if (!loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() || !loan.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||||
|
|| accruedTill == null || loan.isNpa() || !loan.status().isActive()) { return; }
|
||||||
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
|
Collection<LoanScheduleAccrualData> loanScheduleAccrualDatas = new ArrayList<>();
|
||||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||||
Long loanId = loan.getId();
|
Long loanId = loan.getId();
|
||||||
@@ -436,9 +452,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
CurrencyData currencyData = applicationCurrency.toData();
|
CurrencyData currencyData = applicationCurrency.toData();
|
||||||
Set<LoanCharge> loanCharges = loan.charges();
|
Set<LoanCharge> loanCharges = loan.charges();
|
||||||
BigDecimal accruedInterestIncome = null;
|
|
||||||
BigDecimal accruedFeeIncome = null;
|
|
||||||
BigDecimal accruedPenaltyIncome = null;
|
|
||||||
|
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (!accruedTill.isBefore(installment.getDueDate())
|
if (!accruedTill.isBefore(installment.getDueDate())
|
||||||
@@ -463,7 +476,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(),
|
accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(),
|
||||||
installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment
|
installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment
|
||||||
.getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(),
|
.getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(),
|
||||||
accruedInterestIncome, accruedFeeIncome, accruedPenaltyIncome, currencyData, interestCalculatedFrom, installment
|
installment.getInterestAccrued(currency).getAmount(), installment.getFeeAccrued(currency).getAmount(), installment
|
||||||
|
.getPenaltyAccrued(currency).getAmount(), currencyData, interestCalculatedFrom, installment
|
||||||
.getInterestWaived(currency).getAmount());
|
.getInterestWaived(currency).getAmount());
|
||||||
loanScheduleAccrualDatas.add(accrualData);
|
loanScheduleAccrualDatas.add(accrualData);
|
||||||
|
|
||||||
@@ -471,7 +485,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!loanScheduleAccrualDatas.isEmpty()) {
|
if (!loanScheduleAccrualDatas.isEmpty()) {
|
||||||
this.accrualWritePlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas);
|
String error = this.accrualWritePlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas);
|
||||||
|
if (error.length() > 0) {
|
||||||
|
String globalisationMessageCode = "error.msg.accrual.exception";
|
||||||
|
throw new GeneralPlatformDomainRuleException(globalisationMessageCode, error, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -879,4 +879,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoanOverdueInstallmentCharge getOverdueInstallmentCharge() {
|
||||||
|
return this.overdueInstallmentCharge;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+1
@@ -35,6 +35,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
|||||||
@Table(name = "m_loan_recalculation_details")
|
@Table(name = "m_loan_recalculation_details")
|
||||||
public class LoanInterestRecalculationDetails extends AbstractPersistable<Long> {
|
public class LoanInterestRecalculationDetails extends AbstractPersistable<Long> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
@OneToOne
|
@OneToOne
|
||||||
@JoinColumn(name = "loan_id", nullable = false)
|
@JoinColumn(name = "loan_id", nullable = false)
|
||||||
private Loan loan;
|
private Loan loan;
|
||||||
|
|||||||
+15
-7
@@ -24,19 +24,27 @@ public class LoanOverdueInstallmentCharge extends AbstractPersistable<Long> {
|
|||||||
@ManyToOne(optional = false)
|
@ManyToOne(optional = false)
|
||||||
@JoinColumn(name = "loan_schedule_id", referencedColumnName = "id", nullable = false)
|
@JoinColumn(name = "loan_schedule_id", referencedColumnName = "id", nullable = false)
|
||||||
private LoanRepaymentScheduleInstallment installment;
|
private LoanRepaymentScheduleInstallment installment;
|
||||||
|
|
||||||
@Column(name="frequency_number")
|
@Column(name = "frequency_number")
|
||||||
private Integer frequencyNumber;
|
private Integer frequencyNumber;
|
||||||
|
|
||||||
|
public LoanOverdueInstallmentCharge() {
|
||||||
public LoanOverdueInstallmentCharge(){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanOverdueInstallmentCharge(final LoanCharge loanCharge,final LoanRepaymentScheduleInstallment installment, final Integer frequencyNumber){
|
public LoanOverdueInstallmentCharge(final LoanCharge loanCharge, final LoanRepaymentScheduleInstallment installment,
|
||||||
|
final Integer frequencyNumber) {
|
||||||
this.loancharge = loanCharge;
|
this.loancharge = loanCharge;
|
||||||
this.installment = installment;
|
this.installment = installment;
|
||||||
this.frequencyNumber = frequencyNumber;
|
this.frequencyNumber = frequencyNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateLoanRepaymentScheduleInstallment(LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment) {
|
||||||
|
this.installment = loanRepaymentScheduleInstallment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanRepaymentScheduleInstallment getInstallment() {
|
||||||
|
return this.installment;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+75
-54
@@ -65,7 +65,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "accrual_interest_derived", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal interestAccrued;
|
private BigDecimal interestAccrued;
|
||||||
|
|
||||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal feeChargesCharged;
|
private BigDecimal feeChargesCharged;
|
||||||
|
|
||||||
@@ -78,6 +78,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal feeChargesWaived;
|
private BigDecimal feeChargesWaived;
|
||||||
|
|
||||||
|
@Column(name = "accrual_fee_charges_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal feeAccrued;
|
||||||
|
|
||||||
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal penaltyCharges;
|
private BigDecimal penaltyCharges;
|
||||||
|
|
||||||
@@ -90,6 +93,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal penaltyChargesWaived;
|
private BigDecimal penaltyChargesWaived;
|
||||||
|
|
||||||
|
@Column(name = "accrual_penalty_charges_derived", scale = 6, precision = 19, nullable = true)
|
||||||
|
private BigDecimal penaltyAccrued;
|
||||||
|
|
||||||
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true)
|
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true)
|
||||||
private BigDecimal totalPaidInAdvance;
|
private BigDecimal totalPaidInAdvance;
|
||||||
|
|
||||||
@@ -127,10 +133,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.obligationsMet = false;
|
this.obligationsMet = false;
|
||||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment(final Loan loan) {
|
public LoanRepaymentScheduleInstallment(final Loan loan) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
this.installmentNumber = null;
|
this.installmentNumber = null;
|
||||||
this.fromDate = null;
|
this.fromDate = null;
|
||||||
this.dueDate = null;
|
this.dueDate = null;
|
||||||
this.obligationsMet = false;
|
this.obligationsMet = false;
|
||||||
@@ -204,6 +210,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
return getInterestCharged(currency).minus(interestAccountedFor);
|
return getInterestCharged(currency).minus(interestAccountedFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getInterestAccrued(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.interestAccrued);
|
||||||
|
}
|
||||||
|
|
||||||
public Money getFeeChargesCharged(final MonetaryCurrency currency) {
|
public Money getFeeChargesCharged(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.feeChargesCharged);
|
return Money.of(currency, this.feeChargesCharged);
|
||||||
}
|
}
|
||||||
@@ -226,6 +236,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
return getFeeChargesCharged(currency).minus(feeChargesAccountedFor);
|
return getFeeChargesCharged(currency).minus(feeChargesAccountedFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getFeeAccrued(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.feeAccrued);
|
||||||
|
}
|
||||||
|
|
||||||
public Money getPenaltyChargesCharged(final MonetaryCurrency currency) {
|
public Money getPenaltyChargesCharged(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.penaltyCharges);
|
return Money.of(currency, this.penaltyCharges);
|
||||||
}
|
}
|
||||||
@@ -248,6 +262,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
return getPenaltyChargesCharged(currency).minus(feeChargesAccountedFor);
|
return getPenaltyChargesCharged(currency).minus(feeChargesAccountedFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Money getPenaltyAccrued(final MonetaryCurrency currency) {
|
||||||
|
return Money.of(currency, this.penaltyAccrued);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isInterestDue(final MonetaryCurrency currency) {
|
public boolean isInterestDue(final MonetaryCurrency currency) {
|
||||||
return getInterestOutstanding(currency).isGreaterThanZero();
|
return getInterestOutstanding(currency).isGreaterThanZero();
|
||||||
}
|
}
|
||||||
@@ -264,10 +282,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
public void updateLoan(final Loan loan) {
|
public void updateLoan(final Loan loan) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPartlyPaid() {
|
public boolean isPartlyPaid() {
|
||||||
return !this.obligationsMet && (this.interestPaid != null || this.feeChargesPaid != null
|
return !this.obligationsMet && (this.interestPaid != null || this.feeChargesPaid != null || this.principalCompleted != null);
|
||||||
|| this.principalCompleted != null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isObligationsMet() {
|
public boolean isObligationsMet() {
|
||||||
@@ -507,6 +524,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
|
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateAccrualPortion(final Money interest, final Money feeCharges, final Money penalityCharges) {
|
||||||
|
this.interestAccrued = defaultToNullIfZero(interest.getAmount());
|
||||||
|
this.feeAccrued = defaultToNullIfZero(feeCharges.getAmount());
|
||||||
|
this.penaltyAccrued = defaultToNullIfZero(penalityCharges.getAmount());
|
||||||
|
}
|
||||||
|
|
||||||
public void updateDerivedFields(final MonetaryCurrency currency, final LocalDate actualDisbursementDate) {
|
public void updateDerivedFields(final MonetaryCurrency currency, final LocalDate actualDisbursementDate) {
|
||||||
if (!this.obligationsMet && getTotalOutstanding(currency).isZero()) {
|
if (!this.obligationsMet && getTotalOutstanding(currency).isZero()) {
|
||||||
this.obligationsMet = true;
|
this.obligationsMet = true;
|
||||||
@@ -569,59 +592,57 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
public void setRecalculatedInterestComponent(boolean recalculatedInterestComponent) {
|
public void setRecalculatedInterestComponent(boolean recalculatedInterestComponent) {
|
||||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateInstallmentNumber(final Integer installmentNumber) {
|
|
||||||
if(installmentNumber != null) {
|
|
||||||
this.installmentNumber = installmentNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateInterestCharged(final BigDecimal interestCharged) {
|
|
||||||
this.interestCharged = interestCharged;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateObligationMet(final Boolean obligationMet) {
|
|
||||||
this.obligationsMet = obligationMet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateObligationMetOnDate(final LocalDate obligationsMetOnDate) {
|
|
||||||
this.obligationsMetOnDate = (obligationsMetOnDate != null) ? obligationsMetOnDate.toDate() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateInterestWrittenOff(final BigDecimal interestWrittenOff) {
|
|
||||||
this.interestWrittenOff = interestWrittenOff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updatePrincipal(final BigDecimal principal) {
|
|
||||||
this.principal = principal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Comparator<LoanRepaymentScheduleInstallment> installmentNumberComparator
|
|
||||||
= new Comparator<LoanRepaymentScheduleInstallment>() {
|
|
||||||
|
|
||||||
@Override
|
public void updateInstallmentNumber(final Integer installmentNumber) {
|
||||||
public int compare(LoanRepaymentScheduleInstallment arg0,
|
if (installmentNumber != null) {
|
||||||
LoanRepaymentScheduleInstallment arg1) {
|
this.installmentNumber = installmentNumber;
|
||||||
|
}
|
||||||
return arg0.getInstallmentNumber().compareTo(arg1.getInstallmentNumber());
|
}
|
||||||
}
|
|
||||||
|
public void updateInterestCharged(final BigDecimal interestCharged) {
|
||||||
|
this.interestCharged = interestCharged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateObligationMet(final Boolean obligationMet) {
|
||||||
|
this.obligationsMet = obligationMet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateObligationMetOnDate(final LocalDate obligationsMetOnDate) {
|
||||||
|
this.obligationsMetOnDate = (obligationsMetOnDate != null) ? obligationsMetOnDate.toDate() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateInterestWrittenOff(final BigDecimal interestWrittenOff) {
|
||||||
|
this.interestWrittenOff = interestWrittenOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePrincipal(final BigDecimal principal) {
|
||||||
|
this.principal = principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Comparator<LoanRepaymentScheduleInstallment> installmentNumberComparator = new Comparator<LoanRepaymentScheduleInstallment>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(LoanRepaymentScheduleInstallment arg0, LoanRepaymentScheduleInstallment arg1) {
|
||||||
|
|
||||||
|
return arg0.getInstallmentNumber().compareTo(arg1.getInstallmentNumber());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BigDecimal getTotalPaidInAdvance() {
|
public BigDecimal getTotalPaidInAdvance() {
|
||||||
return this.totalPaidInAdvance;
|
return this.totalPaidInAdvance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getTotalPaidLate() {
|
public BigDecimal getTotalPaidLate() {
|
||||||
return this.totalPaidLate;
|
return this.totalPaidLate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getObligationsMetOnDate() {
|
public LocalDate getObligationsMetOnDate() {
|
||||||
LocalDate obligationsMetOnDate = null;
|
LocalDate obligationsMetOnDate = null;
|
||||||
|
|
||||||
if(this.obligationsMetOnDate != null) {
|
if (this.obligationsMetOnDate != null) {
|
||||||
obligationsMetOnDate = new LocalDate(this.obligationsMetOnDate);
|
obligationsMetOnDate = new LocalDate(this.obligationsMetOnDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obligationsMetOnDate;
|
return obligationsMetOnDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+20
-11
@@ -20,24 +20,29 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
public class LoanRepaymentScheduleProcessingWrapper {
|
public class LoanRepaymentScheduleProcessingWrapper {
|
||||||
|
|
||||||
public void reprocess(final MonetaryCurrency currency, final LocalDate disbursementDate,
|
public void reprocess(final MonetaryCurrency currency, final LocalDate disbursementDate,
|
||||||
final List<LoanRepaymentScheduleInstallment> repaymentPeriods, final Set<LoanCharge> loanCharges) {
|
final List<LoanRepaymentScheduleInstallment> repaymentPeriods, final Set<LoanCharge> loanCharges,
|
||||||
|
final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
Money totalInterest = Money.zero(currency);
|
Money totalInterest = Money.zero(currency);
|
||||||
|
Money totalPrincipal = Money.zero(currency);
|
||||||
for (final LoanRepaymentScheduleInstallment installment : repaymentPeriods) {
|
for (final LoanRepaymentScheduleInstallment installment : repaymentPeriods) {
|
||||||
totalInterest = totalInterest.plus(installment.getInterestCharged(currency));
|
totalInterest = totalInterest.plus(installment.getInterestCharged(currency));
|
||||||
|
totalPrincipal = totalPrincipal.plus(installment.getPrincipal(currency));
|
||||||
}
|
}
|
||||||
LocalDate startDate = disbursementDate;
|
LocalDate startDate = disbursementDate;
|
||||||
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||||
|
|
||||||
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
||||||
currency, period, repaymentPeriods.size(), totalInterest, !period.isRecalculatedInterestComponent());
|
currency, period, repaymentPeriods.size(), totalPrincipal, totalInterest, !period.isRecalculatedInterestComponent(),
|
||||||
|
lastTransactionDate);
|
||||||
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
||||||
currency, !period.isRecalculatedInterestComponent());
|
currency, !period.isRecalculatedInterestComponent());
|
||||||
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||||
|
|
||||||
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency, period, repaymentPeriods.size(), totalInterest, !period.isRecalculatedInterestComponent());
|
loanCharges, currency, period, repaymentPeriods.size(), totalPrincipal, totalInterest,
|
||||||
|
!period.isRecalculatedInterestComponent(), lastTransactionDate);
|
||||||
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||||
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
||||||
@@ -53,7 +58,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
|
|
||||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||||
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, int numberOfRepayments,
|
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, int numberOfRepayments,
|
||||||
final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
final Money totalPrincipal, final Money totalInterest, boolean isInstallmentChargeApplicable,
|
||||||
|
final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
@@ -80,14 +86,15 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||||
|
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(period.getLoan().getPrincpal().getAmount()).add(totalInterest.getAmount());
|
amount = amount.add(totalPrincipal.getAmount()).add(totalInterest.getAmount());
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||||
amount = amount.add(totalInterest.getAmount());
|
amount = amount.add(totalInterest.getAmount());
|
||||||
} else {
|
} else {
|
||||||
amount = amount.add(period.getLoan().getPrincpal().getAmount());
|
amount = amount.add(totalPrincipal.getAmount());
|
||||||
}
|
}
|
||||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||||
cumulative = cumulative.plus(loanChargeAmt);
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
@@ -144,7 +151,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
|
|
||||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, LoanRepaymentScheduleInstallment period,
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, LoanRepaymentScheduleInstallment period,
|
||||||
int numberOfRepayments, Money totalInterest, boolean isInstallmentChargeApplicable) {
|
int numberOfRepayments, final Money totalPrincipal, final Money totalInterest, boolean isInstallmentChargeApplicable,
|
||||||
|
final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
@@ -171,14 +179,15 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||||
|
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(period.getLoan().getPrincpal().getAmount()).add(totalInterest.getAmount());
|
amount = amount.add(totalPrincipal.getAmount()).add(totalInterest.getAmount());
|
||||||
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
} else if (loanCharge.getChargeCalculation().isPercentageOfInterest()) {
|
||||||
amount = amount.add(totalInterest.getAmount());
|
amount = amount.add(totalInterest.getAmount());
|
||||||
} else {
|
} else {
|
||||||
amount = amount.add(period.getLoan().getPrincpal().getAmount());
|
amount = amount.add(totalPrincipal.getAmount());
|
||||||
}
|
}
|
||||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||||
cumulative = cumulative.plus(loanChargeAmt);
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
|
|||||||
+5
-4
@@ -241,7 +241,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
final Money feeChargesWaived, final Money penaltyChargesWaived, final Money unrecognizedCharge) {
|
final Money feeChargesWaived, final Money penaltyChargesWaived, final Money unrecognizedCharge) {
|
||||||
final LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate,
|
final LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate,
|
||||||
null);
|
null);
|
||||||
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived,unrecognizedCharge);
|
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived, unrecognizedCharge);
|
||||||
|
|
||||||
return waiver;
|
return waiver;
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
updateChargesComponents(feeCharges, penaltyCharges);
|
updateChargesComponents(feeCharges, penaltyCharges);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChargesComponents(final Money feeCharges, final Money penaltyCharges) {
|
public void updateChargesComponents(final Money feeCharges, final Money penaltyCharges) {
|
||||||
final MonetaryCurrency currency = feeCharges.getCurrency();
|
final MonetaryCurrency currency = feeCharges.getCurrency();
|
||||||
this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges).getAmount());
|
this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges).getAmount());
|
||||||
this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges).getAmount());
|
this.penaltyChargesPortion = defaultToNullIfZero(getPenaltyChargesPortion(currency).plus(penaltyCharges).getAmount());
|
||||||
@@ -322,7 +322,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).plus(interest).getAmount());
|
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).plus(interest).getAmount());
|
||||||
this.unrecognizedIncomePortion = defaultToNullIfZero(getUnrecognizedIncomePortion(currency).plus(unrecognizedInterest).getAmount());
|
this.unrecognizedIncomePortion = defaultToNullIfZero(getUnrecognizedIncomePortion(currency).plus(unrecognizedInterest).getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void adjustInterestComponent(final MonetaryCurrency currency) {
|
public void adjustInterestComponent(final MonetaryCurrency currency) {
|
||||||
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).minus(getUnrecognizedIncomePortion(currency)).getAmount());
|
this.interestPortion = defaultToNullIfZero(getInterestPortion(currency).minus(getUnrecognizedIncomePortion(currency)).getAmount());
|
||||||
}
|
}
|
||||||
@@ -503,7 +503,8 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
|||||||
}
|
}
|
||||||
return new LoanTransactionData(getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData,
|
return new LoanTransactionData(getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData,
|
||||||
currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion,
|
currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion,
|
||||||
this.penaltyChargesPortion, this.overPaymentPortion, this.externalId, transfer, null, outstandingLoanBalance, unrecognizedIncomePortion);
|
this.penaltyChargesPortion, this.overPaymentPortion, this.externalId, transfer, null, outstandingLoanBalance,
|
||||||
|
unrecognizedIncomePortion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Object> toMapData(final CurrencyData currencyData) {
|
public Map<String, Object> toMapData(final CurrencyData currencyData) {
|
||||||
|
|||||||
+24
-4
@@ -48,7 +48,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
@Override
|
@Override
|
||||||
public ChangedTransactionDetail handleTransaction(final LocalDate disbursementDate,
|
public ChangedTransactionDetail handleTransaction(final LocalDate disbursementDate,
|
||||||
final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges) {
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final LocalDate recalculateChargesFrom) {
|
||||||
|
final boolean reprocessCharges = true;
|
||||||
|
return handleTransaction(disbursementDate, transactionsPostDisbursement, currency, installments, charges, recalculateChargesFrom,
|
||||||
|
reprocessCharges);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChangedTransactionDetail handleTransaction(final LocalDate disbursementDate,
|
||||||
|
final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges,
|
||||||
|
final LocalDate recalculateChargesFrom, boolean reprocessCharges) {
|
||||||
|
|
||||||
if (charges != null) {
|
if (charges != null) {
|
||||||
for (final LoanCharge loanCharge : charges) {
|
for (final LoanCharge loanCharge : charges) {
|
||||||
@@ -65,8 +74,10 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
|
|
||||||
// re-process loan charges over repayment periods (picking up on waived
|
// re-process loan charges over repayment periods (picking up on waived
|
||||||
// loan charges)
|
// loan charges)
|
||||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
if (reprocessCharges) {
|
||||||
wrapper.reprocess(currency, disbursementDate, installments, charges);
|
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||||
|
wrapper.reprocess(currency, disbursementDate, installments, charges, recalculateChargesFrom);
|
||||||
|
}
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
final ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
||||||
final List<LoanTransaction> transactionstoBeProcessed = new ArrayList<>();
|
final List<LoanTransaction> transactionstoBeProcessed = new ArrayList<>();
|
||||||
@@ -199,7 +210,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final Money chargeAmountToProcess,
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final Money chargeAmountToProcess,
|
||||||
final boolean isFeeCharge) {
|
final boolean isFeeCharge) {
|
||||||
// to.
|
// to.
|
||||||
if(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()){
|
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||||
loanTransaction.resetDerivedComponents();
|
loanTransaction.resetDerivedComponents();
|
||||||
}
|
}
|
||||||
Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess);
|
Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess);
|
||||||
@@ -500,4 +511,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChangedTransactionDetail populateDerivedFeildsWithoutReprocess(final LocalDate disbursementDate,
|
||||||
|
final List<LoanTransaction> transactionsPostDisbursement, final MonetaryCurrency currency,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final LocalDate recalculateChargesFrom) {
|
||||||
|
final boolean reprocessCharges = false;
|
||||||
|
return handleTransaction(disbursementDate, transactionsPostDisbursement, currency, installments, charges, recalculateChargesFrom,
|
||||||
|
reprocessCharges);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+10
-4
@@ -23,7 +23,8 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
|||||||
Set<LoanCharge> charges);
|
Set<LoanCharge> charges);
|
||||||
|
|
||||||
ChangedTransactionDetail handleTransaction(LocalDate disbursementDate, List<LoanTransaction> repaymentsOrWaivers,
|
ChangedTransactionDetail handleTransaction(LocalDate disbursementDate, List<LoanTransaction> repaymentsOrWaivers,
|
||||||
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Set<LoanCharge> charges);
|
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Set<LoanCharge> charges,
|
||||||
|
LocalDate recalculateChargesFrom);
|
||||||
|
|
||||||
void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency,
|
void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency,
|
||||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||||
@@ -31,13 +32,18 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
|||||||
List<RecalculationDetail> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
List<RecalculationDetail> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
Map<LocalDate, LocalDate> recalculationDates, LoanTransaction preCloseTransaction);
|
Map<LocalDate, LocalDate> recalculationDates, LoanTransaction preCloseTransaction);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in interest recalculation to introduce new interest only installment.
|
* Used in interest recalculation to introduce new interest only
|
||||||
|
* installment.
|
||||||
*/
|
*/
|
||||||
boolean isInterestFirstRepaymentScheduleTransactionProcessor();
|
boolean isInterestFirstRepaymentScheduleTransactionProcessor();
|
||||||
|
|
||||||
void applyTransaction(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
void applyTransaction(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments);
|
List<LoanRepaymentScheduleInstallment> installments);
|
||||||
|
|
||||||
|
ChangedTransactionDetail populateDerivedFeildsWithoutReprocess(LocalDate disbursementDate,
|
||||||
|
List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges, LocalDate recalculateChargesFrom);
|
||||||
|
|
||||||
}
|
}
|
||||||
+195
-81
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -18,14 +19,13 @@ import java.util.TreeMap;
|
|||||||
|
|
||||||
import org.joda.time.Days;
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
|
||||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
@@ -49,22 +49,21 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
|
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
public LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
final List<RecalculationDetail> diffAmt = null;
|
final List<RecalculationDetail> diffAmt = null;
|
||||||
final LocalDate prepayDate = null;
|
final LocalDate prepayDate = null;
|
||||||
return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt,
|
final LocalDate lastTransactionDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
prepayDate);
|
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, diffAmt, prepayDate, lastTransactionDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, List<RecalculationDetail> diffAmt,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, List<RecalculationDetail> diffAmt, LocalDate prepayDate) {
|
final LocalDate prepayDate, final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
|
final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency();
|
||||||
// 1. generate list of proposed schedule due dates
|
// 1. generate list of proposed schedule due dates
|
||||||
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);
|
||||||
workingDays);
|
|
||||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||||
|
|
||||||
// 2. determine the total charges due at time of disbursement
|
// 2. determine the total charges due at time of disbursement
|
||||||
@@ -144,7 +143,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
isFirstRepayment);
|
isFirstRepayment);
|
||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
isHolidayEnabled, holidays, workingDays);
|
holidayDetailDTO);
|
||||||
scheduledDueDateAsPerFrequency = scheduledDueDate;
|
scheduledDueDateAsPerFrequency = scheduledDueDate;
|
||||||
} else {
|
} else {
|
||||||
recalculatedInterestComponent = true;
|
recalculatedInterestComponent = true;
|
||||||
@@ -153,7 +152,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
LocalDate actualRepayment = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
|
LocalDate actualRepayment = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
|
||||||
loanApplicationTerms, isFirstRepayment);
|
loanApplicationTerms, isFirstRepayment);
|
||||||
scheduledDueDateAsPerFrequency = this.scheduledDateGenerator.adjustRepaymentDate(actualRepayment, loanApplicationTerms,
|
scheduledDueDateAsPerFrequency = this.scheduledDateGenerator.adjustRepaymentDate(actualRepayment, loanApplicationTerms,
|
||||||
isHolidayEnabled, holidays, workingDays);
|
holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
|
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
|
||||||
@@ -488,10 +487,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
|
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
|
||||||
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent(),
|
||||||
|
lastTransactionDate);
|
||||||
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent(),
|
||||||
|
lastTransactionDate);
|
||||||
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
||||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
||||||
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount());
|
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount());
|
||||||
@@ -549,8 +550,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
final WorkingDays workingDays) {
|
|
||||||
|
|
||||||
final Loan loan = loanRescheduleRequest.getLoan();
|
final Loan loan = loanRescheduleRequest.getLoan();
|
||||||
final LoanSummary loanSummary = loan.getSummary();
|
final LoanSummary loanSummary = loan.getSummary();
|
||||||
@@ -696,8 +696,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// update the number of repayments
|
// update the number of repayments
|
||||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||||
|
|
||||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);
|
||||||
workingDays);
|
|
||||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||||
|
|
||||||
if (newInterestRate != null) {
|
if (newInterestRate != null) {
|
||||||
@@ -739,7 +738,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||||
isHolidayEnabled, holidays, workingDays);
|
holidayDetailDTO);
|
||||||
|
|
||||||
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
|
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
|
||||||
|
|
||||||
@@ -936,7 +935,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||||
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
||||||
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments,
|
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments,
|
||||||
boolean isInstallmentChargeApplicable) {
|
boolean isInstallmentChargeApplicable, final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
@@ -963,7 +962,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||||
|
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||||
@@ -986,7 +986,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
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 PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
||||||
final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments, boolean isInstallmentChargeApplicable) {
|
final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments, boolean isInstallmentChargeApplicable,
|
||||||
|
final LocalDate lastTransactionDate) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
@@ -1013,7 +1014,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||||
|
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
amount = amount.add(principalDisbursed.getAmount()).add(totalInterestChargedForFullLoanTerm.getAmount());
|
||||||
@@ -1039,11 +1041,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* of over due installment should be collected before collecting principal )
|
* of over due installment should be collected before collecting principal )
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom) {
|
final List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom, final LocalDate lastTransactionDate,
|
||||||
|
final int penaltyWaitPeriod) {
|
||||||
|
|
||||||
LoanScheduleModel loanScheduleModel = null;
|
LoanScheduleModel loanScheduleModel = null;
|
||||||
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
||||||
@@ -1064,8 +1066,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
installments.removeAll(removeInstallments);
|
installments.removeAll(removeInstallments);
|
||||||
if (previousSchedule == null) {
|
if (previousSchedule == null) {
|
||||||
loanScheduleModel = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays,
|
loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO);
|
||||||
workingDays);
|
|
||||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,10 +1083,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
int installmentNumber = findLastProcessedInstallmentNumber(installments, processTransactionsForInterestCompound);
|
int installmentNumber = findLastProcessedInstallmentNumber(installments, processTransactionsForInterestCompound);
|
||||||
List<LoanTransaction> processTransactions = processTransactions(transactions, processTransactionsForInterestCompound);
|
List<LoanTransaction> processTransactions = processTransactions(transactions, processTransactionsForInterestCompound);
|
||||||
if (processTransactions.size() > 0) {
|
if (processTransactions.size() > 0) {
|
||||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, currency,
|
||||||
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, false,
|
recalculationDetails, retainRecalculationDetails, false, installmentNumber, processTransactions,
|
||||||
installmentNumber, processTransactions, preCloseTransaction);
|
preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||||
}
|
}
|
||||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
processTransactionsForInterestCompound = getNextRecalculateFromDate(transactions, processTransactionsForInterestCompound);
|
processTransactionsForInterestCompound = getNextRecalculateFromDate(transactions, processTransactionsForInterestCompound);
|
||||||
@@ -1097,10 +1098,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
||||||
if (loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()
|
if (loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()
|
||||||
&& processTransactions.size() > 0) {
|
&& processTransactions.size() > 0) {
|
||||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, currency,
|
||||||
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, true,
|
recalculationDetails, retainRecalculationDetails, true, installmentNumber, processTransactions,
|
||||||
installmentNumber, processTransactions, preCloseTransaction);
|
preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||||
if (loanScheduleModel != null) {
|
if (loanScheduleModel != null) {
|
||||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
}
|
}
|
||||||
@@ -1114,9 +1115,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
recalculationDetails.addAll(earlypayments);
|
recalculationDetails.addAll(earlypayments);
|
||||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
loanScheduleModel = recalculateInstallment(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, processTransactions,
|
||||||
processTransactions, installmentNumber, recalculationDetails, preCloseTransaction);
|
installmentNumber, recalculationDetails, preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||||
if (loanScheduleModel != null) {
|
if (loanScheduleModel != null) {
|
||||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
}
|
}
|
||||||
@@ -1130,14 +1131,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
/**
|
/**
|
||||||
* Method identifies interest only repayment periods
|
* Method identifies interest only repayment periods
|
||||||
*/
|
*/
|
||||||
private LoanScheduleModel createInterestOnlyRecalculationDetails(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel createInterestOnlyRecalculationDetails(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||||
final List<RecalculationDetail> recalculationDetails, final Map<LocalDate, RecalculationDetail> retainRecalculationDetails,
|
final List<RecalculationDetail> recalculationDetails, final Map<LocalDate, RecalculationDetail> retainRecalculationDetails,
|
||||||
boolean recalculate, int installmentNumber, List<LoanTransaction> processTransactions, LoanTransaction preCloseTransaction) {
|
boolean recalculate, int installmentNumber, List<LoanTransaction> processTransactions, LoanTransaction preCloseTransaction,
|
||||||
|
final LocalDate lastTransactionDate, final int penaltyWaitPeriod, final List<LoanTransaction> actualTransactions) {
|
||||||
LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1);
|
LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1);
|
||||||
if (!(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver())) {
|
if (!(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver())) {
|
||||||
processTransactions.add(loanTransaction);
|
processTransactions.add(loanTransaction);
|
||||||
@@ -1187,10 +1188,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
recalculationDetails.add(recalculationDetail);
|
recalculationDetails.add(recalculationDetail);
|
||||||
if (recalculate) {
|
if (recalculate) {
|
||||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
loanScheduleModel = recalculateInstallment(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
||||||
loanScheduleModel, installments, processTransactions, installmentNumber, recalculationDetails,
|
processTransactions, installmentNumber, recalculationDetails, preCloseTransaction, lastTransactionDate,
|
||||||
preCloseTransaction);
|
penaltyWaitPeriod, actualTransactions);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1221,19 +1222,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* @param preCloseTransaction
|
* @param preCloseTransaction
|
||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel recalculateInstallment(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> processTransactions, final int skipRecalculation,
|
List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> processTransactions, final int skipRecalculation,
|
||||||
final List<RecalculationDetail> recalculationDetails, LoanTransaction preCloseTransaction) {
|
final List<RecalculationDetail> recalculationDetails, LoanTransaction preCloseTransaction, final LocalDate lastTransactionDate,
|
||||||
|
final int penaltyWaitPeriod, final List<LoanTransaction> actualTransactions) {
|
||||||
int processInstallmentsFrom = 0;
|
int processInstallmentsFrom = 0;
|
||||||
Integer lastInstallmentNumber = previousSchedule.size();
|
Integer lastInstallmentNumber = previousSchedule.size();
|
||||||
while (processInstallmentsFrom < lastInstallmentNumber) {
|
while (processInstallmentsFrom < lastInstallmentNumber) {
|
||||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||||
isHolidayEnabled, holidays, workingDays, processTransactions, loanRepaymentScheduleTransactionProcessor,
|
processTransactions, loanRepaymentScheduleTransactionProcessor, processInstallmentsFrom, installments,
|
||||||
processInstallmentsFrom, installments, recalculationDetails, skipRecalculation, preCloseTransaction);
|
recalculationDetails, skipRecalculation, preCloseTransaction, lastTransactionDate, penaltyWaitPeriod,
|
||||||
|
actualTransactions);
|
||||||
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
||||||
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
||||||
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
||||||
@@ -1250,12 +1252,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* @param preCloseTransaction
|
* @param preCloseTransaction
|
||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private RecalculatedSchedule recalculateInterest(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
||||||
final int skipRecalculation, final LoanTransaction preCloseTransaction) {
|
final int skipRecalculation, final LoanTransaction preCloseTransaction, final LocalDate lastTransactionDate,
|
||||||
|
final int penaltyWaitPeriod, final List<LoanTransaction> actualTransactions) {
|
||||||
boolean processRecalculate = false;
|
boolean processRecalculate = false;
|
||||||
int processedInstallmentNumber = installmentNumber;
|
int processedInstallmentNumber = installmentNumber;
|
||||||
|
|
||||||
@@ -1282,15 +1284,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
List<LoanTransaction> transactionsForInstallment = new ArrayList<>();
|
List<LoanTransaction> transactionsForInstallment = new ArrayList<>();
|
||||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||||
LocalDate transactionsDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
LocalDate transactionsDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
||||||
isHolidayEnabled, holidays, workingDays);
|
holidayDetailDTO);
|
||||||
for (LoanTransaction loanTransaction : transactions) {
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
LocalDate loantransactionDate = loanTransaction.getTransactionDate();
|
LocalDate loantransactionDate = loanTransaction.getTransactionDate();
|
||||||
if (!loantransactionDate.isAfter(transactionsDate)) {
|
if (!loantransactionDate.isAfter(transactionsDate)) {
|
||||||
transactionsForInstallment.add(loanTransaction);
|
transactionsForInstallment.add(loanTransaction);
|
||||||
recalculationDates.put(
|
recalculationDates.put(loantransactionDate,
|
||||||
loantransactionDate,
|
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, holidayDetailDTO));
|
||||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, isHolidayEnabled, holidays,
|
|
||||||
workingDays));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1321,15 +1321,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// this block is to identify late payment based on the rest
|
// this block is to identify late payment based on the rest
|
||||||
// calculation date
|
// calculation date
|
||||||
while (totalOutstanding.isGreaterThanZero() && startDate.isBefore(LocalDate.now())) {
|
while (totalOutstanding.isGreaterThanZero() && startDate.isBefore(LocalDate.now())) {
|
||||||
LocalDate recalculateFrom = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, isHolidayEnabled,
|
LocalDate recalculateFrom = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
|
||||||
holidays, workingDays);
|
LocalDate recalcualteTill = getNextRestScheduleDate(recalculateFrom, loanApplicationTerms, holidayDetailDTO);
|
||||||
LocalDate recalcualteTill = getNextRestScheduleDate(recalculateFrom, loanApplicationTerms, isHolidayEnabled, holidays,
|
|
||||||
workingDays);
|
|
||||||
if (reduceStartDate) {
|
if (reduceStartDate) {
|
||||||
startDate = startDate.minusDays(1);
|
startDate = startDate.minusDays(1);
|
||||||
}
|
}
|
||||||
applyRest(transactions, startDate, recalculateFrom, installment, loanRepaymentScheduleTransactionProcessor, currency,
|
applyRest(transactions, startDate, recalculateFrom, installment, loanRepaymentScheduleTransactionProcessor, currency,
|
||||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays, installments, preCloseTransaction);
|
loanApplicationTerms, holidayDetailDTO, installments, preCloseTransaction);
|
||||||
totalOutstanding = installment.getTotalOutstanding(currency);
|
totalOutstanding = installment.getTotalOutstanding(currency);
|
||||||
if (totalOutstanding.isGreaterThanZero()) {
|
if (totalOutstanding.isGreaterThanZero()) {
|
||||||
Money latepaymentoutstanding = installment.getPrincipalOutstanding(currency);
|
Money latepaymentoutstanding = installment.getPrincipalOutstanding(currency);
|
||||||
@@ -1373,12 +1371,130 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
if (preCloseTransaction != null) {
|
if (preCloseTransaction != null) {
|
||||||
prepayDate = preCloseTransaction.getTransactionDate();
|
prepayDate = preCloseTransaction.getTransactionDate();
|
||||||
}
|
}
|
||||||
model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays,
|
model = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, recalculationDetails, prepayDate, lastTransactionDate);
|
||||||
recalculationDetails, prepayDate);
|
model = updateOverDueCharges(loanCharges, model, lastTransactionDate, currency, loanRepaymentScheduleTransactionProcessor,
|
||||||
|
loanApplicationTerms.getExpectedDisbursementDate(), penaltyWaitPeriod, actualTransactions);
|
||||||
}
|
}
|
||||||
return new RecalculatedSchedule(model, processedInstallmentNumber);
|
return new RecalculatedSchedule(model, processedInstallmentNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoanScheduleModel updateOverDueCharges(final Set<LoanCharge> loanCharges, final LoanScheduleModel model,
|
||||||
|
LocalDate lastTransactionDate, final MonetaryCurrency currency,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LocalDate disbursementDate,
|
||||||
|
final int penaltyWaitPeriod, final List<LoanTransaction> transactions) {
|
||||||
|
Set<LoanCharge> chargesForUpdate = new HashSet<>();
|
||||||
|
LoanScheduleModel loanScheduleModel = model;
|
||||||
|
for (LoanCharge loanCharge : loanCharges) {
|
||||||
|
if (loanCharge.isActive() && loanCharge.isOverdueInstallmentCharge() && loanCharge.isPenaltyCharge()
|
||||||
|
&& loanCharge.getChargeCalculation().isPercentageBased() && !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||||
|
chargesForUpdate.add(loanCharge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chargesForUpdate.isEmpty()) {
|
||||||
|
LocalDate graceDate = LocalDate.now().minusDays(penaltyWaitPeriod);
|
||||||
|
Collection<LoanScheduleModelPeriod> periods = model.getPeriods();
|
||||||
|
BigDecimal totalPenaltyChargesCharged = model.getTotalPenaltyChargesCharged();
|
||||||
|
for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
|
||||||
|
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
|
||||||
|
Money overdueChargeForInstallment = overDuePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
|
loanScheduleModelPeriod.periodDueDate(), chargesForUpdate, currency);
|
||||||
|
totalPenaltyChargesCharged = totalPenaltyChargesCharged.subtract(overdueChargeForInstallment.getAmount());
|
||||||
|
BigDecimal feeChargesForInstallment = BigDecimal.ZERO;
|
||||||
|
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment, overdueChargeForInstallment.negated().getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentSchedule(model);
|
||||||
|
List<LoanTransaction> loanTransactions = copyTransactions(transactions);
|
||||||
|
loanRepaymentScheduleTransactionProcessor.populateDerivedFeildsWithoutReprocess(disbursementDate, loanTransactions, currency,
|
||||||
|
installments, loanCharges, lastTransactionDate);
|
||||||
|
Map<Integer, LoanRepaymentScheduleInstallment> installmentMap = getRepaymentsAsMap(installments);
|
||||||
|
|
||||||
|
for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
|
||||||
|
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
|
||||||
|
LoanRepaymentScheduleInstallment installment = installmentMap.get(loanScheduleModelPeriod.periodNumber());
|
||||||
|
Money overdueChargeForInstallment = Money.zero(currency);
|
||||||
|
overdueChargeForInstallment = cumulativeOverDuePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
|
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, installment.getPrincipalOutstanding(currency)
|
||||||
|
.getAmount(), installment.getInterestOutstanding(currency).getAmount(), graceDate.isAfter(installment
|
||||||
|
.getDueDate()));
|
||||||
|
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(overdueChargeForInstallment.getAmount());
|
||||||
|
BigDecimal feeChargesForInstallment = BigDecimal.ZERO;
|
||||||
|
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment, overdueChargeForInstallment.getAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loanScheduleModel = LoanScheduleModel.withOverdueChargeUpdation(periods, model, totalPenaltyChargesCharged);
|
||||||
|
}
|
||||||
|
return loanScheduleModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Integer, LoanRepaymentScheduleInstallment> getRepaymentsAsMap(List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
|
Map<Integer, LoanRepaymentScheduleInstallment> installmentMap = new HashMap<>();
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
installmentMap.put(installment.getInstallmentNumber(), installment);
|
||||||
|
}
|
||||||
|
return installmentMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<LoanTransaction> copyTransactions(List<LoanTransaction> transactions) {
|
||||||
|
List<LoanTransaction> loanTransactions = new ArrayList<>();
|
||||||
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
|
if (loanTransaction.isNotReversed() && !loanTransaction.isAccrual()) {
|
||||||
|
LoanTransaction transaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
|
loanTransactions.add(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loanTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money cumulativeOverDuePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency, final BigDecimal principalOverdue,
|
||||||
|
final BigDecimal interestOverdue, final boolean recalculate) {
|
||||||
|
|
||||||
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
|
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
|
if (recalculate) {
|
||||||
|
switch (loanCharge.getChargeCalculation()) {
|
||||||
|
case PERCENT_OF_AMOUNT:
|
||||||
|
amount = amount.add(principalOverdue);
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_AMOUNT_AND_INTEREST:
|
||||||
|
amount = amount.add(principalOverdue).add(interestOverdue);
|
||||||
|
break;
|
||||||
|
case PERCENT_OF_INTEREST:
|
||||||
|
amount = amount.add(interestOverdue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||||
|
cumulative = cumulative.plus(loanChargeAmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cumulative;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Money overDuePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||||
|
final MonetaryCurrency monetaryCurrency) {
|
||||||
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
|
if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)) {
|
||||||
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cumulative;
|
||||||
|
}
|
||||||
|
|
||||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentSchedule(LoanScheduleModel model) {
|
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentSchedule(LoanScheduleModel model) {
|
||||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
||||||
@@ -1404,18 +1520,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
||||||
LoanRepaymentScheduleInstallment installment,
|
LoanRepaymentScheduleInstallment installment,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
|
||||||
final WorkingDays workingDays, final List<LoanRepaymentScheduleInstallment> installments, LoanTransaction preCloseTransaction) {
|
final List<LoanRepaymentScheduleInstallment> installments, LoanTransaction preCloseTransaction) {
|
||||||
List<LoanTransaction> transactions = new ArrayList<>();
|
List<LoanTransaction> transactions = new ArrayList<>();
|
||||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||||
for (LoanTransaction transaction : loanTransactions) {
|
for (LoanTransaction transaction : loanTransactions) {
|
||||||
LocalDate loantransactionDate = transaction.getTransactionDate();
|
LocalDate loantransactionDate = transaction.getTransactionDate();
|
||||||
if (loantransactionDate.isAfter(from) && !transaction.getTransactionDate().isAfter(to)) {
|
if (loantransactionDate.isAfter(from) && !transaction.getTransactionDate().isAfter(to)) {
|
||||||
transactions.add(transaction);
|
transactions.add(transaction);
|
||||||
recalculationDates.put(
|
recalculationDates.put(loantransactionDate,
|
||||||
loantransactionDate,
|
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, holidayDetailDTO));
|
||||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, isHolidayEnabled, holidays,
|
|
||||||
workingDays));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1424,11 +1538,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
LocalDate nextScheduleDate = null;
|
LocalDate nextScheduleDate = null;
|
||||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||||
nextScheduleDate = this.scheduledDateGenerator.generateNextScheduleDateStartingFromDisburseDate(startDate,
|
nextScheduleDate = this.scheduledDateGenerator.generateNextScheduleDateStartingFromDisburseDate(startDate,
|
||||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
loanApplicationTerms, holidayDetailDTO);
|
||||||
} else {
|
} else {
|
||||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||||
nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
|
nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
|
||||||
|
|||||||
+11
-14
@@ -5,24 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.joda.time.Days;
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.joda.time.Months;
|
import org.joda.time.Months;
|
||||||
import org.joda.time.Weeks;
|
import org.joda.time.Weeks;
|
||||||
import org.joda.time.Years;
|
import org.joda.time.Years;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.holiday.service.HolidayUtil;
|
import org.mifosplatform.organisation.holiday.service.HolidayUtil;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
|
||||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
|
|
||||||
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalDate getLastRepaymentDate(final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled,
|
public LocalDate getLastRepaymentDate(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
|
|
||||||
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
||||||
|
|
||||||
@@ -32,7 +28,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
|||||||
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
|
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
|
||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
}
|
}
|
||||||
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, holidayDetailDTO);
|
||||||
return lastRepaymentDate;
|
return lastRepaymentDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +48,23 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms,
|
public LocalDate adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate, final LoanApplicationTerms loanApplicationTerms,
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
|
||||||
LocalDate adjustedDate = dueRepaymentPeriodDate;
|
LocalDate adjustedDate = dueRepaymentPeriodDate;
|
||||||
/**
|
/**
|
||||||
* Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
|
* Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
|
||||||
*/
|
*/
|
||||||
// recursively check for the next working day.
|
// recursively check for the next working day.
|
||||||
while (WorkingDaysUtil.isNonWorkingDay(workingDays, adjustedDate)) {
|
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDate)) {
|
||||||
|
|
||||||
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||||
loanApplicationTerms.getRepaymentEvery(), adjustedDate);
|
loanApplicationTerms.getRepaymentEvery(), adjustedDate);
|
||||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate, workingDays);
|
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
|
||||||
|
holidayDetailDTO.getWorkingDays());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHolidayEnabled) {
|
if (holidayDetailDTO.isHolidayEnabled()) {
|
||||||
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidays);
|
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays());
|
||||||
}
|
}
|
||||||
return adjustedDate;
|
return adjustedDate;
|
||||||
}
|
}
|
||||||
@@ -161,7 +158,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate,
|
public LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate,
|
||||||
LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled, List<Holiday> holidays, WorkingDays workingDays) {
|
LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||||
|
|
||||||
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
|
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
boolean isFirstRepayment = true;
|
boolean isFirstRepayment = true;
|
||||||
@@ -169,7 +166,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
|||||||
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
|
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
|
||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
}
|
}
|
||||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO);
|
||||||
return generatedDate;
|
return generatedDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+42
-39
@@ -110,7 +110,7 @@ public final class LoanApplicationTerms {
|
|||||||
private BigDecimal maxOutstandingBalance;
|
private BigDecimal maxOutstandingBalance;
|
||||||
|
|
||||||
private List<LoanTermVariationsData> emiAmountVariations;
|
private List<LoanTermVariationsData> emiAmountVariations;
|
||||||
|
|
||||||
private Money totalInterestDue;
|
private Money totalInterestDue;
|
||||||
|
|
||||||
private final DaysInMonthType daysInMonthType;
|
private final DaysInMonthType daysInMonthType;
|
||||||
@@ -505,7 +505,7 @@ public final class LoanApplicationTerms {
|
|||||||
final int periodsElapsed = 0;
|
final int periodsElapsed = 0;
|
||||||
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
||||||
double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
||||||
|
|
||||||
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,16 +521,16 @@ public final class LoanApplicationTerms {
|
|||||||
case FLAT:
|
case FLAT:
|
||||||
final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
|
final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
|
||||||
totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
|
totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case DECLINING_BALANCE:
|
case DECLINING_BALANCE:
|
||||||
break;
|
break;
|
||||||
case INVALID:
|
case INVALID:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.totalInterestDue != null) {
|
if (this.totalInterestDue != null) {
|
||||||
totalInterestDue = this.totalInterestDue;
|
totalInterestDue = this.totalInterestDue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalInterestDue;
|
return totalInterestDue;
|
||||||
@@ -544,7 +544,7 @@ public final class LoanApplicationTerms {
|
|||||||
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
||||||
|
|
||||||
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
||||||
|
|
||||||
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||||
.multiply(loanTermFrequencyBigDecimal);
|
.multiply(loanTermFrequencyBigDecimal);
|
||||||
}
|
}
|
||||||
@@ -1078,59 +1078,62 @@ public final class LoanApplicationTerms {
|
|||||||
return numberOfPeriods;
|
return numberOfPeriods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public RecalculationFrequencyType getRecalculationFrequencyType() {
|
public RecalculationFrequencyType getRecalculationFrequencyType() {
|
||||||
return this.recalculationFrequencyType;
|
return this.recalculationFrequencyType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateNumberOfRepayments(final Integer numberOfRepayments) {
|
public void updateNumberOfRepayments(final Integer numberOfRepayments) {
|
||||||
this.numberOfRepayments = numberOfRepayments;
|
this.numberOfRepayments = numberOfRepayments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePrincipalGrace(final Integer principalGrace) {
|
public void updatePrincipalGrace(final Integer principalGrace) {
|
||||||
this.principalGrace = principalGrace;
|
this.principalGrace = principalGrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateInterestPaymentGrace(final Integer interestPaymentGrace) {
|
public void updateInterestPaymentGrace(final Integer interestPaymentGrace) {
|
||||||
this.interestPaymentGrace = interestPaymentGrace;
|
this.interestPaymentGrace = interestPaymentGrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
|
public void updateInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
|
||||||
if(interestRatePerPeriod != null) {
|
if (interestRatePerPeriod != null) {
|
||||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateAnnualNominalInterestRate(BigDecimal annualNominalInterestRate) {
|
public void updateAnnualNominalInterestRate(BigDecimal annualNominalInterestRate) {
|
||||||
if(annualNominalInterestRate != null) {
|
if (annualNominalInterestRate != null) {
|
||||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getInterestRatePerPeriod() {
|
public BigDecimal getInterestRatePerPeriod() {
|
||||||
return this.interestRatePerPeriod;
|
return this.interestRatePerPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getAnnualNominalInterestRate() {
|
public BigDecimal getAnnualNominalInterestRate() {
|
||||||
return this.annualNominalInterestRate;
|
return this.annualNominalInterestRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateInterestChargedFromDate(LocalDate interestChargedFromDate) {
|
public void updateInterestChargedFromDate(LocalDate interestChargedFromDate) {
|
||||||
if(interestChargedFromDate != null) {
|
if (interestChargedFromDate != null) {
|
||||||
this.interestChargedFromDate = interestChargedFromDate;
|
this.interestChargedFromDate = interestChargedFromDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateLoanTermFrequency(Integer loanTermFrequency) {
|
public void updateLoanTermFrequency(Integer loanTermFrequency) {
|
||||||
if(loanTermFrequency != null) {
|
if (loanTermFrequency != null) {
|
||||||
this.loanTermFrequency = loanTermFrequency;
|
this.loanTermFrequency = loanTermFrequency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateTotalInterestDue(Money totalInterestDue) {
|
public void updateTotalInterestDue(Money totalInterestDue) {
|
||||||
|
|
||||||
if(totalInterestDue != null) {
|
if (totalInterestDue != null) {
|
||||||
this.totalInterestDue = totalInterestDue;
|
this.totalInterestDue = totalInterestDue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationCurrency getApplicationCurrency() {
|
||||||
|
return this.currency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+13
-14
@@ -10,10 +10,9 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
@@ -23,19 +22,19 @@ import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanResched
|
|||||||
|
|
||||||
public interface LoanScheduleGenerator {
|
public interface LoanScheduleGenerator {
|
||||||
|
|
||||||
LoanScheduleModel generate(MathContext mc, ApplicationCurrency applicationCurrency, LoanApplicationTerms loanApplicationTerms,
|
LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||||
Set<LoanCharge> loanCharges, boolean isHolidayEnabled, List<Holiday> holidays, final WorkingDays workingDays);
|
final HolidayDetailDTO holidayDetailDTO);
|
||||||
|
|
||||||
LoanScheduleModel rescheduleNextInstallments(MathContext mc, ApplicationCurrency applicationCurrency,
|
LoanScheduleModel rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||||
LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges, boolean isHolidayEnabled, List<Holiday> holidays,
|
final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
|
||||||
WorkingDays workingDays, List<LoanTransaction> transactions,
|
|
||||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom, LocalDate lastTransactionDate,
|
||||||
|
int penaltyWaitPeriod);
|
||||||
|
|
||||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
||||||
LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc, Set<LoanCharge> charges);
|
MonetaryCurrency currency, LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms,
|
||||||
|
MathContext mc, Set<LoanCharge> charges);
|
||||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
|
||||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO);
|
||||||
}
|
}
|
||||||
+14
@@ -42,6 +42,15 @@ public final class LoanScheduleModel {
|
|||||||
totalOutstanding);
|
totalOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LoanScheduleModel withOverdueChargeUpdation(final Collection<LoanScheduleModelPeriod> periods,
|
||||||
|
final LoanScheduleModel loanScheduleModel, final BigDecimal totalPenaltyChargesCharged) {
|
||||||
|
|
||||||
|
return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays,
|
||||||
|
loanScheduleModel.totalPrincipalDisbursed, loanScheduleModel.totalPrincipalExpected, loanScheduleModel.totalPrincipalPaid,
|
||||||
|
loanScheduleModel.totalInterestCharged, loanScheduleModel.totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||||
|
loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
||||||
|
}
|
||||||
|
|
||||||
private LoanScheduleModel(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||||
@@ -85,4 +94,9 @@ public final class LoanScheduleModel {
|
|||||||
public Collection<LoanScheduleModelPeriod> getPeriods() {
|
public Collection<LoanScheduleModelPeriod> getPeriods() {
|
||||||
return this.periods;
|
return this.periods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||||
|
return this.totalPenaltyChargesCharged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
-9
@@ -5,30 +5,25 @@
|
|||||||
*/
|
*/
|
||||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
|
||||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
|
|
||||||
public interface ScheduledDateGenerator {
|
public interface ScheduledDateGenerator {
|
||||||
|
|
||||||
LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled, List<Holiday> holidays,
|
LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO);
|
||||||
final WorkingDays workingDays);
|
|
||||||
|
|
||||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(PeriodFrequencyType repaymentPeriodFrequencyType, int repaidEvery,
|
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(PeriodFrequencyType repaymentPeriodFrequencyType, int repaidEvery,
|
||||||
final LocalDate firstRepaymentDate);
|
final LocalDate firstRepaymentDate);
|
||||||
|
|
||||||
LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment);
|
LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment);
|
||||||
|
|
||||||
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled,
|
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO);
|
||||||
List<Holiday> holidays, WorkingDays workingDays);
|
|
||||||
|
|
||||||
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate);
|
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate);
|
||||||
|
|
||||||
Boolean isDateFallsInSchedule(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, LocalDate date);
|
Boolean isDateFallsInSchedule(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, LocalDate date);
|
||||||
|
|
||||||
LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms,
|
LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms,
|
||||||
boolean isHolidayEnabled, List<Holiday> holidays, final WorkingDays workingDays);
|
final HolidayDetailDTO holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-12
@@ -43,6 +43,7 @@ import org.mifosplatform.portfolio.group.domain.Group;
|
|||||||
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
|
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
|
||||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
@@ -358,30 +359,34 @@ public class LoanScheduleAssembler {
|
|||||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
|
|
||||||
final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
|
||||||
return loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays,
|
return loanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO);
|
||||||
workingDays);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
||||||
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
||||||
final List<LoanRepaymentScheduleInstallment> previousSchedule,
|
final List<LoanRepaymentScheduleInstallment> previousSchedule,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LocalDate recalculateFrom) {
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LocalDate recalculateFrom,
|
||||||
|
LocalDate recalculateDueDateChargesFrom) {
|
||||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||||
final MathContext mc = new MathContext(8, roundingMode);
|
final MathContext mc = new MathContext(8, roundingMode);
|
||||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository
|
|
||||||
.findOneWithNotFoundDetection(loanApplicationTerms.getCurrency());
|
|
||||||
|
|
||||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms
|
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms
|
||||||
.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
|
||||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
isHolidayEnabled, holidays, workingDays, transactions, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
final Long waitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
recalculateFrom);
|
int penaltyWaitPeriod = 0;
|
||||||
|
if (waitPeriod != null) {
|
||||||
|
penaltyWaitPeriod = waitPeriod.intValue();
|
||||||
|
}
|
||||||
|
return loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, loanCharges, detailDTO, transactions,
|
||||||
|
loanRepaymentScheduleTransactionProcessor, previousSchedule, recalculateFrom, recalculateDueDateChargesFrom,
|
||||||
|
penaltyWaitPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
||||||
@@ -400,8 +405,8 @@ public class LoanScheduleAssembler {
|
|||||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||||
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate.minusDays(1));
|
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate.minusDays(1));
|
||||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||||
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, isHolidayEnabled,
|
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
holidays, workingDays);
|
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, detailDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, nextScheduleDate,
|
return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, nextScheduleDate,
|
||||||
|
|||||||
+1
-1
@@ -176,7 +176,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
|||||||
|
|
||||||
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
||||||
modifiedTransactions, loan.charges(), loan.fetchRepaymentScheduleInstallments(), loanRepaymentScheduleTransactionProcessor,
|
modifiedTransactions, loan.charges(), loan.fetchRepaymentScheduleInstallments(), loanRepaymentScheduleTransactionProcessor,
|
||||||
LocalDate.now());
|
LocalDate.now(), LocalDate.now());
|
||||||
LoanScheduleData scheduleDate = model.toData();
|
LoanScheduleData scheduleDate = model.toData();
|
||||||
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
||||||
for (LoanSchedulePeriodData periodData : periodDatas) {
|
for (LoanSchedulePeriodData periodData : periodDatas) {
|
||||||
|
|||||||
+25
-28
@@ -6,40 +6,37 @@
|
|||||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||||
|
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||||
|
|
||||||
public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoanRescheduleModel reschedule(final MathContext mathContext,
|
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||||
final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
final HolidayDetailDTO holidayDetailDTO) {
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
|
||||||
|
LoanRescheduleModel loanRescheduleModel = null;
|
||||||
LoanRescheduleModel loanRescheduleModel = null;
|
|
||||||
|
switch (interestMethod) {
|
||||||
switch (interestMethod) {
|
case DECLINING_BALANCE:
|
||||||
case DECLINING_BALANCE:
|
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(
|
applicationCurrency, holidayDetailDTO);
|
||||||
mathContext, loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
break;
|
||||||
break;
|
|
||||||
|
case FLAT:
|
||||||
case FLAT:
|
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext,
|
applicationCurrency, holidayDetailDTO);
|
||||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
break;
|
||||||
break;
|
|
||||||
|
case INVALID:
|
||||||
case INVALID:
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
return loanRescheduleModel;
|
||||||
return loanRescheduleModel;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-7
@@ -6,16 +6,14 @@
|
|||||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||||
|
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||||
|
|
||||||
public interface LoanReschedulerFactory {
|
public interface LoanReschedulerFactory {
|
||||||
|
|
||||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
final HolidayDetailDTO holidayDetailDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -18,6 +18,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyReposit
|
|||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||||
@@ -76,8 +77,9 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
|||||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
||||||
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
loanRescheduleRequest, applicationCurrency, holidayDetailDTO);
|
||||||
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
||||||
oldPeriods);
|
oldPeriods);
|
||||||
return loanRescheduleModelWithOldPeriods;
|
return loanRescheduleModelWithOldPeriods;
|
||||||
|
|||||||
+3
-1
@@ -34,6 +34,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
|||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||||
@@ -279,8 +280,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
loanRescheduleRequest, applicationCurrency, holidayDetailDTO);
|
||||||
|
|
||||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||||
|
|||||||
+15
-5
@@ -426,8 +426,14 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
}
|
}
|
||||||
chargeAmount = accruableForInstallment;
|
chargeAmount = accruableForInstallment;
|
||||||
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
||||||
if (canAddCharge && installmentChargeData.getAmountAccrued() == null) {
|
if (canAddCharge
|
||||||
applicableCharges.put(loanCharge, chargeAmount);
|
&& (installmentChargeData.getAmountAccrued() == null || chargeAmount.compareTo(installmentChargeData
|
||||||
|
.getAmountAccrued()) != 0)) {
|
||||||
|
BigDecimal amountForAccrual = chargeAmount;
|
||||||
|
if (installmentChargeData.getAmountAccrued() != null) {
|
||||||
|
amountForAccrual = chargeAmount.subtract(installmentChargeData.getAmountAccrued());
|
||||||
|
}
|
||||||
|
applicableCharges.put(loanCharge, amountForAccrual);
|
||||||
BigDecimal amountAccrued = chargeAmount;
|
BigDecimal amountAccrued = chargeAmount;
|
||||||
if (loanCharge.getAmountAccrued() != null) {
|
if (loanCharge.getAmountAccrued() != null) {
|
||||||
amountAccrued = amountAccrued.add(loanCharge.getAmountAccrued());
|
amountAccrued = amountAccrued.add(loanCharge.getAmountAccrued());
|
||||||
@@ -444,8 +450,12 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
|
chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
|
||||||
}
|
}
|
||||||
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
||||||
if (canAddCharge && loanCharge.getAmountAccrued() == null) {
|
if (canAddCharge && (loanCharge.getAmountAccrued() == null || chargeAmount.compareTo(loanCharge.getAmountAccrued()) != 0)) {
|
||||||
applicableCharges.put(loanCharge, chargeAmount);
|
BigDecimal amountForAccrual = chargeAmount;
|
||||||
|
if (loanCharge.getAmountAccrued() != null) {
|
||||||
|
amountForAccrual = chargeAmount.subtract(loanCharge.getAmountAccrued());
|
||||||
|
}
|
||||||
|
applicableCharges.put(loanCharge, amountForAccrual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +497,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
loanTransactionDatas.add(loanTransactionData);
|
loanTransactionDatas.add(loanTransactionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator();
|
Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator();
|
||||||
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) {
|
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) {
|
||||||
if (recognized.compareTo(BigDecimal.ZERO) != 1 && unrecognized.compareTo(BigDecimal.ZERO) != 1 && iterator.hasNext()) {
|
if (recognized.compareTo(BigDecimal.ZERO) != 1 && unrecognized.compareTo(BigDecimal.ZERO) != 1 && iterator.hasNext()) {
|
||||||
|
|||||||
+289
-108
@@ -86,9 +86,11 @@ import org.mifosplatform.portfolio.group.domain.Group;
|
|||||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||||
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
@@ -120,6 +122,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.ScheduledDate
|
|||||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
|
||||||
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
|
||||||
|
import org.mifosplatform.portfolio.loanproduct.data.LoanOverdueDTO;
|
||||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||||
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||||
import org.mifosplatform.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
import org.mifosplatform.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||||
@@ -299,20 +302,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
CalendarInstance restCalendarInstance = null;
|
CalendarInstance restCalendarInstance = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom,
|
||||||
|
overdurPenaltyWaitPeriod, lastTransactionDate);
|
||||||
|
|
||||||
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||||
holidays, workingDays, recalculateSchedule);
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
||||||
loan, null);
|
loan, null);
|
||||||
}
|
}
|
||||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO);
|
||||||
allowTransactionsOnNonWorkingDay, restCalendarInstance);
|
|
||||||
}
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -496,19 +506,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loan.getLoanTransactions().add(disbursementTransaction);
|
loan.getLoanTransactions().add(disbursementTransaction);
|
||||||
}
|
}
|
||||||
CalendarInstance restCalendarInstance = null;
|
CalendarInstance restCalendarInstance = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays,
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays,
|
||||||
workingDays, recalculateSchedule);
|
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
||||||
|
final ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
firstRepaymentOnDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
||||||
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
||||||
}
|
}
|
||||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
|
||||||
firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO);
|
||||||
allowTransactionsOnNonWorkingDay, restCalendarInstance);
|
|
||||||
}
|
}
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
|
|
||||||
@@ -582,8 +599,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
|
|
||||||
final Map<String, Object> changes = loan.undoDisbursal(this.loanScheduleFactory, applicationCurrency, existingTransactionIds,
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
existingReversedTransactionIds, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO);
|
||||||
|
|
||||||
|
final Map<String, Object> changes = loan
|
||||||
|
.undoDisbursal(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
if (!changes.isEmpty()) {
|
if (!changes.isEmpty()) {
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -740,6 +761,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
ApplicationCurrency applicationCurrency = null;
|
ApplicationCurrency applicationCurrency = null;
|
||||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
@@ -752,12 +777,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
loan.getDisbursementDate(), loan, calendarInstance);
|
loan.getDisbursementDate(), loan, calendarInstance);
|
||||||
|
|
||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||||
|
allowTransactionsOnNonWorkingDay);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
|
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
|
||||||
defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||||
allowTransactionsOnHoliday, holidays, workingDays, isHolidayEnabled, allowTransactionsOnNonWorkingDay,
|
scheduleGeneratorDTO);
|
||||||
this.loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
|
||||||
|
|
||||||
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
|
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
|
||||||
if (paymentDetail != null) {
|
if (paymentDetail != null) {
|
||||||
@@ -856,6 +887,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
List<Holiday> holidays = null;
|
List<Holiday> holidays = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
WorkingDays workingDays = null;
|
WorkingDays workingDays = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
@@ -870,12 +904,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
.waiveInterest(waiveInterestTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
existingReversedTransactionIds, holidays, workingDays, isHolidayEnabled, this.loanScheduleFactory,
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
lastTransactionDate);
|
||||||
|
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
|
||||||
|
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
|
|
||||||
this.loanTransactionRepository.save(waiveInterestTransaction);
|
this.loanTransactionRepository.save(waiveInterestTransaction);
|
||||||
|
|
||||||
@@ -949,14 +986,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
|
|
||||||
CalendarInstance restCalendarInstance = null;
|
CalendarInstance restCalendarInstance = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(),
|
final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(),
|
||||||
changes, existingTransactionIds, existingReversedTransactionIds, currentUser, this.loanScheduleFactory,
|
changes, existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||||
applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
|
||||||
LoanTransaction writeoff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
LoanTransaction writeoff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||||
this.loanTransactionRepository.save(writeoff);
|
this.loanTransactionRepository.save(writeoff);
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
@@ -1017,14 +1061,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
|
|
||||||
CalendarInstance restCalendarInstance = null;
|
CalendarInstance restCalendarInstance = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine(), changes,
|
ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine(), changes,
|
||||||
existingTransactionIds, existingReversedTransactionIds, this.loanScheduleFactory, applicationCurrency,
|
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
|
||||||
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||||
if (possibleClosingTransaction != null) {
|
if (possibleClosingTransaction != null) {
|
||||||
this.loanTransactionRepository.save(possibleClosingTransaction);
|
this.loanTransactionRepository.save(possibleClosingTransaction);
|
||||||
@@ -1133,8 +1185,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge", defaultUserMessage, null, chargeDefinition.getName());
|
throw new LoanChargeCannotBeAddedException("loanCharge", "overdue.charge", defaultUserMessage, null, chargeDefinition.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
addCharge(loan, chargeDefinition, loanCharge);
|
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
|
||||||
|
|
||||||
|
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||||
|
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
if (isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
|
runScheduleRecalculation(loan);
|
||||||
|
}
|
||||||
|
updateOriginalSchedule(loan);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate
|
||||||
|
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
|
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||||
|
}
|
||||||
return new CommandProcessingResultBuilder() //
|
return new CommandProcessingResultBuilder() //
|
||||||
.withCommandId(command.commandId()) //
|
.withCommandId(command.commandId()) //
|
||||||
.withEntityId(loanCharge.getId()) //
|
.withEntityId(loanCharge.getId()) //
|
||||||
@@ -1145,7 +1214,71 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCharge(final Loan loan, final Charge chargeDefinition, final LoanCharge loanCharge) {
|
public void runScheduleRecalculation(Loan loan) {
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
CalendarInstance restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||||
|
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
|
||||||
|
final MonetaryCurrency currency = loan.getCurrency();
|
||||||
|
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
|
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||||
|
CalendarEntityType.LOANS.getValue());
|
||||||
|
LocalDate calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||||
|
loan.getDisbursementDate(), loan, calendarInstance);
|
||||||
|
|
||||||
|
boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()
|
||||||
|
.toDate());
|
||||||
|
WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
final Long overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
|
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom,
|
||||||
|
overdurPenaltyWaitPeriod, lastTransactionDate);
|
||||||
|
ChangedTransactionDetail changedTransactionDetail = loan
|
||||||
|
.handleRegenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
|
||||||
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
if (changedTransactionDetail != null) {
|
||||||
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
|
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||||
|
// update loan with references to the newly created
|
||||||
|
// transactions
|
||||||
|
loan.getLoanTransactions().add(mapEntry.getValue());
|
||||||
|
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateOriginalSchedule(Loan loan) {
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
|
||||||
|
final MonetaryCurrency currency = loan.getCurrency();
|
||||||
|
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||||
|
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||||
|
CalendarEntityType.LOANS.getValue());
|
||||||
|
LocalDate calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||||
|
loan.getDisbursementDate(), loan, calendarInstance);
|
||||||
|
|
||||||
|
boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
|
List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()
|
||||||
|
.toDate());
|
||||||
|
WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO);
|
||||||
|
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addCharge(final Loan loan, final Charge chargeDefinition, final LoanCharge loanCharge) {
|
||||||
|
|
||||||
if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
|
if (!loan.hasCurrencyCodeOf(chargeDefinition.getCurrencyCode())) {
|
||||||
final String errorMessage = "Charge and Loan must have the same currency.";
|
final String errorMessage = "Charge and Loan must have the same currency.";
|
||||||
@@ -1161,34 +1294,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
|
||||||
this.loanChargeRepository.save(loanCharge);
|
this.loanChargeRepository.save(loanCharge);
|
||||||
CalendarInstance restCalendarInstance = null;
|
|
||||||
ApplicationCurrency applicationCurrency = null;
|
|
||||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
|
||||||
List<Holiday> holidays = null;
|
|
||||||
boolean isHolidayEnabled = false;
|
|
||||||
WorkingDays workingDays = null;
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
|
||||||
|
|
||||||
final MonetaryCurrency currency = loan.getCurrency();
|
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge);
|
||||||
applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
|
||||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
|
||||||
CalendarEntityType.LOANS.getValue());
|
|
||||||
calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
|
||||||
loan.getDisbursementDate(), loan, calendarInstance);
|
|
||||||
|
|
||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
|
||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
|
|
||||||
existingReversedTransactionIds, holidays, workingDays, isHolidayEnabled, loanScheduleFactory, applicationCurrency,
|
|
||||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* we want to apply charge transactions only for those loans charges
|
* we want to apply charge transactions only for those loans charges
|
||||||
@@ -1217,13 +1325,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
|
||||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
|
||||||
workingDays);
|
|
||||||
}
|
|
||||||
// we post Journal entries only for loans in active status
|
|
||||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
|
||||||
}
|
}
|
||||||
|
boolean isAppliedOnBackDate = false;
|
||||||
|
if (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate())) {
|
||||||
|
isAppliedOnBackDate = true;
|
||||||
|
}
|
||||||
|
return isAppliedOnBackDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -1311,6 +1419,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
List<Holiday> holidays = null;
|
List<Holiday> holidays = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
WorkingDays workingDays = null;
|
WorkingDays workingDays = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
@@ -1325,8 +1436,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
Money accruedCharge = Money.zero(loan.getCurrency());
|
Money accruedCharge = Money.zero(loan.getCurrency());
|
||||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||||
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
|
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
|
||||||
@@ -1337,8 +1454,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
||||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, holidays, workingDays, isHolidayEnabled,
|
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge);
|
||||||
loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance, accruedCharge);
|
|
||||||
|
|
||||||
this.loanTransactionRepository.save(waiveTransaction);
|
this.loanTransactionRepository.save(waiveTransaction);
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -2011,39 +2127,43 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
|
|
||||||
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
final Map<Long, Collection<OverdueLoanScheduleData>> overdueScheduleData = new HashMap<>();
|
||||||
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
|
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) {
|
||||||
|
if (overdueScheduleData.containsKey(overdueInstallment.getLoanId())) {
|
||||||
|
overdueScheduleData.get(overdueInstallment.getLoanId()).add(overdueInstallment);
|
||||||
|
} else {
|
||||||
|
Collection<OverdueLoanScheduleData> loanData = new ArrayList<>();
|
||||||
|
loanData.add(overdueInstallment);
|
||||||
|
overdueScheduleData.put(overdueInstallment.getLoanId(), loanData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Long loanId : overdueScheduleData.keySet()) {
|
||||||
try {
|
try {
|
||||||
|
applyOverdueChargesForLoan(loanId, overdueScheduleData.get(loanId));
|
||||||
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
|
|
||||||
|
|
||||||
final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper,
|
|
||||||
null, null, null, null, null, overdueInstallment.getLoanId(), null, null, null, null);
|
|
||||||
|
|
||||||
applyChargeToOverdueLoanInstallment(overdueInstallment.getLoanId(), overdueInstallment.getChargeId(),
|
|
||||||
overdueInstallment.getPeriodNumber(), command);
|
|
||||||
|
|
||||||
} catch (final PlatformApiDataValidationException e) {
|
} catch (final PlatformApiDataValidationException e) {
|
||||||
final List<ApiParameterError> errors = e.getErrors();
|
final List<ApiParameterError> errors = e.getErrors();
|
||||||
for (final ApiParameterError error : errors) {
|
for (final ApiParameterError error : errors) {
|
||||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||||
+ " with message " + error.getDeveloperMessage());
|
+ error.getDeveloperMessage());
|
||||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||||
.append(" with message ").append(error.getDeveloperMessage());
|
.append(error.getDeveloperMessage());
|
||||||
}
|
}
|
||||||
} catch (final AbstractPlatformDomainRuleException ex) {
|
} catch (final AbstractPlatformDomainRuleException ex) {
|
||||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||||
+ " with message " + ex.getDefaultUserMessage());
|
+ ex.getDefaultUserMessage());
|
||||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||||
.append(" with message ").append(ex.getDefaultUserMessage());
|
.append(ex.getDefaultUserMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Throwable realCause = e;
|
Throwable realCause = e;
|
||||||
if (e.getCause() != null) {
|
if (e.getCause() != null) {
|
||||||
realCause = e.getCause();
|
realCause = e.getCause();
|
||||||
}
|
}
|
||||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||||
+ " with message " + realCause.getMessage());
|
+ realCause.getMessage());
|
||||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||||
.append(" with message ").append(realCause.getMessage());
|
.append(realCause.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||||
@@ -2051,9 +2171,42 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber,
|
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {
|
||||||
final JsonCommand command) {
|
|
||||||
|
|
||||||
|
Loan loan = null;
|
||||||
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
|
boolean runInterestRecalculation = false;
|
||||||
|
for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduleDatas) {
|
||||||
|
|
||||||
|
final JsonElement parsedCommand = this.fromApiJsonHelper.parse(overdueInstallment.toString());
|
||||||
|
final JsonCommand command = JsonCommand.from(overdueInstallment.toString(), parsedCommand, this.fromApiJsonHelper, null, null,
|
||||||
|
null, null, null, loanId, null, null, null, null);
|
||||||
|
LoanOverdueDTO overdueDTO = applyChargeToOverdueLoanInstallment(loanId, overdueInstallment.getChargeId(),
|
||||||
|
overdueInstallment.getPeriodNumber(), command, loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
loan = overdueDTO.getLoan();
|
||||||
|
runInterestRecalculation = runInterestRecalculation || overdueDTO.isRunInterestRecalculation();
|
||||||
|
}
|
||||||
|
if (loan != null) {
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
|
if (runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
|
runScheduleRecalculation(loan);
|
||||||
|
}
|
||||||
|
updateOriginalSchedule(loan);
|
||||||
|
}
|
||||||
|
|
||||||
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation
|
||||||
|
&& loan.isFeeCompoundingEnabledForInterestRecalculation()) {
|
||||||
|
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoanOverdueDTO applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber,
|
||||||
|
final JsonCommand command, Loan loan, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||||
|
boolean runInterestRecalculation = false;
|
||||||
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(loanChargeId);
|
final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(loanChargeId);
|
||||||
|
|
||||||
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(loanId,
|
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(loanId,
|
||||||
@@ -2087,24 +2240,32 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
scheduleDates.remove(frequency);
|
scheduleDates.remove(frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
Loan loan = null;
|
|
||||||
LoanRepaymentScheduleInstallment installment = null;
|
LoanRepaymentScheduleInstallment installment = null;
|
||||||
if (!scheduleDates.isEmpty()) {
|
if (!scheduleDates.isEmpty()) {
|
||||||
loan = this.loanAssembler.assembleFrom(loanId);
|
if (loan == null) {
|
||||||
checkClientOrGroupActive(loan);
|
loan = this.loanAssembler.assembleFrom(loanId);
|
||||||
|
checkClientOrGroupActive(loan);
|
||||||
|
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||||
|
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||||
|
}
|
||||||
installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
|
installment = loan.fetchRepaymentScheduleInstallment(periodNumber);
|
||||||
}
|
}
|
||||||
|
if (loan != null) {
|
||||||
|
|
||||||
for (Map.Entry<Integer, LocalDate> entry : scheduleDates.entrySet()) {
|
for (Map.Entry<Integer, LocalDate> entry : scheduleDates.entrySet()) {
|
||||||
|
|
||||||
final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command, entry.getValue());
|
final LoanCharge loanCharge = LoanCharge.createNewFromJson(loan, chargeDefinition, command, entry.getValue());
|
||||||
|
|
||||||
LoanOverdueInstallmentCharge overdueInstallmentCharge = new LoanOverdueInstallmentCharge(loanCharge, installment,
|
LoanOverdueInstallmentCharge overdueInstallmentCharge = new LoanOverdueInstallmentCharge(loanCharge, installment,
|
||||||
entry.getKey());
|
entry.getKey());
|
||||||
loanCharge.updateOverdueInstallmentCharge(overdueInstallmentCharge);
|
loanCharge.updateOverdueInstallmentCharge(overdueInstallmentCharge);
|
||||||
|
|
||||||
addCharge(loan, chargeDefinition, loanCharge);
|
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||||
|
runInterestRecalculation = runInterestRecalculation || isAppliedOnBackDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new LoanOverdueDTO(loan, runInterestRecalculation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2122,6 +2283,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
List<Holiday> holidays = null;
|
List<Holiday> holidays = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
WorkingDays workingDays = null;
|
WorkingDays workingDays = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
|
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
@@ -2136,11 +2301,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(existingTransactionIds, existingReversedTransactionIds,
|
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(existingTransactionIds, existingReversedTransactionIds,
|
||||||
isHolidayEnabled, holidays, workingDays, this.loanScheduleFactory, applicationCurrency,
|
scheduleGeneratorDTO);
|
||||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
|
||||||
if (changedTransactionDetail != null) {
|
if (changedTransactionDetail != null) {
|
||||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||||
@@ -2182,13 +2352,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||||
CalendarInstance restCalendarInstance = null;
|
CalendarInstance restCalendarInstance = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
|
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
final ChangedTransactionDetail changedTransactionDetail = loan.updateDisbursementDateForTranche(loanDisbursementDetails, command,
|
final ChangedTransactionDetail changedTransactionDetail = loan.updateDisbursementDateForTranche(loanDisbursementDetails, command,
|
||||||
existingTransactionIds, existingReversedTransactionIds, changes, this.loanScheduleFactory, applicationCurrency,
|
existingTransactionIds, existingReversedTransactionIds, changes, scheduleGeneratorDTO);
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
|
||||||
|
|
||||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||||
|
|
||||||
@@ -2198,8 +2377,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||||
workingDays);
|
|
||||||
}
|
}
|
||||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||||
@@ -2299,6 +2477,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
List<Holiday> holidays = null;
|
List<Holiday> holidays = null;
|
||||||
boolean isHolidayEnabled = false;
|
boolean isHolidayEnabled = false;
|
||||||
WorkingDays workingDays = null;
|
WorkingDays workingDays = null;
|
||||||
|
LocalDate recalculateFrom = null;
|
||||||
|
Long overdurPenaltyWaitPeriod = null;
|
||||||
|
LocalDate lastTransactionDate = null;
|
||||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||||
@@ -2315,9 +2496,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||||
workingDays = this.workingDaysRepository.findOne();
|
workingDays = this.workingDaysRepository.findOne();
|
||||||
|
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||||
}
|
}
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(loanScheduleFactory,
|
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||||
applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance,
|
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||||
|
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||||
|
lastTransactionDate);
|
||||||
|
|
||||||
|
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(generatorDTO,
|
||||||
existingTransactionIds, existingReversedTransactionIds);
|
existingTransactionIds, existingReversedTransactionIds);
|
||||||
|
|
||||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||||
@@ -2340,23 +2526,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLoanScheduleArchive(final Loan loan, final ApplicationCurrency applicationCurrency,
|
private void createLoanScheduleArchive(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
||||||
final WorkingDays workingDays) {
|
|
||||||
LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(loanScheduleFactory, applicationCurrency,
|
|
||||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
|
||||||
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentScheduleFromModel(loanScheduleModel);
|
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentScheduleFromModel(loanScheduleModel);
|
||||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(installments, loan, null);
|
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(installments, loan, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan,
|
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule,
|
||||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule) {
|
|
||||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||||
loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount);
|
||||||
isHolidayEnabled, holidays, workingDays, recalculateSchedule, actualDisbursementDate, emiAmount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
||||||
|
|||||||
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.mifosplatform.portfolio.loanproduct.data;
|
||||||
|
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||||
|
|
||||||
|
public class LoanOverdueDTO {
|
||||||
|
|
||||||
|
private final Loan loan;
|
||||||
|
private final boolean runInterestRecalculation;
|
||||||
|
|
||||||
|
public LoanOverdueDTO(final Loan loan, final boolean runInterestRecalculation) {
|
||||||
|
this.loan = loan;
|
||||||
|
this.runInterestRecalculation = runInterestRecalculation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunInterestRecalculation() {
|
||||||
|
return this.runInterestRecalculation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Loan getLoan() {
|
||||||
|
return this.loan;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -52,4 +52,9 @@ public enum InterestRecalculationCompoundingMethod {
|
|||||||
public String getCode() {
|
public String getCode() {
|
||||||
return this.code;
|
return this.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFeeCompoundingEnabled() {
|
||||||
|
return this.getValue().equals(InterestRecalculationCompoundingMethod.FEE.getValue())
|
||||||
|
|| this.getValue().equals(InterestRecalculationCompoundingMethod.INTEREST_AND_FEE.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user