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 @@
+
+
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