From 0664aca90146239e883c040ba464dc45b3e3f024 Mon Sep 17 00:00:00 2001 From: Emmanuel Nnaa Date: Thu, 6 Nov 2014 16:05:13 +0100 Subject: [PATCH] commit for MIFOSX-1639 (Auto create standing instruction at loan disbursement) --- api-docs/apiLive.htm | 2 +- .../loanaccount/data/LoanAccountData.java | 40 +++++++----- .../portfolio/loanaccount/domain/Loan.java | 32 +++++++--- ...anApplicationCommandFromApiJsonHelper.java | 13 +++- .../loanaccount/service/LoanAssembler.java | 11 ++-- .../service/LoanReadPlatformServiceImpl.java | 6 +- ...WritePlatformServiceJpaRepositoryImpl.java | 61 ++++++++++++++++++- .../savings/domain/SavingsAccount.java | 4 ++ ...er_table_add_create_SI_at_disbursement.sql | 1 + 9 files changed, 139 insertions(+), 31 deletions(-) create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V204_1__alter_table_add_create_SI_at_disbursement.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index a08ca9155..2d1140860 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -7961,7 +7961,7 @@ Request Body: graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, linkAccountId, - fixedEmiAmount, maxOutstandingLoanBalance, disbursementData, graceOnArrearsAgeing + fixedEmiAmount, maxOutstandingLoanBalance, disbursementData, graceOnArrearsAgeing, createStandingInstructionAtDisbursement (requires linkedAccountId if set to true) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java index 43c8ada8c..a7643e621 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanAccountData.java @@ -169,6 +169,7 @@ public class LoanAccountData { private final boolean isInterestRecalculationEnabled; private final LoanInterestRecalculationData interestRecalculationData; + private final Boolean createStandingInstructionAtDisbursement; /** * Used to produce a {@link LoanAccountData} with only collateral options @@ -262,6 +263,7 @@ public class LoanAccountData { final boolean isInterestRecalculationEnabled = false; final LoanInterestRecalculationData interestRecalculationData = null; final LoanScheduleData originalSchedule = null; + final Boolean createStandingInstructionAtDisbursement = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -277,7 +279,7 @@ public class LoanAccountData { syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, daysInYearType, - isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement); } /** @@ -372,6 +374,7 @@ public class LoanAccountData { final boolean isInterestRecalculationEnabled = false; final LoanInterestRecalculationData interestRecalculationData = null; final LoanScheduleData originalSchedule = null; + final Boolean createStandingInstructionAtDisbursement = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -386,7 +389,8 @@ public class LoanAccountData { loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, - isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, + createStandingInstructionAtDisbursement); } public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) { @@ -409,7 +413,7 @@ public class LoanAccountData { acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } /** @@ -505,6 +509,7 @@ public class LoanAccountData { final boolean isInterestRecalculationEnabled = false; final LoanInterestRecalculationData interestRecalculationData = null; final LoanScheduleData originalSchedule = null; + final Boolean createStandingInstructionAtDisbursement = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -519,7 +524,8 @@ public class LoanAccountData { loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmount, maxOutstandingBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, - daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, + createStandingInstructionAtDisbursement); } public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) { @@ -541,7 +547,7 @@ public class LoanAccountData { acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, @@ -650,6 +656,7 @@ public class LoanAccountData { final Boolean inArrears = null; final Boolean isNPA = null; final LoanScheduleData originalSchedule = null; + final Boolean createStandingInstructionAtDisbursement = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId, @@ -668,7 +675,7 @@ public class LoanAccountData { product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(), - originalSchedule); + originalSchedule, createStandingInstructionAtDisbursement); } public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) { @@ -722,7 +729,7 @@ public class LoanAccountData { product.getOutstandingLoanBalance(), acc.emiAmountVariations, acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA, product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(), - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } /* @@ -746,7 +753,7 @@ public class LoanAccountData { final Boolean multiDisburseLoan, final BigDecimal fixedEmiAmont, final BigDecimal outstandingLoanBalance, final Boolean inArrears, final Integer graceOnArrearsAgeing, final Boolean isNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, - final LoanInterestRecalculationData interestRecalculationData) { + final LoanInterestRecalculationData interestRecalculationData, final Boolean createStandingInstructionAtDisbursement) { final LoanScheduleData repaymentSchedule = null; final Collection transactions = null; @@ -792,7 +799,7 @@ public class LoanAccountData { loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter, notes, accountLinkingOptions, linkedAccount, disbursementData, multiDisburseLoan, fixedEmiAmont, outstandingLoanBalance, emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType, - daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule); + daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement); } /* @@ -830,7 +837,8 @@ public class LoanAccountData { accountLinkingOptions, linkedAccount, disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, - acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule); + acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule, + acc.createStandingInstructionAtDisbursement); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -862,7 +870,7 @@ public class LoanAccountData { acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) { @@ -920,7 +928,7 @@ public class LoanAccountData { acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, acc.interestRecalculationData, - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData) { @@ -944,7 +952,7 @@ public class LoanAccountData { acc.linkedAccount, acc.disbursementDetails, acc.multiDisburseLoan, acc.fixedEmiAmount, acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears, acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType, acc.isInterestRecalculationEnabled, interestRecalculationData, - acc.originalSchedule); + acc.originalSchedule, acc.createStandingInstructionAtDisbursement); } public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { @@ -966,7 +974,7 @@ public class LoanAccountData { 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); + originalSchedule, acc.createStandingInstructionAtDisbursement); } private LoanAccountData( @@ -1022,7 +1030,8 @@ public class LoanAccountData { final LoanProductData product, final Boolean inArrears, final Integer graceOnArrearsAgeing, final Collection overdueCharges, final Boolean isNPA, final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled, - final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule) { + final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule, + final Boolean createStandingInstructionAtDisbursement) { this.id = id; this.accountNo = accountNo; this.status = status; @@ -1167,6 +1176,7 @@ public class LoanAccountData { this.isInterestRecalculationEnabled = isInterestRecalculationEnabled; this.interestRecalculationData = interestRecalculationData; this.originalSchedule = originalSchedule; + this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement; } public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index ef00c3a4f..b3461b11b 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -329,19 +329,22 @@ public class Loan extends AbstractPersistable { @Temporal(TemporalType.DATE) @Column(name = "accrued_till") private Date accruedTill; + + @Column(name = "create_standing_instruction_at_disbursement", nullable = true) + private Boolean createStandingInstructionAtDisbursement; public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType, final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose, final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges, final Set collateral, final BigDecimal fixedEmiAmount, final Set disbursementDetails, - final BigDecimal maxOutstandingLoanBalance) { + final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement) { final LoanStatus status = null; final Group group = null; final Boolean syncDisbursementWithMeeting = null; return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, - disbursementDetails, maxOutstandingLoanBalance); + disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement); } public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType, @@ -349,12 +352,13 @@ public class Loan extends AbstractPersistable { final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges, final Set collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, - final Set disbursementDetails, final BigDecimal maxOutstandingLoanBalance) { + final Set disbursementDetails, final BigDecimal maxOutstandingLoanBalance, + final Boolean createStandingInstructionAtDisbursement) { final LoanStatus status = null; final Client client = null; return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, - disbursementDetails, maxOutstandingLoanBalance); + disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement); } public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group, @@ -362,11 +366,12 @@ public class Loan extends AbstractPersistable { final LoanTransactionProcessingStrategy transactionProcessingStrategy, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set loanCharges, final Set collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, - final Set disbursementDetails, final BigDecimal maxOutstandingLoanBalance) { + final Set disbursementDetails, final BigDecimal maxOutstandingLoanBalance, + final Boolean createStandingInstructionAtDisbursement) { final LoanStatus status = null; return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct, loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount, - disbursementDetails, maxOutstandingLoanBalance); + disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement); } protected Loan() { @@ -378,7 +383,7 @@ public class Loan extends AbstractPersistable { final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus, final Set loanCharges, final Set collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount, final Set disbursementDetails, - final BigDecimal maxOutstandingLoanBalance) { + final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement) { this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail; this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings(); @@ -422,6 +427,7 @@ public class Loan extends AbstractPersistable { this.maxOutstandingLoanBalance = maxOutstandingLoanBalance; this.disbursementDetails = disbursementDetails; this.approvedPrincipal = this.loanRepaymentScheduleDetail.getPrincipal().getAmount(); + this.createStandingInstructionAtDisbursement = createStandingInstructionAtDisbursement; } private LoanSummary updateSummaryWithTotalFeeChargesDueAtDisbursement(final BigDecimal feeChargesDueAtDisbursement) { @@ -4726,4 +4732,16 @@ public class Loan extends AbstractPersistable { } return isEnabled; } + + public String getAccountNumber() { + return this.accountNumber; + } + + public Client getClient() { + return this.client; + } + + public Boolean createStandingInstructionAtDisbursement() { + return (this.createStandingInstructionAtDisbursement != null) && this.createStandingInstructionAtDisbursement; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java index 2f2a5aa9c..eb5a8f74b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java @@ -66,7 +66,7 @@ public final class LoanApplicationCommandFromApiJsonHelper { "syncDisbursementWithMeeting",// optional "linkAccountId", LoanApiConstants.disbursementDataParameterName, LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName, - LoanProductConstants.recalculationRestFrequencyDateParamName)); + LoanProductConstants.recalculationRestFrequencyDateParamName, "createStandingInstructionAtDisbursement")); private final FromJsonHelper fromApiJsonHelper; private final CalculateLoanScheduleQueryFromApiJsonHelper apiJsonHelper; @@ -289,6 +289,17 @@ public final class LoanApplicationCommandFromApiJsonHelper { final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element); baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull().longGreaterThanZero(); } + + final String createSiAtDisbursementParameterName = "createStandingInstructionAtDisbursement"; + if(this.fromApiJsonHelper.parameterExists(createSiAtDisbursementParameterName, element)) { + final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper + .extractBooleanNamed(createSiAtDisbursementParameterName, element); + final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element); + + if(createStandingInstructionAtDisbursement) { + baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).notNull().longGreaterThanZero(); + } + } // charges final String chargesParameterName = "charges"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java index 1d89bbdd2..05b1b3652 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAssembler.java @@ -159,7 +159,8 @@ public class LoanAssembler { final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element); final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element); final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element); - + final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper.extractBooleanNamed("createStandingInstructionAtDisbursement", element); + final LoanProduct loanProduct = this.loanProductRepository.findOne(productId); if (loanProduct == null) { throw new LoanProductNotFoundException(productId); } @@ -233,19 +234,21 @@ public class LoanAssembler { loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, - collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance); + collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, + createStandingInstructionAtDisbursement); } else if (group != null) { loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral, - syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance); + syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, + createStandingInstructionAtDisbursement); } else if (client != null) { loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral, - fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance); + fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 8b31a87a0..eba053b35 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -550,7 +550,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " l.interest_recalculation_enabled as isInterestRecalculationEnabled, " + " lir.id as lirId, lir.loan_id as loanId, lir.compound_type_enum as compoundType, lir.reschedule_strategy_enum as rescheduleStrategy, " + " lir.rest_frequency_type_enum as restFrequencyEnum, lir.rest_frequency_interval as restFrequencyInterval, " - + " lir.rest_freqency_date as restFrequencyDate " + + " lir.rest_freqency_date as restFrequencyDate, " + + " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement " + " from m_loan l" // + " join m_product_loan lp on lp.id = l.product_id" // + " left join m_loan_recalculation_details lir on lir.loan_id = l.id " @@ -780,6 +781,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final int daysInYear = JdbcSupport.getInteger(rs, "daysInYear"); final EnumOptionData daysInYearType = CommonEnumerations.daysInYearType(daysInYear); final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled"); + final Boolean createStandingInstructionAtDisbursement = rs.getBoolean("createStandingInstructionAtDisbursement"); LoanInterestRecalculationData interestRecalculationData = null; if (isInterestRecalculationEnabled) { @@ -810,7 +812,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter, multiDisburseLoan, fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType, - isInterestRecalculationEnabled, interestRecalculationData); + isInterestRecalculationEnabled, interestRecalculationData, createStandingInstructionAtDisbursement); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index eeb1b395a..b8cc7c0d1 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -49,9 +49,18 @@ import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWr import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferDTO; import org.mifosplatform.portfolio.account.data.PortfolioAccountData; +import org.mifosplatform.portfolio.account.domain.AccountAssociations; +import org.mifosplatform.portfolio.account.domain.AccountAssociationsRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetailRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; +import org.mifosplatform.portfolio.account.domain.AccountTransferRecurrenceType; import org.mifosplatform.portfolio.account.domain.AccountTransferRepository; +import org.mifosplatform.portfolio.account.domain.AccountTransferStandingInstruction; import org.mifosplatform.portfolio.account.domain.AccountTransferTransaction; import org.mifosplatform.portfolio.account.domain.AccountTransferType; +import org.mifosplatform.portfolio.account.domain.StandingInstructionPriority; +import org.mifosplatform.portfolio.account.domain.StandingInstructionStatus; +import org.mifosplatform.portfolio.account.domain.StandingInstructionType; import org.mifosplatform.portfolio.account.service.AccountAssociationsReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; @@ -181,6 +190,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService; private final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper; + private final AccountAssociationsRepository accountAssociationRepository; + private final AccountTransferDetailRepository accountTransferDetailRepository; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -203,7 +214,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final CalendarRepository calendarRepository, final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository, final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService, - final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper) { + final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper, + final AccountAssociationsRepository accountAssociationRepository, + final AccountTransferDetailRepository accountTransferDetailRepository) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -234,6 +247,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository; this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService; this.loanApplicationCommandFromApiJsonHelper = loanApplicationCommandFromApiJsonHelper; + this.accountAssociationRepository = accountAssociationRepository; + this.accountTransferDetailRepository = accountTransferDetailRepository; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -344,6 +359,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); } } + + // auto create standing instruction + createStandingInstruction(loan); postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); @@ -383,6 +401,47 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .with(changes) // .build(); } + + /** + * create standing instruction for disbursed loan + * + * @param loan the disbursed loan + * @return void + **/ + private void createStandingInstruction(Loan loan) { + + if(loan.createStandingInstructionAtDisbursement()) { + AccountAssociations accountAssociations = this.accountAssociationRepository.findByLoanId(loan.getId()); + + if(accountAssociations != null) { + + SavingsAccount linkedSavingsAccount = accountAssociations.linkedSavingsAccount(); + + // name is auto-generated + final String name = "To loan " + loan.getAccountNumber() + " from savings " + linkedSavingsAccount.getAccountNumber(); + final Office fromOffice = loan.getOffice(); + final Client fromClient = loan.getClient(); + final Office toOffice = loan.getOffice(); + final Client toClient = loan.getClient(); + final Integer priority = StandingInstructionPriority.MEDIUM.getValue(); + final Integer transferType = AccountTransferType.LOAN_REPAYMENT.getValue(); + final Integer instructionType = StandingInstructionType.DUES.getValue(); + final Integer status = StandingInstructionStatus.ACTIVE.getValue(); + final Integer recurrenceType = AccountTransferRecurrenceType.AS_PER_DUES.getValue(); + final LocalDate validFrom = new LocalDate(); + + AccountTransferDetails accountTransferDetails = AccountTransferDetails.savingsToLoanTransfer(fromOffice, + fromClient, linkedSavingsAccount, toOffice, toClient, loan, transferType); + + AccountTransferStandingInstruction accountTransferStandingInstruction = AccountTransferStandingInstruction.create( + accountTransferDetails, name, priority, instructionType, status, null, validFrom, null, + recurrenceType, null, null, null); + accountTransferDetails.updateAccountTransferStandingInstruction(accountTransferStandingInstruction); + + this.accountTransferDetailRepository.save(accountTransferDetails); + } + } + } private void updateRecurringCalendarDatesForInterestRecalculation(final Loan loan) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 0676eebf0..9ae26d358 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -2430,4 +2430,8 @@ public class SavingsAccount extends AbstractPersistable { } return savingsAccountCharge; } + + public String getAccountNumber() { + return this.accountNumber; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V204_1__alter_table_add_create_SI_at_disbursement.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V204_1__alter_table_add_create_SI_at_disbursement.sql new file mode 100644 index 000000000..c33a946ce --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V204_1__alter_table_add_create_SI_at_disbursement.sql @@ -0,0 +1 @@ +alter table `m_loan` add `create_standing_instruction_at_disbursement` tinyint(1) null;