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);
|
||||
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.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
||||
new JournalEntry(Float.valueOf("50.0"), JournalEntry.TransactionType.DEBIT));
|
||||
*/
|
||||
/*
|
||||
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||
* "20 September 2011", new JournalEntry(Float.valueOf("50.0"),
|
||||
* 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";
|
||||
try {
|
||||
this.schedulerJobHelper.excuteJob(jobName);
|
||||
@@ -2645,10 +2649,15 @@ public class ClientLoanIntegrationTest {
|
||||
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
||||
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.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
||||
new JournalEntry(Float.valueOf("29.7"), JournalEntry.TransactionType.DEBIT));*/
|
||||
/*
|
||||
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||
* "20 September 2011", new JournalEntry(Float.valueOf("29.7"),
|
||||
* 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";
|
||||
try {
|
||||
@@ -2850,10 +2859,15 @@ public class ClientLoanIntegrationTest {
|
||||
loanCharges = this.loanTransactionHelper.getLoanCharges(loanID);
|
||||
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.checkJournalEntryForExpenseAccount(expenseAccount, "20 September 2011",
|
||||
new JournalEntry(Float.valueOf("31.51"), JournalEntry.TransactionType.DEBIT));*/
|
||||
/*
|
||||
* this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount,
|
||||
* "20 September 2011", new JournalEntry( Float.valueOf("31.51"),
|
||||
* 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";
|
||||
try {
|
||||
@@ -3141,7 +3155,7 @@ public class ClientLoanIntegrationTest {
|
||||
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.MIFOS_STANDARD_STRATEGY,
|
||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
||||
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,
|
||||
LoanApplicationTestBuilder.MIFOS_STANDARD_STRATEGY, new ArrayList<HashMap>(0));
|
||||
@@ -3231,7 +3245,7 @@ public class ClientLoanIntegrationTest {
|
||||
final Integer loanProductID = createLoanProductWithInterestRecalculation(LoanProductTestBuilder.RBI_INDIA_STRATEGY,
|
||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST,
|
||||
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,
|
||||
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,
|
||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_INTEREST_AND_FEE,
|
||||
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,
|
||||
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
|
||||
public void testLoanScheduleWithInterestRecalculation_WITH_PERIODIC_ACCOUNTING() {
|
||||
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) {
|
||||
Map<String, Object> values = new HashMap<>(3);
|
||||
values.put("dueDate", getDateAsArray(todaysDate, addMonths));
|
||||
@@ -3560,6 +3681,15 @@ public class ClientLoanIntegrationTest {
|
||||
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||
final String recalculationRestFrequencyType, final String recalculationRestFrequencyInterval,
|
||||
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 ---------------------------------------");
|
||||
LoanProductTestBuilder builder = new LoanProductTestBuilder()
|
||||
.withPrincipal("10000000.00")
|
||||
@@ -3578,7 +3708,7 @@ public class ClientLoanIntegrationTest {
|
||||
builder = builder.withAccountingRulePeriodicAccrual(accounts);
|
||||
}
|
||||
|
||||
final String loanProductJSON = builder.build(null);
|
||||
final String loanProductJSON = builder.build(chargeId);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -222,6 +222,18 @@ public class ChargesHelper {
|
||||
System.out.println(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() {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
|
||||
|
||||
// accounting related data validation
|
||||
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)) {
|
||||
|
||||
|
||||
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.command.LoanChargeCommand;
|
||||
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.ScheduleGeneratorDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.ExceedingTrancheCountException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.InvalidLoanStateTransitionException;
|
||||
@@ -466,10 +468,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.accountNumberRequiresAutoGeneration = accountNumberRequiresAutoGeneration;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge, final List<Long> existingTransactionIds,
|
||||
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) {
|
||||
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge) {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
@@ -526,22 +525,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
// store Id's of existing loan transactions and existing reversed loan
|
||||
// transactions
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& (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());
|
||||
}
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||
getLastUserTransactionForChargeCalc());
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
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()) {
|
||||
mapEntry.getValue().updateLoan(this);
|
||||
}
|
||||
@@ -652,12 +643,18 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
&& !loanCharge.isDueForCollectionFromAndUpToAndIncluding(disbursementDate, lastRepaymentPeriodDueDate)) {
|
||||
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,
|
||||
getDisbursementDate(), getLastRepaymentPeriodDueDate(), loanCharge.name());
|
||||
getDisbursementDate(), lastRepaymentPeriodDueDate, loanCharge.name());
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -674,7 +671,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if (removed) {
|
||||
charge.setActive(false);
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||
getLastUserTransactionForChargeCalc());
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
@@ -693,7 +691,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -751,11 +749,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||
getLastUserTransactionForChargeCalc());
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -838,10 +837,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final Integer loanInstallmentNumber, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation,
|
||||
final Money accruedCharge) {
|
||||
final Integer loanInstallmentNumber, final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge) {
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
|
||||
@@ -887,9 +883,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate()))) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
// Waive of charges whose due date falls after latest 'repayment'
|
||||
// transaction dont require entire loan schedule to be reprocessed.
|
||||
@@ -906,11 +900,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
***/
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), allNonContraTransactionsPostDisbursement,
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges());
|
||||
getCurrency(), this.repaymentScheduleInstallments, charges(), getLastUserTransactionForChargeCalc());
|
||||
} else {
|
||||
// reprocess loan schedule based on charge been waived.
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||
getLastUserTransactionForChargeCalc());
|
||||
}
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
@@ -1032,34 +1027,74 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
updateLoanScheduleDependentDerivedFields();
|
||||
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() {
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return; }
|
||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
if (transaction.isAccrual()) {
|
||||
transaction.reverse();
|
||||
}
|
||||
private void applyAccurals() {
|
||||
Collection<LoanTransaction> accruals = retreiveListOfAccrualTransactions();
|
||||
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
applyPeriodicAccruals(accruals);
|
||||
} else if (isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
|
||||
updateAccrualsForNonPeriodicAccruals(accruals);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAccrualsPresent() {
|
||||
boolean isAccrualsPresent = false;
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { return isAccrualsPresent; }
|
||||
List<LoanTransaction> transactions = this.getLoanTransactions();
|
||||
for (final LoanTransaction transaction : transactions) {
|
||||
if (transaction.isAccrual() && transaction.isNotReversed()) {
|
||||
isAccrualsPresent = true;
|
||||
break;
|
||||
private void applyPeriodicAccruals(final Collection<LoanTransaction> accruals) {
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||
Money interest = Money.zero(getCurrency());
|
||||
Money fee = Money.zero(getCurrency());
|
||||
Money penality = Money.zero(getCurrency());
|
||||
for (LoanTransaction loanTransaction : accruals) {
|
||||
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() {
|
||||
@@ -1339,8 +1374,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public void recalculateAllCharges() {
|
||||
Set<LoanCharge> charges = this.charges();
|
||||
int penaltyWaitPeriod = 0;
|
||||
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) {
|
||||
final BigDecimal amount = calculateAmountPercentageAppliedTo(loanCharge);
|
||||
private void updateOverdueScheduleInstallment(final LoanCharge 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 totalChargeAmt = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
@@ -1390,9 +1443,46 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
chargeAmt = chargeAmt.divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}
|
||||
}
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||
totalChargeAmt);
|
||||
validateChargeHasValidSpecifiedDateIfApplicable(loanCharge, getDisbursementDate(), getLastRepaymentPeriodDueDate());
|
||||
if (loanCharge.isActive()) {
|
||||
loanCharge.update(chargeAmt, loanCharge.getDueLocalDate(), amount, repaymentScheduleDetail().getNumberOfRepayments(),
|
||||
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) {
|
||||
@@ -1588,8 +1678,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
// charges are optional
|
||||
int penaltyWaitPeriod = 0;
|
||||
for (final LoanCharge loanCharge : charges()) {
|
||||
recalculateLoanCharge(loanCharge);
|
||||
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||
}
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
@@ -1815,11 +1906,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail disburse(final LoanScheduleGeneratorFactory loanScheduleFactory, final AppUser currentUser,
|
||||
final JsonCommand command, final ApplicationCurrency currency, final Map<String, Object> actualChanges,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final boolean allowTransactionsOnHoliday, final boolean allowTransactionsOnNonWorkingDay,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
|
||||
LoanStatus.fromInt(this.loanStatus));
|
||||
@@ -1836,15 +1924,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
actualChanges.put("dateFormat", command.dateFormat());
|
||||
actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate"));
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||
|
||||
// validate if disbursement date is a holiday or a non-working day
|
||||
validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay);
|
||||
validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays);
|
||||
validateDisbursementDateIsOnNonWorkingDay(holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
@@ -1870,9 +1958,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
}
|
||||
|
||||
public void regenerateScheduleOnDisbursement(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule,
|
||||
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
|
||||
final LocalDate actualDisbursementDate, BigDecimal emiAmount) {
|
||||
boolean isEmiAmountChanged = false;
|
||||
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) {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1958,7 +2043,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
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()) {
|
||||
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
|
||||
* details/state.
|
||||
*/
|
||||
private void regenerateRepaymentSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
private void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
final LoanScheduleModel loanSchedule = regenerateScheduleModel(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
||||
final LoanScheduleModel loanSchedule = regenerateScheduleModel(scheduleGeneratorDTO);
|
||||
|
||||
updateLoanSchedule(loanSchedule);
|
||||
Set<LoanCharge> charges = this.charges();
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
recalculateLoanCharge(loanCharge);
|
||||
recalculateLoanCharge(loanCharge, scheduleGeneratorDTO.getPenaltyWaitPeriod());
|
||||
}
|
||||
}
|
||||
|
||||
public LoanScheduleModel regenerateScheduleModel(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
public LoanScheduleModel regenerateScheduleModel(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
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 MathContext mc = new MathContext(8, roundingMode);
|
||||
@@ -2113,14 +2194,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
loanVariationTermsData.add(data);
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(),
|
||||
loanTermFrequency, loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||
loanVariationTermsData, getInterestChargedFromDate());
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
return loanSchedule;
|
||||
}
|
||||
|
||||
@@ -2220,10 +2301,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return chargesPayment;
|
||||
}
|
||||
|
||||
public Map<String, Object> undoDisbursal(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
public Map<String, Object> undoDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
|
||||
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO);
|
||||
|
||||
@@ -2254,8 +2333,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
// clear off actual disbusrement date so schedule regeneration
|
||||
// uses expected date.
|
||||
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||
if (isDisbueseAmtChanged) {
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
@@ -2298,38 +2376,35 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
this.loanTermVariations.clear();
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges(),
|
||||
getLastUserTransactionForChargeCalc());
|
||||
|
||||
updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
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) {
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
|
||||
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||
waiveInterestTransaction.getTransactionDate());
|
||||
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
|
||||
waiveInterestTransaction.getTransactionDate());
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(waiveInterestTransaction,
|
||||
loanLifecycleStateMachine, null, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay, final boolean isHolidayEnabled,
|
||||
boolean isRecoveryRepayment, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final List<Long> existingReversedTransactionIds, boolean isRecoveryRepayment, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
LoanEvent event = null;
|
||||
if (isRecoveryRepayment) {
|
||||
@@ -2338,31 +2413,36 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||
validateAccountStatus(event);
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(event, repaymentTransaction.getTransactionDate());
|
||||
|
||||
validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||
validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
||||
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(event, repaymentTransaction.getTransactionDate());
|
||||
validateRepaymentDateIsOnHoliday(repaymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||
holidayDetailDTO.getHolidays());
|
||||
validateRepaymentDateIsOnNonWorkingDay(repaymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction,
|
||||
loanLifecycleStateMachine, null, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean allowTransactionsOnNonWorkingDay, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||
final HolidayDetailDTO holidayDetailDTO, final LoanTransaction paymentTransaction, final Integer installmentNumber) {
|
||||
|
||||
validateAccountStatus(LoanEvent.LOAN_CHARGE_PAYMENT);
|
||||
validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CHARGE_PAYMENT, paymentTransaction.getTransactionDate());
|
||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||
validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
||||
validateActivityNotBeforeLastTransactionDateForInterestRecalculation(LoanEvent.LOAN_CHARGE_PAYMENT,
|
||||
paymentTransaction.getTransactionDate());
|
||||
validateRepaymentDateIsOnHoliday(paymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||
holidayDetailDTO.getHolidays());
|
||||
validateRepaymentDateIsOnNonWorkingDay(paymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
if (paymentTransaction.getTransactionDate().isAfter(new LocalDate())) {
|
||||
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,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
@@ -2516,12 +2594,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
if (reprocess) {
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
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()) {
|
||||
mapEntry.getValue().updateLoan(this);
|
||||
}
|
||||
@@ -2607,6 +2685,18 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
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) {
|
||||
|
||||
if (isOverPaid()) {
|
||||
@@ -2767,13 +2857,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail,
|
||||
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean isHolidayEnabled, final boolean allowTransactionsOnNonWorkingDay,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), allowTransactionsOnHoliday, holidays);
|
||||
validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
|
||||
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
|
||||
validateRepaymentDateIsOnHoliday(newTransactionDetail.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
|
||||
holidayDetailDTO.getHolidays());
|
||||
validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
|
||||
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
@@ -2803,17 +2893,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
|
||||
changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine,
|
||||
transactionForAdjustment, loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled,
|
||||
holidays, workingDays, calendarInstanceForInterestRecalculation);
|
||||
transactionForAdjustment, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
@@ -2825,13 +2912,11 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||
charges());
|
||||
charges(), getLastUserTransactionForChargeCalc());
|
||||
updateLoanSummaryDerivedFields();
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
@@ -2893,15 +2978,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public ChangedTransactionDetail closeAsWrittenOff(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final AppUser currentUser, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor,
|
||||
calendarInstanceForInterestRecalculation);
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
|
||||
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
|
||||
|
||||
@@ -2960,25 +3042,20 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail closeDisbursements(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
private ChangedTransactionDetail closeDisbursements(final ScheduleGeneratorDTO scheduleGeneratorDTO,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
if (isDisbursementAllowed() && atleastOnceDisbursed()) {
|
||||
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount());
|
||||
removeDisbursementDetail();
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
}
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
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()) {
|
||||
mapEntry.getValue().updateLoan(this);
|
||||
this.loanTransactions.add(mapEntry.getValue());
|
||||
@@ -3003,9 +3080,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
|
||||
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
|
||||
validateAccountStatus(LoanEvent.LOAN_CLOSED);
|
||||
|
||||
@@ -3032,9 +3107,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(loanScheduleFactory, currency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor,
|
||||
calendarInstanceForInterestRecalculation);
|
||||
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
|
||||
LoanTransaction loanTransaction = null;
|
||||
if (isOpen()) {
|
||||
@@ -3596,7 +3670,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final List<Map<String, Object>> newLoanTransactions = new ArrayList<>();
|
||||
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));
|
||||
} else if (!existingTransactionIds.contains(transaction.getId())) {
|
||||
newLoanTransactions.add(transaction.toMapData(currencyData));
|
||||
@@ -3621,10 +3696,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
if (receivableInterest.isLessThanZero()) {
|
||||
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.";
|
||||
throw new InvalidLoanStateTransitionException("waive", "cannot.be.after.accrual.date", errorMessage, tillDate);
|
||||
}*/
|
||||
/*
|
||||
* 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."
|
||||
* ; throw new InvalidLoanStateTransitionException("waive",
|
||||
* "cannot.be.after.accrual.date", errorMessage, tillDate); }
|
||||
*/
|
||||
}
|
||||
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() {
|
||||
Set<LoanCharge> loanCharges = new HashSet<>();
|
||||
if (this.charges != null) {
|
||||
@@ -4081,9 +4210,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public ChangedTransactionDetail updateDisbursementDateForTranche(final LoanDisbursementDetails disbursementDetails,
|
||||
final JsonCommand command, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
|
||||
final Map<String, Object> actualChanges, final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
|
||||
final LocalDate expectedDisbursementDate = command.localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
|
||||
disbursementDetails.updateExpectedDisbursementDate(expectedDisbursementDate.toDate());
|
||||
@@ -4095,12 +4222,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
} else {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
regenerateRepaymentSchedule(scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
@@ -4108,7 +4232,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||
charges());
|
||||
charges(), getLastUserTransactionForChargeCalc());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
mapEntry.getValue().updateLoan(this);
|
||||
this.loanTransactions.add(mapEntry.getValue());
|
||||
@@ -4178,31 +4302,40 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return maturityDate;
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final ScheduleGeneratorDTO generatorDTO,
|
||||
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
LocalDate recalculateFrom = null;
|
||||
List<LoanTransaction> loanTransactions = this.retreiveListOfTransactionsPostDisbursementExcludeAccruals();
|
||||
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||
if (recalculateFrom == null || loanTransaction.getTransactionDate().isAfter(recalculateFrom)) {
|
||||
recalculateFrom = loanTransaction.getTransactionDate();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* LocalDate recalculateFrom = null; List<LoanTransaction>
|
||||
* loanTransactions =
|
||||
* this.retreiveListOfTransactionsPostDisbursementExcludeAccruals(); for
|
||||
* (LoanTransaction loanTransaction : loanTransactions) { if
|
||||
* (recalculateFrom == null ||
|
||||
* loanTransaction.getTransactionDate().isAfter(recalculateFrom)) {
|
||||
* recalculateFrom = loanTransaction.getTransactionDate(); } }
|
||||
* generatorDTO.setRecalculateFrom(recalculateFrom);
|
||||
*/
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays,
|
||||
calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
|
||||
}
|
||||
return processTransactions();
|
||||
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
|
||||
return processTransactions();
|
||||
|
||||
}
|
||||
|
||||
private ChangedTransactionDetail processTransactions() {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
|
||||
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), this.repaymentScheduleInstallments,
|
||||
charges());
|
||||
charges(), getLastUserTransactionForChargeCalc());
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
mapEntry.getValue().updateLoan(this);
|
||||
}
|
||||
@@ -4216,28 +4349,38 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
updateLoanSummaryDerivedFields();
|
||||
|
||||
this.loanTransactions.removeAll(changedTransactionDetail.getNewTransactionMappings().values());
|
||||
return changedTransactionDetail;
|
||||
|
||||
return changedTransactionDetail;
|
||||
}
|
||||
|
||||
private void regenerateRepaymentScheduleWithInterestRecalculation(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, LocalDate recalculateFrom) {
|
||||
private void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
|
||||
|
||||
final LoanScheduleModel loanSchedule = getRecalculatedSchedule(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation,
|
||||
recalculateFrom);
|
||||
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
||||
generatorDTO.setLastTransactionDate(lastTransactionDate);
|
||||
final LoanScheduleModel loanSchedule = getRecalculatedSchedule(generatorDTO);
|
||||
if (loanSchedule == null) { return; }
|
||||
|
||||
updateLoanSchedule(loanSchedule);
|
||||
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();
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
if (loanCharge.getDueLocalDate() == null || !lastRepaymentDate.isBefore(loanCharge.getDueLocalDate())) {
|
||||
recalculateLoanCharge(loanCharge);
|
||||
loanCharge.updateWaivedAmount(getCurrency());
|
||||
updateOverdueScheduleInstallment(loanCharge);
|
||||
if (loanCharge.getDueLocalDate() == null || (!lastRepaymentDate.isBefore(loanCharge.getDueLocalDate()))) {
|
||||
if (!loanCharge.isWaived() && !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||
recalculateLoanCharge(loanCharge, generatorDTO.getPenaltyWaitPeriod());
|
||||
loanCharge.updateWaivedAmount(getCurrency());
|
||||
}
|
||||
} else {
|
||||
loanCharge.setActive(false);
|
||||
}
|
||||
@@ -4246,28 +4389,25 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
updateLoanSummaryDerivedFields();
|
||||
}
|
||||
|
||||
private LoanScheduleModel getRecalculatedSchedule(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, LocalDate recalculateFrom) {
|
||||
private LoanScheduleModel getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
||||
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa) { return null; }
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || !isOpen()) { return null; }
|
||||
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 MathContext mc = new MathContext(8, roundingMode);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, calendarInstanceForInterestRecalculation);
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO.getApplicationCurrency(),
|
||||
generatorDTO.getCalculatedRepaymentsStartingFromDate(), generatorDTO.getCalendarInstanceForInterestRecalculation());
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, applicationCurrency,
|
||||
loanApplicationTerms, charges(), isHolidayEnabled, holidays, workingDays,
|
||||
retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor,
|
||||
this.repaymentScheduleInstallments, recalculateFrom);
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mc, loanApplicationTerms, charges(),
|
||||
generatorDTO.getHolidayDetailDTO(), retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||
loanRepaymentScheduleTransactionProcessor, this.repaymentScheduleInstallments, generatorDTO.getRecalculateFrom(),
|
||||
generatorDTO.getLastTransactionDate(), generatorDTO.getPenaltyWaitPeriod());
|
||||
return loanSchedule;
|
||||
}
|
||||
|
||||
@@ -4576,4 +4716,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
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.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
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.group.domain.Group;
|
||||
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.ScheduleGeneratorDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualWritePlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
|
||||
@@ -149,6 +152,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
ApplicationCurrency applicationCurrency = null;
|
||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate recalculateDueDateChargesFrom = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
@@ -161,12 +167,17 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
calendarInstance);
|
||||
|
||||
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,
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday,
|
||||
holidays, workingDays, allowTransactionsOnNonWorkingDay, isHolidayEnabled, isRecoveryRepayment, this.loanScheduleFactory,
|
||||
applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
|
||||
scheduleGeneratorDTO);
|
||||
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
|
||||
|
||||
@@ -259,17 +270,21 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
|
||||
final LoanTransaction newPaymentTransaction = LoanTransaction.loanPayment(null, loan.getOffice(), paymentAmout, paymentDetail,
|
||||
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()) {
|
||||
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
|
||||
} 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,
|
||||
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay, newPaymentTransaction,
|
||||
installmentNumber);
|
||||
holidayDetailDTO, newPaymentTransaction, installmentNumber);
|
||||
}
|
||||
saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction);
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -422,7 +437,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
@Override
|
||||
public void recalculateAccruals(Loan loan) {
|
||||
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<>();
|
||||
List<LoanRepaymentScheduleInstallment> installments = loan.fetchRepaymentScheduleInstallments();
|
||||
Long loanId = loan.getId();
|
||||
@@ -436,9 +452,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
CurrencyData currencyData = applicationCurrency.toData();
|
||||
Set<LoanCharge> loanCharges = loan.charges();
|
||||
BigDecimal accruedInterestIncome = null;
|
||||
BigDecimal accruedFeeIncome = null;
|
||||
BigDecimal accruedPenaltyIncome = null;
|
||||
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (!accruedTill.isBefore(installment.getDueDate())
|
||||
@@ -463,7 +476,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(),
|
||||
installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment
|
||||
.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());
|
||||
loanScheduleAccrualDatas.add(accrualData);
|
||||
|
||||
@@ -471,7 +485,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
|
||||
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")
|
||||
public class LoanInterestRecalculationDetails extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@OneToOne
|
||||
@JoinColumn(name = "loan_id", nullable = false)
|
||||
private Loan loan;
|
||||
|
||||
+15
-7
@@ -24,19 +24,27 @@ public class LoanOverdueInstallmentCharge extends AbstractPersistable<Long> {
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_schedule_id", referencedColumnName = "id", nullable = false)
|
||||
private LoanRepaymentScheduleInstallment installment;
|
||||
|
||||
@Column(name="frequency_number")
|
||||
|
||||
@Column(name = "frequency_number")
|
||||
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.installment = installment;
|
||||
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)
|
||||
private BigDecimal interestAccrued;
|
||||
|
||||
|
||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
private BigDecimal totalPaidInAdvance;
|
||||
|
||||
@@ -127,10 +133,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
this.obligationsMet = false;
|
||||
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||
}
|
||||
|
||||
|
||||
public LoanRepaymentScheduleInstallment(final Loan loan) {
|
||||
this.loan = loan;
|
||||
this.installmentNumber = null;
|
||||
this.loan = loan;
|
||||
this.installmentNumber = null;
|
||||
this.fromDate = null;
|
||||
this.dueDate = null;
|
||||
this.obligationsMet = false;
|
||||
@@ -204,6 +210,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
return getInterestCharged(currency).minus(interestAccountedFor);
|
||||
}
|
||||
|
||||
public Money getInterestAccrued(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.interestAccrued);
|
||||
}
|
||||
|
||||
public Money getFeeChargesCharged(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeChargesCharged);
|
||||
}
|
||||
@@ -226,6 +236,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
return getFeeChargesCharged(currency).minus(feeChargesAccountedFor);
|
||||
}
|
||||
|
||||
public Money getFeeAccrued(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.feeAccrued);
|
||||
}
|
||||
|
||||
public Money getPenaltyChargesCharged(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.penaltyCharges);
|
||||
}
|
||||
@@ -248,6 +262,10 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
return getPenaltyChargesCharged(currency).minus(feeChargesAccountedFor);
|
||||
}
|
||||
|
||||
public Money getPenaltyAccrued(final MonetaryCurrency currency) {
|
||||
return Money.of(currency, this.penaltyAccrued);
|
||||
}
|
||||
|
||||
public boolean isInterestDue(final MonetaryCurrency currency) {
|
||||
return getInterestOutstanding(currency).isGreaterThanZero();
|
||||
}
|
||||
@@ -264,10 +282,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
public void updateLoan(final Loan loan) {
|
||||
this.loan = loan;
|
||||
}
|
||||
|
||||
|
||||
public boolean isPartlyPaid() {
|
||||
return !this.obligationsMet && (this.interestPaid != null || this.feeChargesPaid != null
|
||||
|| this.principalCompleted != null);
|
||||
return !this.obligationsMet && (this.interestPaid != null || this.feeChargesPaid != null || this.principalCompleted != null);
|
||||
}
|
||||
|
||||
public boolean isObligationsMet() {
|
||||
@@ -507,6 +524,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
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) {
|
||||
if (!this.obligationsMet && getTotalOutstanding(currency).isZero()) {
|
||||
this.obligationsMet = true;
|
||||
@@ -569,59 +592,57 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
public void setRecalculatedInterestComponent(boolean 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 int compare(LoanRepaymentScheduleInstallment arg0,
|
||||
LoanRepaymentScheduleInstallment arg1) {
|
||||
|
||||
return arg0.getInstallmentNumber().compareTo(arg1.getInstallmentNumber());
|
||||
}
|
||||
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 int compare(LoanRepaymentScheduleInstallment arg0, LoanRepaymentScheduleInstallment arg1) {
|
||||
|
||||
return arg0.getInstallmentNumber().compareTo(arg1.getInstallmentNumber());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public BigDecimal getTotalPaidInAdvance() {
|
||||
return this.totalPaidInAdvance;
|
||||
return this.totalPaidInAdvance;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getTotalPaidLate() {
|
||||
return this.totalPaidLate;
|
||||
return this.totalPaidLate;
|
||||
}
|
||||
|
||||
|
||||
public LocalDate getObligationsMetOnDate() {
|
||||
LocalDate obligationsMetOnDate = null;
|
||||
|
||||
if(this.obligationsMetOnDate != null) {
|
||||
obligationsMetOnDate = new LocalDate(this.obligationsMetOnDate);
|
||||
}
|
||||
|
||||
return obligationsMetOnDate;
|
||||
LocalDate obligationsMetOnDate = null;
|
||||
|
||||
if (this.obligationsMetOnDate != null) {
|
||||
obligationsMetOnDate = new LocalDate(this.obligationsMetOnDate);
|
||||
}
|
||||
|
||||
return obligationsMetOnDate;
|
||||
}
|
||||
}
|
||||
+20
-11
@@ -20,24 +20,29 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
public class LoanRepaymentScheduleProcessingWrapper {
|
||||
|
||||
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 totalPrincipal = Money.zero(currency);
|
||||
for (final LoanRepaymentScheduleInstallment installment : repaymentPeriods) {
|
||||
totalInterest = totalInterest.plus(installment.getInterestCharged(currency));
|
||||
totalPrincipal = totalPrincipal.plus(installment.getPrincipal(currency));
|
||||
}
|
||||
LocalDate startDate = disbursementDate;
|
||||
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||
|
||||
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,
|
||||
currency, !period.isRecalculatedInterestComponent());
|
||||
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||
|
||||
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(),
|
||||
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||
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,
|
||||
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);
|
||||
|
||||
@@ -80,14 +86,15 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
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()) {
|
||||
amount = amount.add(totalInterest.getAmount());
|
||||
} else {
|
||||
amount = amount.add(period.getLoan().getPrincpal().getAmount());
|
||||
amount = amount.add(totalPrincipal.getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
cumulative = cumulative.plus(loanChargeAmt);
|
||||
@@ -144,7 +151,8 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
|
||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||
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);
|
||||
|
||||
@@ -171,14 +179,15 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
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()) {
|
||||
amount = amount.add(totalInterest.getAmount());
|
||||
} else {
|
||||
amount = amount.add(period.getLoan().getPrincpal().getAmount());
|
||||
amount = amount.add(totalPrincipal.getAmount());
|
||||
}
|
||||
BigDecimal loanChargeAmt = amount.multiply(loanCharge.getPercentage()).divide(BigDecimal.valueOf(100));
|
||||
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 LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate,
|
||||
null);
|
||||
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived,unrecognizedCharge);
|
||||
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived, unrecognizedCharge);
|
||||
|
||||
return waiver;
|
||||
}
|
||||
@@ -304,7 +304,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
|
||||
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();
|
||||
this.feeChargesPortion = defaultToNullIfZero(getFeeChargesPortion(currency).plus(feeCharges).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.unrecognizedIncomePortion = defaultToNullIfZero(getUnrecognizedIncomePortion(currency).plus(unrecognizedInterest).getAmount());
|
||||
}
|
||||
|
||||
|
||||
public void adjustInterestComponent(final MonetaryCurrency currency) {
|
||||
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,
|
||||
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) {
|
||||
|
||||
+24
-4
@@ -48,7 +48,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
||||
@Override
|
||||
public ChangedTransactionDetail handleTransaction(final LocalDate disbursementDate,
|
||||
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) {
|
||||
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
|
||||
// loan charges)
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(currency, disbursementDate, installments, charges);
|
||||
if (reprocessCharges) {
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(currency, disbursementDate, installments, charges, recalculateChargesFrom);
|
||||
}
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = new ChangedTransactionDetail();
|
||||
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 boolean isFeeCharge) {
|
||||
// to.
|
||||
if(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()){
|
||||
if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||
loanTransaction.resetDerivedComponents();
|
||||
}
|
||||
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);
|
||||
|
||||
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,
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||
@@ -31,13 +32,18 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
||||
List<RecalculationDetail> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
||||
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();
|
||||
|
||||
|
||||
void applyTransaction(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||
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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -18,14 +19,13 @@ import java.util.TreeMap;
|
||||
|
||||
import org.joda.time.Days;
|
||||
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.MonetaryCurrency;
|
||||
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.service.CalendarUtils;
|
||||
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.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -49,22 +49,21 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
private final PaymentPeriodsInOneYearCalculator paymentPeriodsInOneYearCalculator = new DefaultPaymentPeriodsInOneYearCalculator();
|
||||
|
||||
@Override
|
||||
public LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
public LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO) {
|
||||
final List<RecalculationDetail> diffAmt = null;
|
||||
final LocalDate prepayDate = null;
|
||||
return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt,
|
||||
prepayDate);
|
||||
final LocalDate lastTransactionDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
return generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, diffAmt, prepayDate, lastTransactionDate);
|
||||
}
|
||||
|
||||
private LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, List<RecalculationDetail> diffAmt, LocalDate prepayDate) {
|
||||
private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, List<RecalculationDetail> diffAmt,
|
||||
final LocalDate prepayDate, final LocalDate lastTransactionDate) {
|
||||
|
||||
final ApplicationCurrency applicationCurrency = loanApplicationTerms.getApplicationCurrency();
|
||||
// 1. generate list of proposed schedule due dates
|
||||
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);
|
||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||
|
||||
// 2. determine the total charges due at time of disbursement
|
||||
@@ -144,7 +143,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
holidayDetailDTO);
|
||||
scheduledDueDateAsPerFrequency = scheduledDueDate;
|
||||
} else {
|
||||
recalculatedInterestComponent = true;
|
||||
@@ -153,7 +152,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
LocalDate actualRepayment = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
|
||||
loanApplicationTerms, isFirstRepayment);
|
||||
scheduledDueDateAsPerFrequency = this.scheduledDateGenerator.adjustRepaymentDate(actualRepayment, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
holidayDetailDTO);
|
||||
}
|
||||
|
||||
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 feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent(),
|
||||
lastTransactionDate);
|
||||
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
||||
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent(),
|
||||
lastTransactionDate);
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
||||
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount());
|
||||
@@ -549,8 +550,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays) {
|
||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO) {
|
||||
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
@@ -696,8 +696,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// update the number of repayments
|
||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||
|
||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, holidayDetailDTO);
|
||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||
|
||||
if (newInterestRate != null) {
|
||||
@@ -739,7 +738,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
holidayDetailDTO);
|
||||
|
||||
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,
|
||||
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
||||
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments,
|
||||
boolean isInstallmentChargeApplicable) {
|
||||
boolean isInstallmentChargeApplicable, final LocalDate lastTransactionDate) {
|
||||
|
||||
Money cumulative = Money.zero(monetaryCurrency);
|
||||
|
||||
@@ -963,7 +962,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
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,
|
||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency,
|
||||
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);
|
||||
|
||||
@@ -1013,7 +1014,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||
&& loanCharge.getChargeCalculation().isPercentageBased()
|
||||
&& !lastTransactionDate.isAfter(loanCharge.getDueLocalDate())) {
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||
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 )
|
||||
*/
|
||||
@Override
|
||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||
public LoanScheduleModel rescheduleNextInstallments(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom) {
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom, final LocalDate lastTransactionDate,
|
||||
final int penaltyWaitPeriod) {
|
||||
|
||||
LoanScheduleModel loanScheduleModel = null;
|
||||
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
||||
@@ -1064,8 +1066,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
installments.removeAll(removeInstallments);
|
||||
if (previousSchedule == null) {
|
||||
loanScheduleModel = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
loanScheduleModel = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO);
|
||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||
}
|
||||
|
||||
@@ -1082,10 +1083,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
int installmentNumber = findLastProcessedInstallmentNumber(installments, processTransactionsForInterestCompound);
|
||||
List<LoanTransaction> processTransactions = processTransactions(transactions, processTransactionsForInterestCompound);
|
||||
if (processTransactions.size() > 0) {
|
||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, false,
|
||||
installmentNumber, processTransactions, preCloseTransaction);
|
||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, currency,
|
||||
recalculationDetails, retainRecalculationDetails, false, installmentNumber, processTransactions,
|
||||
preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||
}
|
||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||
processTransactionsForInterestCompound = getNextRecalculateFromDate(transactions, processTransactionsForInterestCompound);
|
||||
@@ -1097,10 +1098,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
||||
if (loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()
|
||||
&& processTransactions.size() > 0) {
|
||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, true,
|
||||
installmentNumber, processTransactions, preCloseTransaction);
|
||||
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, currency,
|
||||
recalculationDetails, retainRecalculationDetails, true, installmentNumber, processTransactions,
|
||||
preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||
if (loanScheduleModel != null) {
|
||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||
}
|
||||
@@ -1114,9 +1115,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||
recalculationDetails.addAll(earlypayments);
|
||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
||||
holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
||||
processTransactions, installmentNumber, recalculationDetails, preCloseTransaction);
|
||||
loanScheduleModel = recalculateInstallment(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments, processTransactions,
|
||||
installmentNumber, recalculationDetails, preCloseTransaction, lastTransactionDate, penaltyWaitPeriod, transactions);
|
||||
if (loanScheduleModel != null) {
|
||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||
}
|
||||
@@ -1130,14 +1131,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
/**
|
||||
* Method identifies interest only repayment periods
|
||||
*/
|
||||
private LoanScheduleModel createInterestOnlyRecalculationDetails(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
private LoanScheduleModel createInterestOnlyRecalculationDetails(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||
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);
|
||||
if (!(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver())) {
|
||||
processTransactions.add(loanTransaction);
|
||||
@@ -1187,10 +1188,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
recalculationDetails.add(recalculationDetail);
|
||||
if (recalculate) {
|
||||
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||
loanScheduleModel, installments, processTransactions, installmentNumber, recalculationDetails,
|
||||
preCloseTransaction);
|
||||
loanScheduleModel = recalculateInstallment(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||
loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
||||
processTransactions, installmentNumber, recalculationDetails, preCloseTransaction, lastTransactionDate,
|
||||
penaltyWaitPeriod, actualTransactions);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1221,19 +1222,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
* @param preCloseTransaction
|
||||
* TODO
|
||||
*/
|
||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||
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;
|
||||
Integer lastInstallmentNumber = previousSchedule.size();
|
||||
while (processInstallmentsFrom < lastInstallmentNumber) {
|
||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, processTransactions, loanRepaymentScheduleTransactionProcessor,
|
||||
processInstallmentsFrom, installments, recalculationDetails, skipRecalculation, preCloseTransaction);
|
||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, loanApplicationTerms, loanCharges, holidayDetailDTO,
|
||||
processTransactions, loanRepaymentScheduleTransactionProcessor, processInstallmentsFrom, installments,
|
||||
recalculationDetails, skipRecalculation, preCloseTransaction, lastTransactionDate, penaltyWaitPeriod,
|
||||
actualTransactions);
|
||||
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
||||
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
||||
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
||||
@@ -1250,12 +1252,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
* @param preCloseTransaction
|
||||
* TODO
|
||||
*/
|
||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
final Set<LoanCharge> loanCharges, final HolidayDetailDTO holidayDetailDTO, final List<LoanTransaction> transactions,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
||||
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;
|
||||
int processedInstallmentNumber = installmentNumber;
|
||||
|
||||
@@ -1282,15 +1284,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
List<LoanTransaction> transactionsForInstallment = new ArrayList<>();
|
||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||
LocalDate transactionsDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
holidayDetailDTO);
|
||||
for (LoanTransaction loanTransaction : transactions) {
|
||||
LocalDate loantransactionDate = loanTransaction.getTransactionDate();
|
||||
if (!loantransactionDate.isAfter(transactionsDate)) {
|
||||
transactionsForInstallment.add(loanTransaction);
|
||||
recalculationDates.put(
|
||||
loantransactionDate,
|
||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays));
|
||||
recalculationDates.put(loantransactionDate,
|
||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, holidayDetailDTO));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1321,15 +1321,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// this block is to identify late payment based on the rest
|
||||
// calculation date
|
||||
while (totalOutstanding.isGreaterThanZero() && startDate.isBefore(LocalDate.now())) {
|
||||
LocalDate recalculateFrom = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
LocalDate recalcualteTill = getNextRestScheduleDate(recalculateFrom, loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
LocalDate recalculateFrom = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
|
||||
LocalDate recalcualteTill = getNextRestScheduleDate(recalculateFrom, loanApplicationTerms, holidayDetailDTO);
|
||||
if (reduceStartDate) {
|
||||
startDate = startDate.minusDays(1);
|
||||
}
|
||||
applyRest(transactions, startDate, recalculateFrom, installment, loanRepaymentScheduleTransactionProcessor, currency,
|
||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays, installments, preCloseTransaction);
|
||||
loanApplicationTerms, holidayDetailDTO, installments, preCloseTransaction);
|
||||
totalOutstanding = installment.getTotalOutstanding(currency);
|
||||
if (totalOutstanding.isGreaterThanZero()) {
|
||||
Money latepaymentoutstanding = installment.getPrincipalOutstanding(currency);
|
||||
@@ -1373,12 +1371,130 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (preCloseTransaction != null) {
|
||||
prepayDate = preCloseTransaction.getTransactionDate();
|
||||
}
|
||||
model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays,
|
||||
recalculationDetails, prepayDate);
|
||||
model = generate(mc, loanApplicationTerms, loanCharges, holidayDetailDTO, recalculationDetails, prepayDate, lastTransactionDate);
|
||||
model = updateOverDueCharges(loanCharges, model, lastTransactionDate, currency, loanRepaymentScheduleTransactionProcessor,
|
||||
loanApplicationTerms.getExpectedDisbursementDate(), penaltyWaitPeriod, actualTransactions);
|
||||
}
|
||||
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) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
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,
|
||||
LoanRepaymentScheduleInstallment installment,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
||||
final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays, final List<LoanRepaymentScheduleInstallment> installments, LoanTransaction preCloseTransaction) {
|
||||
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, LoanTransaction preCloseTransaction) {
|
||||
List<LoanTransaction> transactions = new ArrayList<>();
|
||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||
for (LoanTransaction transaction : loanTransactions) {
|
||||
LocalDate loantransactionDate = transaction.getTransactionDate();
|
||||
if (loantransactionDate.isAfter(from) && !transaction.getTransactionDate().isAfter(to)) {
|
||||
transactions.add(transaction);
|
||||
recalculationDates.put(
|
||||
loantransactionDate,
|
||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays));
|
||||
recalculationDates.put(loantransactionDate,
|
||||
getNextRestScheduleDate(loantransactionDate.minusDays(1), loanApplicationTerms, holidayDetailDTO));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1424,11 +1538,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
private LocalDate getNextRestScheduleDate(LocalDate startDate, LoanApplicationTerms loanApplicationTerms,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
final HolidayDetailDTO holidayDetailDTO) {
|
||||
LocalDate nextScheduleDate = null;
|
||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||
nextScheduleDate = this.scheduledDateGenerator.generateNextScheduleDateStartingFromDisburseDate(startDate,
|
||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
} else {
|
||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||
nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), startDate);
|
||||
|
||||
+11
-14
@@ -5,24 +5,20 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.Months;
|
||||
import org.joda.time.Weeks;
|
||||
import org.joda.time.Years;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.holiday.service.HolidayUtil;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
|
||||
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@Override
|
||||
public LocalDate getLastRepaymentDate(final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
public LocalDate getLastRepaymentDate(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||
|
||||
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
|
||||
|
||||
@@ -32,7 +28,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
}
|
||||
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
||||
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, holidayDetailDTO);
|
||||
return lastRepaymentDate;
|
||||
}
|
||||
|
||||
@@ -52,22 +48,23 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@Override
|
||||
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;
|
||||
/**
|
||||
* Fix for https://mifosforge.jira.com/browse/MIFOSX-1357
|
||||
*/
|
||||
// recursively check for the next working day.
|
||||
while (WorkingDaysUtil.isNonWorkingDay(workingDays, adjustedDate)) {
|
||||
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDate)) {
|
||||
|
||||
final LocalDate nextDueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery(), adjustedDate);
|
||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate, workingDays);
|
||||
adjustedDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(adjustedDate, nextDueRepaymentPeriodDate,
|
||||
holidayDetailDTO.getWorkingDays());
|
||||
}
|
||||
|
||||
if (isHolidayEnabled) {
|
||||
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidays);
|
||||
if (holidayDetailDTO.isHolidayEnabled()) {
|
||||
adjustedDate = HolidayUtil.getRepaymentRescheduleDateToIfHoliday(adjustedDate, holidayDetailDTO.getHolidays());
|
||||
}
|
||||
return adjustedDate;
|
||||
}
|
||||
@@ -161,7 +158,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
|
||||
@Override
|
||||
public LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate,
|
||||
LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled, List<Holiday> holidays, WorkingDays workingDays) {
|
||||
LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
|
||||
|
||||
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
boolean isFirstRepayment = true;
|
||||
@@ -169,7 +166,7 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
|
||||
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
|
||||
isFirstRepayment = false;
|
||||
}
|
||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
||||
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO);
|
||||
return generatedDate;
|
||||
}
|
||||
}
|
||||
+42
-39
@@ -110,7 +110,7 @@ public final class LoanApplicationTerms {
|
||||
private BigDecimal maxOutstandingBalance;
|
||||
|
||||
private List<LoanTermVariationsData> emiAmountVariations;
|
||||
|
||||
|
||||
private Money totalInterestDue;
|
||||
|
||||
private final DaysInMonthType daysInMonthType;
|
||||
@@ -505,7 +505,7 @@ public final class LoanApplicationTerms {
|
||||
final int periodsElapsed = 0;
|
||||
final double paymentPerRepaymentPeriod = paymentPerPeriod(periodicInterestRate, this.principal, periodsElapsed);
|
||||
double totalRepayment = paymentPerRepaymentPeriod * this.numberOfRepayments;
|
||||
|
||||
|
||||
return Money.of(this.principal.getCurrency(), BigDecimal.valueOf(totalRepayment));
|
||||
}
|
||||
|
||||
@@ -521,16 +521,16 @@ public final class LoanApplicationTerms {
|
||||
case FLAT:
|
||||
final BigDecimal interestRateForLoanTerm = calculateFlatInterestRateForLoanTerm(calculator, mc);
|
||||
totalInterestDue = this.principal.multiplyRetainScale(interestRateForLoanTerm, mc.getRoundingMode());
|
||||
|
||||
|
||||
break;
|
||||
case DECLINING_BALANCE:
|
||||
break;
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
if(this.totalInterestDue != null) {
|
||||
totalInterestDue = this.totalInterestDue;
|
||||
|
||||
if (this.totalInterestDue != null) {
|
||||
totalInterestDue = this.totalInterestDue;
|
||||
}
|
||||
|
||||
return totalInterestDue;
|
||||
@@ -544,7 +544,7 @@ public final class LoanApplicationTerms {
|
||||
final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear);
|
||||
|
||||
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
||||
|
||||
|
||||
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||
.multiply(loanTermFrequencyBigDecimal);
|
||||
}
|
||||
@@ -1078,59 +1078,62 @@ public final class LoanApplicationTerms {
|
||||
return numberOfPeriods;
|
||||
}
|
||||
|
||||
|
||||
public RecalculationFrequencyType getRecalculationFrequencyType() {
|
||||
return this.recalculationFrequencyType;
|
||||
}
|
||||
|
||||
|
||||
public void updateNumberOfRepayments(final Integer numberOfRepayments) {
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
this.numberOfRepayments = numberOfRepayments;
|
||||
}
|
||||
|
||||
|
||||
public void updatePrincipalGrace(final Integer principalGrace) {
|
||||
this.principalGrace = principalGrace;
|
||||
this.principalGrace = principalGrace;
|
||||
}
|
||||
|
||||
|
||||
public void updateInterestPaymentGrace(final Integer interestPaymentGrace) {
|
||||
this.interestPaymentGrace = interestPaymentGrace;
|
||||
this.interestPaymentGrace = interestPaymentGrace;
|
||||
}
|
||||
|
||||
|
||||
public void updateInterestRatePerPeriod(BigDecimal interestRatePerPeriod) {
|
||||
if(interestRatePerPeriod != null) {
|
||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||
}
|
||||
if (interestRatePerPeriod != null) {
|
||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void updateAnnualNominalInterestRate(BigDecimal annualNominalInterestRate) {
|
||||
if(annualNominalInterestRate != null) {
|
||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||
}
|
||||
if (annualNominalInterestRate != null) {
|
||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getInterestRatePerPeriod() {
|
||||
return this.interestRatePerPeriod;
|
||||
return this.interestRatePerPeriod;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return this.annualNominalInterestRate;
|
||||
return this.annualNominalInterestRate;
|
||||
}
|
||||
|
||||
|
||||
public void updateInterestChargedFromDate(LocalDate interestChargedFromDate) {
|
||||
if(interestChargedFromDate != null) {
|
||||
this.interestChargedFromDate = interestChargedFromDate;
|
||||
}
|
||||
if (interestChargedFromDate != null) {
|
||||
this.interestChargedFromDate = interestChargedFromDate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void updateLoanTermFrequency(Integer loanTermFrequency) {
|
||||
if(loanTermFrequency != null) {
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
}
|
||||
if (loanTermFrequency != null) {
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void updateTotalInterestDue(Money totalInterestDue) {
|
||||
|
||||
if(totalInterestDue != null) {
|
||||
this.totalInterestDue = totalInterestDue;
|
||||
}
|
||||
|
||||
if (totalInterestDue != null) {
|
||||
this.totalInterestDue = totalInterestDue;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationCurrency getApplicationCurrency() {
|
||||
return this.currency;
|
||||
}
|
||||
}
|
||||
+13
-14
@@ -10,10 +10,9 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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.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.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
@@ -23,19 +22,19 @@ import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanResched
|
||||
|
||||
public interface LoanScheduleGenerator {
|
||||
|
||||
LoanScheduleModel generate(MathContext mc, ApplicationCurrency applicationCurrency, LoanApplicationTerms loanApplicationTerms,
|
||||
Set<LoanCharge> loanCharges, boolean isHolidayEnabled, List<Holiday> holidays, final WorkingDays workingDays);
|
||||
LoanScheduleModel generate(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||
final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LoanScheduleModel rescheduleNextInstallments(MathContext mc, ApplicationCurrency applicationCurrency,
|
||||
LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges, boolean isHolidayEnabled, List<Holiday> holidays,
|
||||
WorkingDays workingDays, List<LoanTransaction> transactions,
|
||||
LoanScheduleModel rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Set<LoanCharge> loanCharges,
|
||||
final HolidayDetailDTO holidayDetailDTO, List<LoanTransaction> transactions,
|
||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom, LocalDate lastTransactionDate,
|
||||
int penaltyWaitPeriod);
|
||||
|
||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, 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,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
||||
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments,
|
||||
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 HolidayDetailDTO holidayDetailDTO);
|
||||
}
|
||||
+14
@@ -42,6 +42,15 @@ public final class LoanScheduleModel {
|
||||
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,
|
||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||
@@ -85,4 +94,9 @@ public final class LoanScheduleModel {
|
||||
public Collection<LoanScheduleModelPeriod> getPeriods() {
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getTotalPenaltyChargesCharged() {
|
||||
return this.totalPenaltyChargesCharged;
|
||||
}
|
||||
}
|
||||
+4
-9
@@ -5,30 +5,25 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.loanaccount.data.HolidayDetailDTO;
|
||||
|
||||
public interface ScheduledDateGenerator {
|
||||
|
||||
LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled, List<Holiday> holidays,
|
||||
final WorkingDays workingDays);
|
||||
LocalDate getLastRepaymentDate(LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(PeriodFrequencyType repaymentPeriodFrequencyType, int repaidEvery,
|
||||
final LocalDate firstRepaymentDate);
|
||||
|
||||
LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms, boolean isFirstRepayment);
|
||||
|
||||
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms, boolean isHolidayEnabled,
|
||||
List<Holiday> holidays, WorkingDays workingDays);
|
||||
LocalDate adjustRepaymentDate(LocalDate dueRepaymentPeriodDate, LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO);
|
||||
|
||||
LocalDate getRepaymentPeriodDate(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate);
|
||||
|
||||
Boolean isDateFallsInSchedule(PeriodFrequencyType frequency, int repaidEvery, LocalDate startDate, LocalDate date);
|
||||
|
||||
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.loanaccount.api.LoanApiConstants;
|
||||
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.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -358,30 +359,34 @@ public class LoanScheduleAssembler {
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
|
||||
final MonetaryCurrency currency = loanApplicationTerms.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
return loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
return loanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO);
|
||||
}
|
||||
|
||||
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
|
||||
List<LoanTransaction> transactions, final Set<LoanCharge> loanCharges,
|
||||
final List<LoanRepaymentScheduleInstallment> previousSchedule,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LocalDate recalculateFrom) {
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, LocalDate recalculateFrom,
|
||||
LocalDate recalculateDueDateChargesFrom) {
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository
|
||||
.findOneWithNotFoundDetection(loanApplicationTerms.getCurrency());
|
||||
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms
|
||||
.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
|
||||
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod());
|
||||
return loanScheduleGenerator.rescheduleNextInstallments(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||
isHolidayEnabled, holidays, workingDays, transactions, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||
recalculateFrom);
|
||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
final Long waitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
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,
|
||||
@@ -400,8 +405,8 @@ public class LoanScheduleAssembler {
|
||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate.minusDays(1));
|
||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, detailDTO);
|
||||
}
|
||||
|
||||
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(),
|
||||
modifiedTransactions, loan.charges(), loan.fetchRepaymentScheduleInstallments(), loanRepaymentScheduleTransactionProcessor,
|
||||
LocalDate.now());
|
||||
LocalDate.now(), LocalDate.now());
|
||||
LoanScheduleData scheduleDate = model.toData();
|
||||
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
||||
for (LoanSchedulePeriodData periodData : periodDatas) {
|
||||
|
||||
+25
-28
@@ -6,40 +6,37 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
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.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.FlatInterestLoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext,
|
||||
final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = null;
|
||||
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(
|
||||
mathContext, loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
break;
|
||||
|
||||
case FLAT:
|
||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
break;
|
||||
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
final HolidayDetailDTO holidayDetailDTO) {
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = null;
|
||||
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO);
|
||||
break;
|
||||
|
||||
case FLAT:
|
||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO);
|
||||
break;
|
||||
|
||||
case INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -6,16 +6,14 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
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.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public interface LoanReschedulerFactory {
|
||||
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays);
|
||||
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
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.workingdays.domain.WorkingDays;
|
||||
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.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
@@ -76,8 +77,9 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
||||
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO);
|
||||
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
||||
oldPeriods);
|
||||
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.workingdays.domain.WorkingDays;
|
||||
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.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
@@ -279,8 +280,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
+15
-5
@@ -426,8 +426,14 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
}
|
||||
chargeAmount = accruableForInstallment;
|
||||
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
||||
if (canAddCharge && installmentChargeData.getAmountAccrued() == null) {
|
||||
applicableCharges.put(loanCharge, chargeAmount);
|
||||
if (canAddCharge
|
||||
&& (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;
|
||||
if (loanCharge.getAmountAccrued() != null) {
|
||||
amountAccrued = amountAccrued.add(loanCharge.getAmountAccrued());
|
||||
@@ -444,8 +450,12 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
||||
chargeAmount = chargeAmount.subtract(loanCharge.getAmountUnrecognized());
|
||||
}
|
||||
boolean canAddCharge = chargeAmount.compareTo(BigDecimal.ZERO) == 1;
|
||||
if (canAddCharge && loanCharge.getAmountAccrued() == null) {
|
||||
applicableCharges.put(loanCharge, chargeAmount);
|
||||
if (canAddCharge && (loanCharge.getAmountAccrued() == null || chargeAmount.compareTo(loanCharge.getAmountAccrued()) != 0)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Iterator<LoanTransactionData> iterator = loanTransactionDatas.iterator();
|
||||
for (LoanSchedulePeriodData loanSchedulePeriodData : loanSchedulePeriodDatas) {
|
||||
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.loanaccount.api.LoanApiConstants;
|
||||
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.LoanChargePaidByData;
|
||||
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.DefaultLoanLifecycleStateMachine;
|
||||
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.serialization.LoanEventApiJsonValidator;
|
||||
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.exception.InvalidCurrencyException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LinkedAccountRequiredException;
|
||||
@@ -299,20 +302,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
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,
|
||||
holidays, workingDays, recalculateSchedule);
|
||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(),
|
||||
loan, null);
|
||||
}
|
||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay, restCalendarInstance);
|
||||
|
||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO);
|
||||
}
|
||||
if (!changes.isEmpty()) {
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -496,19 +506,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.getLoanTransactions().add(disbursementTransaction);
|
||||
}
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
}
|
||||
regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays,
|
||||
workingDays, recalculateSchedule);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays,
|
||||
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
firstRepaymentOnDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||
lastTransactionDate);
|
||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
||||
loan.fetchRepaymentScheduleInstallments(), loan, null);
|
||||
}
|
||||
changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes,
|
||||
firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay, restCalendarInstance);
|
||||
|
||||
changedTransactionDetail = loan.disburse(currentUser, command, changes, scheduleGeneratorDTO);
|
||||
}
|
||||
if (!changes.isEmpty()) {
|
||||
|
||||
@@ -582,8 +599,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
|
||||
final Map<String, Object> changes = loan.undoDisbursal(this.loanScheduleFactory, applicationCurrency, existingTransactionIds,
|
||||
existingReversedTransactionIds, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(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()) {
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -740,6 +761,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
ApplicationCurrency applicationCurrency = null;
|
||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
@@ -752,12 +777,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
|
||||
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,
|
||||
defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||
allowTransactionsOnHoliday, holidays, workingDays, isHolidayEnabled, allowTransactionsOnNonWorkingDay,
|
||||
this.loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
scheduleGeneratorDTO);
|
||||
|
||||
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
|
||||
if (paymentDetail != null) {
|
||||
@@ -856,6 +887,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
List<Holiday> holidays = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
WorkingDays workingDays = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
@@ -870,12 +904,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
}
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan
|
||||
.waiveInterest(waiveInterestTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
|
||||
existingReversedTransactionIds, holidays, workingDays, isHolidayEnabled, this.loanScheduleFactory,
|
||||
applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||
lastTransactionDate);
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
|
||||
this.loanTransactionRepository.save(waiveInterestTransaction);
|
||||
|
||||
@@ -949,14 +986,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
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(),
|
||||
changes, existingTransactionIds, existingReversedTransactionIds, currentUser, this.loanScheduleFactory,
|
||||
applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
||||
changes, existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||
LoanTransaction writeoff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||
this.loanTransactionRepository.save(writeoff);
|
||||
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);
|
||||
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
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,
|
||||
existingTransactionIds, existingReversedTransactionIds, this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
|
||||
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||
if (possibleClosingTransaction != null) {
|
||||
this.loanTransactionRepository.save(possibleClosingTransaction);
|
||||
@@ -1133,8 +1185,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
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() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(loanCharge.getId()) //
|
||||
@@ -1145,7 +1214,71 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.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())) {
|
||||
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);
|
||||
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();
|
||||
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);
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge);
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -1311,6 +1419,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
List<Holiday> holidays = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
WorkingDays workingDays = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
@@ -1325,8 +1436,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
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());
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
|
||||
@@ -1337,8 +1454,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, holidays, workingDays, isHolidayEnabled,
|
||||
loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance, accruedCharge);
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge);
|
||||
|
||||
this.loanTransactionRepository.save(waiveTransaction);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2011,39 +2127,43 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
if (!overdueLoanScheduledInstallments.isEmpty()) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final Map<Long, Collection<OverdueLoanScheduleData>> overdueScheduleData = new HashMap<>();
|
||||
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 {
|
||||
|
||||
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);
|
||||
applyOverdueChargesForLoan(loanId, overdueScheduleData.get(loanId));
|
||||
|
||||
} catch (final PlatformApiDataValidationException e) {
|
||||
final List<ApiParameterError> errors = e.getErrors();
|
||||
for (final ApiParameterError error : errors) {
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
||||
+ " with message " + error.getDeveloperMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
||||
.append(" with message ").append(error.getDeveloperMessage());
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||
+ error.getDeveloperMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||
.append(error.getDeveloperMessage());
|
||||
}
|
||||
} catch (final AbstractPlatformDomainRuleException ex) {
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
||||
+ " with message " + ex.getDefaultUserMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
||||
.append(" with message ").append(ex.getDefaultUserMessage());
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||
+ ex.getDefaultUserMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||
.append(ex.getDefaultUserMessage());
|
||||
} catch (Exception e) {
|
||||
Throwable realCause = e;
|
||||
if (e.getCause() != null) {
|
||||
realCause = e.getCause();
|
||||
}
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + overdueInstallment.getLoanId()
|
||||
+ " with message " + realCause.getMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(overdueInstallment.getLoanId())
|
||||
.append(" with message ").append(realCause.getMessage());
|
||||
logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message "
|
||||
+ realCause.getMessage());
|
||||
sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ")
|
||||
.append(realCause.getMessage());
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||
@@ -2051,9 +2171,42 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void applyChargeToOverdueLoanInstallment(final Long loanId, final Long loanChargeId, final Integer periodNumber,
|
||||
final JsonCommand command) {
|
||||
public void applyOverdueChargesForLoan(final Long loanId, Collection<OverdueLoanScheduleData> overdueLoanScheduleDatas) {
|
||||
|
||||
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);
|
||||
|
||||
Collection<Integer> frequencyNumbers = loanChargeReadPlatformService.retrieveOverdueInstallmentChargeFrequencyNumber(loanId,
|
||||
@@ -2087,24 +2240,32 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
scheduleDates.remove(frequency);
|
||||
}
|
||||
|
||||
Loan loan = null;
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
if (!scheduleDates.isEmpty()) {
|
||||
loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
if (loan == null) {
|
||||
loan = this.loanAssembler.assembleFrom(loanId);
|
||||
checkClientOrGroupActive(loan);
|
||||
existingTransactionIds.addAll(loan.findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
|
||||
}
|
||||
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,
|
||||
entry.getKey());
|
||||
loanCharge.updateOverdueInstallmentCharge(overdueInstallmentCharge);
|
||||
LoanOverdueInstallmentCharge overdueInstallmentCharge = new LoanOverdueInstallmentCharge(loanCharge, installment,
|
||||
entry.getKey());
|
||||
loanCharge.updateOverdueInstallmentCharge(overdueInstallmentCharge);
|
||||
|
||||
addCharge(loan, chargeDefinition, loanCharge);
|
||||
boolean isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);
|
||||
runInterestRecalculation = runInterestRecalculation || isAppliedOnBackDate;
|
||||
}
|
||||
}
|
||||
|
||||
return new LoanOverdueDTO(loan, runInterestRecalculation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2122,6 +2283,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
List<Holiday> holidays = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
WorkingDays workingDays = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
|
||||
@@ -2136,11 +2301,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
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,
|
||||
isHolidayEnabled, holidays, workingDays, this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
scheduleGeneratorDTO);
|
||||
if (changedTransactionDetail != null) {
|
||||
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
|
||||
this.loanTransactionRepository.save(mapEntry.getValue());
|
||||
@@ -2182,13 +2352,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
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,
|
||||
existingTransactionIds, existingReversedTransactionIds, changes, this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance);
|
||||
existingTransactionIds, existingReversedTransactionIds, changes, scheduleGeneratorDTO);
|
||||
|
||||
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
|
||||
@@ -2198,8 +2377,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
@@ -2299,6 +2477,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
List<Holiday> holidays = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
WorkingDays workingDays = null;
|
||||
LocalDate recalculateFrom = null;
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
LocalDate lastTransactionDate = null;
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
@@ -2315,9 +2496,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
}
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(loanScheduleFactory,
|
||||
applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, restCalendarInstance,
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, recalculateFrom, overdurPenaltyWaitPeriod,
|
||||
lastTransactionDate);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(generatorDTO,
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2340,23 +2526,18 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
private void createLoanScheduleArchive(final Loan loan, final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays) {
|
||||
LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
||||
private void createLoanScheduleArchive(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
LoanScheduleModel loanScheduleModel = loan.regenerateScheduleModel(scheduleGeneratorDTO);
|
||||
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentScheduleFromModel(loanScheduleModel);
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(installments, loan, null);
|
||||
|
||||
}
|
||||
|
||||
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, final boolean recalculateSchedule) {
|
||||
private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final boolean recalculateSchedule,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
|
||||
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
|
||||
loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, recalculateSchedule, actualDisbursementDate, emiAmount);
|
||||
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount);
|
||||
}
|
||||
|
||||
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() {
|
||||
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