Merge pull request #1067 from pramodn02/develop
MIFOSX-1537 : added original schedule to history
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
ALTER TABLE `m_loan_repayment_schedule_history`
|
||||
DROP COLUMN `principal_completed_derived`,
|
||||
DROP COLUMN `principal_writtenoff_derived`,
|
||||
DROP COLUMN `interest_completed_derived`,
|
||||
DROP COLUMN `interest_writtenoff_derived`,
|
||||
DROP COLUMN `interest_waived_derived`,
|
||||
DROP COLUMN `accrual_interest_derived`,
|
||||
DROP COLUMN `fee_charges_completed_derived`,
|
||||
DROP COLUMN `fee_charges_writtenoff_derived`,
|
||||
DROP COLUMN `fee_charges_waived_derived`,
|
||||
DROP COLUMN `accrual_fee_charges_derived`,
|
||||
DROP COLUMN `penalty_charges_completed_derived`,
|
||||
DROP COLUMN `penalty_charges_writtenoff_derived`,
|
||||
DROP COLUMN `penalty_charges_waived_derived`,
|
||||
DROP COLUMN `accrual_penalty_charges_derived`,
|
||||
DROP COLUMN `total_paid_in_advance_derived`,
|
||||
DROP COLUMN `total_paid_late_derived`,
|
||||
DROP COLUMN `completed_derived`,
|
||||
DROP COLUMN `obligations_met_on_date`,
|
||||
ADD COLUMN `version` INT(5) NOT NULL;
|
||||
+15
-3
@@ -77,6 +77,7 @@ import org.mifosplatform.portfolio.loanaccount.guarantor.service.GuarantorReadPl
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
@@ -137,6 +138,7 @@ public class LoansApiResource {
|
||||
private final NoteReadPlatformServiceImpl noteReadPlatformService;
|
||||
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
|
||||
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
|
||||
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
|
||||
@@ -153,7 +155,8 @@ public class LoansApiResource {
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
|
||||
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService) {
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanProductReadPlatformService = loanProductReadPlatformService;
|
||||
@@ -175,6 +178,7 @@ public class LoansApiResource {
|
||||
this.noteReadPlatformService = noteReadPlatformService;
|
||||
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
|
||||
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
|
||||
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -318,8 +322,8 @@ public class LoansApiResource {
|
||||
if (!associationParameters.isEmpty()) {
|
||||
|
||||
if (associationParameters.contains("all")) {
|
||||
associationParameters.addAll(Arrays.asList("repaymentSchedule", "futureSchedule", "transactions", "charges", "guarantors",
|
||||
"collateral", "notes", "linkedAccount", "multiDisburseDetails"));
|
||||
associationParameters.addAll(Arrays.asList("repaymentSchedule", "futureSchedule", "originalSchedule", "transactions",
|
||||
"charges", "guarantors", "collateral", "notes", "linkedAccount", "multiDisburseDetails"));
|
||||
}
|
||||
|
||||
if (associationParameters.contains("guarantors")) {
|
||||
@@ -360,6 +364,14 @@ public class LoansApiResource {
|
||||
mandatoryResponseParameters.add("futureSchedule");
|
||||
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
|
||||
}
|
||||
|
||||
if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled()
|
||||
&& loanBasicDetails.isInterestRecalculationEnabled()) {
|
||||
mandatoryResponseParameters.add("originalSchedule");
|
||||
LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(
|
||||
loanId, repaymentScheduleRelatedData, disbursementData);
|
||||
loanBasicDetails = LoanAccountData.withOriginalSchedule(loanBasicDetails, loanScheduleData);
|
||||
}
|
||||
}
|
||||
|
||||
if (associationParameters.contains("charges")) {
|
||||
|
||||
+52
-16
@@ -115,6 +115,7 @@ public class LoanAccountData {
|
||||
private final CalendarData meeting;
|
||||
private final Collection<NoteData> notes;
|
||||
private final Collection<DisbursementData> disbursementDetails;
|
||||
private final LoanScheduleData originalSchedule;
|
||||
// template
|
||||
private final Collection<LoanProductData> productOptions;
|
||||
private final Collection<StaffData> loanOfficerOptions;
|
||||
@@ -260,6 +261,7 @@ public class LoanAccountData {
|
||||
final EnumOptionData daysInYearType = null;
|
||||
final boolean isInterestRecalculationEnabled = false;
|
||||
final LoanInterestRecalculationData interestRecalculationData = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -275,7 +277,7 @@ public class LoanAccountData {
|
||||
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
|
||||
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
|
||||
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, interestRecalculationData);
|
||||
isInterestRecalculationEnabled, interestRecalculationData, originalSchedule);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,6 +371,7 @@ public class LoanAccountData {
|
||||
final EnumOptionData daysInYearType = null;
|
||||
final boolean isInterestRecalculationEnabled = false;
|
||||
final LoanInterestRecalculationData interestRecalculationData = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -383,7 +386,7 @@ public class LoanAccountData {
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount,
|
||||
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
|
||||
@@ -405,7 +408,8 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,6 +504,7 @@ public class LoanAccountData {
|
||||
final EnumOptionData daysInYearType = null;
|
||||
final boolean isInterestRecalculationEnabled = false;
|
||||
final LoanInterestRecalculationData interestRecalculationData = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -514,7 +519,7 @@ public class LoanAccountData {
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||
@@ -535,7 +540,8 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
|
||||
@@ -643,6 +649,7 @@ public class LoanAccountData {
|
||||
Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final Boolean inArrears = null;
|
||||
final Boolean isNPA = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
|
||||
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
@@ -660,7 +667,8 @@ public class LoanAccountData {
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi,
|
||||
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData());
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -694,7 +702,7 @@ public class LoanAccountData {
|
||||
for (final ChargeData charge : product.charges()) {
|
||||
charges.add(charge.toLoanChargeData());
|
||||
}
|
||||
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(),
|
||||
product.getFundName(), acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName,
|
||||
@@ -713,7 +721,8 @@ public class LoanAccountData {
|
||||
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
|
||||
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(),
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData());
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -768,6 +777,7 @@ public class LoanAccountData {
|
||||
final Map<Long, LoanBorrowerCycleData> memberVariations = null;
|
||||
final LoanProductData product = null;
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
@@ -782,7 +792,7 @@ public class LoanAccountData {
|
||||
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
|
||||
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData);
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -820,7 +830,7 @@ public class LoanAccountData {
|
||||
accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount,
|
||||
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
@@ -851,7 +861,8 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -908,7 +919,8 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData);
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
|
||||
@@ -931,7 +943,30 @@ public class LoanAccountData {
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData);
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData,
|
||||
acc.originalSchedule);
|
||||
}
|
||||
|
||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.principal,
|
||||
acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency, acc.termPeriodFrequencyType,
|
||||
acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
acc.repaymentFrequencyTypeOptions, acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions,
|
||||
acc.amortizationTypeOptions, acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions,
|
||||
acc.chargeOptions, null, acc.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions,
|
||||
acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
|
||||
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges,
|
||||
acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData,
|
||||
originalSchedule);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
@@ -987,7 +1022,7 @@ public class LoanAccountData {
|
||||
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
|
||||
final Collection<ChargeData> overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType,
|
||||
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final LoanInterestRecalculationData interestRecalculationData) {
|
||||
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.status = status;
|
||||
@@ -1131,6 +1166,7 @@ public class LoanAccountData {
|
||||
this.daysInYearType = daysInYearType;
|
||||
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
|
||||
this.interestRecalculationData = interestRecalculationData;
|
||||
this.originalSchedule = originalSchedule;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
@@ -1207,8 +1243,8 @@ public class LoanAccountData {
|
||||
public Long loanProductId() {
|
||||
return this.loanProductId;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalOutstandingAmount(){
|
||||
|
||||
public BigDecimal getTotalOutstandingAmount() {
|
||||
return this.summary.getTotalOutstanding();
|
||||
}
|
||||
|
||||
|
||||
+201
-167
@@ -61,8 +61,8 @@ import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
|
||||
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
@@ -97,7 +97,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
@@ -226,7 +225,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "rescheduledon_date")
|
||||
private Date rescheduledOnDate;
|
||||
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "rescheduledon_userid", nullable = true)
|
||||
private AppUser rescheduledByUser;
|
||||
@@ -468,7 +467,9 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge, final List<Long> existingTransactionIds,
|
||||
final List<Long> existingReversedTransactionIds) {
|
||||
final List<Long> existingReversedTransactionIds, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
@@ -528,7 +529,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper();
|
||||
wrapper.reprocess(getCurrency(), getDisbursementDate(), this.repaymentScheduleInstallments, charges());
|
||||
|
||||
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());
|
||||
}
|
||||
if (!loanCharge.isDueAtDisbursement()) {
|
||||
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
|
||||
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
|
||||
@@ -825,7 +834,9 @@ 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 Integer loanInstallmentNumber, final List<Holiday> holidays, final WorkingDays workingDays,
|
||||
final boolean isHolidayEnabled, final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency currency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation) {
|
||||
|
||||
validateLoanIsNotClosed(loanCharge);
|
||||
|
||||
@@ -854,6 +865,12 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
transactionDate, feeChargesWaived, penaltyChargesWaived);
|
||||
this.loanTransactions.add(waiveLoanChargeTransaction);
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& (loanCharge.getDueLocalDate() == null || LocalDate.now().isAfter(loanCharge.getDueLocalDate()))) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
}
|
||||
// Waive of charges whose due date falls after latest 'repayment'
|
||||
// transaction dont require entire loan schedule to be reprocessed.
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
@@ -1811,17 +1828,19 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.loanTermVariations.add(loanVariationTerms);
|
||||
isEmiAmountChanged = true;
|
||||
}
|
||||
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
} else if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule
|
||||
|| isEmiAmountChanged) {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
}
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||
updateLoanSummaryDerivedFields();
|
||||
handleDisbursementTransaction(actualDisbursementDate);
|
||||
@@ -2028,6 +2047,19 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
|
||||
final LoanScheduleModel loanSchedule = regenerateScheduleModel(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
updateLoanSchedule(loanSchedule);
|
||||
Set<LoanCharge> charges = this.charges();
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
recalculateLoanCharge(loanCharge);
|
||||
}
|
||||
}
|
||||
|
||||
public LoanScheduleModel regenerateScheduleModel(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
|
||||
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod);
|
||||
|
||||
@@ -2064,12 +2096,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
updateLoanSchedule(loanSchedule);
|
||||
Set<LoanCharge> charges = this.charges();
|
||||
for (LoanCharge loanCharge : charges) {
|
||||
recalculateLoanCharge(loanCharge);
|
||||
}
|
||||
return loanSchedule;
|
||||
}
|
||||
|
||||
private void handleDisbursementTransaction(final LocalDate disbursedOn) {
|
||||
@@ -4003,13 +4030,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName));
|
||||
existingTransactionIds.addAll(findExistingTransactionIds());
|
||||
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
} else {
|
||||
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
}
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
@@ -4310,173 +4338,179 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.isNpa;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @return List of loan repayments schedule objects
|
||||
**/
|
||||
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() {
|
||||
return this.repaymentScheduleInstallments;
|
||||
return this.repaymentScheduleInstallments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan product minimum repayments schedule related detail
|
||||
|
||||
/**
|
||||
* @return Loan product minimum repayments schedule related detail
|
||||
**/
|
||||
public LoanProductRelatedDetail getLoanRepaymentScheduleDetail() {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan Fixed Emi amount
|
||||
|
||||
/**
|
||||
* @return Loan Fixed Emi amount
|
||||
**/
|
||||
public BigDecimal getFixedEmiAmount() {
|
||||
return this.fixedEmiAmount;
|
||||
return this.fixedEmiAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return maximum outstanding loan balance
|
||||
|
||||
/**
|
||||
* @return maximum outstanding loan balance
|
||||
**/
|
||||
public BigDecimal getMaxOutstandingLoanBalance() {
|
||||
return this.maxOutstandingLoanBalance;
|
||||
return this.maxOutstandingLoanBalance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param dueDate the due date of the installment
|
||||
* @return a schedule installment with similar due date to the one provided
|
||||
* @param dueDate
|
||||
* the due date of the installment
|
||||
* @return a schedule installment with similar due date to the one provided
|
||||
**/
|
||||
public LoanRepaymentScheduleInstallment getRepaymentScheduleInstallment(LocalDate dueDate) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
|
||||
if(dueDate != null) {
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : this.repaymentScheduleInstallments) {
|
||||
|
||||
if(repaymentScheduleInstallment.getDueDate().isEqual(dueDate)) {
|
||||
installment = repaymentScheduleInstallment;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return installment;
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
|
||||
if (dueDate != null) {
|
||||
for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : this.repaymentScheduleInstallments) {
|
||||
|
||||
if (repaymentScheduleInstallment.getDueDate().isEqual(dueDate)) {
|
||||
installment = repaymentScheduleInstallment;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return installment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loan the Loan object
|
||||
* @return loan disbursement data
|
||||
**/
|
||||
public List<DisbursementData> getDisbursmentData() {
|
||||
Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator();
|
||||
List<DisbursementData> disbursementData = new ArrayList<DisbursementData>();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
LoanDisbursementDetails loanDisbursementDetails = iterator.next();
|
||||
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
|
||||
if(loanDisbursementDetails.expectedDisbursementDate() != null) {
|
||||
expectedDisbursementDate = new LocalDate(loanDisbursementDetails.expectedDisbursementDate());
|
||||
}
|
||||
|
||||
if(loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
actualDisbursementDate = new LocalDate(loanDisbursementDetails.actualDisbursementDate());
|
||||
}
|
||||
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate,
|
||||
actualDisbursementDate, loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loan the Loan object
|
||||
* @return application terms of the Loan object
|
||||
**/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency) {
|
||||
LoanProduct loanProduct = loanProduct();
|
||||
//LoanProductRelatedDetail loanProductRelatedDetail = getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency();
|
||||
|
||||
final Integer loanTermFrequency = getTermFrequency();
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
|
||||
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
|
||||
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail.getInterestCalculationPeriodMethod();
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||
|
||||
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
|
||||
final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate();
|
||||
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
||||
|
||||
// TODO get calender linked to loan if any exist. As of 17-07-2014, could not find a link in the database.
|
||||
// skip for now and set the Calender object to null
|
||||
Calendar calendar = null;
|
||||
|
||||
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment();
|
||||
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged();
|
||||
final LocalDate interestChargedFromDate = getInterestChargedFromDate();
|
||||
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnDueDate();
|
||||
|
||||
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
|
||||
final BigDecimal emiAmount = getFixedEmiAmount();
|
||||
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
|
||||
|
||||
final List<DisbursementData> disbursementData = getDisbursmentData();
|
||||
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>();
|
||||
|
||||
/**
|
||||
* @param loan
|
||||
* the Loan object
|
||||
* @return loan disbursement data
|
||||
**/
|
||||
public List<DisbursementData> getDisbursmentData() {
|
||||
Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator();
|
||||
List<DisbursementData> disbursementData = new ArrayList<DisbursementData>();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
LoanDisbursementDetails loanDisbursementDetails = iterator.next();
|
||||
|
||||
LocalDate expectedDisbursementDate = null;
|
||||
LocalDate actualDisbursementDate = null;
|
||||
|
||||
if (loanDisbursementDetails.expectedDisbursementDate() != null) {
|
||||
expectedDisbursementDate = new LocalDate(loanDisbursementDetails.expectedDisbursementDate());
|
||||
}
|
||||
|
||||
if (loanDisbursementDetails.actualDisbursementDate() != null) {
|
||||
actualDisbursementDate = new LocalDate(loanDisbursementDetails.actualDisbursementDate());
|
||||
}
|
||||
|
||||
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
|
||||
loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
|
||||
}
|
||||
|
||||
return disbursementData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loan
|
||||
* the Loan object
|
||||
* @return application terms of the Loan object
|
||||
**/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency) {
|
||||
LoanProduct loanProduct = loanProduct();
|
||||
// LoanProductRelatedDetail loanProductRelatedDetail =
|
||||
// getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency();
|
||||
|
||||
final Integer loanTermFrequency = getTermFrequency();
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
|
||||
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
|
||||
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail
|
||||
.getInterestCalculationPeriodMethod();
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||
|
||||
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
|
||||
final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate();
|
||||
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
|
||||
|
||||
// TODO get calender linked to loan if any exist. As of 17-07-2014,
|
||||
// could not find a link in the database.
|
||||
// skip for now and set the Calender object to null
|
||||
Calendar calendar = null;
|
||||
|
||||
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment();
|
||||
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment();
|
||||
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged();
|
||||
final LocalDate interestChargedFromDate = getInterestChargedFromDate();
|
||||
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnDueDate();
|
||||
|
||||
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
|
||||
final BigDecimal emiAmount = getFixedEmiAmount();
|
||||
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
|
||||
|
||||
final List<DisbursementData> disbursementData = getDisbursmentData();
|
||||
|
||||
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>();
|
||||
LoanTermVariationsData data = new LoanTermVariationsData(null,
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
|
||||
loanVariationTermsData.add(data);
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney,
|
||||
this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance,
|
||||
loanVariationTermsData, interestChargedFromDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan summary embedded object
|
||||
**/
|
||||
public LoanSummary getLoanSummary() {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
||||
this.rescheduledByUser = rescheduledByUser;
|
||||
}
|
||||
|
||||
public LoanProductRelatedDetail getLoanProductRelatedDetail() {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.loanRepaymentScheduleDetail.updateNumberOfRepayments(numberOfRepayments);
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney,
|
||||
this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance,
|
||||
loanVariationTermsData, interestChargedFromDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Loan summary embedded object
|
||||
**/
|
||||
public LoanSummary getLoanSummary() {
|
||||
return this.summary;
|
||||
}
|
||||
|
||||
public void updateRescheduledByUser(AppUser rescheduledByUser) {
|
||||
this.rescheduledByUser = rescheduledByUser;
|
||||
}
|
||||
|
||||
public LoanProductRelatedDetail getLoanProductRelatedDetail() {
|
||||
return this.loanRepaymentScheduleDetail;
|
||||
}
|
||||
|
||||
public void updateNumberOfRepayments(Integer numberOfRepayments) {
|
||||
this.loanRepaymentScheduleDetail.updateNumberOfRepayments(numberOfRepayments);
|
||||
}
|
||||
|
||||
public void updateRescheduledOnDate(LocalDate rescheduledOnDate) {
|
||||
|
||||
if (rescheduledOnDate != null) {
|
||||
this.rescheduledOnDate = rescheduledOnDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTermFrequency(Integer termFrequency) {
|
||||
|
||||
if (termFrequency != null) {
|
||||
this.termFrequency = termFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateRescheduledOnDate(LocalDate rescheduledOnDate) {
|
||||
|
||||
if(rescheduledOnDate != null) {
|
||||
this.rescheduledOnDate = rescheduledOnDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTermFrequency(Integer termFrequency) {
|
||||
|
||||
if(termFrequency != null) {
|
||||
this.termFrequency = termFrequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -75,6 +75,27 @@ public class LoanScheduleData {
|
||||
this.totalOutstanding = totalOutstanding;
|
||||
}
|
||||
|
||||
public LoanScheduleData(final CurrencyData currency, final Collection<LoanSchedulePeriodData> periods, final Integer loanTermInDays,
|
||||
final BigDecimal totalPrincipalDisbursed, final BigDecimal totalPrincipalExpected, final BigDecimal totalInterestCharged,
|
||||
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected) {
|
||||
this.currency = currency;
|
||||
this.periods = periods;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
this.totalPrincipalDisbursed = totalPrincipalDisbursed;
|
||||
this.totalPrincipalExpected = totalPrincipalExpected;
|
||||
this.totalPrincipalPaid = null;
|
||||
this.totalInterestCharged = totalInterestCharged;
|
||||
this.totalFeeChargesCharged = totalFeeChargesCharged;
|
||||
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
|
||||
this.totalWaived = null;
|
||||
this.totalWrittenOff = null;
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
this.totalRepayment = null;
|
||||
this.totalPaidInAdvance = null;
|
||||
this.totalPaidLate = null;
|
||||
this.totalOutstanding = null;
|
||||
}
|
||||
|
||||
public Collection<LoanSchedulePeriodData> getPeriods() {
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
+288
-351
@@ -9,9 +9,8 @@ import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -30,18 +29,16 @@ import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementDisbursementDateException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -522,354 +519,294 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected,
|
||||
totalOutstanding);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled,
|
||||
final List<Holiday> holidays, final WorkingDays workingDays) {
|
||||
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
|
||||
// create an archive of the current loan schedule installments
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = createLoanScheduleArchive(loanRescheduleRequest);
|
||||
|
||||
// get the initial list of repayment installments
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
// sort list by installment number in ASC order
|
||||
Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<LoanRescheduleModelRepaymentPeriod>();
|
||||
|
||||
Money outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
LocalDate fromDate = repaymentScheduleInstallment.getFromDate();
|
||||
LocalDate dueDate = repaymentScheduleInstallment.getDueDate();
|
||||
Money principalDue = repaymentScheduleInstallment.getPrincipal(currency);
|
||||
Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency);
|
||||
Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency);
|
||||
Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency);
|
||||
Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(principalDue);
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(oldPeriodNumber, oldPeriodNumber,
|
||||
fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue,
|
||||
totalDue, false);
|
||||
|
||||
periods.add(period);
|
||||
}
|
||||
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||
final WorkingDays workingDays) {
|
||||
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
|
||||
// create an archive of the current loan schedule installments
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = null;
|
||||
|
||||
// get the initial list of repayment installments
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
// sort list by installment number in ASC order
|
||||
Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<>();
|
||||
|
||||
Money outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
LocalDate fromDate = repaymentScheduleInstallment.getFromDate();
|
||||
LocalDate dueDate = repaymentScheduleInstallment.getDueDate();
|
||||
Money principalDue = repaymentScheduleInstallment.getPrincipal(currency);
|
||||
Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency);
|
||||
Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency);
|
||||
Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency);
|
||||
Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(principalDue);
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod
|
||||
.instance(oldPeriodNumber, oldPeriodNumber, fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue,
|
||||
feeChargesDue, penaltyChargesDue, totalDue, false);
|
||||
|
||||
periods.add(period);
|
||||
}
|
||||
|
||||
Money outstandingBalance = loan.getPrincpal();
|
||||
Money totalCumulativePrincipal = Money.zero(currency);
|
||||
Money totalCumulativeInterest = Money.zero(currency);
|
||||
Money actualTotalCumulativeInterest = Money.zero(currency);
|
||||
Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency);
|
||||
|
||||
LocalDate installmentDueDate = null;
|
||||
LocalDate adjustedInstallmentDueDate = null;
|
||||
LocalDate installmentFromDate = null;
|
||||
Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment());
|
||||
Integer installmentNumber = rescheduleFromInstallmentNo;
|
||||
Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms());
|
||||
final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest();
|
||||
Integer numberOfRepayments = repaymentScheduleInstallments.size();
|
||||
Integer rescheduleNumberOfRepayments = numberOfRepayments;
|
||||
final Money principal = loan.getPrincpal();
|
||||
final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding());
|
||||
LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate();
|
||||
BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate();
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
|
||||
if(rescheduleFromInstallmentNo > 0) {
|
||||
// this will hold the loan repayment installment that is before the reschedule start installment
|
||||
// (rescheduleFrominstallment)
|
||||
LoanRepaymentScheduleInstallment previousInstallment = null;
|
||||
|
||||
// get the install number of the previous installment
|
||||
int previousInstallmentNo = rescheduleFromInstallmentNo - 1;
|
||||
|
||||
// only fetch the installment if the number is greater than 0
|
||||
if(previousInstallmentNo > 0) {
|
||||
previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo);
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1);
|
||||
|
||||
// the "installment from date" is equal to the due date of the previous installment, if it exists
|
||||
if(previousInstallment != null) {
|
||||
installmentFromDate = previousInstallment.getDueDate();
|
||||
}
|
||||
|
||||
else {
|
||||
installmentFromDate = firstInstallment.getFromDate();
|
||||
}
|
||||
|
||||
installmentDueDate = installmentFromDate;
|
||||
LocalDate periodStartDateApplicableForInterest = installmentFromDate;
|
||||
Integer periodNumber = 1;
|
||||
outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) {
|
||||
|
||||
totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
rescheduleNumberOfRepayments--;
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
|
||||
while(graceOnPrincipal > 0 || graceOnInterest > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
if(graceOnPrincipal > 0) {
|
||||
graceOnPrincipal--;
|
||||
}
|
||||
|
||||
if(graceOnInterest > 0) {
|
||||
graceOnInterest--;
|
||||
}
|
||||
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
while(extraTerms > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
extraTerms--;
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
// get the loan application terms from the Loan object
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency);
|
||||
|
||||
// update the number of repayments
|
||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||
|
||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||
|
||||
if(newInterestRate != null) {
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate);
|
||||
loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate);
|
||||
}
|
||||
|
||||
graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
|
||||
loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest);
|
||||
loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal);
|
||||
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
Money totalCumulativePrincipal = Money.zero(currency);
|
||||
Money totalCumulativeInterest = Money.zero(currency);
|
||||
Money actualTotalCumulativeInterest = Money.zero(currency);
|
||||
Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
|
||||
Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency);
|
||||
|
||||
LocalDate installmentDueDate = null;
|
||||
LocalDate adjustedInstallmentDueDate = null;
|
||||
LocalDate installmentFromDate = null;
|
||||
Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment());
|
||||
Integer installmentNumber = rescheduleFromInstallmentNo;
|
||||
Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms());
|
||||
final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest();
|
||||
Integer numberOfRepayments = repaymentScheduleInstallments.size();
|
||||
Integer rescheduleNumberOfRepayments = numberOfRepayments;
|
||||
final Money principal = loan.getPrincpal();
|
||||
final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding());
|
||||
LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate();
|
||||
BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate();
|
||||
int loanTermInDays = Integer.valueOf(0);
|
||||
|
||||
if (rescheduleFromInstallmentNo > 0) {
|
||||
// this will hold the loan repayment installment that is before the
|
||||
// reschedule start installment
|
||||
// (rescheduleFrominstallment)
|
||||
LoanRepaymentScheduleInstallment previousInstallment = null;
|
||||
|
||||
// get the install number of the previous installment
|
||||
int previousInstallmentNo = rescheduleFromInstallmentNo - 1;
|
||||
|
||||
// only fetch the installment if the number is greater than 0
|
||||
if (previousInstallmentNo > 0) {
|
||||
previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo);
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1);
|
||||
|
||||
// the "installment from date" is equal to the due date of the
|
||||
// previous installment, if it exists
|
||||
if (previousInstallment != null) {
|
||||
installmentFromDate = previousInstallment.getDueDate();
|
||||
}
|
||||
|
||||
else {
|
||||
installmentFromDate = firstInstallment.getFromDate();
|
||||
}
|
||||
|
||||
installmentDueDate = installmentFromDate;
|
||||
LocalDate periodStartDateApplicableForInterest = installmentFromDate;
|
||||
Integer periodNumber = 1;
|
||||
outstandingLoanBalance = loan.getPrincpal();
|
||||
|
||||
for (LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if (period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) {
|
||||
|
||||
totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
rescheduleNumberOfRepayments--;
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
|
||||
while (graceOnPrincipal > 0 || graceOnInterest > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
if (graceOnPrincipal > 0) {
|
||||
graceOnPrincipal--;
|
||||
}
|
||||
|
||||
if (graceOnInterest > 0) {
|
||||
graceOnInterest--;
|
||||
}
|
||||
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
while (extraTerms > 0) {
|
||||
|
||||
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
|
||||
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
|
||||
Money.zero(currency), Money.zero(currency), true);
|
||||
|
||||
periods.add(period);
|
||||
|
||||
extraTerms--;
|
||||
rescheduleNumberOfRepayments++;
|
||||
numberOfRepayments++;
|
||||
}
|
||||
|
||||
// get the loan application terms from the Loan object
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency);
|
||||
|
||||
// update the number of repayments
|
||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||
|
||||
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
||||
|
||||
if (newInterestRate != null) {
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate);
|
||||
loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate);
|
||||
}
|
||||
|
||||
graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
|
||||
graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
|
||||
|
||||
loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest);
|
||||
loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal);
|
||||
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
|
||||
loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments);
|
||||
loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest);
|
||||
|
||||
|
||||
Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
|
||||
this.paymentPeriodsInOneYearCalculator, mathContext);
|
||||
|
||||
if(!recalculateInterest && newInterestRate == null) {
|
||||
totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged());
|
||||
totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest);
|
||||
|
||||
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
|
||||
this.paymentPeriodsInOneYearCalculator, mathContext);
|
||||
|
||||
if (!recalculateInterest && newInterestRate == null) {
|
||||
totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged());
|
||||
totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest);
|
||||
|
||||
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
|
||||
}
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate()) ||
|
||||
period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) ||
|
||||
period.isNew()) {
|
||||
|
||||
installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
false);
|
||||
|
||||
if(adjustedDueDate != null && periodNumber == 1) {
|
||||
installmentDueDate = adjustedDueDate;
|
||||
}
|
||||
|
||||
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
|
||||
|
||||
period.updatePeriodNumber(installmentNumber);
|
||||
period.updatePeriodFromDate(installmentFromDate);
|
||||
period.updatePeriodDueDate(adjustedInstallmentDueDate);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, adjustedInstallmentDueDate,
|
||||
periodStartDateApplicableForInterest, loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
||||
loanApplicationTerms.getRepaymentEvery());
|
||||
|
||||
// ========================= Calculate the interest due ========================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total Principal Paid
|
||||
// interest calculation is always based on the total principal outstanding
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
|
||||
// determine the interest & principal for the period
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
||||
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
|
||||
|
||||
// update the interest due for the period
|
||||
period.updateInterestDue(principalInterestForThisPeriod.interest());
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
// ========================== Calculate the principal due ======================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total cumulative Principal Amount before the reschedule installment
|
||||
loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod));
|
||||
|
||||
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
||||
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
|
||||
|
||||
period.updatePrincipalDue(principalInterestForThisPeriod.principal());
|
||||
|
||||
// ==============================================================================================
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
period.updateOutstandingLoanBalance(outstandingLoanBalance);
|
||||
|
||||
Money principalDue = Money.of(currency, period.principalDue());
|
||||
Money interestDue = Money.of(currency, period.interestDue());
|
||||
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
|
||||
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
|
||||
|
||||
Money totalDue = principalDue
|
||||
.plus(interestDue)
|
||||
.plus(feeChargesDue)
|
||||
.plus(penaltyChargesDue);
|
||||
|
||||
period.updateTotalDue(totalDue);
|
||||
|
||||
// update cumulative fields for principal & interest
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue());
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
|
||||
installmentFromDate = adjustedInstallmentDueDate;
|
||||
installmentNumber++;
|
||||
periodNumber++;
|
||||
loanTermInDays += daysInInstallment;
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Money totalRepaymentExpected = principal // get the loan Principal amount
|
||||
.plus(actualTotalCumulativeInterest) // add the actual total cumulative interest
|
||||
.plus(loanSummary.getTotalFeeChargesCharged()) // add the total fees charged
|
||||
.plus(loanSummary.getTotalPenaltyChargesCharged()); // finally add the total penalty charged
|
||||
|
||||
return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays,
|
||||
loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(), actualTotalCumulativeInterest.getAmount(),
|
||||
loanSummary.getTotalFeeChargesCharged(), loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(),
|
||||
loanSummary.getTotalOutstanding());
|
||||
}
|
||||
|
||||
private Collection<LoanRepaymentScheduleHistory> createLoanScheduleArchive(final LoanRescheduleRequest loanRescheduleRequest) {
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
List<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = new ArrayList<LoanRepaymentScheduleHistory>();
|
||||
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
final Integer installmentNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
Date fromDate = null;
|
||||
Date dueDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getFromDate() != null) {
|
||||
fromDate = repaymentScheduleInstallment.getFromDate().toDate();
|
||||
}
|
||||
|
||||
if(repaymentScheduleInstallment.getDueDate() != null) {
|
||||
dueDate = repaymentScheduleInstallment.getDueDate().toDate();
|
||||
}
|
||||
|
||||
final BigDecimal principal = repaymentScheduleInstallment.getPrincipal(currency).getAmount();
|
||||
final BigDecimal principalCompleted = repaymentScheduleInstallment.getPrincipalCompleted(currency).getAmount();
|
||||
final BigDecimal principalWrittenOff = repaymentScheduleInstallment.getPrincipalWrittenOff(currency).getAmount();
|
||||
final BigDecimal interestCharged = repaymentScheduleInstallment.getInterestCharged(currency).getAmount();
|
||||
final BigDecimal interestPaid = repaymentScheduleInstallment.getInterestPaid(currency).getAmount();
|
||||
final BigDecimal interestWaived = repaymentScheduleInstallment.getInterestWaived(currency).getAmount();
|
||||
final BigDecimal interestWrittenOff = repaymentScheduleInstallment.getInterestWrittenOff(currency).getAmount();
|
||||
final BigDecimal feeChargesCharged = repaymentScheduleInstallment.getFeeChargesCharged(currency).getAmount();
|
||||
final BigDecimal feeChargesPaid = repaymentScheduleInstallment.getFeeChargesPaid(currency).getAmount();
|
||||
final BigDecimal feeChargesWrittenOff = repaymentScheduleInstallment.getFeeChargesWrittenOff(currency).getAmount();
|
||||
final BigDecimal feeChargesWaived = repaymentScheduleInstallment.getFeeChargesWaived(currency).getAmount();
|
||||
final BigDecimal penaltyCharges = repaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount();
|
||||
final BigDecimal penaltyChargesPaid = repaymentScheduleInstallment.getPenaltyChargesPaid(currency).getAmount();
|
||||
final BigDecimal penaltyChargesWrittenOff = repaymentScheduleInstallment.getPenaltyChargesWrittenOff(currency).getAmount();
|
||||
final BigDecimal penaltyChargesWaived = repaymentScheduleInstallment.getPenaltyChargesWaived(currency).getAmount();
|
||||
final BigDecimal totalPaidInAdvance = repaymentScheduleInstallment.getTotalPaidInAdvance();
|
||||
final BigDecimal totalPaidLate = repaymentScheduleInstallment.getTotalPaidLate();
|
||||
final boolean obligationsMet = repaymentScheduleInstallment.isObligationsMet();
|
||||
Date obligationsMetOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getObligationsMetOnDate() != null) {
|
||||
obligationsMetOnDate = repaymentScheduleInstallment.getObligationsMetOnDate().toDate();
|
||||
}
|
||||
|
||||
Date createdOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getCreatedDate() != null) {
|
||||
createdOnDate = repaymentScheduleInstallment.getCreatedDate().toDate();
|
||||
}
|
||||
|
||||
final AppUser createdByUser = repaymentScheduleInstallment.getCreatedBy();
|
||||
final AppUser lastModifiedByUser = repaymentScheduleInstallment.getLastModifiedBy();
|
||||
|
||||
Date lastModifiedOnDate = null;
|
||||
|
||||
if(repaymentScheduleInstallment.getLastModifiedDate() != null) {
|
||||
lastModifiedOnDate = repaymentScheduleInstallment.getLastModifiedDate().toDate();
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleHistory loanRepaymentScheduleHistory = LoanRepaymentScheduleHistory.instance(loan,
|
||||
loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal, principalCompleted,
|
||||
principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges,
|
||||
penaltyChargesPaid, penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate,
|
||||
obligationsMet, obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
|
||||
loanRepaymentScheduleHistoryList.add(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
return loanRepaymentScheduleHistoryList;
|
||||
|
||||
for (LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if (period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate())
|
||||
|| period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || period.isNew()) {
|
||||
|
||||
installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
false);
|
||||
|
||||
if (adjustedDueDate != null && periodNumber == 1) {
|
||||
installmentDueDate = adjustedDueDate;
|
||||
}
|
||||
|
||||
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
|
||||
isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
|
||||
|
||||
period.updatePeriodNumber(installmentNumber);
|
||||
period.updatePeriodFromDate(installmentFromDate);
|
||||
period.updatePeriodDueDate(adjustedInstallmentDueDate);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
|
||||
adjustedInstallmentDueDate, periodStartDateApplicableForInterest,
|
||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
||||
|
||||
// ========================= Calculate the interest due
|
||||
// ========================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total
|
||||
// Principal Paid
|
||||
// interest calculation is always based on the total
|
||||
// principal outstanding
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
|
||||
// determine the interest & principal for the period
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
||||
totalOutstandingInterestPaymentDueToGrace, daysInInstallment, outstandingBalance, loanApplicationTerms,
|
||||
periodNumber, mathContext);
|
||||
|
||||
// update the interest due for the period
|
||||
period.updateInterestDue(principalInterestForThisPeriod.interest());
|
||||
|
||||
// =============================================================================================
|
||||
|
||||
// ========================== Calculate the principal due
|
||||
// ======================================
|
||||
|
||||
// change the principal to => Principal Disbursed - Total
|
||||
// cumulative Principal Amount before the reschedule
|
||||
// installment
|
||||
loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod));
|
||||
|
||||
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(this.paymentPeriodsInOneYearCalculator,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest,
|
||||
totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, daysInInstallment,
|
||||
outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
|
||||
|
||||
period.updatePrincipalDue(principalInterestForThisPeriod.principal());
|
||||
|
||||
// ==============================================================================================
|
||||
|
||||
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
|
||||
period.updateOutstandingLoanBalance(outstandingLoanBalance);
|
||||
|
||||
Money principalDue = Money.of(currency, period.principalDue());
|
||||
Money interestDue = Money.of(currency, period.interestDue());
|
||||
Money feeChargesDue = Money.of(currency, period.feeChargesDue());
|
||||
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
|
||||
|
||||
Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
|
||||
|
||||
period.updateTotalDue(totalDue);
|
||||
|
||||
// update cumulative fields for principal & interest
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue());
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue());
|
||||
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
|
||||
installmentFromDate = adjustedInstallmentDueDate;
|
||||
installmentNumber++;
|
||||
periodNumber++;
|
||||
loanTermInDays += daysInInstallment;
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(period.principalDue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Money totalRepaymentExpected = principal // get the loan Principal
|
||||
// amount
|
||||
.plus(actualTotalCumulativeInterest) // add the actual total
|
||||
// cumulative interest
|
||||
.plus(loanSummary.getTotalFeeChargesCharged()) // add the total
|
||||
// fees charged
|
||||
.plus(loanSummary.getTotalPenaltyChargesCharged()); // finally
|
||||
// add the
|
||||
// total
|
||||
// penalty
|
||||
// charged
|
||||
|
||||
return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays,
|
||||
loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(),
|
||||
actualTotalCumulativeInterest.getAmount(), loanSummary.getTotalFeeChargesCharged(),
|
||||
loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(), loanSummary.getTotalOutstanding());
|
||||
}
|
||||
|
||||
private BigDecimal calculateInterestForSpecificDays(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||
@@ -1669,14 +1606,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return precloseTransaction;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* set the value to zero if the provided value is null
|
||||
|
||||
/**
|
||||
* set the value to zero if the provided value is null
|
||||
*
|
||||
* @return integer value equal/greater than 0
|
||||
**/
|
||||
private Integer defaultToZeroIfNull(Integer value) {
|
||||
|
||||
return (value != null) ? value : 0;
|
||||
|
||||
return (value != null) ? value : 0;
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_repayment_schedule_history")
|
||||
public class LoanRepaymentScheduleHistory extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_id")
|
||||
private Loan loan;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@OneToOne(optional = true)
|
||||
@JoinColumn(name = "loan_reschedule_request_id")
|
||||
private LoanRescheduleRequest loanRescheduleRequest;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "installment", nullable = false)
|
||||
private Integer installmentNumber;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "fromdate", nullable = true)
|
||||
private Date fromDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "duedate", nullable = false)
|
||||
private Date dueDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principal;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestCharged;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesCharged;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyCharges;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "created_date")
|
||||
private Date createdOnDate;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "createdby_id")
|
||||
private AppUser createdByUser;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "lastmodifiedby_id")
|
||||
private AppUser lastModifiedByUser;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "lastmodified_date")
|
||||
private Date lastModifiedOnDate;
|
||||
|
||||
@Column(name = "version")
|
||||
@SuppressWarnings("unused")
|
||||
private Integer version;
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
protected LoanRepaymentScheduleHistory() {}
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
private LoanRepaymentScheduleHistory(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal interestCharged, final BigDecimal feeChargesCharged, final BigDecimal penaltyCharges,
|
||||
final Date createdOnDate, final AppUser createdByUser, final AppUser lastModifiedByUser, final Date lastModifiedOnDate,
|
||||
final Integer version) {
|
||||
|
||||
this.loan = loan;
|
||||
this.loanRescheduleRequest = loanRescheduleRequest;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.principal = principal;
|
||||
this.interestCharged = interestCharged;
|
||||
this.feeChargesCharged = feeChargesCharged;
|
||||
this.penaltyCharges = penaltyCharges;
|
||||
this.createdOnDate = createdOnDate;
|
||||
this.createdByUser = createdByUser;
|
||||
this.lastModifiedByUser = lastModifiedByUser;
|
||||
this.lastModifiedOnDate = lastModifiedOnDate;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the LoanRepaymentScheduleHistory class
|
||||
**/
|
||||
public static LoanRepaymentScheduleHistory instance(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal interestCharged, final BigDecimal feeChargesCharged, final BigDecimal penaltyCharges,
|
||||
final Date createdOnDate, final AppUser createdByUser, final AppUser lastModifiedByUser, final Date lastModifiedOnDate,
|
||||
final Integer version) {
|
||||
|
||||
return new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
|
||||
interestCharged, feeChargesCharged, penaltyCharges, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate,
|
||||
version);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
|
||||
public interface LoanScheduleHistoryReadPlatformService {
|
||||
|
||||
Integer fetchCurrentVersionNumber(Long loanId);
|
||||
|
||||
LoanScheduleData retrieveRepaymentArchiveSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||
Collection<DisbursementData> disbursementData);
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.organisation.monetary.data.CurrencyData;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanScheduleHistoryReadPlatformServiceImpl implements LoanScheduleHistoryReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final PlatformSecurityContext context;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleHistoryReadPlatformServiceImpl(final RoutingDataSource dataSource, final PlatformSecurityContext context) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer fetchCurrentVersionNumber(Long loanId) {
|
||||
final String sql = "select MAX(lrs.version) from m_loan_repayment_schedule_history lrs where lrs.loan_id = ?";
|
||||
return this.jdbcTemplate.queryForInt(sql, loanId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleData retrieveRepaymentArchiveSchedule(final Long loanId,
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
Integer versionNumber = fetchCurrentVersionNumber(loanId);
|
||||
if (versionNumber == 0) { return null; }
|
||||
final LoanScheduleArchiveResultSetExtractor fullResultsetExtractor = new LoanScheduleArchiveResultSetExtractor(
|
||||
repaymentScheduleRelatedLoanData, disbursementData);
|
||||
final String sql = "select " + fullResultsetExtractor.schema()
|
||||
+ " where ls.loan_id = ? and ls.version = ? order by ls.loan_id, ls.installment";
|
||||
|
||||
return this.jdbcTemplate.query(sql, fullResultsetExtractor, new Object[] { loanId, versionNumber });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new LoanNotFoundException(loanId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LoanScheduleArchiveResultSetExtractor implements ResultSetExtractor<LoanScheduleData> {
|
||||
|
||||
private final CurrencyData currency;
|
||||
private final DisbursementData disbursement;
|
||||
private final BigDecimal totalFeeChargesDueAtDisbursement;
|
||||
private final Collection<DisbursementData> disbursementData;
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
|
||||
public LoanScheduleArchiveResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||
Collection<DisbursementData> disbursementData) {
|
||||
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
||||
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||
this.lastDueDate = this.disbursement.disbursementDate();
|
||||
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
|
||||
this.disbursementData = disbursementData;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
StringBuilder stringBuilder = new StringBuilder(200);
|
||||
stringBuilder.append(" ls.installment as period, ls.fromdate as fromDate, ls.duedate as dueDate, ");
|
||||
stringBuilder
|
||||
.append("ls.principal_amount as principalDue, ls.interest_amount as interestDue, ls.fee_charges_amount as feeChargesDue, ls.penalty_charges_amount as penaltyChargesDue ");
|
||||
stringBuilder.append(" from m_loan_repayment_schedule_history ls ");
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanScheduleData extractData(final ResultSet rs) throws SQLException, DataAccessException {
|
||||
|
||||
final LoanSchedulePeriodData disbursementPeriod = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
this.disbursement.disbursementDate(), this.disbursement.amount(), this.totalFeeChargesDueAtDisbursement,
|
||||
this.disbursement.isDisbursed());
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periods = new ArrayList<>();
|
||||
final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.code(), this.currency.decimalPlaces(),
|
||||
this.currency.currencyInMultiplesOf());
|
||||
BigDecimal totalPrincipalDisbursed = BigDecimal.ZERO;
|
||||
if (disbursementData == null || disbursementData.isEmpty()) {
|
||||
periods.add(disbursementPeriod);
|
||||
totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount();
|
||||
} else {
|
||||
this.outstandingLoanPrincipalBalance = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
Money totalPrincipalExpected = Money.zero(monCurrency);
|
||||
Money totalInterestCharged = Money.zero(monCurrency);
|
||||
Money totalFeeChargesCharged = Money.zero(monCurrency);
|
||||
Money totalPenaltyChargesCharged = Money.zero(monCurrency);
|
||||
Money totalRepaymentExpected = Money.zero(monCurrency);
|
||||
|
||||
// update totals with details of fees charged during disbursement
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.plus(disbursementPeriod.feeChargesDue());
|
||||
totalRepaymentExpected = totalRepaymentExpected.plus(disbursementPeriod.feeChargesDue());
|
||||
|
||||
Integer loanTermInDays = Integer.valueOf(0);
|
||||
while (rs.next()) {
|
||||
final Integer period = JdbcSupport.getInteger(rs, "period");
|
||||
LocalDate fromDate = JdbcSupport.getLocalDate(rs, "fromDate");
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||
if (disbursementData != null) {
|
||||
BigDecimal principal = BigDecimal.ZERO;
|
||||
for (DisbursementData data : disbursementData) {
|
||||
if (fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate)) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), this.totalFeeChargesDueAtDisbursement, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
} else if (data.isDueForDisbursement(fromDate, dueDate)
|
||||
&& this.outstandingLoanPrincipalBalance.compareTo(BigDecimal.ZERO) == 1) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
}
|
||||
}
|
||||
totalPrincipalDisbursed = totalPrincipalDisbursed.add(principal);
|
||||
}
|
||||
|
||||
Integer daysInPeriod = Integer.valueOf(0);
|
||||
if (fromDate != null) {
|
||||
daysInPeriod = Days.daysBetween(fromDate, dueDate).getDays();
|
||||
loanTermInDays = Integer.valueOf(loanTermInDays.intValue() + daysInPeriod.intValue());
|
||||
}
|
||||
|
||||
final BigDecimal principalDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principalDue");
|
||||
totalPrincipalExpected = totalPrincipalExpected.plus(principalDue);
|
||||
|
||||
final BigDecimal interestExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interestDue");
|
||||
totalInterestCharged = totalInterestCharged.plus(interestExpectedDue);
|
||||
|
||||
final BigDecimal feeChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "feeChargesDue");
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.plus(feeChargesExpectedDue);
|
||||
|
||||
final BigDecimal penaltyChargesExpectedDue = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penaltyChargesDue");
|
||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.plus(penaltyChargesExpectedDue);
|
||||
|
||||
final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(
|
||||
penaltyChargesExpectedDue);
|
||||
|
||||
final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod);
|
||||
|
||||
totalRepaymentExpected = totalRepaymentExpected.plus(totalDueForPeriod);
|
||||
|
||||
if (fromDate == null) {
|
||||
fromDate = this.lastDueDate;
|
||||
}
|
||||
final BigDecimal outstandingPrincipalBalanceOfLoan = this.outstandingLoanPrincipalBalance.subtract(principalDue);
|
||||
|
||||
// update based on current period values
|
||||
this.lastDueDate = dueDate;
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.subtract(principalDue);
|
||||
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.repaymentOnlyPeriod(period, fromDate, dueDate,
|
||||
principalDue, outstandingPrincipalBalanceOfLoan, interestExpectedDue, feeChargesExpectedDue,
|
||||
penaltyChargesExpectedDue, totalDueForPeriod);
|
||||
|
||||
periods.add(periodData);
|
||||
}
|
||||
|
||||
return new LoanScheduleData(this.currency, periods, loanTermInDays, totalPrincipalDisbursed,
|
||||
totalPrincipalExpected.getAmount(), totalInterestCharged.getAmount(), totalFeeChargesCharged.getAmount(),
|
||||
totalPenaltyChargesCharged.getAmount(), totalRepaymentExpected.getAmount());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
|
||||
public interface LoanScheduleHistoryWritePlatformService {
|
||||
|
||||
List<LoanRepaymentScheduleHistory> createLoanScheduleArchive(
|
||||
final List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, final Loan loan,
|
||||
final LoanRescheduleRequest loanRescheduleRequest);
|
||||
|
||||
void createAndSaveLoanScheduleArchive(List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Loan loan,
|
||||
LoanRescheduleRequest loanRescheduleRequest);
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanScheduleHistoryWritePlatformServiceImpl implements LoanScheduleHistoryWritePlatformService {
|
||||
|
||||
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
|
||||
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleHistoryWritePlatformServiceImpl(final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
|
||||
final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository) {
|
||||
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
|
||||
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoanRepaymentScheduleHistory> createLoanScheduleArchive(
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Loan loan, LoanRescheduleRequest loanRescheduleRequest) {
|
||||
Integer version = this.loanScheduleHistoryReadPlatformService.fetchCurrentVersionNumber(loan.getId()) + 1;
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
final List<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = new ArrayList<>();
|
||||
|
||||
for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
final Integer installmentNumber = repaymentScheduleInstallment.getInstallmentNumber();
|
||||
Date fromDate = null;
|
||||
Date dueDate = null;
|
||||
|
||||
if (repaymentScheduleInstallment.getFromDate() != null) {
|
||||
fromDate = repaymentScheduleInstallment.getFromDate().toDate();
|
||||
}
|
||||
|
||||
if (repaymentScheduleInstallment.getDueDate() != null) {
|
||||
dueDate = repaymentScheduleInstallment.getDueDate().toDate();
|
||||
}
|
||||
|
||||
final BigDecimal principal = repaymentScheduleInstallment.getPrincipal(currency).getAmount();
|
||||
final BigDecimal interestCharged = repaymentScheduleInstallment.getInterestCharged(currency).getAmount();
|
||||
final BigDecimal feeChargesCharged = repaymentScheduleInstallment.getFeeChargesCharged(currency).getAmount();
|
||||
final BigDecimal penaltyCharges = repaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount();
|
||||
|
||||
Date createdOnDate = null;
|
||||
if (repaymentScheduleInstallment.getCreatedDate() != null) {
|
||||
createdOnDate = repaymentScheduleInstallment.getCreatedDate().toDate();
|
||||
}
|
||||
|
||||
final AppUser createdByUser = repaymentScheduleInstallment.getCreatedBy();
|
||||
final AppUser lastModifiedByUser = repaymentScheduleInstallment.getLastModifiedBy();
|
||||
|
||||
Date lastModifiedOnDate = null;
|
||||
|
||||
if (repaymentScheduleInstallment.getLastModifiedDate() != null) {
|
||||
lastModifiedOnDate = repaymentScheduleInstallment.getLastModifiedDate().toDate();
|
||||
}
|
||||
|
||||
LoanRepaymentScheduleHistory loanRepaymentScheduleHistory = LoanRepaymentScheduleHistory.instance(loan, loanRescheduleRequest,
|
||||
installmentNumber, fromDate, dueDate, principal, interestCharged, feeChargesCharged, penaltyCharges, createdOnDate,
|
||||
createdByUser, lastModifiedByUser, lastModifiedOnDate, version);
|
||||
|
||||
loanRepaymentScheduleHistoryList.add(loanRepaymentScheduleHistory);
|
||||
}
|
||||
return loanRepaymentScheduleHistoryList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndSaveLoanScheduleArchive(List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments, Loan loan,
|
||||
LoanRescheduleRequest loanRescheduleRequest) {
|
||||
List<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = createLoanScheduleArchive(repaymentScheduleInstallments,
|
||||
loan, loanRescheduleRequest);
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistoryList);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_repayment_schedule_history")
|
||||
public class LoanRepaymentScheduleHistory extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "loan_id")
|
||||
private Loan loan;
|
||||
|
||||
@OneToOne(optional = true)
|
||||
@JoinColumn(name = "loan_reschedule_request_id")
|
||||
private LoanRescheduleRequest loanRescheduleRequest;
|
||||
|
||||
@Column(name = "installment", nullable = false)
|
||||
private Integer installmentNumber;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "fromdate", nullable = true)
|
||||
private Date fromDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "duedate", nullable = false)
|
||||
private Date dueDate;
|
||||
|
||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principal;
|
||||
|
||||
@Column(name = "principal_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principalCompleted;
|
||||
|
||||
@Column(name = "principal_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principalWrittenOff;
|
||||
|
||||
@Column(name = "interest_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestCharged;
|
||||
|
||||
@Column(name = "interest_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestPaid;
|
||||
|
||||
@Column(name = "interest_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestWaived;
|
||||
|
||||
@Column(name = "interest_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestWrittenOff;
|
||||
|
||||
@Column(name = "fee_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesCharged;
|
||||
|
||||
@Column(name = "fee_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesPaid;
|
||||
|
||||
@Column(name = "fee_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesWrittenOff;
|
||||
|
||||
@Column(name = "fee_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal feeChargesWaived;
|
||||
|
||||
@Column(name = "penalty_charges_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyCharges;
|
||||
|
||||
@Column(name = "penalty_charges_completed_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesPaid;
|
||||
|
||||
@Column(name = "penalty_charges_writtenoff_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesWrittenOff;
|
||||
|
||||
@Column(name = "penalty_charges_waived_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal penaltyChargesWaived;
|
||||
|
||||
@Column(name = "total_paid_in_advance_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal totalPaidInAdvance;
|
||||
|
||||
@Column(name = "total_paid_late_derived", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal totalPaidLate;
|
||||
|
||||
@Column(name = "completed_derived", nullable = false)
|
||||
private boolean obligationsMet;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "obligations_met_on_date")
|
||||
private Date obligationsMetOnDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "created_date")
|
||||
private Date createdOnDate;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "createdby_id")
|
||||
private AppUser createdByUser;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "lastmodifiedby_id")
|
||||
private AppUser lastModifiedByUser;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "lastmodified_date")
|
||||
private Date lastModifiedOnDate;
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
protected LoanRepaymentScheduleHistory() {}
|
||||
|
||||
/**
|
||||
* LoanRepaymentScheduleHistory constructor
|
||||
**/
|
||||
private LoanRepaymentScheduleHistory(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
this.loan = loan;
|
||||
this.loanRescheduleRequest = loanRescheduleRequest;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.principal = principal;
|
||||
this.principalCompleted = principalCompleted;
|
||||
this.principalWrittenOff = principalWrittenOff;
|
||||
this.interestCharged = interestCharged;
|
||||
this.interestPaid = interestPaid;
|
||||
this.interestWaived = interestWaived;
|
||||
this.interestWrittenOff = interestWrittenOff;
|
||||
this.feeChargesCharged = feeChargesCharged;
|
||||
this.feeChargesPaid = feeChargesPaid;
|
||||
this.feeChargesWrittenOff = feeChargesWrittenOff;
|
||||
this.feeChargesWaived = feeChargesWaived;
|
||||
this.penaltyCharges = penaltyCharges;
|
||||
this.penaltyChargesPaid = penaltyChargesPaid;
|
||||
this.penaltyChargesWrittenOff = penaltyChargesWrittenOff;
|
||||
this.penaltyChargesWaived = penaltyChargesWaived;
|
||||
this.totalPaidInAdvance = totalPaidInAdvance;
|
||||
this.totalPaidLate = totalPaidLate;
|
||||
this.obligationsMet = obligationsMet;
|
||||
this.obligationsMetOnDate = obligationsMetOnDate;
|
||||
this.createdOnDate = createdOnDate;
|
||||
this.createdByUser = createdByUser;
|
||||
this.lastModifiedByUser = lastModifiedByUser;
|
||||
this.lastModifiedOnDate = lastModifiedOnDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an instance of the LoanRepaymentScheduleHistory class
|
||||
**/
|
||||
public static LoanRepaymentScheduleHistory instance(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
return new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
|
||||
principalCompleted, principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges, penaltyChargesPaid,
|
||||
penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate, obligationsMet,
|
||||
obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void create(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final Integer installmentNumber, final Date fromDate, final Date dueDate, final BigDecimal principal,
|
||||
final BigDecimal principalCompleted, final BigDecimal principalWrittenOff, final BigDecimal interestCharged,
|
||||
final BigDecimal interestPaid, final BigDecimal interestWaived, final BigDecimal interestWrittenOff,
|
||||
final BigDecimal feeChargesCharged, final BigDecimal feeChargesPaid, final BigDecimal feeChargesWrittenOff,
|
||||
final BigDecimal feeChargesWaived, final BigDecimal penaltyCharges, final BigDecimal penaltyChargesPaid,
|
||||
final BigDecimal penaltyChargesWrittenOff, final BigDecimal penaltyChargesWaived,
|
||||
final BigDecimal totalPaidInAdvance, final BigDecimal totalPaidLate, final boolean obligationsMet,
|
||||
final Date obligationsMetOnDate, final Date createdOnDate, final AppUser createdByUser,
|
||||
final AppUser lastModifiedByUser, final Date lastModifiedOnDate) {
|
||||
|
||||
new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
|
||||
principalCompleted, principalWrittenOff, interestCharged, interestPaid, interestWaived, interestWrittenOff,
|
||||
feeChargesCharged, feeChargesPaid, feeChargesWrittenOff, feeChargesWaived, penaltyCharges, penaltyChargesPaid,
|
||||
penaltyChargesWrittenOff, penaltyChargesWaived, totalPaidInAdvance, totalPaidLate, obligationsMet,
|
||||
obligationsMetOnDate, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate);
|
||||
}
|
||||
}
|
||||
+38
-26
@@ -14,13 +14,14 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
|
||||
public final class LoanRescheduleModel {
|
||||
|
||||
private final Collection<LoanRescheduleModelRepaymentPeriod> periods;
|
||||
private final Collection<LoanRepaymentScheduleHistory> oldPeriods;
|
||||
private final ApplicationCurrency applicationCurrency;
|
||||
private final int loanTermInDays;
|
||||
private final Collection<LoanRescheduleModelRepaymentPeriod> periods;
|
||||
private final Collection<LoanRepaymentScheduleHistory> oldPeriods;
|
||||
private final ApplicationCurrency applicationCurrency;
|
||||
private final int loanTermInDays;
|
||||
private final Money totalPrincipalDisbursed;
|
||||
private final BigDecimal totalPrincipalExpected;
|
||||
private final BigDecimal totalPrincipalPaid;
|
||||
@@ -29,14 +30,14 @@ public final class LoanRescheduleModel {
|
||||
private final BigDecimal totalPenaltyChargesCharged;
|
||||
private final BigDecimal totalRepaymentExpected;
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
public LoanRescheduleModel(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods,
|
||||
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed,
|
||||
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged,
|
||||
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected,
|
||||
final BigDecimal totalOutstanding) {
|
||||
this.periods = periods;
|
||||
this.oldPeriods = oldPeriods;
|
||||
|
||||
private LoanRescheduleModel(final Collection<LoanRescheduleModelRepaymentPeriod> periods,
|
||||
final Collection<LoanRepaymentScheduleHistory> oldPeriods, final ApplicationCurrency applicationCurrency,
|
||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||
final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
|
||||
this.periods = periods;
|
||||
this.oldPeriods = oldPeriods;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
this.loanTermInDays = loanTermInDays;
|
||||
this.totalPrincipalDisbursed = principalDisbursed;
|
||||
@@ -48,25 +49,36 @@ public final class LoanRescheduleModel {
|
||||
this.totalRepaymentExpected = totalRepaymentExpected;
|
||||
this.totalOutstanding = totalOutstanding;
|
||||
}
|
||||
|
||||
public static LoanRescheduleModel instance(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods,
|
||||
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed,
|
||||
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged,
|
||||
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected,
|
||||
final BigDecimal totalOutstanding) {
|
||||
|
||||
return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected,
|
||||
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
|
||||
public static LoanRescheduleModel instance(final Collection<LoanRescheduleModelRepaymentPeriod> periods,
|
||||
final Collection<LoanRepaymentScheduleHistory> oldPeriods, final ApplicationCurrency applicationCurrency,
|
||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||
final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
|
||||
|
||||
return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed,
|
||||
totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, totalOutstanding);
|
||||
}
|
||||
|
||||
|
||||
public static LoanRescheduleModel createWithSchedulehistory(LoanRescheduleModel loanRescheduleModel,
|
||||
final Collection<LoanRepaymentScheduleHistory> oldPeriods) {
|
||||
|
||||
return new LoanRescheduleModel(loanRescheduleModel.periods, oldPeriods, loanRescheduleModel.applicationCurrency,
|
||||
loanRescheduleModel.loanTermInDays, loanRescheduleModel.totalPrincipalDisbursed,
|
||||
loanRescheduleModel.totalPrincipalExpected, loanRescheduleModel.totalPrincipalPaid,
|
||||
loanRescheduleModel.totalInterestCharged, loanRescheduleModel.totalFeeChargesCharged,
|
||||
loanRescheduleModel.totalPenaltyChargesCharged, loanRescheduleModel.totalRepaymentExpected,
|
||||
loanRescheduleModel.totalOutstanding);
|
||||
}
|
||||
|
||||
public LoanScheduleData toData() {
|
||||
|
||||
final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal();
|
||||
final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf();
|
||||
final CurrencyData currency = this.applicationCurrency.toData(decimalPlaces, inMultiplesOf);
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periodsData = new ArrayList<LoanSchedulePeriodData>();
|
||||
final Collection<LoanSchedulePeriodData> periodsData = new ArrayList<>();
|
||||
for (final LoanRescheduleModalPeriod modelPeriod : this.periods) {
|
||||
periodsData.add(modelPeriod.toData());
|
||||
}
|
||||
@@ -82,12 +94,12 @@ public final class LoanRescheduleModel {
|
||||
this.totalPenaltyChargesCharged, totalWaived, totalWrittenOff, this.totalRepaymentExpected, totalRepayment,
|
||||
totalPaidInAdvance, totalPaidLate, this.totalOutstanding);
|
||||
}
|
||||
|
||||
|
||||
public Collection<LoanRescheduleModelRepaymentPeriod> getPeriods() {
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
|
||||
public Collection<LoanRepaymentScheduleHistory> getOldPeriods() {
|
||||
return this.oldPeriods;
|
||||
return this.oldPeriods;
|
||||
}
|
||||
}
|
||||
|
||||
+44
-40
@@ -19,6 +19,8 @@ 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.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
@@ -31,52 +33,54 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
|
||||
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final ConfigurationDomainService configurationDomainService,
|
||||
final HolidayRepository holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId) {
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(requestId);
|
||||
}
|
||||
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel previewLoanReschedule(Long requestId) {
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
|
||||
|
||||
if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(requestId); }
|
||||
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
return loanRescheduleModel;
|
||||
}
|
||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
||||
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
||||
oldPeriods);
|
||||
return loanRescheduleModelWithOldPeriods;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+390
-411
@@ -8,14 +8,11 @@ package org.mifosplatform.portfolio.loanaccount.rescheduleloan.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -37,35 +34,25 @@ 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.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistory;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRepaymentScheduleHistoryRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -76,404 +63,396 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
||||
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestWritePlatformServiceImpl constructor
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
@Autowired
|
||||
public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper,
|
||||
PlatformSecurityContext platformSecurityContext,
|
||||
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
|
||||
LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepository holidayRepository,
|
||||
WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository) {
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object
|
||||
* and persist
|
||||
*
|
||||
* @return CommandProcessingResult object
|
||||
**/
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult create(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
// get the loan id from the JsonCommand object
|
||||
final Long loanId = jsonCommand.longValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.loanIdParamName);
|
||||
|
||||
// use the loan id to get a Loan entity object
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan);
|
||||
|
||||
// get the reschedule reason code value id from the JsonCommand object
|
||||
final Long rescheduleReasonId = jsonCommand.longValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonIdParamName);
|
||||
|
||||
// use the reschedule reason code value id to get a CodeValue entity object
|
||||
final CodeValue rescheduleReasonCodeValue = this.codeValueRepositoryWrapper
|
||||
.findOneWithNotFoundDetection(rescheduleReasonId);
|
||||
|
||||
// get the grace on principal integer value from the JsonCommand object
|
||||
final Integer graceOnPrincipal = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.graceOnPrincipalParamName);
|
||||
|
||||
// get the grace on interest integer value from the JsonCommand object
|
||||
final Integer graceOnInterest = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.graceOnInterestParamName);
|
||||
|
||||
// get the extra terms to be added at the end of the new schedule from the JsonCommand object
|
||||
final Integer extraTerms = jsonCommand.integerValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.extraTermsParamName);
|
||||
|
||||
// get the new interest rate that would be applied to the new loan schedule
|
||||
final BigDecimal interestRate = jsonCommand.bigDecimalValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.newInterestRateParamName);
|
||||
|
||||
// get the reschedule reason comment text from the JsonCommand object
|
||||
final String rescheduleReasonComment = jsonCommand.stringValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleReasonCommentParamName);
|
||||
|
||||
// get the recalculate interest option
|
||||
final Boolean recalculateInterest = jsonCommand.booleanObjectValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.recalculateInterestParamName);
|
||||
|
||||
// initialize set the value to null
|
||||
Date submittedOnDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.submittedOnDateParamName)) {
|
||||
// create a LocalDate object from the "submittedOnDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.submittedOnDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// update the value of the "submittedOnDate" variable
|
||||
submittedOnDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
// initially set the value to null
|
||||
Date rescheduleFromDate = null;
|
||||
|
||||
// start point of the rescheduling exercise
|
||||
Integer rescheduleFromInstallment = null;
|
||||
|
||||
// initially set the value to null
|
||||
Date adjustedDueDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) {
|
||||
// create a LocalDate object from the "rescheduleFromDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.rescheduleFromDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// get installment by due date
|
||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate);
|
||||
rescheduleFromInstallment = installment.getInstallmentNumber();
|
||||
|
||||
// update the value of the "rescheduleFromDate" variable
|
||||
rescheduleFromDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.adjustedDueDateParamName)) {
|
||||
// create a LocalDate object from the "adjustedDueDate" Date string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(
|
||||
RescheduleLoansApiConstants.adjustedDueDateParamName);
|
||||
|
||||
if(localDate != null) {
|
||||
// update the value of the "adjustedDueDate"variable
|
||||
adjustedDueDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(
|
||||
loan, LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal,
|
||||
graceOnInterest, rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
|
||||
rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(),
|
||||
null, null, null, null);
|
||||
|
||||
// create a new entry in the m_loan_reschedule_request table
|
||||
this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequest.getId())
|
||||
.withLoanId(loan.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleCreateActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approve(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId);
|
||||
}
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForApproveAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat
|
||||
.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("approvedByUserId", appUser.getId());
|
||||
|
||||
if(!changes.isEmpty()) {
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
for(LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if(period.isNew()) {
|
||||
LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(
|
||||
loan, period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
|
||||
period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false);
|
||||
|
||||
repaymentScheduleInstallments.add(repaymentScheduleInstallment);
|
||||
}
|
||||
|
||||
else {
|
||||
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
if(repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
|
||||
|
||||
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
|
||||
repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
|
||||
repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
|
||||
repaymentScheduleInstallment.updatePrincipal(period.principalDue());
|
||||
repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
|
||||
|
||||
if(Money.of(currency, period.principalDue()).isZero() &&
|
||||
Money.of(currency, period.interestDue()).isZero() &&
|
||||
repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero() &&
|
||||
repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero() &&
|
||||
repaymentScheduleInstallment.isNotFullyPaidOff()) {
|
||||
|
||||
repaymentScheduleInstallment.updateObligationMet(true);
|
||||
repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = loanRescheduleModel.getOldPeriods();
|
||||
|
||||
for(LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
loan.updateRescheduledByUser(appUser);
|
||||
loan.updateRescheduledOnDate(new LocalDate());
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(),
|
||||
true);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
|
||||
// update the loan term frequency (loan term frequency = number of repayments)
|
||||
loan.updateTermFrequency(periods.size());
|
||||
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId())
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleApproveActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult reject(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = loanRescheduleRequestRepository
|
||||
.findOne(loanRescheduleRequestId);
|
||||
|
||||
if(loanRescheduleRequest == null) {
|
||||
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId);
|
||||
}
|
||||
|
||||
// validate the request in the JsonCommand object passed as parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForRejectAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat
|
||||
.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("rejectedOnDate", rejectedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("rejectedByUserId", appUser.getId());
|
||||
|
||||
if(!changes.isEmpty()) {
|
||||
loanRescheduleRequest.reject(appUser, rejectedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder()
|
||||
.withCommandId(jsonCommand.commandId())
|
||||
.withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId())
|
||||
.with(changes)
|
||||
.build();
|
||||
}
|
||||
|
||||
catch(final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleRejectActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the create method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleCreateActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
|
||||
private final PlatformSecurityContext platformSecurityContext;
|
||||
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final HolidayRepository holidayRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestWritePlatformServiceImpl constructor
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
@Autowired
|
||||
public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
|
||||
CodeValueRepositoryWrapper codeValueRepositoryWrapper, PlatformSecurityContext platformSecurityContext,
|
||||
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
|
||||
LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ConfigurationDomainService configurationDomainService,
|
||||
HolidayRepository holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.holidayRepository = holidayRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new instance of the LoanRescheduleRequest object from the
|
||||
* JsonCommand object and persist
|
||||
*
|
||||
* @return CommandProcessingResult object
|
||||
**/
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult create(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
// get the loan id from the JsonCommand object
|
||||
final Long loanId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.loanIdParamName);
|
||||
|
||||
// use the loan id to get a Loan entity object
|
||||
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
|
||||
// validate the request in the JsonCommand object passed as
|
||||
// parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan);
|
||||
|
||||
// get the reschedule reason code value id from the JsonCommand
|
||||
// object
|
||||
final Long rescheduleReasonId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleReasonIdParamName);
|
||||
|
||||
// use the reschedule reason code value id to get a CodeValue entity
|
||||
// object
|
||||
final CodeValue rescheduleReasonCodeValue = this.codeValueRepositoryWrapper.findOneWithNotFoundDetection(rescheduleReasonId);
|
||||
|
||||
// get the grace on principal integer value from the JsonCommand
|
||||
// object
|
||||
final Integer graceOnPrincipal = jsonCommand
|
||||
.integerValueOfParameterNamed(RescheduleLoansApiConstants.graceOnPrincipalParamName);
|
||||
|
||||
// get the grace on interest integer value from the JsonCommand
|
||||
// object
|
||||
final Integer graceOnInterest = jsonCommand.integerValueOfParameterNamed(RescheduleLoansApiConstants.graceOnInterestParamName);
|
||||
|
||||
// get the extra terms to be added at the end of the new schedule
|
||||
// from the JsonCommand object
|
||||
final Integer extraTerms = jsonCommand.integerValueOfParameterNamed(RescheduleLoansApiConstants.extraTermsParamName);
|
||||
|
||||
// get the new interest rate that would be applied to the new loan
|
||||
// schedule
|
||||
final BigDecimal interestRate = jsonCommand
|
||||
.bigDecimalValueOfParameterNamed(RescheduleLoansApiConstants.newInterestRateParamName);
|
||||
|
||||
// get the reschedule reason comment text from the JsonCommand
|
||||
// object
|
||||
final String rescheduleReasonComment = jsonCommand
|
||||
.stringValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleReasonCommentParamName);
|
||||
|
||||
// get the recalculate interest option
|
||||
final Boolean recalculateInterest = jsonCommand
|
||||
.booleanObjectValueOfParameterNamed(RescheduleLoansApiConstants.recalculateInterestParamName);
|
||||
|
||||
// initialize set the value to null
|
||||
Date submittedOnDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if (jsonCommand.hasParameter(RescheduleLoansApiConstants.submittedOnDateParamName)) {
|
||||
// create a LocalDate object from the "submittedOnDate" Date
|
||||
// string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.submittedOnDateParamName);
|
||||
|
||||
if (localDate != null) {
|
||||
// update the value of the "submittedOnDate" variable
|
||||
submittedOnDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
// initially set the value to null
|
||||
Date rescheduleFromDate = null;
|
||||
|
||||
// start point of the rescheduling exercise
|
||||
Integer rescheduleFromInstallment = null;
|
||||
|
||||
// initially set the value to null
|
||||
Date adjustedDueDate = null;
|
||||
|
||||
// check if the parameter is in the JsonCommand object
|
||||
if (jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) {
|
||||
// create a LocalDate object from the "rescheduleFromDate" Date
|
||||
// string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleFromDateParamName);
|
||||
|
||||
if (localDate != null) {
|
||||
// get installment by due date
|
||||
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate);
|
||||
rescheduleFromInstallment = installment.getInstallmentNumber();
|
||||
|
||||
// update the value of the "rescheduleFromDate" variable
|
||||
rescheduleFromDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonCommand.hasParameter(RescheduleLoansApiConstants.adjustedDueDateParamName)) {
|
||||
// create a LocalDate object from the "adjustedDueDate" Date
|
||||
// string
|
||||
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.adjustedDueDateParamName);
|
||||
|
||||
if (localDate != null) {
|
||||
// update the value of the "adjustedDueDate"variable
|
||||
adjustedDueDate = localDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(loan,
|
||||
LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
|
||||
rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
|
||||
rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(), null, null, null, null);
|
||||
|
||||
// create a new entry in the m_loan_reschedule_request table
|
||||
this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequest.getId())
|
||||
.withLoanId(loan.getId()).build();
|
||||
}
|
||||
|
||||
catch (final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleCreateActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult approve(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
|
||||
|
||||
if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); }
|
||||
|
||||
// validate the request in the JsonCommand object passed as
|
||||
// parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForApproveAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(
|
||||
jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("approvedByUserId", appUser.getId());
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
|
||||
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
|
||||
for (LoanRescheduleModelRepaymentPeriod period : periods) {
|
||||
|
||||
if (period.isNew()) {
|
||||
LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(loan,
|
||||
period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
|
||||
period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false);
|
||||
|
||||
repaymentScheduleInstallments.add(repaymentScheduleInstallment);
|
||||
}
|
||||
|
||||
else {
|
||||
for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
|
||||
|
||||
if (repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
|
||||
|
||||
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
|
||||
repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
|
||||
repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
|
||||
repaymentScheduleInstallment.updatePrincipal(period.principalDue());
|
||||
repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
|
||||
|
||||
if (Money.of(currency, period.principalDue()).isZero() && Money.of(currency, period.interestDue()).isZero()
|
||||
&& repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero()
|
||||
&& repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero()
|
||||
&& repaymentScheduleInstallment.isNotFullyPaidOff()) {
|
||||
|
||||
repaymentScheduleInstallment.updateObligationMet(true);
|
||||
repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
|
||||
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
|
||||
}
|
||||
|
||||
loan.updateRescheduledByUser(appUser);
|
||||
loan.updateRescheduledOnDate(new LocalDate());
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
|
||||
// update the loan term frequency (loan term frequency = number
|
||||
// of repayments)
|
||||
loan.updateTermFrequency(periods.size());
|
||||
|
||||
// update the status of the request
|
||||
loanRescheduleRequest.approve(appUser, approvedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).build();
|
||||
}
|
||||
|
||||
catch (final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleApproveActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult reject(JsonCommand jsonCommand) {
|
||||
|
||||
try {
|
||||
final Long loanRescheduleRequestId = jsonCommand.entityId();
|
||||
|
||||
final LoanRescheduleRequest loanRescheduleRequest = loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
|
||||
|
||||
if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); }
|
||||
|
||||
// validate the request in the JsonCommand object passed as
|
||||
// parameter
|
||||
this.loanRescheduleRequestDataValidator.validateForRejectAction(jsonCommand, loanRescheduleRequest);
|
||||
|
||||
final AppUser appUser = this.platformSecurityContext.authenticatedUser();
|
||||
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
|
||||
|
||||
LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(
|
||||
jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
changes.put("rejectedOnDate", rejectedOnDate.toString(dateTimeFormatter));
|
||||
changes.put("rejectedByUserId", appUser.getId());
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
loanRescheduleRequest.reject(appUser, rejectedOnDate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
|
||||
.withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).build();
|
||||
}
|
||||
|
||||
catch (final DataIntegrityViolationException dve) {
|
||||
// handle the data integrity violation
|
||||
handleRejectActionDataIntegrityViolation(jsonCommand, dve);
|
||||
|
||||
// return an empty command processing result object
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the create
|
||||
* method
|
||||
*
|
||||
* @param jsonCommand
|
||||
* JSON command object
|
||||
* @param dve
|
||||
* data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleCreateActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the reject method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleRejectActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the reject
|
||||
* method
|
||||
*
|
||||
* @param jsonCommand
|
||||
* JSON command object
|
||||
* @param dve
|
||||
* data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleRejectActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the approve method
|
||||
*
|
||||
* @param jsonCommand JSON command object
|
||||
* @param dve data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleApproveActionDataIntegrityViolation(final JsonCommand jsonCommand,
|
||||
final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
}
|
||||
|
||||
/**
|
||||
* handles the data integrity violation exception caught in the approve
|
||||
* method
|
||||
*
|
||||
* @param jsonCommand
|
||||
* JSON command object
|
||||
* @param dve
|
||||
* data integrity violation exception
|
||||
* @return void
|
||||
**/
|
||||
@SuppressWarnings("unused")
|
||||
private void handleApproveActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+93
-6
@@ -111,7 +111,10 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFound
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator;
|
||||
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.LoanProductData;
|
||||
@@ -168,6 +171,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final AccountTransferRepository accountTransferRepository;
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -188,7 +192,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanReadPlatformService loanReadPlatformService,
|
||||
final FromJsonHelper fromApiJsonHelper, final AccountTransferRepository accountTransferRepository,
|
||||
final CalendarRepository calendarRepository,
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
|
||||
this.context = context;
|
||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||
this.loanAssembler = loanAssembler;
|
||||
@@ -217,6 +222,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.accountTransferRepository = accountTransferRepository;
|
||||
this.calendarRepository = calendarRepository;
|
||||
this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -316,7 +322,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
}
|
||||
@@ -510,6 +519,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
}
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
createLoanScheduleArchive(loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays);
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
}
|
||||
final Set<LoanCharge> loanCharges = loan.charges();
|
||||
@@ -1130,9 +1142,31 @@ 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);
|
||||
existingReversedTransactionIds, holidays, workingDays, isHolidayEnabled, loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
|
||||
/**
|
||||
* we want to apply charge transactions only for those loans charges
|
||||
@@ -1161,7 +1195,10 @@ 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);
|
||||
}
|
||||
@@ -1246,9 +1283,31 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
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 LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber);
|
||||
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, holidays, workingDays, isHolidayEnabled,
|
||||
loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
|
||||
this.loanTransactionRepository.save(waiveTransaction);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -2103,7 +2162,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
|
||||
workingDays);
|
||||
}
|
||||
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
|
||||
this.loanAccountDomainService.recalculateAccruals(loan);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
@@ -2243,4 +2305,29 @@ 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);
|
||||
List<LoanRepaymentScheduleInstallment> installments = retrieveRepaymentScheduleFromModel(loanScheduleModel);
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(installments, loan, null);
|
||||
|
||||
}
|
||||
|
||||
private List<LoanRepaymentScheduleInstallment> retrieveRepaymentScheduleFromModel(LoanScheduleModel model) {
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(null,
|
||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent());
|
||||
installments.add(installment);
|
||||
}
|
||||
}
|
||||
return installments;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user