Merge pull request #1067 from pramodn02/develop

MIFOSX-1537 : added original schedule to history
This commit is contained in:
Vishwas Babu A J
2014-09-14 10:11:38 -07:00
17 changed files with 1620 additions and 1231 deletions
@@ -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;
@@ -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.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService; 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.LoanChargeReadPlatformService;
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService; import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
@@ -137,6 +138,7 @@ public class LoansApiResource {
private final NoteReadPlatformServiceImpl noteReadPlatformService; private final NoteReadPlatformServiceImpl noteReadPlatformService;
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService; private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService; private final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService;
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
@Autowired @Autowired
public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService, public LoansApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
@@ -153,7 +155,8 @@ public class LoansApiResource {
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService, final CalendarReadPlatformService calendarReadPlatformService, final NoteReadPlatformServiceImpl noteReadPlatformService,
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl, final PortfolioAccountReadPlatformService portfolioAccountReadPlatformServiceImpl,
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService) { final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService) {
this.context = context; this.context = context;
this.loanReadPlatformService = loanReadPlatformService; this.loanReadPlatformService = loanReadPlatformService;
this.loanProductReadPlatformService = loanProductReadPlatformService; this.loanProductReadPlatformService = loanProductReadPlatformService;
@@ -175,6 +178,7 @@ public class LoansApiResource {
this.noteReadPlatformService = noteReadPlatformService; this.noteReadPlatformService = noteReadPlatformService;
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl; this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformServiceImpl;
this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService; this.accountAssociationsReadPlatformService = accountAssociationsReadPlatformService;
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
} }
@GET @GET
@@ -318,8 +322,8 @@ public class LoansApiResource {
if (!associationParameters.isEmpty()) { if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) { if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("repaymentSchedule", "futureSchedule", "transactions", "charges", "guarantors", associationParameters.addAll(Arrays.asList("repaymentSchedule", "futureSchedule", "originalSchedule", "transactions",
"collateral", "notes", "linkedAccount", "multiDisburseDetails")); "charges", "guarantors", "collateral", "notes", "linkedAccount", "multiDisburseDetails"));
} }
if (associationParameters.contains("guarantors")) { if (associationParameters.contains("guarantors")) {
@@ -360,6 +364,14 @@ public class LoansApiResource {
mandatoryResponseParameters.add("futureSchedule"); mandatoryResponseParameters.add("futureSchedule");
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId); 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")) { if (associationParameters.contains("charges")) {
@@ -115,6 +115,7 @@ public class LoanAccountData {
private final CalendarData meeting; private final CalendarData meeting;
private final Collection<NoteData> notes; private final Collection<NoteData> notes;
private final Collection<DisbursementData> disbursementDetails; private final Collection<DisbursementData> disbursementDetails;
private final LoanScheduleData originalSchedule;
// template // template
private final Collection<LoanProductData> productOptions; private final Collection<LoanProductData> productOptions;
private final Collection<StaffData> loanOfficerOptions; private final Collection<StaffData> loanOfficerOptions;
@@ -260,6 +261,7 @@ public class LoanAccountData {
final EnumOptionData daysInYearType = null; final EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false; final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null; final LoanInterestRecalculationData interestRecalculationData = null;
final LoanScheduleData originalSchedule = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -275,7 +277,7 @@ public class LoanAccountData {
syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount,
disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations,
product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType,
isInterestRecalculationEnabled, interestRecalculationData); isInterestRecalculationEnabled, interestRecalculationData, originalSchedule);
} }
/** /**
@@ -369,6 +371,7 @@ public class LoanAccountData {
final EnumOptionData daysInYearType = null; final EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false; final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null; final LoanInterestRecalculationData interestRecalculationData = null;
final LoanScheduleData originalSchedule = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -383,7 +386,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount,
maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, 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) { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, 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 EnumOptionData daysInYearType = null;
final boolean isInterestRecalculationEnabled = false; final boolean isInterestRecalculationEnabled = false;
final LoanInterestRecalculationData interestRecalculationData = null; final LoanInterestRecalculationData interestRecalculationData = null;
final LoanScheduleData originalSchedule = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -514,7 +519,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, 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) { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, 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, public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -643,6 +649,7 @@ public class LoanAccountData {
Map<Long, LoanBorrowerCycleData> memberVariations = null; Map<Long, LoanBorrowerCycleData> memberVariations = null;
final Boolean inArrears = null; final Boolean inArrears = null;
final Boolean isNPA = null; final Boolean isNPA = null;
final LoanScheduleData originalSchedule = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId, 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, notes, accountLinkingOptions, linkedAccount, disbursementData, product.getMultiDisburseLoan(), fixedEmi,
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears, product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(), 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) { public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -713,7 +721,8 @@ public class LoanAccountData {
acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount, acc.linkedAccount, acc.disbursementDetails, product.getMultiDisburseLoan(), acc.fixedEmiAmount,
product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears, product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears,
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(), 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 Map<Long, LoanBorrowerCycleData> memberVariations = null;
final LoanProductData product = null; final LoanProductData product = null;
final Collection<ChargeData> overdueCharges = null; final Collection<ChargeData> overdueCharges = null;
final LoanScheduleData originalSchedule = null;
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
@@ -782,7 +792,7 @@ public class LoanAccountData {
loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter,
notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance,
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, 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, accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount,
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, 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, 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, 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) { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, 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) { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter, acc.notes, acc.accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance,
acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, 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( private LoanAccountData(
@@ -987,7 +1022,7 @@ public class LoanAccountData {
final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing, final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing,
final Collection<ChargeData> overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType, final Collection<ChargeData> overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType,
final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
final LoanInterestRecalculationData interestRecalculationData) { final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule) {
this.id = id; this.id = id;
this.accountNo = accountNo; this.accountNo = accountNo;
this.status = status; this.status = status;
@@ -1131,6 +1166,7 @@ public class LoanAccountData {
this.daysInYearType = daysInYearType; this.daysInYearType = daysInYearType;
this.isInterestRecalculationEnabled = isInterestRecalculationEnabled; this.isInterestRecalculationEnabled = isInterestRecalculationEnabled;
this.interestRecalculationData = interestRecalculationData; this.interestRecalculationData = interestRecalculationData;
this.originalSchedule = originalSchedule;
} }
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() { public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -1208,7 +1244,7 @@ public class LoanAccountData {
return this.loanProductId; return this.loanProductId;
} }
public BigDecimal getTotalOutstandingAmount(){ public BigDecimal getTotalOutstandingAmount() {
return this.summary.getTotalOutstanding(); return this.summary.getTotalOutstanding();
} }
@@ -61,8 +61,8 @@ import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType; 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.Calendar;
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType;
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException; 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.InterestCalculationPeriodMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; 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.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy; import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType; import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
@@ -468,7 +467,9 @@ public class Loan extends AbstractPersistable<Long> {
} }
public ChangedTransactionDetail addLoanCharge(final LoanCharge loanCharge, final List<Long> existingTransactionIds, 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; ChangedTransactionDetail changedTransactionDetail = null;
validateLoanIsNotClosed(loanCharge); validateLoanIsNotClosed(loanCharge);
@@ -528,7 +529,15 @@ public class Loan extends AbstractPersistable<Long> {
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LoanRepaymentScheduleProcessingWrapper wrapper = new LoanRepaymentScheduleProcessingWrapper(); 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()) { if (!loanCharge.isDueAtDisbursement()) {
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement(); final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(), changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
@@ -825,7 +834,9 @@ public class Loan extends AbstractPersistable<Long> {
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine, public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final Integer loanInstallmentNumber) { final 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); validateLoanIsNotClosed(loanCharge);
@@ -854,6 +865,12 @@ public class Loan extends AbstractPersistable<Long> {
transactionDate, feeChargesWaived, penaltyChargesWaived); transactionDate, feeChargesWaived, penaltyChargesWaived);
this.loanTransactions.add(waiveLoanChargeTransaction); 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' // Waive of charges whose due date falls after latest 'repayment'
// transaction dont require entire loan schedule to be reprocessed. // transaction dont require entire loan schedule to be reprocessed.
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
@@ -1811,17 +1828,19 @@ public class Loan extends AbstractPersistable<Long> {
this.loanTermVariations.add(loanVariationTerms); this.loanTermVariations.add(loanVariationTerms);
isEmiAmountChanged = true; isEmiAmountChanged = true;
} }
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) {
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
}
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled() if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) { && (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom); isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
} else if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule
|| isEmiAmountChanged) {
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
} }
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate); updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
updateLoanSummaryDerivedFields(); updateLoanSummaryDerivedFields();
handleDisbursementTransaction(actualDisbursementDate); handleDisbursementTransaction(actualDisbursementDate);
@@ -2028,6 +2047,19 @@ public class Loan extends AbstractPersistable<Long> {
final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate, final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate,
final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) { 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 InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod); 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(), final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, applicationCurrency, loanApplicationTerms, charges(),
isHolidayEnabled, holidays, workingDays); isHolidayEnabled, holidays, workingDays);
return loanSchedule;
updateLoanSchedule(loanSchedule);
Set<LoanCharge> charges = this.charges();
for (LoanCharge loanCharge : charges) {
recalculateLoanCharge(loanCharge);
}
} }
private void handleDisbursementTransaction(final LocalDate disbursedOn) { private void handleDisbursementTransaction(final LocalDate disbursedOn) {
@@ -4003,13 +4030,14 @@ public class Loan extends AbstractPersistable<Long> {
command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName)); command.stringValueOfParameterNamed(LoanApiConstants.disbursementIdParameterName));
existingTransactionIds.addAll(findExistingTransactionIds()); existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) { if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
LocalDate recalculateFrom = null; LocalDate recalculateFrom = null;
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom); isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
} else {
regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
} }
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
@@ -4314,169 +4342,175 @@ public class Loan extends AbstractPersistable<Long> {
* @return List of loan repayments schedule objects * @return List of loan repayments schedule objects
**/ **/
public List<LoanRepaymentScheduleInstallment> getRepaymentScheduleInstallments() { 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() { public LoanProductRelatedDetail getLoanRepaymentScheduleDetail() {
return this.loanRepaymentScheduleDetail; return this.loanRepaymentScheduleDetail;
} }
/** /**
* @return Loan Fixed Emi amount * @return Loan Fixed Emi amount
**/ **/
public BigDecimal getFixedEmiAmount() { public BigDecimal getFixedEmiAmount() {
return this.fixedEmiAmount; return this.fixedEmiAmount;
} }
/** /**
* @return maximum outstanding loan balance * @return maximum outstanding loan balance
**/ **/
public BigDecimal getMaxOutstandingLoanBalance() { public BigDecimal getMaxOutstandingLoanBalance() {
return this.maxOutstandingLoanBalance; return this.maxOutstandingLoanBalance;
} }
/** /**
* @param dueDate the due date of the installment * @param dueDate
* the due date of the installment
* @return a schedule installment with similar due date to the one provided * @return a schedule installment with similar due date to the one provided
**/ **/
public LoanRepaymentScheduleInstallment getRepaymentScheduleInstallment(LocalDate dueDate) { public LoanRepaymentScheduleInstallment getRepaymentScheduleInstallment(LocalDate dueDate) {
LoanRepaymentScheduleInstallment installment = null; LoanRepaymentScheduleInstallment installment = null;
if(dueDate != null) { if (dueDate != null) {
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : this.repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : this.repaymentScheduleInstallments) {
if(repaymentScheduleInstallment.getDueDate().isEqual(dueDate)) { if (repaymentScheduleInstallment.getDueDate().isEqual(dueDate)) {
installment = repaymentScheduleInstallment; installment = repaymentScheduleInstallment;
break; break;
} }
} }
} }
return installment; return installment;
} }
/** /**
* @param loan the Loan object * @param loan
* @return loan disbursement data * the Loan object
**/ * @return loan disbursement data
public List<DisbursementData> getDisbursmentData() { **/
Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator(); public List<DisbursementData> getDisbursmentData() {
List<DisbursementData> disbursementData = new ArrayList<DisbursementData>(); Iterator<LoanDisbursementDetails> iterator = this.getDisbursementDetails().iterator();
List<DisbursementData> disbursementData = new ArrayList<DisbursementData>();
while(iterator.hasNext()) { while (iterator.hasNext()) {
LoanDisbursementDetails loanDisbursementDetails = iterator.next(); LoanDisbursementDetails loanDisbursementDetails = iterator.next();
LocalDate expectedDisbursementDate = null; LocalDate expectedDisbursementDate = null;
LocalDate actualDisbursementDate = null; LocalDate actualDisbursementDate = null;
if(loanDisbursementDetails.expectedDisbursementDate() != null) { if (loanDisbursementDetails.expectedDisbursementDate() != null) {
expectedDisbursementDate = new LocalDate(loanDisbursementDetails.expectedDisbursementDate()); expectedDisbursementDate = new LocalDate(loanDisbursementDetails.expectedDisbursementDate());
} }
if(loanDisbursementDetails.actualDisbursementDate() != null) { if (loanDisbursementDetails.actualDisbursementDate() != null) {
actualDisbursementDate = new LocalDate(loanDisbursementDetails.actualDisbursementDate()); actualDisbursementDate = new LocalDate(loanDisbursementDetails.actualDisbursementDate());
} }
disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, disbursementData.add(new DisbursementData(loanDisbursementDetails.getId(), expectedDisbursementDate, actualDisbursementDate,
actualDisbursementDate, loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal())); loanDisbursementDetails.principal(), loanDisbursementDetails.approvedPrincipal()));
} }
return disbursementData; return disbursementData;
} }
/** /**
* @param loan the Loan object * @param loan
* @return application terms of the Loan object * the Loan object
**/ * @return application terms of the Loan object
@SuppressWarnings({ "unused" }) **/
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency) { @SuppressWarnings({ "unused" })
LoanProduct loanProduct = loanProduct(); public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency) {
//LoanProductRelatedDetail loanProductRelatedDetail = getLoanRepaymentScheduleDetail(); LoanProduct loanProduct = loanProduct();
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency(); // LoanProductRelatedDetail loanProductRelatedDetail =
// getLoanRepaymentScheduleDetail();
final MonetaryCurrency currency = this.loanRepaymentScheduleDetail.getCurrency();
final Integer loanTermFrequency = getTermFrequency(); final Integer loanTermFrequency = getTermFrequency();
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType(); final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments(); final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery(); final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType(); final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod(); final AmortizationMethod amortizationMethod = this.loanRepaymentScheduleDetail.getAmortizationMethod();
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail.getInterestCalculationPeriodMethod(); final InterestCalculationPeriodMethod interestCalculationPeriodMethod = this.loanRepaymentScheduleDetail
.getInterestCalculationPeriodMethod();
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod(); final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType(); final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate(); final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal(); final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate(); final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate(); final LocalDate repaymentsStartingFromDate = getExpectedFirstRepaymentOnDate();
LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate; 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. // TODO get calender linked to loan if any exist. As of 17-07-2014,
// skip for now and set the Calender object to null // could not find a link in the database.
Calendar calendar = null; // skip for now and set the Calender object to null
Calendar calendar = null;
final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment(); final Integer graceOnPrincipalPayment = this.loanRepaymentScheduleDetail.graceOnPrincipalPayment();
final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment(); final Integer graceOnInterestPayment = this.loanRepaymentScheduleDetail.graceOnInterestPayment();
final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged(); final Integer graceOnInterestCharged = this.loanRepaymentScheduleDetail.graceOnInterestCharged();
final LocalDate interestChargedFromDate = getInterestChargedFromDate(); final LocalDate interestChargedFromDate = getInterestChargedFromDate();
final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnDueDate(); final Integer graceOnArrearsAgeing = this.loanRepaymentScheduleDetail.getGraceOnDueDate();
final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance(); final Money inArrearsToleranceMoney = this.loanRepaymentScheduleDetail.getInArrearsTolerance();
final BigDecimal emiAmount = getFixedEmiAmount(); final BigDecimal emiAmount = getFixedEmiAmount();
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance(); final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
final List<DisbursementData> disbursementData = getDisbursmentData(); final List<DisbursementData> disbursementData = getDisbursmentData();
final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>(); final List<LoanTermVariationsData> loanVariationTermsData = new ArrayList<LoanTermVariationsData>();
LoanTermVariationsData data = new LoanTermVariationsData(null, LoanTermVariationsData data = new LoanTermVariationsData(null,
LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount); LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount);
loanVariationTermsData.add(data); loanVariationTermsData.add(data);
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType,
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, inArrearsToleranceMoney,
this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData, maxOutstandingBalance,
loanVariationTermsData, interestChargedFromDate); 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) { /**
* @return Loan summary embedded object
**/
public LoanSummary getLoanSummary() {
return this.summary;
}
if(rescheduledOnDate != null) { public void updateRescheduledByUser(AppUser rescheduledByUser) {
this.rescheduledOnDate = rescheduledOnDate.toDate(); this.rescheduledByUser = rescheduledByUser;
} }
}
public void updateTermFrequency(Integer termFrequency) { public LoanProductRelatedDetail getLoanProductRelatedDetail() {
return this.loanRepaymentScheduleDetail;
}
if(termFrequency != null) { public void updateNumberOfRepayments(Integer numberOfRepayments) {
this.termFrequency = termFrequency; 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;
}
}
} }
@@ -75,6 +75,27 @@ public class LoanScheduleData {
this.totalOutstanding = totalOutstanding; 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() { public Collection<LoanSchedulePeriodData> getPeriods() {
return this.periods; return this.periods;
} }
@@ -9,9 +9,8 @@ import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor; 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.MultiDisbursementDisbursementDateException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException; import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementEmiAmountException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException; 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.LoanRescheduleModel;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
import org.mifosplatform.useradministration.domain.AppUser;
/** /**
* *
@@ -524,352 +521,292 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
} }
@Override @Override
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, final List<Holiday> holidays,
final List<Holiday> holidays, final WorkingDays workingDays) { final WorkingDays workingDays) {
final Loan loan = loanRescheduleRequest.getLoan(); final Loan loan = loanRescheduleRequest.getLoan();
final LoanSummary loanSummary = loan.getSummary(); final LoanSummary loanSummary = loan.getSummary();
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
// create an archive of the current loan schedule installments // create an archive of the current loan schedule installments
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = createLoanScheduleArchive(loanRescheduleRequest); Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = null;
// get the initial list of repayment installments // get the initial list of repayment installments
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments(); List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
// sort list by installment number in ASC order // sort list by installment number in ASC order
Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator); Collections.sort(repaymentScheduleInstallments, LoanRepaymentScheduleInstallment.installmentNumberComparator);
final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<LoanRescheduleModelRepaymentPeriod>(); final Collection<LoanRescheduleModelRepaymentPeriod> periods = new ArrayList<>();
Money outstandingLoanBalance = loan.getPrincpal(); Money outstandingLoanBalance = loan.getPrincpal();
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber(); Integer oldPeriodNumber = repaymentScheduleInstallment.getInstallmentNumber();
LocalDate fromDate = repaymentScheduleInstallment.getFromDate(); LocalDate fromDate = repaymentScheduleInstallment.getFromDate();
LocalDate dueDate = repaymentScheduleInstallment.getDueDate(); LocalDate dueDate = repaymentScheduleInstallment.getDueDate();
Money principalDue = repaymentScheduleInstallment.getPrincipal(currency); Money principalDue = repaymentScheduleInstallment.getPrincipal(currency);
Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency); Money interestDue = repaymentScheduleInstallment.getInterestCharged(currency);
Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency); Money feeChargesDue = repaymentScheduleInstallment.getFeeChargesCharged(currency);
Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency); Money penaltyChargesDue = repaymentScheduleInstallment.getPenaltyChargesCharged(currency);
Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue); Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
outstandingLoanBalance = outstandingLoanBalance.minus(principalDue); outstandingLoanBalance = outstandingLoanBalance.minus(principalDue);
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(oldPeriodNumber, oldPeriodNumber, LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod
fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue, feeChargesDue, penaltyChargesDue, .instance(oldPeriodNumber, oldPeriodNumber, fromDate, dueDate, principalDue, outstandingLoanBalance, interestDue,
totalDue, false); feeChargesDue, penaltyChargesDue, totalDue, false);
periods.add(period); periods.add(period);
} }
Money outstandingBalance = loan.getPrincpal(); Money outstandingBalance = loan.getPrincpal();
Money totalCumulativePrincipal = Money.zero(currency); Money totalCumulativePrincipal = Money.zero(currency);
Money totalCumulativeInterest = Money.zero(currency); Money totalCumulativeInterest = Money.zero(currency);
Money actualTotalCumulativeInterest = Money.zero(currency); Money actualTotalCumulativeInterest = Money.zero(currency);
Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency); Money totalOutstandingInterestPaymentDueToGrace = Money.zero(currency);
Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency); Money totalPrincipalBeforeReschedulePeriod = Money.zero(currency);
LocalDate installmentDueDate = null; LocalDate installmentDueDate = null;
LocalDate adjustedInstallmentDueDate = null; LocalDate adjustedInstallmentDueDate = null;
LocalDate installmentFromDate = null; LocalDate installmentFromDate = null;
Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment()); Integer rescheduleFromInstallmentNo = defaultToZeroIfNull(loanRescheduleRequest.getRescheduleFromInstallment());
Integer installmentNumber = rescheduleFromInstallmentNo; Integer installmentNumber = rescheduleFromInstallmentNo;
Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); Integer graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); Integer graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms()); Integer extraTerms = defaultToZeroIfNull(loanRescheduleRequest.getExtraTerms());
final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest(); final boolean recalculateInterest = loanRescheduleRequest.getRecalculateInterest();
Integer numberOfRepayments = repaymentScheduleInstallments.size(); Integer numberOfRepayments = repaymentScheduleInstallments.size();
Integer rescheduleNumberOfRepayments = numberOfRepayments; Integer rescheduleNumberOfRepayments = numberOfRepayments;
final Money principal = loan.getPrincpal(); final Money principal = loan.getPrincpal();
final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding()); final Money totalPrincipalOutstanding = Money.of(currency, loanSummary.getTotalPrincipalOutstanding());
LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate(); LocalDate adjustedDueDate = loanRescheduleRequest.getAdjustedDueDate();
BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate(); BigDecimal newInterestRate = loanRescheduleRequest.getInterestRate();
int loanTermInDays = Integer.valueOf(0); int loanTermInDays = Integer.valueOf(0);
if(rescheduleFromInstallmentNo > 0) { if (rescheduleFromInstallmentNo > 0) {
// this will hold the loan repayment installment that is before the reschedule start installment // this will hold the loan repayment installment that is before the
// (rescheduleFrominstallment) // reschedule start installment
LoanRepaymentScheduleInstallment previousInstallment = null; // (rescheduleFrominstallment)
LoanRepaymentScheduleInstallment previousInstallment = null;
// get the install number of the previous installment // get the install number of the previous installment
int previousInstallmentNo = rescheduleFromInstallmentNo - 1; int previousInstallmentNo = rescheduleFromInstallmentNo - 1;
// only fetch the installment if the number is greater than 0 // only fetch the installment if the number is greater than 0
if(previousInstallmentNo > 0) { if (previousInstallmentNo > 0) {
previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo); previousInstallment = loan.fetchRepaymentScheduleInstallment(previousInstallmentNo);
} }
LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1); LoanRepaymentScheduleInstallment firstInstallment = loan.fetchRepaymentScheduleInstallment(1);
// the "installment from date" is equal to the due date of the previous installment, if it exists // the "installment from date" is equal to the due date of the
if(previousInstallment != null) { // previous installment, if it exists
installmentFromDate = previousInstallment.getDueDate(); if (previousInstallment != null) {
} installmentFromDate = previousInstallment.getDueDate();
}
else { else {
installmentFromDate = firstInstallment.getFromDate(); installmentFromDate = firstInstallment.getFromDate();
} }
installmentDueDate = installmentFromDate; installmentDueDate = installmentFromDate;
LocalDate periodStartDateApplicableForInterest = installmentFromDate; LocalDate periodStartDateApplicableForInterest = installmentFromDate;
Integer periodNumber = 1; Integer periodNumber = 1;
outstandingLoanBalance = loan.getPrincpal(); outstandingLoanBalance = loan.getPrincpal();
for(LoanRescheduleModelRepaymentPeriod period : periods) { for (LoanRescheduleModelRepaymentPeriod period : periods) {
if(period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) { if (period.periodDueDate().isBefore(loanRescheduleRequest.getRescheduleFromDate())) {
totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue()); totalPrincipalBeforeReschedulePeriod = totalPrincipalBeforeReschedulePeriod.plus(period.principalDue());
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
rescheduleNumberOfRepayments--; rescheduleNumberOfRepayments--;
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
outstandingBalance = outstandingBalance.minus(period.principalDue()); outstandingBalance = outstandingBalance.minus(period.principalDue());
} }
} }
while(graceOnPrincipal > 0 || graceOnInterest > 0) { while (graceOnPrincipal > 0 || graceOnInterest > 0) {
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
Money.zero(currency), Money.zero(currency), true); Money.zero(currency), Money.zero(currency), true);
periods.add(period); periods.add(period);
if(graceOnPrincipal > 0) { if (graceOnPrincipal > 0) {
graceOnPrincipal--; graceOnPrincipal--;
} }
if(graceOnInterest > 0) { if (graceOnInterest > 0) {
graceOnInterest--; graceOnInterest--;
} }
rescheduleNumberOfRepayments++; rescheduleNumberOfRepayments++;
numberOfRepayments++; numberOfRepayments++;
} }
while(extraTerms > 0) { while (extraTerms > 0) {
LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(), LoanRescheduleModelRepaymentPeriod period = LoanRescheduleModelRepaymentPeriod.instance(0, 0, new LocalDate(),
new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency), new LocalDate(), Money.zero(currency), Money.zero(currency), Money.zero(currency), Money.zero(currency),
Money.zero(currency), Money.zero(currency), true); Money.zero(currency), Money.zero(currency), true);
periods.add(period); periods.add(period);
extraTerms--; extraTerms--;
rescheduleNumberOfRepayments++; rescheduleNumberOfRepayments++;
numberOfRepayments++; numberOfRepayments++;
} }
// get the loan application terms from the Loan object // get the loan application terms from the Loan object
final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency); final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency);
// update the number of repayments // update the number of repayments
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments); loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays,
holidays, workingDays); workingDays);
loanApplicationTerms.updateLoanEndDate(loanEndDate); loanApplicationTerms.updateLoanEndDate(loanEndDate);
if(newInterestRate != null) { if (newInterestRate != null) {
loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate); loanApplicationTerms.updateAnnualNominalInterestRate(newInterestRate);
loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate); loanApplicationTerms.updateInterestRatePerPeriod(newInterestRate);
} }
graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal()); graceOnPrincipal = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnPrincipal());
graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest()); graceOnInterest = defaultToZeroIfNull(loanRescheduleRequest.getGraceOnInterest());
loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest); loanApplicationTerms.updateInterestPaymentGrace(graceOnInterest);
loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal); loanApplicationTerms.updatePrincipalGrace(graceOnPrincipal);
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments); loanApplicationTerms.updateNumberOfRepayments(rescheduleNumberOfRepayments);
loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments); loanApplicationTerms.updateLoanTermFrequency(rescheduleNumberOfRepayments);
loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest); loanApplicationTerms.updateInterestChargedFromDate(periodStartDateApplicableForInterest);
Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged( Money totalInterestChargedForFullLoanTerm = loanApplicationTerms.calculateTotalInterestCharged(
this.paymentPeriodsInOneYearCalculator, mathContext); this.paymentPeriodsInOneYearCalculator, mathContext);
if(!recalculateInterest && newInterestRate == null) { if (!recalculateInterest && newInterestRate == null) {
totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged()); totalInterestChargedForFullLoanTerm = Money.of(currency, loanSummary.getTotalInterestCharged());
totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest); totalInterestChargedForFullLoanTerm = totalInterestChargedForFullLoanTerm.minus(actualTotalCumulativeInterest);
loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm); loanApplicationTerms.updateTotalInterestDue(totalInterestChargedForFullLoanTerm);
} }
for(LoanRescheduleModelRepaymentPeriod period : periods) { for (LoanRescheduleModelRepaymentPeriod period : periods) {
if(period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate()) || if (period.periodDueDate().isEqual(loanRescheduleRequest.getRescheduleFromDate())
period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || || period.periodDueDate().isAfter(loanRescheduleRequest.getRescheduleFromDate()) || period.isNew()) {
period.isNew()) {
installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms, installmentDueDate = this.scheduledDateGenerator.generateNextRepaymentDate(installmentDueDate, loanApplicationTerms,
false); false);
if(adjustedDueDate != null && periodNumber == 1) { if (adjustedDueDate != null && periodNumber == 1) {
installmentDueDate = adjustedDueDate; installmentDueDate = adjustedDueDate;
} }
adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms, adjustedInstallmentDueDate = this.scheduledDateGenerator.adjustRepaymentDate(installmentDueDate, loanApplicationTerms,
isHolidayEnabled, holidays, workingDays); isHolidayEnabled, holidays, workingDays);
final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays(); final int daysInInstallment = Days.daysBetween(installmentFromDate, adjustedInstallmentDueDate).getDays();
period.updatePeriodNumber(installmentNumber); period.updatePeriodNumber(installmentNumber);
period.updatePeriodFromDate(installmentFromDate); period.updatePeriodFromDate(installmentFromDate);
period.updatePeriodDueDate(adjustedInstallmentDueDate); period.updatePeriodDueDate(adjustedInstallmentDueDate);
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, adjustedInstallmentDueDate, .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
periodStartDateApplicableForInterest, loanApplicationTerms.getLoanTermPeriodFrequencyType(), adjustedInstallmentDueDate, periodStartDateApplicableForInterest,
loanApplicationTerms.getRepaymentEvery()); loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
// ========================= Calculate the interest due ======================================== // ========================= Calculate the interest due
// ========================================
// change the principal to => Principal Disbursed - Total Principal Paid // change the principal to => Principal Disbursed - Total
// interest calculation is always based on the total principal outstanding // Principal Paid
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding); // interest calculation is always based on the total
// principal outstanding
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
// determine the interest & principal for the period // determine the interest & principal for the period
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext); totalOutstandingInterestPaymentDueToGrace, daysInInstallment, outstandingBalance, loanApplicationTerms,
periodNumber, mathContext);
// update the interest due for the period // update the interest due for the period
period.updateInterestDue(principalInterestForThisPeriod.interest()); period.updateInterestDue(principalInterestForThisPeriod.interest());
// ============================================================================================= // =============================================================================================
// ========================== Calculate the principal due ====================================== // ========================== Calculate the principal due
// ======================================
// change the principal to => Principal Disbursed - Total cumulative Principal Amount before the reschedule installment // change the principal to => Principal Disbursed - Total
loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod)); // cumulative Principal Amount before the reschedule
// installment
loanApplicationTerms.setPrincipal(principal.minus(totalPrincipalBeforeReschedulePeriod));
principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod( principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(this.paymentPeriodsInOneYearCalculator,
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest,
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, daysInInstallment,
daysInInstallment, outstandingBalance, loanApplicationTerms, periodNumber, mathContext); outstandingBalance, loanApplicationTerms, periodNumber, mathContext);
period.updatePrincipalDue(principalInterestForThisPeriod.principal()); period.updatePrincipalDue(principalInterestForThisPeriod.principal());
// ============================================================================================== // ==============================================================================================
outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue()); outstandingLoanBalance = outstandingLoanBalance.minus(period.principalDue());
period.updateOutstandingLoanBalance(outstandingLoanBalance); period.updateOutstandingLoanBalance(outstandingLoanBalance);
Money principalDue = Money.of(currency, period.principalDue()); Money principalDue = Money.of(currency, period.principalDue());
Money interestDue = Money.of(currency, period.interestDue()); Money interestDue = Money.of(currency, period.interestDue());
Money feeChargesDue = Money.of(currency, period.feeChargesDue()); Money feeChargesDue = Money.of(currency, period.feeChargesDue());
Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue()); Money penaltyChargesDue = Money.of(currency, period.penaltyChargesDue());
Money totalDue = principalDue Money totalDue = principalDue.plus(interestDue).plus(feeChargesDue).plus(penaltyChargesDue);
.plus(interestDue)
.plus(feeChargesDue)
.plus(penaltyChargesDue);
period.updateTotalDue(totalDue); period.updateTotalDue(totalDue);
// update cumulative fields for principal & interest // update cumulative fields for principal & interest
totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue()); totalCumulativePrincipal = totalCumulativePrincipal.plus(period.principalDue());
totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue()); totalCumulativeInterest = totalCumulativeInterest.plus(period.interestDue());
actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue()); actualTotalCumulativeInterest = actualTotalCumulativeInterest.plus(period.interestDue());
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace(); totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
installmentFromDate = adjustedInstallmentDueDate; installmentFromDate = adjustedInstallmentDueDate;
installmentNumber++; installmentNumber++;
periodNumber++; periodNumber++;
loanTermInDays += daysInInstallment; loanTermInDays += daysInInstallment;
outstandingBalance = outstandingBalance.minus(period.principalDue()); outstandingBalance = outstandingBalance.minus(period.principalDue());
} }
} }
} }
final Money totalRepaymentExpected = principal // get the loan Principal amount final Money totalRepaymentExpected = principal // get the loan Principal
.plus(actualTotalCumulativeInterest) // add the actual total cumulative interest // amount
.plus(loanSummary.getTotalFeeChargesCharged()) // add the total fees charged .plus(actualTotalCumulativeInterest) // add the actual total
.plus(loanSummary.getTotalPenaltyChargesCharged()); // finally add the total penalty charged // 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, return LoanRescheduleModel.instance(periods, loanRepaymentScheduleHistoryList, applicationCurrency, loanTermInDays,
loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(), actualTotalCumulativeInterest.getAmount(), loan.getPrincpal(), loan.getPrincpal().getAmount(), loanSummary.getTotalPrincipalRepaid(),
loanSummary.getTotalFeeChargesCharged(), loanSummary.getTotalPenaltyChargesCharged(), totalRepaymentExpected.getAmount(), actualTotalCumulativeInterest.getAmount(), loanSummary.getTotalFeeChargesCharged(),
loanSummary.getTotalOutstanding()); 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;
} }
private BigDecimal calculateInterestForSpecificDays(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, private BigDecimal calculateInterestForSpecificDays(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
@@ -1677,6 +1614,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
**/ **/
private Integer defaultToZeroIfNull(Integer value) { private Integer defaultToZeroIfNull(Integer value) {
return (value != null) ? value : 0; return (value != null) ? value : 0;
} }
} }
@@ -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);
}
}
@@ -3,7 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file, * 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/. * 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.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -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);
}
@@ -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());
}
}
}
@@ -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);
}
@@ -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);
}
}
@@ -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);
}
}
@@ -14,13 +14,14 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData; import org.mifosplatform.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
public final class LoanRescheduleModel { public final class LoanRescheduleModel {
private final Collection<LoanRescheduleModelRepaymentPeriod> periods; private final Collection<LoanRescheduleModelRepaymentPeriod> periods;
private final Collection<LoanRepaymentScheduleHistory> oldPeriods; private final Collection<LoanRepaymentScheduleHistory> oldPeriods;
private final ApplicationCurrency applicationCurrency; private final ApplicationCurrency applicationCurrency;
private final int loanTermInDays; private final int loanTermInDays;
private final Money totalPrincipalDisbursed; private final Money totalPrincipalDisbursed;
private final BigDecimal totalPrincipalExpected; private final BigDecimal totalPrincipalExpected;
private final BigDecimal totalPrincipalPaid; private final BigDecimal totalPrincipalPaid;
@@ -30,13 +31,13 @@ public final class LoanRescheduleModel {
private final BigDecimal totalRepaymentExpected; private final BigDecimal totalRepaymentExpected;
private final BigDecimal totalOutstanding; private final BigDecimal totalOutstanding;
public LoanRescheduleModel(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods, private LoanRescheduleModel(final Collection<LoanRescheduleModelRepaymentPeriod> periods,
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed, final Collection<LoanRepaymentScheduleHistory> oldPeriods, final ApplicationCurrency applicationCurrency,
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
final BigDecimal totalOutstanding) { final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
this.periods = periods; this.periods = periods;
this.oldPeriods = oldPeriods; this.oldPeriods = oldPeriods;
this.applicationCurrency = applicationCurrency; this.applicationCurrency = applicationCurrency;
this.loanTermInDays = loanTermInDays; this.loanTermInDays = loanTermInDays;
this.totalPrincipalDisbursed = principalDisbursed; this.totalPrincipalDisbursed = principalDisbursed;
@@ -49,24 +50,35 @@ public final class LoanRescheduleModel {
this.totalOutstanding = totalOutstanding; this.totalOutstanding = totalOutstanding;
} }
public static LoanRescheduleModel instance(final Collection<LoanRescheduleModelRepaymentPeriod> periods, final Collection<LoanRepaymentScheduleHistory> oldPeriods, public static LoanRescheduleModel instance(final Collection<LoanRescheduleModelRepaymentPeriod> periods,
final ApplicationCurrency applicationCurrency, final int loanTermInDays, final Money principalDisbursed, final Collection<LoanRepaymentScheduleHistory> oldPeriods, final ApplicationCurrency applicationCurrency,
final BigDecimal totalPrincipalExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
final BigDecimal totalFeeChargesCharged, final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
final BigDecimal totalOutstanding) { final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed, totalPrincipalExpected, return new LoanRescheduleModel(periods, oldPeriods, applicationCurrency, loanTermInDays, principalDisbursed,
totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged, totalPrincipalExpected, totalPrincipalPaid, totalInterestCharged, totalFeeChargesCharged, totalPenaltyChargesCharged,
totalRepaymentExpected, totalOutstanding); 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() { public LoanScheduleData toData() {
final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal(); final int decimalPlaces = this.totalPrincipalDisbursed.getCurrencyDigitsAfterDecimal();
final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf(); final Integer inMultiplesOf = this.totalPrincipalDisbursed.getCurrencyInMultiplesOf();
final CurrencyData currency = this.applicationCurrency.toData(decimalPlaces, inMultiplesOf); 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) { for (final LoanRescheduleModalPeriod modelPeriod : this.periods) {
periodsData.add(modelPeriod.toData()); periodsData.add(modelPeriod.toData());
} }
@@ -88,6 +100,6 @@ public final class LoanRescheduleModel {
} }
public Collection<LoanRepaymentScheduleHistory> getOldPeriods() { public Collection<LoanRepaymentScheduleHistory> getOldPeriods() {
return this.oldPeriods; return this.oldPeriods;
} }
} }
@@ -19,6 +19,8 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; 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.DefaultLoanReschedulerFactory;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
@@ -32,51 +34,53 @@ import org.springframework.stereotype.Service;
@Service @Service
public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService { public class LoanReschedulePreviewPlatformServiceImpl implements LoanReschedulePreviewPlatformService {
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository; private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final ConfigurationDomainService configurationDomainService; private final ConfigurationDomainService configurationDomainService;
private final HolidayRepository holidayRepository; private final HolidayRepository holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
@Autowired @Autowired
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository, public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final ConfigurationDomainService configurationDomainService, final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
final HolidayRepository holidayRepository, final WorkingDaysRepositoryWrapper workingDaysRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository) { final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository; this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository; this.applicationCurrencyRepository = applicationCurrencyRepository;
this.configurationDomainService = configurationDomainService; this.configurationDomainService = configurationDomainService;
this.holidayRepository = holidayRepository; this.holidayRepository = holidayRepository;
this.workingDaysRepository = workingDaysRepository; this.workingDaysRepository = workingDaysRepository;
} this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
}
@Override @Override
public LoanRescheduleModel previewLoanReschedule(Long requestId) { public LoanRescheduleModel previewLoanReschedule(Long requestId) {
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId); final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(requestId);
if(loanRescheduleRequest == null) { if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(requestId); }
throw new LoanRescheduleRequestNotFoundException(requestId);
}
Loan loan = loanRescheduleRequest.getLoan(); Loan loan = loanRescheduleRequest.getLoan();
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); .getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
final WorkingDays workingDays = this.workingDaysRepository.findOne(); final WorkingDays workingDays = this.workingDaysRepository.findOne();
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod(); final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
final RoundingMode roundingMode = RoundingMode.HALF_EVEN; final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
final MathContext mathContext = new MathContext(8, roundingMode); final MathContext mathContext = new MathContext(8, roundingMode);
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod, LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays); loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
return loanRescheduleModel; oldPeriods);
} return loanRescheduleModelWithOldPeriods;
}
} }
@@ -8,14 +8,11 @@ package org.mifosplatform.portfolio.loanaccount.rescheduleloan.service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat; 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.monetary.domain.Money;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.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.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanDisbursementDetails;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus; import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummary;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms; 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.RescheduleLoansApiConstants;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.DefaultLoanReschedulerFactory; 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.LoanRescheduleModel;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.mifosplatform.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
import org.mifosplatform.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException; 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.InterestMethod;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -77,403 +64,395 @@ import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService { public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class); private final static Logger logger = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
private final LoanRepositoryWrapper loanRepositoryWrapper; private final LoanRepositoryWrapper loanRepositoryWrapper;
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper; private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
private final PlatformSecurityContext platformSecurityContext; private final PlatformSecurityContext platformSecurityContext;
private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator; private final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator;
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository; private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository; private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final ConfigurationDomainService configurationDomainService; private final ConfigurationDomainService configurationDomainService;
private final HolidayRepository holidayRepository; private final HolidayRepository holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository; private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
/**
* LoanRescheduleRequestWritePlatformServiceImpl constructor /**
* * LoanRescheduleRequestWritePlatformServiceImpl constructor
* @return void *
**/ * @return void
@Autowired **/
public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper, @Autowired
CodeValueRepositoryWrapper codeValueRepositoryWrapper, public LoanRescheduleRequestWritePlatformServiceImpl(LoanRepositoryWrapper loanRepositoryWrapper,
PlatformSecurityContext platformSecurityContext, CodeValueRepositoryWrapper codeValueRepositoryWrapper, PlatformSecurityContext platformSecurityContext,
LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator, LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
LoanRescheduleRequestRepository loanRescheduleRequestRepository, LoanRescheduleRequestRepository loanRescheduleRequestRepository,
ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, ConfigurationDomainService configurationDomainService,
ConfigurationDomainService configurationDomainService, HolidayRepository holidayRepository, WorkingDaysRepositoryWrapper workingDaysRepository,
HolidayRepository holidayRepository, LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
WorkingDaysRepositoryWrapper workingDaysRepository, final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository) { this.loanRepositoryWrapper = loanRepositoryWrapper;
this.loanRepositoryWrapper = loanRepositoryWrapper; this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper; this.platformSecurityContext = platformSecurityContext;
this.platformSecurityContext = platformSecurityContext; this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator; this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository; this.applicationCurrencyRepository = applicationCurrencyRepository;
this.applicationCurrencyRepository = applicationCurrencyRepository; this.configurationDomainService = configurationDomainService;
this.configurationDomainService = configurationDomainService; this.holidayRepository = holidayRepository;
this.holidayRepository = holidayRepository; this.workingDaysRepository = workingDaysRepository;
this.workingDaysRepository = workingDaysRepository; this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository; this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
} }
/** /**
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object * create a new instance of the LoanRescheduleRequest object from the
* and persist * JsonCommand object and persist
* *
* @return CommandProcessingResult object * @return CommandProcessingResult object
**/ **/
@Override @Override
@Transactional @Transactional
public CommandProcessingResult create(JsonCommand jsonCommand) { public CommandProcessingResult create(JsonCommand jsonCommand) {
try { try {
// get the loan id from the JsonCommand object // get the loan id from the JsonCommand object
final Long loanId = jsonCommand.longValueOfParameterNamed( final Long loanId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.loanIdParamName);
RescheduleLoansApiConstants.loanIdParamName);
// use the loan id to get a Loan entity object
// use the loan id to get a Loan entity object final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
// validate the request in the JsonCommand object passed as
// validate the request in the JsonCommand object passed as parameter // parameter
this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan); this.loanRescheduleRequestDataValidator.validateForCreateAction(jsonCommand, loan);
// get the reschedule reason code value id from the JsonCommand object // get the reschedule reason code value id from the JsonCommand
final Long rescheduleReasonId = jsonCommand.longValueOfParameterNamed( // object
RescheduleLoansApiConstants.rescheduleReasonIdParamName); final Long rescheduleReasonId = jsonCommand.longValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleReasonIdParamName);
// use the reschedule reason code value id to get a CodeValue entity object // use the reschedule reason code value id to get a CodeValue entity
final CodeValue rescheduleReasonCodeValue = this.codeValueRepositoryWrapper // object
.findOneWithNotFoundDetection(rescheduleReasonId); final CodeValue rescheduleReasonCodeValue = this.codeValueRepositoryWrapper.findOneWithNotFoundDetection(rescheduleReasonId);
// get the grace on principal integer value from the JsonCommand object // get the grace on principal integer value from the JsonCommand
final Integer graceOnPrincipal = jsonCommand.integerValueOfParameterNamed( // object
RescheduleLoansApiConstants.graceOnPrincipalParamName); final Integer graceOnPrincipal = jsonCommand
.integerValueOfParameterNamed(RescheduleLoansApiConstants.graceOnPrincipalParamName);
// get the grace on interest integer value from the JsonCommand object
final Integer graceOnInterest = jsonCommand.integerValueOfParameterNamed( // get the grace on interest integer value from the JsonCommand
RescheduleLoansApiConstants.graceOnInterestParamName); // 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( // get the extra terms to be added at the end of the new schedule
RescheduleLoansApiConstants.extraTermsParamName); // 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( // get the new interest rate that would be applied to the new loan
RescheduleLoansApiConstants.newInterestRateParamName); // schedule
final BigDecimal interestRate = jsonCommand
// get the reschedule reason comment text from the JsonCommand object .bigDecimalValueOfParameterNamed(RescheduleLoansApiConstants.newInterestRateParamName);
final String rescheduleReasonComment = jsonCommand.stringValueOfParameterNamed(
RescheduleLoansApiConstants.rescheduleReasonCommentParamName); // get the reschedule reason comment text from the JsonCommand
// object
// get the recalculate interest option final String rescheduleReasonComment = jsonCommand
final Boolean recalculateInterest = jsonCommand.booleanObjectValueOfParameterNamed( .stringValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleReasonCommentParamName);
RescheduleLoansApiConstants.recalculateInterestParamName);
// get the recalculate interest option
// initialize set the value to null final Boolean recalculateInterest = jsonCommand
Date submittedOnDate = null; .booleanObjectValueOfParameterNamed(RescheduleLoansApiConstants.recalculateInterestParamName);
// check if the parameter is in the JsonCommand object // initialize set the value to null
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.submittedOnDateParamName)) { Date submittedOnDate = null;
// create a LocalDate object from the "submittedOnDate" Date string
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed( // check if the parameter is in the JsonCommand object
RescheduleLoansApiConstants.submittedOnDateParamName); if (jsonCommand.hasParameter(RescheduleLoansApiConstants.submittedOnDateParamName)) {
// create a LocalDate object from the "submittedOnDate" Date
if(localDate != null) { // string
// update the value of the "submittedOnDate" variable LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.submittedOnDateParamName);
submittedOnDate = localDate.toDate();
} 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 // initially set the value to null
Integer rescheduleFromInstallment = null; Date rescheduleFromDate = null;
// initially set the value to null // start point of the rescheduling exercise
Date adjustedDueDate = null; Integer rescheduleFromInstallment = null;
// check if the parameter is in the JsonCommand object // initially set the value to null
if(jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) { Date adjustedDueDate = null;
// create a LocalDate object from the "rescheduleFromDate" Date string
LocalDate localDate = jsonCommand.localDateValueOfParameterNamed( // check if the parameter is in the JsonCommand object
RescheduleLoansApiConstants.rescheduleFromDateParamName); if (jsonCommand.hasParameter(RescheduleLoansApiConstants.rescheduleFromDateParamName)) {
// create a LocalDate object from the "rescheduleFromDate" Date
if(localDate != null) { // string
// get installment by due date LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.rescheduleFromDateParamName);
LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate);
rescheduleFromInstallment = installment.getInstallmentNumber(); if (localDate != null) {
// get installment by due date
// update the value of the "rescheduleFromDate" variable LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(localDate);
rescheduleFromDate = localDate.toDate(); 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 (jsonCommand.hasParameter(RescheduleLoansApiConstants.adjustedDueDateParamName)) {
// create a LocalDate object from the "adjustedDueDate" Date
if(localDate != null) { // string
// update the value of the "adjustedDueDate"variable LocalDate localDate = jsonCommand.localDateValueOfParameterNamed(RescheduleLoansApiConstants.adjustedDueDateParamName);
adjustedDueDate = localDate.toDate();
} 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(), final LoanRescheduleRequest loanRescheduleRequest = LoanRescheduleRequest.instance(loan,
null, null, null, null); LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), rescheduleFromInstallment, graceOnPrincipal, graceOnInterest,
rescheduleFromDate, adjustedDueDate, extraTerms, recalculateInterest, interestRate, rescheduleReasonCodeValue,
// create a new entry in the m_loan_reschedule_request table rescheduleReasonComment, submittedOnDate, this.platformSecurityContext.authenticatedUser(), null, null, null, null);
this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
// create a new entry in the m_loan_reschedule_request table
return new CommandProcessingResultBuilder() this.loanRescheduleRequestRepository.save(loanRescheduleRequest);
.withCommandId(jsonCommand.commandId())
.withEntityId(loanRescheduleRequest.getId()) return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequest.getId())
.withLoanId(loan.getId()) .withLoanId(loan.getId()).build();
.build(); }
}
catch (final DataIntegrityViolationException dve) {
catch(final DataIntegrityViolationException dve) { // handle the data integrity violation
// handle the data integrity violation handleCreateActionDataIntegrityViolation(jsonCommand, dve);
handleCreateActionDataIntegrityViolation(jsonCommand, dve);
// return an empty command processing result object
// return an empty command processing result object return CommandProcessingResult.empty();
return CommandProcessingResult.empty(); }
} }
}
@Override
@Override @Transactional
@Transactional public CommandProcessingResult approve(JsonCommand jsonCommand) {
public CommandProcessingResult approve(JsonCommand jsonCommand) {
try {
try { final Long loanRescheduleRequestId = jsonCommand.entityId();
final Long loanRescheduleRequestId = jsonCommand.entityId();
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
final LoanRescheduleRequest loanRescheduleRequest = this.loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); }
if(loanRescheduleRequest == null) {
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); // validate the request in the JsonCommand object passed as
} // parameter
this.loanRescheduleRequestDataValidator.validateForApproveAction(jsonCommand, loanRescheduleRequest);
// 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>();
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(
LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate"); jsonCommand.extractLocale());
final DateTimeFormatter dateTimeFormatter = DateTimeFormat
.forPattern(jsonCommand.dateFormat()) changes.put("locale", jsonCommand.locale());
.withLocale(jsonCommand.extractLocale()); changes.put("dateFormat", jsonCommand.dateFormat());
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter));
changes.put("locale", jsonCommand.locale()); changes.put("approvedByUserId", appUser.getId());
changes.put("dateFormat", jsonCommand.dateFormat());
changes.put("approvedOnDate", approvedOnDate.toString(dateTimeFormatter)); if (!changes.isEmpty()) {
changes.put("approvedByUserId", appUser.getId()); Loan loan = loanRescheduleRequest.getLoan();
final LoanSummary loanSummary = loan.getSummary();
if(!changes.isEmpty()) {
Loan loan = loanRescheduleRequest.getLoan(); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final LoanSummary loanSummary = loan.getSummary(); final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); final WorkingDays workingDays = this.workingDaysRepository.findOne();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
final WorkingDays workingDays = this.workingDaysRepository.findOne(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final RoundingMode roundingMode = RoundingMode.HALF_EVEN;
final MathContext mathContext = new MathContext(8, roundingMode);
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
final RoundingMode roundingMode = RoundingMode.HALF_EVEN; Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService
final MathContext mathContext = new MathContext(8, roundingMode); .createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod, LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays); loanRescheduleRequest, applicationCurrency, isHolidayEnabled, holidays, workingDays);
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods(); final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments(); List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
for(LoanRescheduleModelRepaymentPeriod period : periods) { for (LoanRescheduleModelRepaymentPeriod period : periods) {
if(period.isNew()) { if (period.isNew()) {
LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment( LoanRepaymentScheduleInstallment repaymentScheduleInstallment = new LoanRepaymentScheduleInstallment(loan,
loan, period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(), period.periodNumber(), period.periodFromDate(), period.periodDueDate(), period.principalDue(),
period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false); period.interestDue(), BigDecimal.ZERO, BigDecimal.ZERO, false);
repaymentScheduleInstallments.add(repaymentScheduleInstallment); repaymentScheduleInstallments.add(repaymentScheduleInstallment);
} }
else { else {
for(LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) { for (LoanRepaymentScheduleInstallment repaymentScheduleInstallment : repaymentScheduleInstallments) {
if(repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) { if (repaymentScheduleInstallment.getInstallmentNumber().equals(period.oldPeriodNumber())) {
repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber()); repaymentScheduleInstallment.updateInstallmentNumber(period.periodNumber());
repaymentScheduleInstallment.updateFromDate(period.periodFromDate()); repaymentScheduleInstallment.updateFromDate(period.periodFromDate());
repaymentScheduleInstallment.updateDueDate(period.periodDueDate()); repaymentScheduleInstallment.updateDueDate(period.periodDueDate());
repaymentScheduleInstallment.updatePrincipal(period.principalDue()); repaymentScheduleInstallment.updatePrincipal(period.principalDue());
repaymentScheduleInstallment.updateInterestCharged(period.interestDue()); repaymentScheduleInstallment.updateInterestCharged(period.interestDue());
if(Money.of(currency, period.principalDue()).isZero() && if (Money.of(currency, period.principalDue()).isZero() && Money.of(currency, period.interestDue()).isZero()
Money.of(currency, period.interestDue()).isZero() && && repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero()
repaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).isZero() && && repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero()
repaymentScheduleInstallment.getFeeChargesOutstanding(currency).isZero() && && repaymentScheduleInstallment.isNotFullyPaidOff()) {
repaymentScheduleInstallment.isNotFullyPaidOff()) {
repaymentScheduleInstallment.updateObligationMet(true);
repaymentScheduleInstallment.updateObligationMet(true); repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate());
repaymentScheduleInstallment.updateObligationMetOnDate(new LocalDate()); }
}
break;
break; }
} }
} }
} }
}
for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
Collection<LoanRepaymentScheduleHistory> loanRepaymentScheduleHistoryList = loanRescheduleModel.getOldPeriods(); this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
}
for(LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory); loan.updateRescheduledByUser(appUser);
} loan.updateRescheduledOnDate(new LocalDate());
loan.updateRescheduledByUser(appUser); // update the Loan summary
loan.updateRescheduledOnDate(new LocalDate()); loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true);
// update the Loan summary // update the total number of schedule repayments
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), loan.updateNumberOfRepayments(periods.size());
true);
// update the loan term frequency (loan term frequency = number
// update the total number of schedule repayments // of repayments)
loan.updateNumberOfRepayments(periods.size()); loan.updateTermFrequency(periods.size());
// update the loan term frequency (loan term frequency = number of repayments) // update the status of the request
loan.updateTermFrequency(periods.size()); loanRescheduleRequest.approve(appUser, approvedOnDate);
}
// 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();
}
return new CommandProcessingResultBuilder()
.withCommandId(jsonCommand.commandId()) catch (final DataIntegrityViolationException dve) {
.withEntityId(loanRescheduleRequestId) // handle the data integrity violation
.withLoanId(loanRescheduleRequest.getLoan().getId()) handleApproveActionDataIntegrityViolation(jsonCommand, dve);
.with(changes)
.build(); // return an empty command processing result object
} return CommandProcessingResult.empty();
}
catch(final DataIntegrityViolationException dve) { }
// handle the data integrity violation
handleApproveActionDataIntegrityViolation(jsonCommand, dve); @Override
@Transactional
// return an empty command processing result object public CommandProcessingResult reject(JsonCommand jsonCommand) {
return CommandProcessingResult.empty();
} try {
} final Long loanRescheduleRequestId = jsonCommand.entityId();
@Override final LoanRescheduleRequest loanRescheduleRequest = loanRescheduleRequestRepository.findOne(loanRescheduleRequestId);
@Transactional
public CommandProcessingResult reject(JsonCommand jsonCommand) { if (loanRescheduleRequest == null) { throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); }
try { // validate the request in the JsonCommand object passed as
final Long loanRescheduleRequestId = jsonCommand.entityId(); // parameter
this.loanRescheduleRequestDataValidator.validateForRejectAction(jsonCommand, loanRescheduleRequest);
final LoanRescheduleRequest loanRescheduleRequest = loanRescheduleRequestRepository
.findOne(loanRescheduleRequestId); final AppUser appUser = this.platformSecurityContext.authenticatedUser();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
if(loanRescheduleRequest == null) {
throw new LoanRescheduleRequestNotFoundException(loanRescheduleRequestId); LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate");
} final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(
jsonCommand.extractLocale());
// validate the request in the JsonCommand object passed as parameter
this.loanRescheduleRequestDataValidator.validateForRejectAction(jsonCommand, loanRescheduleRequest); changes.put("locale", jsonCommand.locale());
changes.put("dateFormat", jsonCommand.dateFormat());
final AppUser appUser = this.platformSecurityContext.authenticatedUser(); changes.put("rejectedOnDate", rejectedOnDate.toString(dateTimeFormatter));
final Map<String, Object> changes = new LinkedHashMap<String, Object>(); changes.put("rejectedByUserId", appUser.getId());
LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate"); if (!changes.isEmpty()) {
final DateTimeFormatter dateTimeFormatter = DateTimeFormat loanRescheduleRequest.reject(appUser, rejectedOnDate);
.forPattern(jsonCommand.dateFormat()) }
.withLocale(jsonCommand.extractLocale());
return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
changes.put("locale", jsonCommand.locale()); .withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).build();
changes.put("dateFormat", jsonCommand.dateFormat()); }
changes.put("rejectedOnDate", rejectedOnDate.toString(dateTimeFormatter));
changes.put("rejectedByUserId", appUser.getId()); catch (final DataIntegrityViolationException dve) {
// handle the data integrity violation
if(!changes.isEmpty()) { handleRejectActionDataIntegrityViolation(jsonCommand, dve);
loanRescheduleRequest.reject(appUser, rejectedOnDate);
} // return an empty command processing result object
return CommandProcessingResult.empty();
return new CommandProcessingResultBuilder() }
.withCommandId(jsonCommand.commandId()) }
.withEntityId(loanRescheduleRequestId)
.withLoanId(loanRescheduleRequest.getLoan().getId()) /**
.with(changes) * handles the data integrity violation exception caught in the create
.build(); * method
} *
* @param jsonCommand
catch(final DataIntegrityViolationException dve) { * JSON command object
// handle the data integrity violation * @param dve
handleRejectActionDataIntegrityViolation(jsonCommand, dve); * data integrity violation exception
* @return void
// return an empty command processing result object **/
return CommandProcessingResult.empty(); @SuppressWarnings("unused")
} private void handleCreateActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
}
logger.error(dve.getMessage(), dve);
/**
* handles the data integrity violation exception caught in the create method throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
*
* @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."); "Unknown data integrity issue with resource.");
} }
/** /**
* handles the data integrity violation exception caught in the reject method * handles the data integrity violation exception caught in the reject
* * method
* @param jsonCommand JSON command object *
* @param dve data integrity violation exception * @param jsonCommand
* @return void * JSON command object
**/ * @param dve
@SuppressWarnings("unused") * data integrity violation exception
private void handleRejectActionDataIntegrityViolation(final JsonCommand jsonCommand, * @return void
final DataIntegrityViolationException dve) { **/
@SuppressWarnings("unused")
private void handleRejectActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
"Unknown data integrity issue with resource."); "Unknown data integrity issue with resource.");
} }
/** /**
* handles the data integrity violation exception caught in the approve method * handles the data integrity violation exception caught in the approve
* * method
* @param jsonCommand JSON command object *
* @param dve data integrity violation exception * @param jsonCommand
* @return void * JSON command object
**/ * @param dve
@SuppressWarnings("unused") * data integrity violation exception
private void handleApproveActionDataIntegrityViolation(final JsonCommand jsonCommand, * @return void
final DataIntegrityViolationException dve) { **/
@SuppressWarnings("unused")
private void handleApproveActionDataIntegrityViolation(final JsonCommand jsonCommand, final DataIntegrityViolationException dve) {
logger.error(dve.getMessage(), dve); logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.group.unknown.data.integrity.issue",
"Unknown data integrity issue with resource."); "Unknown data integrity issue with resource.");
} }
} }
@@ -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.data.OverdueLoanScheduleData;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; 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.domain.ScheduledDateGenerator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator; import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator;
import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer;
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
@@ -168,6 +171,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final AccountTransferRepository accountTransferRepository; private final AccountTransferRepository accountTransferRepository;
private final CalendarRepository calendarRepository; private final CalendarRepository calendarRepository;
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
@Autowired @Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -188,7 +192,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanReadPlatformService loanReadPlatformService, final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanReadPlatformService loanReadPlatformService,
final FromJsonHelper fromApiJsonHelper, final AccountTransferRepository accountTransferRepository, final FromJsonHelper fromApiJsonHelper, final AccountTransferRepository accountTransferRepository,
final CalendarRepository calendarRepository, final CalendarRepository calendarRepository,
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) { final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository,
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService) {
this.context = context; this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler; this.loanAssembler = loanAssembler;
@@ -217,6 +222,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransferRepository = accountTransferRepository; this.accountTransferRepository = accountTransferRepository;
this.calendarRepository = calendarRepository; this.calendarRepository = calendarRepository;
this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository; this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
} }
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -316,7 +322,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
} }
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); 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); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
} }
final Set<LoanCharge> loanCharges = loan.charges(); final Set<LoanCharge> loanCharges = loan.charges();
@@ -1130,9 +1142,31 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<>(); final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>(); final List<Long> existingReversedTransactionIds = new ArrayList<>();
this.loanChargeRepository.save(loanCharge); this.loanChargeRepository.save(loanCharge);
CalendarInstance restCalendarInstance = null;
ApplicationCurrency applicationCurrency = null;
LocalDate calculatedRepaymentsStartingFromDate = null;
List<Holiday> holidays = null;
boolean isHolidayEnabled = false;
WorkingDays workingDays = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue());
final MonetaryCurrency currency = loan.getCurrency();
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, 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 * 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()); 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 // we post Journal entries only for loans in active status
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
} }
@@ -1246,9 +1283,31 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<>(); final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>(); final List<Long> existingReversedTransactionIds = new ArrayList<>();
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, 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); this.loanTransactionRepository.save(waiveTransaction);
saveLoanWithDataIntegrityViolationChecks(loan); saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2103,7 +2162,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
} }
} }
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays,
workingDays);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
this.loanAccountDomainService.recalculateAccruals(loan); this.loanAccountDomainService.recalculateAccruals(loan);
return new CommandProcessingResultBuilder() // 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;
}
} }