Skip to content

Commit

Permalink
Feature/FBR-422: Edit Fund sources (#293)
Browse files Browse the repository at this point in the history
Co-authored-by: Julius Peter Oketayot <[email protected]>
  • Loading branch information
fiter-julius-oketayot and Julius Peter Oketayot authored Nov 22, 2023
1 parent d5fa200 commit dd561ec
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.fineract.infrastructure.accountnumberformat.service.AccountNumberFormatConstants;
import org.apache.fineract.organisation.bankcheque.api.BankChequeApiConstants;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
import org.apache.fineract.portfolio.self.pockets.api.PocketApiConstants;
Expand Down Expand Up @@ -328,6 +329,15 @@ public CommandWrapperBuilder createGuarantor(final Long loanId) {
return this;
}

public CommandWrapperBuilder editLoanFund(final Long loanId) {
this.actionName = LoanApiConstants.LOAN_ACTION_UPDATE_FUND;
this.entityName = LoanApiConstants.LOAN_RESOURCE_NAME;
this.entityId = loanId;
this.loanId = loanId;
this.href = "/loans/" + loanId + "?command=editloanfund";
return this;
}

public CommandWrapperBuilder recoverFromGuarantor(final Long loanId) {
this.actionName = "RECOVERGUARANTEES";
this.entityName = "LOAN";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.infrastructure.creditbureau.data.CreditBureauLoanProductMappingData;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

@Service
public class CreditBureauLoanProductMappingReadPlatformServiceImpl implements CreditBureauLoanProductMappingReadPlatformService {
Expand Down Expand Up @@ -100,11 +102,15 @@ public Collection<CreditBureauLoanProductMappingData> readCreditBureauLoanProduc
@Override
public CreditBureauLoanProductMappingData readMappingByLoanId(long loanProductId) {
this.context.authenticatedUser();

final CreditBureauLoanProductMapper rm = new CreditBureauLoanProductMapper();
final String sql = "select " + rm.schema() + " and cblp.loan_product_id=?";

return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { loanProductId }); // NOSONAR
List<CreditBureauLoanProductMappingData> creditBureauLoanProductMappingDataList = this.jdbcTemplate.query(sql, rm,
new Object[] { loanProductId }); // NOSONAR
CreditBureauLoanProductMappingData creditBureauLoanProductMappingData = null;
if (!CollectionUtils.isEmpty(creditBureauLoanProductMappingDataList)) {
creditBureauLoanProductMappingData = creditBureauLoanProductMappingDataList.get(0);
}
return creditBureauLoanProductMappingData;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,6 @@ public interface LoanApiConstants {
String DEPOSIT_GUARANTEE_NUMBER = "depositGuaranteeNo";
String CHEQUE_DESCRIPTION = "description";
String CHEQUE_ID = "chequeId";
String LOAN_RESOURCE_NAME = "LOAN";
String LOAN_ACTION_UPDATE_FUND = "UPDATEFUND";
}
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,9 @@ public String stateTransitions(@PathParam("loanId") @Parameter(description = "lo
} else if (is(commandParam, "recoverGuarantees")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().recoverFromGuarantor(loanId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "editloanfund")) {
final CommandWrapper commandRequest = builder.editLoanFund(loanId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}

if (result == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.exception;

import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;

/**
* {@link AbstractPlatformDomainRuleException} thrown when trying to modify a loan in an invalid state.
*/
public class LoanApplicationNotInClosedStateCannotBeModified extends AbstractPlatformDomainRuleException {

public LoanApplicationNotInClosedStateCannotBeModified(final Long id) {
super("error.msg.loan.cannot.edit.loan.fund.in.its.present.state",
"Loan application with identifier " + id + " cannot be modified in its current state.", id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.handler;

import javax.persistence.PersistenceException;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.service.LoanApplicationWritePlatformService;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;

@Service
@CommandType(entity = LoanApiConstants.LOAN_RESOURCE_NAME, action = LoanApiConstants.LOAN_ACTION_UPDATE_FUND)
@RequiredArgsConstructor
public class EditFundCommandHandler implements NewCommandSourceHandler {

private final LoanApplicationWritePlatformService writePlatformService;
private final DataIntegrityErrorHandler dataIntegrityErrorHandler;

@Override
public CommandProcessingResult processCommand(JsonCommand command) {
try {
return this.writePlatformService.editLoanFund(command);
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
dataIntegrityErrorHandler.handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loans", "edit lain fund");
return CommandProcessingResult.empty();
} catch (final PersistenceException dve) {
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
dataIntegrityErrorHandler.handleDataIntegrityIssues(command, throwable, dve, "loans", "edit lain fund");
return CommandProcessingResult.empty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ public interface LoanApplicationWritePlatformService {
CommandProcessingResult rejectGLIMApplicationApproval(Long loanId, JsonCommand command);

CommandProcessingResult disburseLoanByCheques(JsonCommand command);

CommandProcessingResult editLoanFund(JsonCommand command);
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
import org.apache.fineract.portfolio.loanaccount.domain.LoanTopupDetails;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInClosedStateCannotBeModified;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
Expand Down Expand Up @@ -1933,4 +1934,30 @@ public CommandProcessingResult disburseLoanByCheques(JsonCommand command) {
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).build();
}

@Override
public CommandProcessingResult editLoanFund(final JsonCommand command) {
final Long loanId = command.getLoanId();
final Loan existingLoanApplication = retrieveLoanBy(loanId);
if (existingLoanApplication.isClosed()) {
throw new LoanApplicationNotInClosedStateCannotBeModified(loanId);
}
final String fundIdParameterName = "fundId";
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
if (command.parameterExists(fundIdParameterName)) {
final Long fundId = command.longValueOfParameterNamed(fundIdParameterName);
baseDataValidator.reset().parameter(fundIdParameterName).value(fundId).ignoreIfNull().integerGreaterThanZero();
final Fund fund = this.loanAssembler.findFundByIdIfProvided(fundId);
existingLoanApplication.updateFund(fund);
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
this.loanRepositoryWrapper.saveAndFlush(existingLoanApplication);
return new CommandProcessingResultBuilder().withEntityId(loanId).withOfficeId(existingLoanApplication.getOfficeId())
.withClientId(existingLoanApplication.getClientId()).withGroupId(existingLoanApplication.getGroupId())
.withLoanId(existingLoanApplication.getId()).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,7 @@ public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId,

this.context.authenticatedUser();

if (!"group".equals(templateType)) {
if (!"individual".equals(templateType) && clientId != null) {
ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId);
String blacklistString = "select count(*) from m_client_blacklist where dpi=? and status=?";
String dpiNumber = clientData.getDpiNumber();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,125 @@
</preConditions>
<modifyDataType tableName="job_run_history" columnName="error_message" newDataType="LONGTEXT"/>
</changeSet>

<changeSet author="fineract" id="10">
<insert tableName="m_permission">
<column name="grouping" value="portfolio"/>
<column name="code" value="UPDATEFUND_LOAN"/>
<column name="entity_name" value="LOAN"/>
<column name="action_name" value="UPDATEFUND"/>
<column name="can_maker_checker" valueBoolean="false"/>
</insert>
</changeSet>

<changeSet id="11" author="fineract">
<sql>
<![CDATA[
UPDATE stretchy_report SET report_sql = "SELECT
dt.clientId AS 'Nro Solicitud',
dt.fundName AS 'Origen de Fondos',
dt.clientNo AS 'Codigo Cliente',
dt.clientName AS 'Cliente',
dt.groupNo AS 'Nro Grupo',
dt.groupName AS 'Nombre Grupo',
dt.agencyName AS 'Agencia',
dt.approvedPrincipal AS 'Monto Otorgado',
dt.outstandingPrincipal AS 'Saldo Capital',
dt.repaidPrincipal AS 'Capital Recuperado',
dt.repaidInterest AS 'Intereses',
dt.totalRepaid AS 'Total Cobrad'
FROM (
SELECT
mc.id AS clientId,
mf.name AS fundName,
mc.account_no AS clientNo,
mc.display_name AS clientName,
COALESCE(mg.account_no, 'N/A') AS groupNo,
COALESCE(mg.display_name, 'N/A') AS groupName,
COALESCE(ma.name, 'N/A') AS agencyName,
ml.approved_principal AS approvedPrincipal ,
ml.principal_outstanding_derived AS outstandingPrincipal,
ml.principal_repaid_derived AS repaidPrincipal,
ml.interest_repaid_derived AS repaidInterest,
ml.total_repaid AS totalRepaid
FROM m_office mo
INNER JOIN m_office office_under ON office_under.hierarchy LIKE CONCAT(mo.hierarchy, '%')AND office_under.hierarchy LIKE CONCAT('${currentUserHierarchy}', '%')
INNER JOIN m_client mc ON mc.office_id = office_under.id
INNER JOIN (
SELECT
SUM(IFNULL(m_loan.approved_principal, 0)) AS approved_principal,
SUM(IFNULL(m_loan.principal_outstanding_derived, 0)) AS principal_outstanding_derived,
SUM(IFNULL(m_loan.principal_repaid_derived, 0)) AS principal_repaid_derived,
SUM(IFNULL(m_loan.interest_repaid_derived, 0)) AS interest_repaid_derived,
SUM(IFNULL(m_loan.principal_repaid_derived, 0) + IFNULL(m_loan.interest_repaid_derived, 0)) AS total_repaid,
m_loan.client_id AS client_id,
MIN(m_loan.fund_id) AS fund_id
FROM m_loan m_loan
WHERE(m_loan.submittedon_date BETWEEN '${submittedOnStartDate}' AND '${submittedOnEndDate}') AND (m_loan.fund_id = '${fundId}' OR '-1' = '${fundId}')AND (m_loan.loan_status_id = 300)
GROUP BY m_loan.client_id
) ml ON ml.client_id = mc.id
LEFT JOIN m_group_client mgc ON mgc.client_id = mc.id
LEFT JOIN m_group mg ON mg.id = mgc.group_id
LEFT JOIN m_group center ON center.id = mg.parent_id
LEFT JOIN m_portfolio mp ON mp.id = center.portfolio_id
LEFT JOIN m_supervision ms ON ms.id = mp.supervision_id
LEFT JOIN m_agency ma ON ma.id = ms.agency_id
LEFT JOIN m_fund mf ON mf.id = ml.fund_id
GROUP BY mc.id ORDER BY mc.id
)dt" WHERE report_name = "Reporte por Clienta de Cobranzas y Saldos por Financista";
]]>
</sql>
</changeSet>

<changeSet id="12" author="fineract">
<sql>
<![CDATA[
UPDATE stretchy_report SET report_sql = "SELECT
dt.groupId AS 'Nro Solicitud',
dt.fundName AS 'Origen de Fondos',
dt.groupNo AS 'Nro Grupo',
dt.groupName AS 'Nombre Grupo',
dt.agencyName AS 'Agencia',
dt.approvedPrincipal AS 'Monto Otorgado',
dt.outstandingPrincipal AS 'Saldo Capital',
dt.repaidPrincipal AS 'Capital Recuperado',
dt.repaidInterest AS 'Intereses'
FROM (
SELECT
mg.id AS groupId,
mg.account_no AS groupNo,
mg.display_name AS groupName,
COALESCE(ma.name, 'N/A') AS agencyName,
ml.approved_principal AS approvedPrincipal ,
ml.principal_outstanding_derived AS outstandingPrincipal,
ml.principal_repaid_derived AS repaidPrincipal,
ml.interest_repaid_derived AS repaidInterest,
ml.total_repaid AS totalRepaid,
mf.name AS fundName
FROM m_office mo
INNER JOIN m_office office_under ON office_under.hierarchy LIKE CONCAT(mo.hierarchy, '%')AND office_under.hierarchy LIKE CONCAT('${currentUserHierarchy}', '%')
INNER JOIN m_group mg ON (office_under.id = mg.office_id AND mg.parent_id IS NOT NULL)
INNER JOIN (
SELECT
SUM(IFNULL(m_loan.approved_principal, 0)) AS approved_principal,
SUM(IFNULL(m_loan.principal_outstanding_derived, 0)) AS principal_outstanding_derived,
SUM(IFNULL(m_loan.principal_repaid_derived, 0)) AS principal_repaid_derived,
SUM(IFNULL(m_loan.interest_repaid_derived, 0)) AS interest_repaid_derived,
SUM(IFNULL(m_loan.principal_repaid_derived, 0) + IFNULL(m_loan.interest_repaid_derived, 0)) AS total_repaid,
m_loan.group_id AS group_id,
MIN(m_loan.fund_id) AS fund_id
FROM m_loan m_loan
WHERE (m_loan.submittedon_date BETWEEN '${submittedOnStartDate}' AND '${submittedOnEndDate}') AND(m_loan.fund_id = '${fundId}' OR '-1' = '${fundId}')AND (m_loan.loan_status_id = 300)
GROUP BY m_loan.group_id
) ml ON ml.group_id = mg.id
LEFT JOIN m_group center ON center.id = mg.parent_id
LEFT JOIN m_portfolio mp ON mp.id = center.portfolio_id
LEFT JOIN m_supervision ms ON ms.id = mp.supervision_id
LEFT JOIN m_agency ma ON ma.id = ms.agency_id
LEFT JOIN m_fund mf ON mf.id = ml.fund_id
GROUP BY mg.id ORDER BY mg.id
)dt" WHERE report_name = "Reporte Grupal de Cobranzas y Saldos por Financista";
]]>
</sql>
</changeSet>
</databaseChangeLog>

0 comments on commit dd561ec

Please sign in to comment.