blob: 7873e441f31e29a4dd5da7bc6ddea952ab8c8e86 [file] [log] [blame]
/**
* 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.accounting.provisioning.service;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
@Slf4j
public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningEntriesReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PaginationHelper loanProductProvisioningEntryDataPaginationHelper;
private final PaginationHelper provisioningEntryDataPaginationHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator;
@Override
public Collection<LoanProductProvisioningEntryData> retrieveLoanProductsProvisioningData(LocalDate date) {
String formattedDate = DateUtils.DEFAULT_DATE_FORMATTER.format(date);
LoanProductProvisioningEntryMapper mapper = new LoanProductProvisioningEntryMapper(sqlGenerator);
final String sql = mapper.schema();
return this.jdbcTemplate.query(sql, mapper, formattedDate, formattedDate, formattedDate);
}
private static final class LoanProductProvisioningEntryMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery;
private LoanProductProvisioningEntryMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
sqlQuery = new StringBuilder().append(
"select (CASE WHEN loan.loan_type_enum=1 THEN mclient.office_id ELSE mgroup.office_id END) as office_id, loan.loan_type_enum, pcd.criteria_id as criteriaid, loan.product_id,loan.currency_code,")
.append("GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate")
+ ", 0) as numberofdaysoverdue,sch.duedate, pcd.category_id, pcd.provision_percentage,")
.append("loan.total_outstanding_derived as outstandingbalance, pcd.liability_account, pcd.expense_account from m_loan_repayment_schedule sch")
.append(" LEFT JOIN m_loan loan on sch.loan_id = loan.id")
.append(" JOIN m_loanproduct_provisioning_mapping lpm on lpm.product_id = loan.product_id")
.append(" JOIN m_provisioning_criteria_definition pcd on pcd.criteria_id = lpm.criteria_id and ")
.append("(pcd.min_age <= GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate") + ",0) and GREATEST("
+ sqlGenerator.dateDiff("?", "sch.duedate") + ",0) <= pcd.max_age) and pcd.criteria_id is not null ")
.append("LEFT JOIN m_client mclient ON mclient.id = loan.client_id ")
.append("LEFT JOIN m_group mgroup ON mgroup.id = loan.group_id ")
.append("where loan.loan_status_id=300 and sch.duedate = ")
.append("(select MIN(sch1.duedate) from m_loan_repayment_schedule sch1 where sch1.loan_id=loan.id and sch1.completed_derived=false)");
}
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long officeId = rs.getLong("office_id");
Long productId = rs.getLong("product_id");
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("numberofdaysoverdue");
Long categoryId = rs.getLong("category_id");
BigDecimal percentage = rs.getBigDecimal("provision_percentage");
BigDecimal outstandingBalance = rs.getBigDecimal("outstandingbalance");
Long liabilityAccountCode = rs.getLong("liability_account");
Long expenseAccountCode = rs.getLong("expense_account");
Long criteriaId = rs.getLong("criteriaid");
Long historyId = null;
return new LoanProductProvisioningEntryData().setHistoryId(historyId).setOfficeId(officeId).setCurrencyCode(currentcyCode)
.setProductId(productId).setCategoryId(categoryId).setOverdueInDays(overdueDays).setPercentage(percentage)
.setBalance(outstandingBalance).setLiablityAccount(liabilityAccountCode).setExpenseAccount(expenseAccountCode)
.setCriteriaId(criteriaId);
}
public String schema() {
return sqlQuery.toString();
}
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(Long entryId) {
ProvisioningEntryDataMapperWithSumReserved mapper1 = new ProvisioningEntryDataMapperWithSumReserved();
// Programmatic query, disable sonar
final String sql = "select" + mapper1.getSchema() + " where entry.id = ? group by entry.id, created.username, modified.username";
return this.jdbcTemplate.queryForObject(sql, mapper1, entryId);// NOSONAR
}
private static final class ProvisioningEntryDataMapper implements RowMapper<ProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append(" entry.id, entry.journal_entry_created, entry.createdby_id, entry.created_date, created.username as createduser,")
.append("entry.lastmodifiedby_id, modified.username as modifieduser, entry.lastmodified_date ")
.append("from m_provisioning_history entry ").append("left JOIN m_appuser created ON created.id = entry.createdby_id ")
.append("left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id ");
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Boolean journalEntry = rs.getBoolean("journal_entry_created");
Long createdById = rs.getLong("createdby_id");
String createdUser = rs.getString("createduser");
Date createdDate = rs.getDate("created_date");
Long modifiedById = rs.getLong("lastmodifiedby_id");
String modifieUser = rs.getString("modifieduser");
BigDecimal totalReservedAmount = null;
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(journalEntry).setCreatedById(createdById)
.setCreatedUser(createdUser).setCreatedDate(createdLocalDate).setModifiedById(modifiedById).setModifiedUser(modifieUser)
.setReservedAmount(totalReservedAmount);
}
public String getSchema() {
return sqlQuery.toString();
}
}
private static final class LoanProductProvisioningEntryRowMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder().append(
" entry.id, entry.history_id as historyId, office_id, entry.criteria_id as criteriaid, office.name as officename, product.name as productname, entry.product_id, ")
.append("category_id, category.category_name, liability.id as liabilityid, liability.gl_code as liabilitycode, liability.name as liabilityname, ")
.append("expense.id as expenseid, expense.gl_code as expensecode, expense.name as expensename, entry.currency_code, entry.overdue_in_days, entry.reseve_amount from m_loanproduct_provisioning_entry entry ")
.append("left join m_office office ON office.id = entry.office_id ")
.append("left join m_product_loan product ON product.id = entry.product_id ")
.append("left join m_provision_category category ON category.id = entry.category_id ")
.append("left join acc_gl_account liability ON liability.id = entry.liability_account ")
.append("left join acc_gl_account expense ON expense.id = entry.expense_account ");
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long historyId = rs.getLong("historyId");
Long officeId = rs.getLong("office_id");
String officeName = rs.getString("officename");
Long productId = rs.getLong("product_id");
String productName = rs.getString("productname");
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("overdue_in_days");
Long categoryId = rs.getLong("category_id");
String categoryName = rs.getString("category_name");
BigDecimal amountreserved = rs.getBigDecimal("reseve_amount");
Long liabilityAccountCode = rs.getLong("liabilityid");
String liabilityAccountglCode = rs.getString("liabilitycode");
String expenseAccountglCode = rs.getString("expensecode");
Long expenseAccountCode = rs.getLong("expenseid");
Long criteriaId = rs.getLong("criteriaid");
String liabilityAccountName = rs.getString("liabilityname");
String expenseAccountName = rs.getString("expensename");
return new LoanProductProvisioningEntryData().setHistoryId(historyId).setOfficeId(officeId).setOfficeName(officeName)
.setCurrencyCode(currentcyCode).setProductId(productId).setProductName(productName).setCategoryId(categoryId)
.setCategoryName(categoryName).setOverdueInDays(overdueDays).setAmountreserved(amountreserved)
.setLiablityAccount(liabilityAccountCode).setLiabilityAccountCode(liabilityAccountglCode)
.setLiabilityAccountName(liabilityAccountName).setExpenseAccount(expenseAccountCode)
.setExpenseAccountCode(expenseAccountglCode).setExpenseAccountName(expenseAccountName).setCriteriaId(criteriaId);
}
public String getSchema() {
return sqlQuery.toString();
}
}
private static final class ProvisioningEntryDataMapperWithSumReserved implements RowMapper<ProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append(" entry.id, journal_entry_created, createdby_id, created_date, created.username as createduser,")
.append("lastmodifiedby_id, modified.username as modifieduser, lastmodified_date, SUM(reserved.reseve_amount) as totalreserved ")
.append("from m_provisioning_history entry ")
.append("JOIN m_loanproduct_provisioning_entry reserved on entry.id = reserved.history_id ")
.append("left JOIN m_appuser created ON created.id = entry.createdby_id ")
.append("left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id ");
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Boolean journalEntry = rs.getBoolean("journal_entry_created");
Long createdById = rs.getLong("createdby_id");
String createdUser = rs.getString("createduser");
Date createdDate = rs.getDate("created_date");
Long modifiedById = rs.getLong("lastmodifiedby_id");
String modifieUser = rs.getString("modifieduser");
BigDecimal totalReservedAmount = rs.getBigDecimal("totalreserved");
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(journalEntry).setCreatedById(createdById)
.setCreatedUser(createdUser).setCreatedDate(createdLocalDate).setModifiedById(modifiedById).setModifiedUser(modifieUser)
.setReservedAmount(totalReservedAmount);
}
public String getSchema() {
return sqlQuery.toString();
}
}
@Override
public Page<ProvisioningEntryData> retrieveAllProvisioningEntries(Integer offset, Integer limit) {
ProvisioningEntryDataMapper mapper = new ProvisioningEntryDataMapper();
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
sqlBuilder.append(mapper.getSchema());
sqlBuilder.append(" order by entry.created_date");
if (limit != null) {
sqlBuilder.append(" limit ").append(limit);
}
if (offset != null) {
sqlBuilder.append(" offset ").append(offset);
}
Object[] whereClauseItemsitems = new Object[] {};
return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems,
mapper);
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(String date) {
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper();
date = date + "%";
final String sql1 = "select " + mapper1.getSchema() + " where entry.created_date like ? ";
ProvisioningEntryData data = null;
try {
data = this.jdbcTemplate.queryForObject(sql1, mapper1, date); // NOSONAR
} catch (EmptyResultDataAccessException e) {
log.error("Problem occurred in retrieveProvisioningEntryData function", e);
}
return data;
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId) {
ProvisioningEntryData data = null;
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final String sql = "select " + mapper.getSchema() + " where entry.criteria_id = ?";
Collection<LoanProductProvisioningEntryData> entries = this.jdbcTemplate.query(sql, mapper, criteriaId); // NOSONAR
if (entries != null && entries.size() > 0) {
Long entryId = ((LoanProductProvisioningEntryData) entries.toArray()[0]).getHistoryId();
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper();
final String sql1 = "select " + mapper1.getSchema() + " where entry.id = ?";
data = this.jdbcTemplate.queryForObject(sql1, mapper1, entryId); // NOSONAR
data.setProvisioningEntries(entries);
}
return data;
}
@Override
public ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals() {
ProvisioningEntryData data = null;
ProvisioningEntryIdDateRowMapper mapper = new ProvisioningEntryIdDateRowMapper();
try {
data = this.jdbcTemplate.queryForObject(mapper.schema(), mapper, new Object[] {});
} catch (EmptyResultDataAccessException e) {
data = null;
}
return data;
}
private static final class ProvisioningEntryIdDateRowMapper implements RowMapper<ProvisioningEntryData> {
StringBuilder buff = new StringBuilder().append("select history1.id, history1.created_date from m_provisioning_history history1 ")
.append("where history1.created_date = (select max(history2.created_date) from m_provisioning_history history2 ")
.append("where history2.journal_entry_created='1')");
@Override
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Date createdDate = rs.getDate("created_date");
Long createdBy = null;
String createdName = null;
Long modifiedBy = null;
String modifiedName = null;
BigDecimal totalReservedAmount = null;
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(Boolean.TRUE).setCreatedById(createdBy).setCreatedUser(createdName)
.setCreatedDate(createdLocalDate).setModifiedById(modifiedBy).setModifiedUser(modifiedName)
.setReservedAmount(totalReservedAmount);
}
public String schema() {
return buff.toString();
}
}
@Override
public Page<LoanProductProvisioningEntryData> retrieveProvisioningEntries(SearchParameters searchParams) {
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
sqlBuilder.append(mapper.getSchema());
String whereClose = " where ";
List<Object> items = new ArrayList<>();
if (searchParams.hasProvisioningEntryId()) {
sqlBuilder.append(whereClose + " entry.history_id = ?");
items.add(searchParams.getProvisioningEntryId());
whereClose = " and ";
}
if (searchParams.hasOfficeId()) {
sqlBuilder.append(whereClose + " entry.office_id = ?");
items.add(searchParams.getOfficeId());
whereClose = " and ";
}
if (searchParams.hasProductId()) {
sqlBuilder.append(whereClose + " entry.product_id = ?");
items.add(searchParams.getProductId());
whereClose = " and ";
}
if (searchParams.hasCategoryId()) {
sqlBuilder.append(whereClose + " entry.category_id = ?");
items.add(searchParams.getCategoryId());
}
sqlBuilder.append(" order by entry.id");
if (searchParams.hasLimit()) {
sqlBuilder.append(" limit ").append(searchParams.getLimit());
if (searchParams.hasOffset()) {
sqlBuilder.append(" offset ").append(searchParams.getOffset());
}
}
Object[] whereClauseItemsitems = items.toArray();
return this.loanProductProvisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(),
whereClauseItemsitems, mapper);
}
}