From 9010ae6a80bf2a7ca09c397db985f30f89f930d0 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Wed, 3 Jul 2013 18:15:37 +0530 Subject: [PATCH] Add Loan counter for Group and JLG Loan --- api-docs/apiLive.htm | 101 +++++++++- ...__alter_m_loan_counter_table_add_group.sql | 15 ++ .../ClientReadPlatformServiceImpl.java | 5 +- .../loanaccount/api/LoansApiResource.java | 3 +- .../loanaccount/data/LoanAccountData.java | 44 +++-- .../portfolio/loanaccount/domain/Loan.java | 31 ++- .../loanaccount/domain/LoanCycle.java | 60 ------ .../domain/LoanCycleRepository.java | 15 -- .../loanaccount/domain/LoanRepository.java | 86 ++++++-- .../service/LoanReadPlatformServiceImpl.java | 8 +- ...WritePlatformServiceJpaRepositoryImpl.java | 186 +++++++++++++----- .../loanproduct/domain/LoanProduct.java | 4 + 12 files changed, 406 insertions(+), 152 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V74__alter_m_loan_counter_table_add_group.sql delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycle.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycleRepository.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index db216eae6..44487fe14 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -191,6 +191,22 @@ + + + clients/{clientId}?command=assignStaff + Assign Staff + + + + + + + clients/{clientId}?command=unassignStaff + Unassign Staff + + + + clients/{clientId}/loans @@ -1818,6 +1834,12 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti business that doesnt suit the firstname,middlename,lastname structure. + + staffId + + + The staffId of the staff member dealing with the client office. The staff member is not specifically the loan officer. + @@ -1906,7 +1928,7 @@ GET https://DomainName/api/v1/clients/template
Optional Fields
- groupId, externalId, accountNo + groupId, externalId, accountNo, staffId @@ -2013,6 +2035,59 @@ Request Body: { "clientId":15, "resourceId":15 +} + + + + +   +
+
+

Assign a Staff

+

Allows you to assign Staff for existed Client.

+

The selected Staff member should be belongs to the client's office.

+
+
+ POST https://Domain Name/api/v1/clients/{clientId}?command=assignStaff + POST clients/1?command=assignStaff +Content-Type: application/json +Request Body: +{ + "staffId": "1" +} + + +{ + "officeId": 1, + "clientId": 1, + "resourceId": 1, + "changes": {"staffId":1} +} + +
+
+ +   +
+
+

Unassign a Staff

+

Allows you to unassign the Staff.

+
+
+ POST https://Domain Name/api/v1/clients/{clientId}?command=unassignStaff + POST clients/1?command=unassignStaff +Content-Type: application/json +Request Body: +{ + "staffId":"1" +} + + +{ + "officeId": 1, + "clientId": 1, + "resourceId": 1, + "changes": {"staffId":1} }
@@ -7084,6 +7159,12 @@ Request Body: For associating a loan product with a given fund by default. + + includeInBorrowerCycle + + + It is a flag, Used to denote whether the loans should include in loan cycle counter or not. + currencyCode @@ -7786,7 +7867,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Optional Fields
- inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings + inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle @@ -15832,6 +15913,22 @@ GET https://DomainName/api/v1/search?query=000000111 + + + clients/{clientId}?command=assignStaff + Assign Staff + + + + + + + clients/{clientId}?command=unassignStaff + Unassign Staff + + + + clients/{clientId}/loans diff --git a/mifosng-db/migrations/core_db/V74__alter_m_loan_counter_table_add_group.sql b/mifosng-db/migrations/core_db/V74__alter_m_loan_counter_table_add_group.sql new file mode 100644 index 000000000..c9c5263b3 --- /dev/null +++ b/mifosng-db/migrations/core_db/V74__alter_m_loan_counter_table_add_group.sql @@ -0,0 +1,15 @@ +ALTER TABLE `m_client_loan_counter` + ADD COLUMN `group_id` BIGINT(20) NOT NULL AFTER `client_id`, + ADD CONSTRAINT `FK_m_group_loan_counter` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`); + +RENAME TABLE `m_client_loan_counter` TO `m_loan_counter`; + +ALTER TABLE `m_loan_counter` + CHANGE COLUMN `client_id` `client_id` BIGINT(20) NULL DEFAULT NULL AFTER `id`, + CHANGE COLUMN `group_id` `group_id` BIGINT(20) NULL DEFAULT NULL AFTER `client_id`; + +ALTER TABLE `m_loan` + ADD COLUMN `loan_counter` SMALLINT NULL DEFAULT NULL AFTER `sync_disbursement_with_meeting`, + ADD COLUMN `loan_product_counter` SMALLINT NULL DEFAULT NULL AFTER `loan_counter`; + +DROP TABLE `m_loan_counter`; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index 6104022ec..544593133 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -521,9 +521,8 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService StringBuilder accountsSummary = new StringBuilder("l.id as id, l.account_no as accountNo, l.external_id as externalId,"); accountsSummary.append("l.product_id as productId, lp.name as productName,") .append("l.loan_status_id as statusId, l.loan_type_enum as loanType, ") - .append("lc.running_count as loanCycle ").append(" from m_loan l ") - .append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id ") - .append("LEFT JOIN m_client_loan_counter lc on l.id = lc.loan_id "); + .append("l.loan_product_counter as loanCycle ").append(" from m_loan l ") + .append("LEFT JOIN m_product_loan AS lp ON lp.id = l.product_id"); return accountsSummary.toString(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index 43d7abb10..87dcab42f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -96,7 +96,8 @@ public class LoansApiResource { "summary", "repaymentSchedule", "transactions", "charges", "collateral", "guarantors", "meeting", "productOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions", "repaymentFrequencyTypeOptions", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", - "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting")); + "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting", + "loanCounter", "loanProductCounter")); private final String resourceNameForPermissions = "LOAN"; 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 7cb6ab09f..d8f4294fa 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 @@ -122,6 +122,10 @@ public class LoanAccountData { @Transient private final BigDecimal feeChargesAtDisbursementCharged; private final BigDecimal totalOverpaid; + + //loanCycle + private final Integer loanCounter; + private final Integer loanProductCounter; /** * Used to produce a {@link LoanAccountData} with only collateral options @@ -192,6 +196,8 @@ public class LoanAccountData { final Collection loanPurposeOptions = null; final Collection calendarOptions = null; final Boolean syncDisbursementWithMeeting = null; + final Integer loancounter = null; + final Integer loanProductCounter = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -203,7 +209,7 @@ public class LoanAccountData { productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - syncDisbursementWithMeeting); + syncDisbursementWithMeeting, loancounter, loanProductCounter); } /** @@ -275,6 +281,8 @@ public class LoanAccountData { final Collection loanCollateralOptions = null; final Collection calendarOptions = null; final Boolean syncDisbursementWithMeeting = null; + final Integer loancounter = null; + final Integer loanProductCounter = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -285,7 +293,8 @@ public class LoanAccountData { feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, - fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting); + fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, + syncDisbursementWithMeeting, loancounter, loanProductCounter); } public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) { @@ -302,7 +311,8 @@ public class LoanAccountData { 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.loanOfficerOptions, acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, + acc.loanCounter, acc.loanProductCounter); } /** @@ -375,6 +385,8 @@ public class LoanAccountData { final Collection loanCollateralOptions = null; final Collection calendarOptions = null; final Boolean syncDisbursementWithMeeting = null; + final Integer loancounter = null; + final Integer loanProductCounter = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName, @@ -386,7 +398,7 @@ public class LoanAccountData { productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - syncDisbursementWithMeeting); + syncDisbursementWithMeeting, loancounter, loanProductCounter); } public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) { @@ -404,7 +416,7 @@ public class LoanAccountData { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter); } public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, @@ -469,6 +481,9 @@ public class LoanAccountData { for (ChargeData charge : product.charges()) { charges.add(charge.toLoanChargeData()); } + + final Integer loancounter = null; + final Integer loanProductCounter = null; return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId, @@ -482,7 +497,7 @@ public class LoanAccountData { repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, - loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting); + loanPurposeOptions, loanCollateralOptions, calendarOptions, syncDisbursementWithMeeting, loancounter, loanProductCounter); } public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) { @@ -528,7 +543,8 @@ public class LoanAccountData { acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, - fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, acc.syncDisbursementWithMeeting); + fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, + acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter); } /* @@ -547,7 +563,8 @@ public class LoanAccountData { final EnumOptionData interestCalculationPeriodType, final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline, final LoanSummaryData loanSummary, - final BigDecimal feeChargesDueAtDisbursementCharged, final Boolean syncDisbursementWithMeeting) { + final BigDecimal feeChargesDueAtDisbursementCharged, final Boolean syncDisbursementWithMeeting, + final Integer loancounter, final Integer loanProductCounter) { final LoanScheduleData repaymentSchedule = null; final Collection transactions = null; @@ -581,7 +598,7 @@ public class LoanAccountData { productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - syncDisbursementWithMeeting); + syncDisbursementWithMeeting, loancounter, loanProductCounter); } /* @@ -611,7 +628,7 @@ public class LoanAccountData { productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, transactionProcessingStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, loanOfficerOptions, loanPurposeOptions, loanCollateralOptions, calendarOptions, - acc.syncDisbursementWithMeeting); + acc.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -637,7 +654,7 @@ public class LoanAccountData { 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.syncDisbursementWithMeeting, acc.loanCounter, acc.loanProductCounter); } private LoanAccountData( @@ -684,7 +701,7 @@ public class LoanAccountData { final Collection fundOptions, final Collection chargeOptions, final ChargeData chargeTemplate, final Collection loanOfficerOptions, final Collection loanPurposeOptions, final Collection loanCollateralOptions, final Collection calendarOptions, - final Boolean syncDisbursementWithMeeting) { + final Boolean syncDisbursementWithMeeting, final Integer loanCounter, final Integer loanProductCounter) { this.id = id; this.accountNo = accountNo; this.status = status; @@ -789,6 +806,9 @@ public class LoanAccountData { } else { this.calendarOptions = calendarOptions; } + + this.loanCounter = loanCounter; + this.loanProductCounter = loanProductCounter; } 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 0c0fe41cb..84ee403a5 100644 --- 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 @@ -100,7 +100,6 @@ public class Loan extends AbstractPersistable { @JoinColumn(name = "group_id", nullable = true) private Group group; - @SuppressWarnings("unused") @Column(name = "loan_type_enum", nullable = false) private Integer loanType; @@ -230,6 +229,12 @@ public class Loan extends AbstractPersistable { @SuppressWarnings("unused") @Column(name = "total_overpaid_derived", scale = 6, precision = 19) private BigDecimal totalOverpaid; + + @Column(name = "loan_counter") + private Integer loanCounter; + + @Column(name = "loan_product_counter") + private Integer loanProductCounter; @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(cascade = CascadeType.ALL, mappedBy = "loan", orphanRemoval = true) @@ -2349,4 +2354,28 @@ public class Loan extends AbstractPersistable { } } + public Group group() { + return this.group; + } + + public Integer getCurrentLoanCounter() { + return this.loanCounter; + } + + public Integer getLoanProductLoanCounter() { + return this.loanProductCounter; + } + + public void updateClientLoanCounter(Integer newLoanCounter) { + this.loanCounter = newLoanCounter; + } + + public void updateLoanProductLoanCounter(Integer newLoanProductLoanCounter) { + this.loanProductCounter = newLoanProductLoanCounter; + } + + public boolean isGroupLoan() { + return LoanType.fromInt(this.loanType).isGroupLoan(); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycle.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycle.java deleted file mode 100644 index 6f22867a3..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycle.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.mifosplatform.portfolio.loanaccount.domain; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -import org.mifosplatform.portfolio.client.domain.Client; -import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; -import org.springframework.data.jpa.domain.AbstractPersistable; - -@Entity -@Table(name = "m_client_loan_counter") -public class LoanCycle extends AbstractPersistable { - - @ManyToOne - @JoinColumn(name = "client_id", nullable = false) - private Client client; - - @ManyToOne - @JoinColumn(name = "loan_product_id", nullable = false) - private LoanProduct loanProduct; - - @ManyToOne - @JoinColumn(name = "loan_id", nullable = false) - private Loan loan; - - @Column(name = "running_count", nullable = false) - private Integer runningCount; - - public LoanCycle() { - - } - - private LoanCycle(final Client client, final LoanProduct loanProduct, final Loan loan, final Integer runningCounter) { - this.client = client; - this.loanProduct = loanProduct; - this.loan = loan; - this.runningCount = runningCounter; - } - - public static LoanCycle create(final Client client, final LoanProduct loanProduct, final Loan loan, final Integer runningCounter) { - return new LoanCycle(client, loanProduct, loan, runningCounter); - } - - public Integer getRunningCounter() { - return this.runningCount; - } - - public Loan loan() { - return this.loan; - } - - public LoanCycle updateLoanCycleCounter(final Integer runningCount) { - this.runningCount = runningCount; - return this; - } - -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycleRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycleRepository.java deleted file mode 100644 index bcfcd9f48..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCycleRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.mifosplatform.portfolio.loanaccount.domain; - -import java.util.List; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -public interface LoanCycleRepository extends JpaRepository, JpaSpecificationExecutor { - - @Query("from LoanCycle lc where lc.client.id=:clientId and lc.loanProduct.id=:loanProductId order by lc.runningCount") - List findByClientIdAndLoanProductId(@Param("clientId") Long clientId, @Param("loanProductId") Long loanProductId); - -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java index 48987933d..cf0d38fc6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java @@ -14,17 +14,79 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -public interface LoanRepository extends JpaRepository, - JpaSpecificationExecutor { - @Query("from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId") - List findByClientIdAndGroupId(@Param("clientId") Long clientId, @Param("groupId") Long groupId); - - @Query("from Loan loan where loan.client.id = :clientId") - List findLoanByClientId(@Param("clientId") Long clientId); +public interface LoanRepository extends JpaRepository, JpaSpecificationExecutor { - @Query("from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes") - List findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection ids, @Param("loanStatuses") Collection loanStatuses, @Param("loanTypes") Collection loanTypes); - - @Query("select loan.id from Loan loan where loan.actualDisbursementDate > :disbursalDate order by loan.actualDisbursementDate") - List getLoansDisbursedAfter(@Param("disbursalDate") Date disbursalDate); + public static final String FIND_GROUP_LOANS_DISBURSED_AFTER = "from Loan l where l.actualDisbursementDate > :disbursementDate and " + + "l.group.id = :groupId and l.loanType = :loanType order by l.actualDisbursementDate"; + + public static final String FIND_CLIENT_OR_JLG_LOANS_DISBURSED_AFTER = "from Loan l where l.actualDisbursementDate > :disbursementDate and " + + "l.client.id = :clientId order by l.actualDisbursementDate"; + + public static final String FIND_MAX_GROUP_LOAN_COUNTER_QUERY = "Select MAX(l.loanCounter) from Loan l where l.group.id = :groupId " + + "and l.loanType = :loanType"; + + public static final String FIND_MAX_GROUP_LOAN_PRODUCT_COUNTER_QUERY = "Select MAX(l.loanProductCounter) from Loan l where " + + "l.group.id = :groupId and l.loanType = :loanType and l.loanProduct.id = :productId"; + + public static final String FIND_MAX_CLIENT_OR_JLG_LOAN_COUNTER_QUERY = "Select MAX(l.loanCounter) from Loan l where " + + "l.client.id = :clientId"; + + public static final String FIND_MAX_CLIENT_OR_JLG_LOAN_PRODUCT_COUNTER_QUERY = "Select MAX(l.loanProductCounter) from Loan l where " + + "l.client.id = :clientId and l.loanProduct.id = :productId"; + + public static final String FIND_GROUP_LOANS_TO_UPDATE = "from Loan l where l.loanCounter > :loanCounter and " + + "l.group.id = :groupId and l.loanType = :groupLoanType order by l.loanCounter"; + + public static final String FIND_CLIENT_OR_JLG_LOANS_TO_UPDATE = "from Loan l where l.loanCounter > :loanCounter and " + + "l.client.id = :clientId order by l.loanCounter"; + + public static final String FIND_GROUP_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER = "from Loan l where l.loanProductCounter > :loanProductCounter" + + " and l.group.id = :groupId and l.loanType = :groupLoanType and l.loanCounter is NULL order by l.loanProductCounter"; + + public static final String FIND_CLIENT_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER = "from Loan l where l.loanProductCounter > :loanProductCounter" + + " and l.client.id = :clientId and l.loanCounter is NULL order by l.loanProductCounter"; + + @Query("from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId") + List findByClientIdAndGroupId(@Param("clientId") Long clientId, @Param("groupId") Long groupId); + + @Query("from Loan loan where loan.client.id = :clientId") + List findLoanByClientId(@Param("clientId") Long clientId); + + @Query("from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes") + List findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection ids, + @Param("loanStatuses") Collection loanStatuses, @Param("loanTypes") Collection loanTypes); + + @Query(FIND_GROUP_LOANS_DISBURSED_AFTER) + List getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId, + @Param("loanType") Integer loanType); + + @Query(FIND_CLIENT_OR_JLG_LOANS_DISBURSED_AFTER) + List getClientOrJLGLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("clientId") Long clientId); + + @Query(FIND_MAX_GROUP_LOAN_COUNTER_QUERY) + Integer getMaxGroupLoanCounter(@Param("groupId") Long groupId, @Param("loanType") Integer loanType); + + @Query(FIND_MAX_GROUP_LOAN_PRODUCT_COUNTER_QUERY) + Integer getMaxGroupLoanProductCounter(@Param("productId") Long productId, @Param("groupId") Long groupId, + @Param("loanType") Integer loanType); + + @Query(FIND_MAX_CLIENT_OR_JLG_LOAN_COUNTER_QUERY) + Integer getMaxClientOrJLGLoanCounter(@Param("clientId") Long clientId); + + @Query(FIND_MAX_CLIENT_OR_JLG_LOAN_PRODUCT_COUNTER_QUERY) + Integer getMaxClientOrJLGLoanProductCounter(@Param("productId") Long productId, @Param("clientId") Long clientId); + + @Query(FIND_GROUP_LOANS_TO_UPDATE) + List getGroupLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("groupId") Long groupId, + @Param("groupLoanType") Integer groupLoanType); + + @Query(FIND_CLIENT_OR_JLG_LOANS_TO_UPDATE) + List getClientOrJLGLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("clientId") Long clientId); + + @Query(FIND_GROUP_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER) + List getGroupLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter, @Param("groupId") Long groupId, + @Param("groupLoanType") Integer groupLoanType); + + @Query(FIND_CLIENT_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER) + List getClientLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter, @Param("clientId") Long clientId); } \ No newline at end of file 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 142d642a6..e867ddfcd 100644 --- 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 @@ -443,7 +443,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " la.penalty_charges_overdue_derived as penaltyChargesOverdue," + " la.total_overdue_derived as totalOverdue," + " la.overdue_since_date_derived as overdueSinceDate," - + " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting" + + " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting," + + " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter" + " from m_loan l" // + " join m_product_loan lp on lp.id = l.product_id" // + " join m_currency rc on rc.`code` = l.currency_code" // @@ -648,6 +649,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { groupData = GroupGeneralData.instance(groupId, groupName, groupExternalId, groupStatus, activationDate, groupOfficeId, null, groupParentId, null, groupStaffId, null, groupHierarchy); } + + final Integer loanCounter = JdbcSupport.getInteger(rs, "loanCounter"); + final Integer loanProductCounter = JdbcSupport.getInteger(rs, "loanProductCounter"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, @@ -656,7 +660,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged, - syncDisbursementWithMeeting); + syncDisbursementWithMeeting, loanCounter, loanProductCounter); } } 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 8b66e0d24..f75dbdd19 100644 --- 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 @@ -39,7 +39,6 @@ import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeUpdatedExc import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException; import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeWaivedException.LOAN_CHARGE_CANNOT_BE_WAIVED_REASON; import org.mifosplatform.portfolio.charge.exception.LoanChargeNotFoundException; -import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand; import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand; import org.mifosplatform.portfolio.collectionsheet.command.SingleDisbursalCommand; @@ -50,8 +49,6 @@ import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateM import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge; import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository; -import org.mifosplatform.portfolio.loanaccount.domain.LoanCycle; -import org.mifosplatform.portfolio.loanaccount.domain.LoanCycleRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; @@ -67,7 +64,6 @@ import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFound import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; import org.mifosplatform.portfolio.loanaccount.serialization.LoanEventApiJsonValidator; import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFromApiJsonDeserializer; -import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct; import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException; import org.mifosplatform.portfolio.note.domain.Note; @@ -98,7 +94,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanScheduleGeneratorFactory loanScheduleFactory; private final CalendarInstanceRepository calendarInstanceRepository; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; - private final LoanCycleRepository loanCycleRepository; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -110,7 +105,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper, final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository, - final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final LoanCycleRepository loanCycleRepository) { + final PaymentDetailWritePlatformService paymentDetailWritePlatformService) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -127,7 +122,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanScheduleFactory = loanScheduleFactory; this.calendarInstanceRepository = calendarInstanceRepository; this.paymentDetailWritePlatformService = paymentDetailWritePlatformService; - this.loanCycleRepository = loanCycleRepository; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -167,31 +161,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); final LocalDate firstRepaymentMeetingDate = getFirstRepaymentMeetingDate(actualDisbursementDate, loan, calendarInstance); - // create loan cycle - final LoanProduct product = loan.loanProduct(); - final Client client = loan.client(); - // get the loan Ids which have disbursement date greater than - // 'actualDisbursementDate'. - final List loansToUpdateLoanCycle = this.loanRepository.getLoansDisbursedAfter(actualDisbursementDate.toDate()); + updateLoanCounters(loan, actualDisbursementDate); - // get all available loan cycles. - final List loanCycles = this.loanCycleRepository.findByClientIdAndLoanProductId(client.getId(), product.getId()); - - Integer loanCounter = loanCycles.size() + 1; - for (final long loanCycleLoanId : loansToUpdateLoanCycle) { - for (final LoanCycle loanCycle : loanCycles) { - if (loanCycle.loan().getId() == loanCycleLoanId) { - int runningCounter = loanCycle.getRunningCounter(); - if (loanCounter > runningCounter) { - loanCounter = runningCounter; - } - loanCycle.updateLoanCycleCounter(++runningCounter); - } - } - } - final LoanCycle loanCycle = LoanCycle.create(client, product, loan, loanCounter); - this.loanCycleRepository.save(loanCycle); - loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, existingTransactionIds, existingReversedTransactionIds, changes, paymentDetail, firstRepaymentMeetingDate); @@ -1042,21 +1013,148 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private void removeLoanCycle(final Loan loan) { - final Client client = loan.client(); - final LoanProduct product = loan.loanProduct(); - final List loanCycles = this.loanCycleRepository.findByClientIdAndLoanProductId(client.getId(), product.getId()); - LoanCycle loanCycleToRemove = null; - for (LoanCycle loanCycle : loanCycles) { - if (loanCycle.loan().getId().equals(loan.getId())) { - loanCycleToRemove = loanCycle; - } else if (loanCycleToRemove != null) { - loanCycle = loanCycle.updateLoanCycleCounter(loanCycle.getRunningCounter() - 1); + final List loansToUpdate; + if (loan.isGroupLoan()) { + if (loan.loanProduct().isIncludeInBorrowerCycle()) { + loansToUpdate = this.loanRepository.getGroupLoansToUpdateLoanCounter(loan.getCurrentLoanCounter(), loan.getGroupId(), + LoanType.GROUP.getValue()); + } else { + loansToUpdate = this.loanRepository.getGroupLoansToUpdateLoanProductCounter(loan.getLoanProductLoanCounter(), + loan.getGroupId(), LoanType.GROUP.getValue()); } + + } else { + if (loan.loanProduct().isIncludeInBorrowerCycle()) { + loansToUpdate = this.loanRepository + .getClientOrJLGLoansToUpdateLoanCounter(loan.getCurrentLoanCounter(), loan.getClientId()); + } else { + loansToUpdate = this.loanRepository.getClientLoansToUpdateLoanProductCounter(loan.getLoanProductLoanCounter(), + loan.getClientId()); + } + } - - if (loanCycleToRemove != null) { - this.loanCycleRepository.save(loanCycles); - this.loanCycleRepository.delete(loanCycleToRemove); + if (loansToUpdate != null) { + updateLoanCycleCounter(loansToUpdate, loan); + } + loan.updateClientLoanCounter(null); + loan.updateLoanProductLoanCounter(null); + + } + + private void updateLoanCounters(final Loan loan, final LocalDate actualDisbursementDate) { + + if (loan.isGroupLoan()) { + final List loansToUpdateForLoanCounter = this.loanRepository.getGroupLoansDisbursedAfter(actualDisbursementDate.toDate(), + loan.getGroupId(), LoanType.GROUP.getValue()); + final Integer newLoanCounter = getNewGroupLoanCounter(loan); + final Integer newLoanProductCounter = getNewGroupLoanProductCounter(loan); + updateLoanCounter(loan, loansToUpdateForLoanCounter, newLoanCounter, newLoanProductCounter); + } else { + final List loansToUpdateForLoanCounter = this.loanRepository.getClientOrJLGLoansDisbursedAfter( + actualDisbursementDate.toDate(), loan.getClientId()); + final Integer newLoanCounter = getNewClientOrJLGLoanCounter(loan); + final Integer newLoanProductCounter = getNewClientOrJLGLoanProductCounter(loan); + updateLoanCounter(loan, loansToUpdateForLoanCounter, newLoanCounter, newLoanProductCounter); } } + + private Integer getNewGroupLoanCounter(final Loan loan) { + + Integer maxClientLoanCounter = this.loanRepository.getMaxGroupLoanCounter(loan.getGroupId(), LoanType.GROUP.getValue()); + if (maxClientLoanCounter == null) { + maxClientLoanCounter = 1; + } else { + maxClientLoanCounter = maxClientLoanCounter + 1; + } + return maxClientLoanCounter; + } + + private Integer getNewGroupLoanProductCounter(final Loan loan) { + + Integer maxLoanProductLoanCounter = this.loanRepository.getMaxGroupLoanProductCounter(loan.loanProduct().getId(), + loan.getGroupId(), LoanType.GROUP.getValue()); + if (maxLoanProductLoanCounter == null) { + maxLoanProductLoanCounter = 1; + } else { + maxLoanProductLoanCounter = maxLoanProductLoanCounter + 1; + } + return maxLoanProductLoanCounter; + } + + private void updateLoanCounter(final Loan loan, final List loansToUpdateForLoanCounter, Integer newLoanCounter, + Integer newLoanProductCounter) { + + final boolean includeInBorrowerCycle = loan.loanProduct().isIncludeInBorrowerCycle(); + for (final Loan loanToUpdate : loansToUpdateForLoanCounter) { + //Update client loan counter if loan product includeInBorrowerCycle is true + if (includeInBorrowerCycle) { + Integer currentLoanCounter = loanToUpdate.getCurrentLoanCounter(); + if (newLoanCounter > currentLoanCounter) { + newLoanCounter = currentLoanCounter; + } + loanToUpdate.updateClientLoanCounter(++currentLoanCounter); + } + + if (loanToUpdate.loanProduct().getId().equals(loan.loanProduct().getId())) { + Integer loanProductLoanCounter = loanToUpdate.getLoanProductLoanCounter(); + if (newLoanProductCounter > loanProductLoanCounter) { + newLoanProductCounter = loanProductLoanCounter; + } + loanToUpdate.updateLoanProductLoanCounter(++loanProductLoanCounter); + } + } + + if (includeInBorrowerCycle) { + loan.updateClientLoanCounter(newLoanCounter); + } else { + loan.updateClientLoanCounter(null); + } + loan.updateLoanProductLoanCounter(newLoanProductCounter); + this.loanRepository.save(loansToUpdateForLoanCounter); + } + + private Integer getNewClientOrJLGLoanCounter(final Loan loan) { + + Integer maxClientLoanCounter = this.loanRepository.getMaxClientOrJLGLoanCounter(loan.getClientId()); + if (maxClientLoanCounter == null) { + maxClientLoanCounter = 1; + } else { + maxClientLoanCounter = maxClientLoanCounter + 1; + } + return maxClientLoanCounter; + } + + private Integer getNewClientOrJLGLoanProductCounter(final Loan loan) { + + Integer maxLoanProductLoanCounter = this.loanRepository.getMaxClientOrJLGLoanProductCounter(loan.loanProduct().getId(), + loan.getClientId()); + if (maxLoanProductLoanCounter == null) { + maxLoanProductLoanCounter = 1; + } else { + maxLoanProductLoanCounter = maxLoanProductLoanCounter + 1; + } + return maxLoanProductLoanCounter; + } + + private void updateLoanCycleCounter(final List loansToUpdate, final Loan loan) { + + final Integer currentLoancounter = loan.getCurrentLoanCounter(); + final Integer currentLoanProductCounter = loan.getLoanProductLoanCounter(); + + for (final Loan loanToUpdate : loansToUpdate) { + if (loan.loanProduct().isIncludeInBorrowerCycle()) { + Integer runningLoancounter = loanToUpdate.getCurrentLoanCounter(); + if (runningLoancounter > currentLoancounter) { + loanToUpdate.updateClientLoanCounter(--runningLoancounter); + } + } + if (loan.loanProduct().getId().equals(loanToUpdate.loanProduct().getId())) { + Integer runningLoanProductCounter = loanToUpdate.getLoanProductLoanCounter(); + if (runningLoanProductCounter > currentLoanProductCounter) { + loanToUpdate.updateLoanProductLoanCounter(--runningLoanProductCounter); + } + } + } + this.loanRepository.save(loansToUpdate); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index 6d4e69acb..53622d085 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -329,4 +329,8 @@ public class LoanProduct extends AbstractPersistable { null, null, null, null) : this.loanProductMinMaxConstraints; return loanProductMinMaxConstraints; } + + public boolean isIncludeInBorrowerCycle() { + return this.includeInBorrowerCycle; + } } \ No newline at end of file