Add Loan counter for Group and JLG Loan

This commit is contained in:
mifoscontributer
2013-07-03 18:17:21 +05:30
parent 94e11c2dea
commit 9010ae6a80
12 changed files with 406 additions and 152 deletions
+99 -2
View File
@@ -191,6 +191,22 @@
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}?command=assignStaff</td>
<td><a href="#clients_assignStaff">Assign Staff</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>clients/{clientId}?command=unassignStaff</td>
<td><a href="#clients_unassignStaff">Unassign Staff</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}/loans</td>
@@ -1818,6 +1834,12 @@ function executeAjaxRequest(url, verbType, jsonData, basicAuthKey, successFuncti
business that doesnt suit the firstname,middlename,lastname
structure.</td>
</tr>
<tr class=alt>
<td>staffId</td>
</tr>
<tr>
<td class=fielddesc>The staffId of the staff member dealing with the client office. The staff member is not specifically the loan officer.</td>
</tr>
</table>
</div>
</div>
@@ -1906,7 +1928,7 @@ GET https://DomainName/api/v1/clients/template
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>groupId, externalId, accountNo</td>
<td>groupId, externalId, accountNo, staffId</td>
</tr>
</table>
</div>
@@ -2013,6 +2035,59 @@ Request Body:
{
"clientId":15,
"resourceId":15
}
</code>
</div>
</div>
<a id="clients_assignStaff" name="clients_assignStaff" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Assign a Staff</h4>
<p>Allows you to assign <i>Staff</i> for existed Client.</p>
<p>The selected Staff member should be belongs to the client's office.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/clients/{clientId}?command=assignStaff</code>
<code class="method-request">POST clients/1?command=assignStaff
Content-Type: application/json
Request Body:
{
"staffId": "1"
}
</code>
<code class="method-response">
{
"officeId": 1,
"clientId": 1,
"resourceId": 1,
"changes": {"staffId":1}
}
</code>
</div>
</div>
<a id="clients_unassignStaff" name="clients_unassignStaff" class="old-syle-anchor">&nbsp;</a>
<div class="method-section">
<div class="method-description">
<h4>Unassign a Staff</h4>
<p>Allows you to unassign the <i>Staff</i>.</p>
</div>
<div class="method-example">
<code class="method-declaration">POST https://Domain Name/api/v1/clients/{clientId}?command=unassignStaff</code>
<code class="method-request">POST clients/1?command=unassignStaff
Content-Type: application/json
Request Body:
{
"staffId":"1"
}
</code>
<code class="method-response">
{
"officeId": 1,
"clientId": 1,
"resourceId": 1,
"changes": {"staffId":1}
}
</code>
</div>
@@ -7084,6 +7159,12 @@ Request Body:
<tr>
<td class=fielddesc>For associating a loan product with a given fund by default.</td>
</tr>
<tr class=alt>
<td>includeInBorrowerCycle</td>
</tr>
<tr>
<td class=fielddesc>It is a flag, Used to denote whether the loans should include in loan cycle counter or not.</td>
</tr>
<tr class=alt>
<td>currencyCode</td>
</tr>
@@ -7786,7 +7867,7 @@ GET https://DomainName/api/v1/loanproducts/{productId}
<td><div class="mifosXHeading2">Optional Fields</div></td>
</tr>
<tr class=alt>
<td>inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings</td>
<td>inArrearsTolerance, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, charges, paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, penaltyToIncomeAccountMappings, includeInBorrowerCycle</td>
</tr>
<tr class="matrixHeadingBG">
@@ -15832,6 +15913,22 @@ GET https://DomainName/api/v1/search?query=000000111
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}?command=assignStaff</td>
<td><a href="#clients_assignStaff">Assign Staff</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>clients/{clientId}?command=unassignStaff</td>
<td><a href="#clients_unassignStaff">Unassign Staff</a></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr class="alt">
<td></td>
<td>clients/{clientId}/loans</td>
@@ -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`;
@@ -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();
}
@@ -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";
@@ -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<CodeValueData> loanPurposeOptions = null;
final Collection<CalendarData> 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<CodeValueData> loanCollateralOptions = null;
final Collection<CalendarData> 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<CodeValueData> loanCollateralOptions = null;
final Collection<CalendarData> 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<LoanTransactionData> 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<LoanProductData> 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<FundData> fundOptions, final Collection<ChargeData> chargeOptions, final ChargeData chargeTemplate,
final Collection<StaffData> loanOfficerOptions, final Collection<CodeValueData> loanPurposeOptions,
final Collection<CodeValueData> loanCollateralOptions, final Collection<CalendarData> 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() {
@@ -100,7 +100,6 @@ public class Loan extends AbstractPersistable<Long> {
@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<Long> {
@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<Long> {
}
}
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();
}
}
@@ -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<Long> {
@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;
}
}
@@ -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<LoanCycle, Long>, JpaSpecificationExecutor<LoanCycle> {
@Query("from LoanCycle lc where lc.client.id=:clientId and lc.loanProduct.id=:loanProductId order by lc.runningCount")
List<LoanCycle> findByClientIdAndLoanProductId(@Param("clientId") Long clientId, @Param("loanProductId") Long loanProductId);
}
@@ -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<Loan, Long>,
JpaSpecificationExecutor<Loan> {
@Query("from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId")
List<Loan> findByClientIdAndGroupId(@Param("clientId") Long clientId, @Param("groupId") Long groupId);
@Query("from Loan loan where loan.client.id = :clientId")
List<Loan> findLoanByClientId(@Param("clientId") Long clientId);
public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificationExecutor<Loan> {
@Query("from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes")
List<Loan> findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection<Long> ids, @Param("loanStatuses") Collection<Integer> loanStatuses, @Param("loanTypes") Collection<Integer> loanTypes);
@Query("select loan.id from Loan loan where loan.actualDisbursementDate > :disbursalDate order by loan.actualDisbursementDate")
List<Long> 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<Loan> findByClientIdAndGroupId(@Param("clientId") Long clientId, @Param("groupId") Long groupId);
@Query("from Loan loan where loan.client.id = :clientId")
List<Loan> 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<Loan> findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection<Long> ids,
@Param("loanStatuses") Collection<Integer> loanStatuses, @Param("loanTypes") Collection<Integer> loanTypes);
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
@Param("loanType") Integer loanType);
@Query(FIND_CLIENT_OR_JLG_LOANS_DISBURSED_AFTER)
List<Loan> 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<Loan> getGroupLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("groupId") Long groupId,
@Param("groupLoanType") Integer groupLoanType);
@Query(FIND_CLIENT_OR_JLG_LOANS_TO_UPDATE)
List<Loan> getClientOrJLGLoansToUpdateLoanCounter(@Param("loanCounter") Integer loanCounter, @Param("clientId") Long clientId);
@Query(FIND_GROUP_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER)
List<Loan> getGroupLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter, @Param("groupId") Long groupId,
@Param("groupLoanType") Integer groupLoanType);
@Query(FIND_CLIENT_LOANS_TO_UPDATE_LOANPRODUCT_COUNTER)
List<Loan> getClientLoansToUpdateLoanProductCounter(@Param("loanProductCounter") Integer loanProductCounter, @Param("clientId") Long clientId);
}
@@ -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);
}
}
@@ -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<Long> loansToUpdateLoanCycle = this.loanRepository.getLoansDisbursedAfter(actualDisbursementDate.toDate());
updateLoanCounters(loan, actualDisbursementDate);
// get all available loan cycles.
final List<LoanCycle> 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<LoanCycle> 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<Loan> 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<Loan> 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<Loan> 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<Loan> 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<Loan> 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);
}
}
@@ -329,4 +329,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
null, null, null, null) : this.loanProductMinMaxConstraints;
return loanProductMinMaxConstraints;
}
public boolean isIncludeInBorrowerCycle() {
return this.includeInBorrowerCycle;
}
}