Fixes integration tests

FINERACT-1557: Fixes for integration tests

FINERACT-1557: Fixes for integration tests

Fixes integration tests
This commit is contained in:
Jose Hernandez
2022-04-14 00:37:55 +02:00
committed by Aleksandar Vidakovic
parent eb11c32ee3
commit 3d3e8c4083
34 changed files with 425 additions and 157 deletions
@@ -131,7 +131,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
+ "group by t2.account_id desc) t1)";
}
}
final Object[] paramaterArray = new Object[3];
final Object[] parameterArray = new Object[3];
int arrayPos = 0;
boolean filtersPresent = false;
if ((accountClassification != null) || StringUtils.isNotBlank(searchParam) || (usage != null) || (manualTransactionsAllowed != null)
@@ -143,8 +143,8 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
if (filtersPresent) {
boolean firstWhereConditionAdded = false;
if (accountClassification != null) {
sql += " classification_enum like ?";
paramaterArray[arrayPos] = accountClassification;
sql += " classification_enum = ?";
parameterArray[arrayPos] = accountClassification.shortValue();
arrayPos = arrayPos + 1;
firstWhereConditionAdded = true;
}
@@ -153,9 +153,9 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
sql += " and ";
}
sql += " ( name like %?% or gl_code like %?% )";
paramaterArray[arrayPos] = searchParam;
parameterArray[arrayPos] = searchParam;
arrayPos = arrayPos + 1;
paramaterArray[arrayPos] = searchParam;
parameterArray[arrayPos] = searchParam;
arrayPos = arrayPos + 1;
firstWhereConditionAdded = true;
}
@@ -175,11 +175,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
sql += " and ";
}
if (manualTransactionsAllowed) {
sql += " manual_journal_entries_allowed = 1";
} else {
sql += " manual_journal_entries_allowed = 0";
}
sql += " manual_journal_entries_allowed = " + manualTransactionsAllowed;
firstWhereConditionAdded = true;
}
if (disabled != null) {
@@ -187,18 +183,14 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
sql += " and ";
}
if (disabled) {
sql += " disabled = 1";
} else {
sql += " disabled = 0";
}
sql += " disabled = " + disabled;
firstWhereConditionAdded = true;
}
}
sql += " ORDER BY gl_code ASC";
final Object[] finalObjectArray = Arrays.copyOf(paramaterArray, arrayPos);
final Object[] finalObjectArray = Arrays.copyOf(parameterArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray); // NOSONAR
}
@@ -27,6 +27,7 @@ import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
@@ -40,6 +41,10 @@ public final class DateUtils {
}
public static ZoneId getSystemZoneId() {
return ZoneId.systemDefault();
}
public static ZoneId getDateTimeZoneOfTenant() {
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
ZoneId zone = ZoneId.systemDefault();
@@ -59,13 +64,28 @@ public final class DateUtils {
}
public static Date getDateOfTenant() {
return Date.from(getLocalDateOfTenant().atStartOfDay(getDateTimeZoneOfTenant()).toInstant());
return convertLocalDateToDate(getLocalDateOfTenant());
}
public static Date convertLocalDateToDate(LocalDate localDate) {
return createDate(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth());
}
public static Date createDate(int year, int month, int day) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, day);
return cal.getTime();
}
public static LocalDate getLocalDateOfTenant() {
final ZoneId zone = getDateTimeZoneOfTenant();
LocalDate today = LocalDate.now(zone);
return today;
return LocalDate.now(zone);
}
public static LocalDate getLocalDateOfTenant(final ZoneId zone) {
return LocalDate.now(zone);
}
public static LocalDateTime getLocalDateTimeOfTenant() {
@@ -97,6 +97,16 @@ public class DatabaseSpecificSQLGenerator {
}
}
public String currentDateTime() {
if (databaseTypeResolver.isMySQL()) {
return "CURRENT_TIMESTAMP()";
} else if (databaseTypeResolver.isPostgreSQL()) {
return "CURRENT_TIMESTAMP";
} else {
throw new IllegalStateException("Database type is not supported for current date " + databaseTypeResolver.databaseType());
}
}
public String subDate(String date, String multiplier, String unit) {
if (databaseTypeResolver.isMySQL()) {
return format("DATE_SUB(%s, INTERVAL %s %s)", date, multiplier, unit);
@@ -34,4 +34,25 @@ public final class DataTableApiConstant {
public static final String localParamName = "locale";
public static final String DATATABLE_RESOURCE_NAME = "dataTables";
public static final String CREATEDAT_FIELD_NAME = "created_at";
public static final String UPDATEDAT_FIELD_NAME = "updated_at";
// Field Types
public static final String DATETIME_FIELD_TYPE = "DateTime";
// associationParameters
public static final String allAssociateParamName = "all";
public static final String repaymentScheduleAssociateParamName = "repaymentSchedule";
public static final String originalScheduleAssociateParamName = "originalSchedule";
public static final String transactionsAssociateParamName = "transactions";
public static final String chargesAssociateParamName = "charges";
public static final String guarantorsAssociateParamName = "guarantors";
public static final String collateralAssociateParamName = "collateral";
public static final String notesAssociateParamName = "notes";
public static final String linkedAccountAssociateParamName = "linkedAccount";
public static final String multiDisburseDetailsAssociateParamName = "multiDisburseDetails";
public static final String futureScheduleAssociateParamName = "futureSchedule";
public static final String meetingAssociateParamName = "meeting";
public static final String emiAmountVariationsAssociateParamName = "emiAmountVariations";
public static final String collectionAssociateParamName = "collection";
}
@@ -329,6 +329,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
return category.equals(DataTableApiConstant.CATEGORY_PPI);
}
private JsonElement addColumn(final String name, final String dataType, final boolean isMandatory, final Integer length) {
JsonObject column = new JsonObject();
column.addProperty("name", name);
column.addProperty("type", dataType);
if (dataType.equalsIgnoreCase("string")) {
column.addProperty("length", length);
}
column.addProperty("mandatory", (isMandatory ? "true" : "false"));
return column;
}
@Override
public String getDataTableName(String url) {
@@ -491,7 +502,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private void assertDataTableExists(final String datatableName) {
final String sql = "select (CASE WHEN exists (select 1 from information_schema.tables where table_schema = "
+ sqlGenerator.currentSchema() + " and table_name = ?) THEN 'true' ELSE 'false' END)";
final String dataTableExistsString = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { datatableName }); // NOSONAR
final String dataTableExistsString = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { datatableName });
final boolean dataTableExists = Boolean.valueOf(dataTableExistsString);
if (!dataTableExists) {
throw new PlatformDataIntegrityException("error.msg.invalid.datatable", "Invalid Data Table: " + datatableName, "name",
@@ -559,7 +570,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
} else if (type.equalsIgnoreCase("Decimal")) {
sqlBuilder = sqlBuilder.append("(19,6)");
} else if (type.equalsIgnoreCase("Dropdown")) {
sqlBuilder = sqlBuilder.append("(11)");
if (databaseTypeResolver.isMySQL()) {
sqlBuilder = sqlBuilder.append("(11)");
}
}
}
@@ -613,12 +626,20 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
sqlBuilder = sqlBuilder.append("CREATE TABLE " + sqlGenerator.escape(datatableName) + " (");
if (multiRow) {
sqlBuilder = sqlBuilder.append("id BIGINT NOT NULL AUTO_INCREMENT, ")
.append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, ");
} else {
sqlBuilder = sqlBuilder.append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, ");
if (databaseTypeResolver.isMySQL()) {
sqlBuilder = sqlBuilder.append("id BIGINT NOT NULL AUTO_INCREMENT, ");
} else if (databaseTypeResolver.isPostgreSQL()) {
sqlBuilder = sqlBuilder.append(
"id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ), ");
} else {
throw new IllegalStateException("Current database is not supported");
}
}
sqlBuilder = sqlBuilder.append(sqlGenerator.escape(fkColumnName) + " BIGINT NOT NULL, ");
// Add Created At and Updated At
columns.add(addColumn(DataTableApiConstant.CREATEDAT_FIELD_NAME, DataTableApiConstant.DATETIME_FIELD_TYPE, false, null));
columns.add(addColumn(DataTableApiConstant.UPDATEDAT_FIELD_NAME, DataTableApiConstant.DATETIME_FIELD_TYPE, false, null));
for (final JsonElement column : columns) {
parseDatatableColumnObjectForCreate(column.getAsJsonObject(), sqlBuilder, constrainBuilder, dataTableNameAlias,
codeMappings, isConstraintApproach);
@@ -629,10 +650,12 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
String fullFkName = "fk_" + fkName;
if (multiRow) {
sqlBuilder = sqlBuilder.append(", PRIMARY KEY (id)")
.append(", KEY " + sqlGenerator.escape("fk_" + apptableName.substring(2) + "_id") + " ("
+ sqlGenerator.escape(fkColumnName) + ")")
.append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ")
sqlBuilder = sqlBuilder.append(", PRIMARY KEY (id)");
if (databaseTypeResolver.isMySQL()) {
sqlBuilder = sqlBuilder.append(", KEY " + sqlGenerator.escape("fk_" + apptableName.substring(2) + "_id") + " ("
+ sqlGenerator.escape(fkColumnName) + ")");
}
sqlBuilder = sqlBuilder.append(", CONSTRAINT " + sqlGenerator.escape(fullFkName) + " ")
.append("FOREIGN KEY (" + sqlGenerator.escape(fkColumnName) + ") ")
.append("REFERENCES " + sqlGenerator.escape(actualAppTableName) + " (id)");
} else {
@@ -647,6 +670,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (databaseTypeResolver.isMySQL()) {
sqlBuilder.append(" ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4;");
}
LOG.debug("SQL:: {}", sqlBuilder.toString());
this.jdbcTemplate.execute(sqlBuilder.toString());
registerDatatable(datatableName, apptableName, entitySubType);
@@ -938,9 +963,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final boolean isConstraintApproach = this.configurationDomainService.isConstraintApproachEnabledForDatatables();
if (!StringUtils.isBlank(entitySubType)) {
final String updateLegalFormSQL = "update x_registered_table SET entity_subtype='" + entitySubType
String updateLegalFormSQL = "update x_registered_table SET entity_subtype='" + entitySubType
+ "' WHERE registered_table_name = '" + datatableName + "'";
this.jdbcTemplate.execute(updateLegalFormSQL); // NOSONAR
this.jdbcTemplate.execute(updateLegalFormSQL);
}
if (!StringUtils.isBlank(apptableName)) {
@@ -1154,7 +1179,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private int getRowCount(final String datatableName) {
final String sql = "select count(*) from " + sqlGenerator.escape(datatableName);
return this.jdbcTemplate.queryForObject(sql, Integer.class); // NOSONAR
return this.jdbcTemplate.queryForObject(sql, Integer.class);
}
@Transactional
@@ -1476,7 +1501,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
SQLInjectionValidator.validateSQLInput(datatable);
final String sql = "SELECT application_table_name FROM x_registered_table where registered_table_name = ?";
String applicationTableName;
String applicationTableName = "";
final SqlRowSet rowSet = jdbcTemplate.queryForRowSet(sql, new Object[] { datatable }); // NOSONAR
if (rowSet.next()) {
@@ -1524,6 +1549,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
columnName = sqlGenerator.escape(key);
insertColumns += ", " + columnName;
selectColumns += "," + pValueWrite + " as " + columnName;
} else {
if (key.equalsIgnoreCase(DataTableApiConstant.CREATEDAT_FIELD_NAME)
|| key.equalsIgnoreCase(DataTableApiConstant.UPDATEDAT_FIELD_NAME)) {
columnName = sqlGenerator.escape(key);
insertColumns += ", " + columnName;
selectColumns += "," + sqlGenerator.currentDateTime() + " as " + columnName;
}
}
}
@@ -1626,6 +1658,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
}
}
sql += sqlGenerator.escape(key) + " = " + pValueWrite;
} else {
if (key.equalsIgnoreCase(DataTableApiConstant.UPDATEDAT_FIELD_NAME)) {
sql += ", " + sqlGenerator.escape(key) + " = " + sqlGenerator.currentDateTime();
}
}
}
@@ -1879,8 +1915,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
public Long countDatatableEntries(final String datatableName, final Long appTableId, String foreignKeyColumn) {
final String sqlString = "SELECT COUNT(" + sqlGenerator.escape(foreignKeyColumn) + ") FROM " + sqlGenerator.escape(datatableName)
+ " WHERE " + sqlGenerator.escape(foreignKeyColumn) + "=?";
final Long count = this.jdbcTemplate.queryForObject(sqlString, Long.class, new Object[] { appTableId }); // NOSONAR
+ " WHERE " + sqlGenerator.escape(foreignKeyColumn) + "=" + appTableId;
final Long count = this.jdbcTemplate.queryForObject(sqlString, Long.class);
return count;
}
@@ -160,7 +160,7 @@ public class StaffReadPlatformServiceImpl implements StaffReadPlatformService {
public Collection<StaffData> retrieveAllLoanOfficersInOfficeById(final Long officeId) {
SQLBuilder extraCriteria = new SQLBuilder();
extraCriteria.addCriteria(" office_id = ", officeId);
extraCriteria.addCriteria(" is_loan_officer = ", 1);
extraCriteria.addCriteria(" is_loan_officer = ", true);
return retrieveAllStaff(extraCriteria);
}
@@ -73,6 +73,10 @@ public class PortfolioAccountDTO {
return this.accountStatus;
}
public long getFirstAccountStatus() {
return this.accountStatus[0];
}
public Integer getDepositType() {
return this.depositType;
}
@@ -105,12 +105,9 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
// sqlParams.add(portfolioAccountDTO.getClientId());
Collection<PortfolioAccountData> accounts = null;
String sql = null;
String defaultAccountStatus = "300";
long defaultAccountStatus = 300; // Active Status
if (portfolioAccountDTO.getAccountStatus() != null) {
for (final long status : portfolioAccountDTO.getAccountStatus()) {
defaultAccountStatus += ", " + status;
}
defaultAccountStatus = defaultAccountStatus.substring(defaultAccountStatus.indexOf(",") + 1);
defaultAccountStatus = portfolioAccountDTO.getFirstAccountStatus();
}
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(portfolioAccountDTO.getAccountTypeId());
switch (accountType) {
@@ -150,11 +147,11 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
if (portfolioAccountDTO.getDepositType() != null) {
sql += " and sa.deposit_type_enum = ?";
sqlParams.add(portfolioAccountDTO.getDepositType());
sqlParams.add(portfolioAccountDTO.getDepositType().shortValue());
}
if (portfolioAccountDTO.isExcludeOverDraftAccounts()) {
sql += " and sa.allow_overdraft = 0";
sql += " and sa.allow_overdraft = false";
}
if (portfolioAccountDTO.getClientId() == null && portfolioAccountDTO.getGroupId() != null) {
@@ -74,6 +74,7 @@ import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSer
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
@@ -104,6 +105,7 @@ import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.fund.service.FundReadPlatformService;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.GlimRepaymentTemplate;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
@@ -494,7 +496,6 @@ public class LoansApiResource {
public String retrieveLoan(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
@DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly,
@Context final UriInfo uriInfo) {
long start = System.currentTimeMillis();
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
LoanAccountData loanBasicDetails = this.loanReadPlatformService.retrieveOne(loanId);
@@ -542,75 +543,83 @@ public class LoansApiResource {
Collection<LoanTermVariationsData> emiAmountVariations = null;
Collection<LoanCollateralResponseData> loanCollateralManagements = null;
Collection<LoanCollateralManagementData> loanCollateralManagementData = new ArrayList<>();
CollectionData collectionData = CollectionData.template();
final Set<String> mandatoryResponseParameters = new HashSet<>();
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
associationParameters.addAll(Arrays.asList("repaymentSchedule", "futureSchedule", "originalSchedule", "transactions",
"charges", "guarantors", "collateral", "notes", "linkedAccount", "multiDisburseDetails"));
if (associationParameters.contains(DataTableApiConstant.allAssociateParamName)) {
associationParameters.addAll(Arrays.asList(DataTableApiConstant.repaymentScheduleAssociateParamName,
DataTableApiConstant.futureScheduleAssociateParamName, DataTableApiConstant.originalScheduleAssociateParamName,
DataTableApiConstant.transactionsAssociateParamName, DataTableApiConstant.chargesAssociateParamName,
DataTableApiConstant.guarantorsAssociateParamName, DataTableApiConstant.collateralAssociateParamName,
DataTableApiConstant.notesAssociateParamName, DataTableApiConstant.linkedAccountAssociateParamName,
DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName));
}
ApiParameterHelper.excludeAssociationsForResponseIfProvided(uriInfo.getQueryParameters(), associationParameters);
if (associationParameters.contains("guarantors")) {
mandatoryResponseParameters.add("guarantors");
if (associationParameters.contains(DataTableApiConstant.guarantorsAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.guarantorsAssociateParamName);
guarantors = this.guarantorReadPlatformService.retrieveGuarantorsForLoan(loanId);
if (CollectionUtils.isEmpty(guarantors)) {
guarantors = null;
}
}
if (associationParameters.contains("transactions")) {
mandatoryResponseParameters.add("transactions");
if (associationParameters.contains(DataTableApiConstant.transactionsAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.transactionsAssociateParamName);
final Collection<LoanTransactionData> currentLoanRepayments = this.loanReadPlatformService.retrieveLoanTransactions(loanId);
if (!CollectionUtils.isEmpty(currentLoanRepayments)) {
loanRepayments = currentLoanRepayments;
}
}
if (associationParameters.contains("multiDisburseDetails") || associationParameters.contains("repaymentSchedule")) {
mandatoryResponseParameters.add("multiDisburseDetails");
if (associationParameters.contains(DataTableApiConstant.multiDisburseDetailsAssociateParamName)
|| associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.multiDisburseDetailsAssociateParamName);
disbursementData = this.loanReadPlatformService.retrieveLoanDisbursementDetails(loanId);
}
if (associationParameters.contains("emiAmountVariations") || associationParameters.contains("repaymentSchedule")) {
mandatoryResponseParameters.add("emiAmountVariations");
if (associationParameters.contains(DataTableApiConstant.emiAmountVariationsAssociateParamName)
|| associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.emiAmountVariationsAssociateParamName);
emiAmountVariations = this.loanReadPlatformService.retrieveLoanTermVariations(loanId,
LoanTermVariationType.EMI_AMOUNT.getValue());
}
if (associationParameters.contains("repaymentSchedule")) {
mandatoryResponseParameters.add("repaymentSchedule");
if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.repaymentScheduleRelatedData();
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData,
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(), loanBasicDetails.getTotalPaidFeeCharges());
if (associationParameters.contains("futureSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) {
mandatoryResponseParameters.add("futureSchedule");
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
&& loanBasicDetails.isInterestRecalculationEnabled()) {
mandatoryResponseParameters.add(DataTableApiConstant.futureScheduleAssociateParamName);
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
}
if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled()
&& loanBasicDetails.isActive()) {
mandatoryResponseParameters.add("originalSchedule");
if (associationParameters.contains(DataTableApiConstant.originalScheduleAssociateParamName)
&& loanBasicDetails.isInterestRecalculationEnabled() && loanBasicDetails.isActive()) {
mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName);
LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(loanId,
repaymentScheduleRelatedData, disbursementData);
loanBasicDetails = LoanAccountData.withOriginalSchedule(loanBasicDetails, loanScheduleData);
}
}
if (associationParameters.contains("charges")) {
mandatoryResponseParameters.add("charges");
if (associationParameters.contains(DataTableApiConstant.chargesAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.chargesAssociateParamName);
charges = this.loanChargeReadPlatformService.retrieveLoanCharges(loanId);
if (CollectionUtils.isEmpty(charges)) {
charges = null;
}
}
if (associationParameters.contains("collateral")) {
mandatoryResponseParameters.add("collateral");
if (associationParameters.contains(DataTableApiConstant.collateralAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.collateralAssociateParamName);
loanCollateralManagements = this.loanCollateralManagementReadPlatformService.getLoanCollateralResponseDataList(loanId);
for (LoanCollateralResponseData loanCollateralManagement : loanCollateralManagements) {
loanCollateralManagementData.add(loanCollateralManagement.toCommand());
@@ -620,24 +629,30 @@ public class LoansApiResource {
}
}
if (associationParameters.contains("meeting")) {
mandatoryResponseParameters.add("meeting");
if (associationParameters.contains(DataTableApiConstant.meetingAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.meetingAssociateParamName);
meeting = this.calendarReadPlatformService.retrieveLoanCalendar(loanId);
}
if (associationParameters.contains("notes")) {
mandatoryResponseParameters.add("notes");
if (associationParameters.contains(DataTableApiConstant.notesAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.notesAssociateParamName);
notes = this.noteReadPlatformService.retrieveNotesByResource(loanId, NoteType.LOAN.getValue());
if (CollectionUtils.isEmpty(notes)) {
notes = null;
}
}
if (associationParameters.contains("linkedAccount")) {
mandatoryResponseParameters.add("linkedAccount");
if (associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName);
linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId);
}
if (associationParameters.contains(DataTableApiConstant.collectionAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.collectionAssociateParamName);
if (loanBasicDetails.isActive()) {
collectionData = this.loanReadPlatformService.retrieveLoanCollectionData(loanId);
}
}
}
Collection<LoanProductData> productOptions = null;
@@ -707,8 +722,8 @@ public class LoansApiResource {
loanBasicDetails.clientId(), currencyCode, accountStatus, DepositAccountType.SAVINGS_DEPOSIT.getValue());
accountLinkingOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(portfolioAccountDTO);
if (!associationParameters.contains("linkedAccount")) {
mandatoryResponseParameters.add("linkedAccount");
if (!associationParameters.contains(DataTableApiConstant.linkedAccountAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName);
linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(loanId);
}
if (loanBasicDetails.groupId() != null) {
@@ -740,12 +755,12 @@ public class LoansApiResource {
repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled);
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled,
collectionData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
String toReturn = this.toApiJsonSerializer.serialize(settings, loanAccount, this.loanDataParameters);
return toReturn;
return this.toApiJsonSerializer.serialize(settings, loanAccount, this.loanDataParameters);
}
@GET
@@ -0,0 +1,90 @@
/**
* 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.data;
import java.math.BigDecimal;
import java.time.LocalDate;
public final class CollectionData {
private final BigDecimal availableDisbursementAmount;
private final int pastDueDays;
private final LocalDate nextPaymentDueDate;
private final int delinquentDays;
private final LocalDate delinquentDate;
private final BigDecimal delinquentAmount;
private final LocalDate lastPaymentDate;
private final BigDecimal lastPaymentAmount;
private CollectionData(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate, int delinquentDays,
LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate, BigDecimal lastPaymentAmount) {
this.availableDisbursementAmount = availableDisbursementAmount;
this.pastDueDays = pastDueDays;
this.nextPaymentDueDate = nextPaymentDueDate;
this.delinquentDays = delinquentDays;
this.delinquentDate = delinquentDate;
this.delinquentAmount = delinquentAmount;
this.lastPaymentDate = lastPaymentDate;
this.lastPaymentAmount = lastPaymentAmount;
}
public static CollectionData instance(BigDecimal availableDisbursementAmount, int pastDueDays, LocalDate nextPaymentDueDate,
int delinquentDays, LocalDate delinquentDate, BigDecimal delinquentAmount, LocalDate lastPaymentDate,
BigDecimal lastPaymentAmount) {
return new CollectionData(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
delinquentAmount, lastPaymentDate, lastPaymentAmount);
}
public static CollectionData template() {
final BigDecimal zero = BigDecimal.ZERO;
return new CollectionData(zero, 0, null, 0, null, zero, null, zero);
}
public BigDecimal getAvailableDisbursementAmount() {
return availableDisbursementAmount;
}
public int getPastDueDays() {
return pastDueDays;
}
public LocalDate getNextPaymentDueDate() {
return nextPaymentDueDate;
}
public int getDelinquentDays() {
return delinquentDays;
}
public LocalDate getDelinquentDate() {
return delinquentDate;
}
public BigDecimal getDelinquentAmount() {
return delinquentAmount;
}
public LocalDate getLastPaymentDate() {
return lastPaymentDate;
}
public BigDecimal getLastPaymentAmount() {
return lastPaymentAmount;
}
}
@@ -238,6 +238,8 @@ public final class LoanAccountData {
private Long groupId;
private LocalDate expectedDisbursementDate;
private final CollectionData delinquent;
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
Integer repaymentEvery, EnumOptionData repaidEveryFrequencyEnums, Integer loanTermFrequency,
@@ -423,6 +425,7 @@ public final class LoanAccountData {
this.isEqualAmortization = null;
this.isRatesEnabled = false;
this.fixedPrincipalPercentagePerInstallment = null;
this.delinquent = null;
}
public Integer getRowIndex() {
@@ -588,6 +591,7 @@ public final class LoanAccountData {
final List<RateData> rates = null;
final Boolean isRatesEnabled = false;
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -609,8 +613,7 @@ public final class LoanAccountData {
interestRecalculationData, originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
fixedPrincipalPercentagePerInstallment);
fixedPrincipalPercentagePerInstallment, delinquent);
}
/**
@@ -734,6 +737,7 @@ public final class LoanAccountData {
final List<RateData> rates = null;
final Boolean isRatesEnabled = false;
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -755,8 +759,7 @@ public final class LoanAccountData {
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
fixedPrincipalPercentagePerInstallment);
fixedPrincipalPercentagePerInstallment, delinquent);
}
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
@@ -787,7 +790,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
/**
@@ -913,6 +916,7 @@ public final class LoanAccountData {
final List<RateData> rates = null;
final Boolean isRatesEnabled = false;
final BigDecimal fixedPrincipalPercentagePerInstallment = null;
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -934,8 +938,7 @@ public final class LoanAccountData {
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods,
isVariableInstallmentsAllowed, minimumGap, maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup,
closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, rates, isRatesEnabled,
fixedPrincipalPercentagePerInstallment);
fixedPrincipalPercentagePerInstallment, delinquent);
}
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
@@ -965,8 +968,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product,
@@ -1107,6 +1109,7 @@ public final class LoanAccountData {
final BigDecimal topupAmount = null;
final List<RateData> rates = null;
final Boolean isRatesEnabled = false;
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(),
@@ -1133,7 +1136,7 @@ public final class LoanAccountData {
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
product.getMaximumGapBetweenInstallments(), subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId,
closureLoanAccountNo, topupAmount, product.isEqualAmortization(), rates, isRatesEnabled,
product.getFixedPrincipalPercentagePerInstallment());
product.getFixedPrincipalPercentagePerInstallment(), delinquent);
}
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
@@ -1175,6 +1178,7 @@ public final class LoanAccountData {
netDisbursalAmount = netDisbursalAmount.subtract(charge.getAmount());
}
}
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientAccountNo, acc.clientName,
acc.clientOfficeId, acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(),
@@ -1202,8 +1206,7 @@ public final class LoanAccountData {
product.isVariableInstallmentsAllowed(), product.getMinimumGapBetweenInstallments(),
product.getMaximumGapBetweenInstallments(), acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, product.isEqualAmortization(), acc.rates, acc.isRatesEnabled,
product.getFixedPrincipalPercentagePerInstallment());
product.getFixedPrincipalPercentagePerInstallment(), delinquent);
}
/*
@@ -1273,6 +1276,7 @@ public final class LoanAccountData {
final Collection<LoanAccountSummaryData> clientActiveLoanOptions = null;
final List<RateData> rates = null;
final Boolean isRatesEnabled = false;
final CollectionData delinquent = CollectionData.template();
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, group,
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName,
@@ -1293,7 +1297,7 @@ public final class LoanAccountData {
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, isVariableInstallmentsAllowed, minimumGap,
maximumGap, subStatus, canUseForTopup, clientActiveLoanOptions, isTopup, closureLoanId, closureLoanAccountNo, topupAmount,
isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment);
isEqualAmortization, rates, isRatesEnabled, fixedPrincipalPercentagePerInstallment, delinquent);
}
/*
@@ -1316,7 +1320,8 @@ public final class LoanAccountData {
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods,
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final List<RateData> rates, final Boolean isRatesEnabled) {
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final List<RateData> rates, final Boolean isRatesEnabled,
final CollectionData delinquent) {
LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
if (acc.product != null) {
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
@@ -1346,7 +1351,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, clientActiveLoanOptions, acc.isTopup, acc.closureLoanId,
acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, rates, isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, delinquent);
}
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
@@ -1359,7 +1364,7 @@ public final class LoanAccountData {
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance,
acc.interestRatesPeriods, acc.clientActiveLoanOptions, acc.rates, isRatesEnabled);
acc.interestRatesPeriods, acc.clientActiveLoanOptions, acc.rates, isRatesEnabled, acc.delinquent);
}
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
@@ -1389,7 +1394,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
@@ -1455,8 +1460,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData,
@@ -1490,7 +1494,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
@@ -1519,7 +1523,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
@@ -1549,7 +1553,7 @@ public final class LoanAccountData {
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods, acc.isVariableInstallmentsAllowed,
acc.minimumGap, acc.maximumGap, acc.subStatus, acc.canUseForTopup, acc.clientActiveLoanOptions, acc.isTopup,
acc.closureLoanId, acc.closureLoanAccountNo, acc.topupAmount, acc.isEqualAmortization, acc.rates, acc.isRatesEnabled,
acc.fixedPrincipalPercentagePerInstallment);
acc.fixedPrincipalPercentagePerInstallment, acc.delinquent);
}
private LoanAccountData(final Long id, //
@@ -1602,7 +1606,7 @@ public final class LoanAccountData {
final Integer minimumGap, final Integer maximumGap, final EnumOptionData subStatus, final Boolean canUseForTopup,
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final boolean isTopup, final Long closureLoanId,
final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization, final List<RateData> rates,
final Boolean isRatesEnabled, final BigDecimal fixedPrincipalPercentagePerInstallment) {
final Boolean isRatesEnabled, final BigDecimal fixedPrincipalPercentagePerInstallment, final CollectionData delinquent) {
this.id = id;
this.accountNo = accountNo;
@@ -1789,6 +1793,7 @@ public final class LoanAccountData {
this.isEqualAmortization = isEqualAmortization;
this.rates = rates;
this.fixedPrincipalPercentagePerInstallment = fixedPrincipalPercentagePerInstallment;
this.delinquent = delinquent;
}
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
@@ -147,4 +148,5 @@ public interface LoanReadPlatformService {
List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId);
CollectionData retrieveLoanCollectionData(Long loanId);
}
@@ -81,6 +81,7 @@ import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.group.data.GroupRoleData;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
import org.apache.fineract.portfolio.loanaccount.data.CollectionData;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.LoanAccountData;
import org.apache.fineract.portfolio.loanaccount.data.LoanApplicationTimelineData;
@@ -2349,4 +2350,58 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final String sql = "select count(*) from m_loan";
return this.jdbcTemplate.queryForObject(sql, Integer.class);
}
@Override
public CollectionData retrieveLoanCollectionData(Long loanId) {
final CollectionDataMapper mapper = new CollectionDataMapper(sqlGenerator);
String sql = "select " + mapper.schema();
CollectionData collectionData = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { loanId });
return collectionData;
}
private static final class CollectionDataMapper implements RowMapper<CollectionData> {
private final DatabaseSpecificSQLGenerator sqlGenerator;
CollectionDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
this.sqlGenerator = sqlGenerator;
}
public String schema() {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append(
"l.id as loanId, coalesce((l.approved_principal - l.principal_disbursed_derived), 0) as availableDisbursementAmount, ");
sqlBuilder.append(sqlGenerator.dateDiff(sqlGenerator.currentDate(), "laa.overdue_since_date_derived") + " as pastDueDays, ");
sqlBuilder.append(
"(select coalesce(min(lrs.duedate), null) as duedate from m_loan_repayment_schedule lrs where lrs.loan_id=l.id and lrs.completed_derived is false and lrs.duedate >= "
+ sqlGenerator.currentDate() + ") as nextPaymentDueDate, ");
sqlBuilder.append(sqlGenerator.dateDiff(sqlGenerator.currentDate(), "laa.overdue_since_date_derived") + " as delinquentDays, ");
sqlBuilder.append(
sqlGenerator.currentDate() + " as delinquentDate, coalesce(laa.total_overdue_derived, 0) as delinquentAmount, ");
sqlBuilder.append("lre.transactionDate as lastPaymentDate, coalesce(lre.amount, 0) as lastPaymentAmount ");
sqlBuilder.append("from m_loan l inner join m_loan_arrears_aging laa on laa.loan_id = l.id ");
sqlBuilder.append(
"left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt ");
sqlBuilder.append(
"where lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc limit 1) lre on lre.loan_id = l.id ");
sqlBuilder.append("where l.id=? ");
return sqlBuilder.toString();
}
@Override
public CollectionData mapRow(ResultSet rs, int rowNum) throws SQLException {
final LocalDate nextPaymentDueDate = JdbcSupport.getLocalDate(rs, "nextPaymentDueDate");
final LocalDate delinquentDate = JdbcSupport.getLocalDate(rs, "delinquentDate");
final LocalDate lastPaymentDate = JdbcSupport.getLocalDate(rs, "lastPaymentDate");
final BigDecimal availableDisbursementAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "availableDisbursementAmount");
final BigDecimal delinquentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "delinquentAmount");
final BigDecimal lastPaymentAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "lastPaymentAmount");
final int pastDueDays = rs.getInt("pastDueDays");
final int delinquentDays = rs.getInt("delinquentDays");
return CollectionData.instance(availableDisbursementAmount, pastDueDays, nextPaymentDueDate, delinquentDays, delinquentDate,
delinquentAmount, lastPaymentDate, lastPaymentAmount);
}
}
}
@@ -32,7 +32,7 @@
<!-- See https://github.com/apache/fineract/#logging-guidelines for why by default we log only to INFO, only (WARN and) ERROR
but it's still possible to override this using java -Dlogging.level.root=info -jar fineract-provider.jar, as per https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-custom-log-levels -->
<root level="warn">
<root level="info">
<appender-ref ref="CONSOLE" />
</root>
@@ -24,7 +24,7 @@
<resetJUL>false</resetJUL>
</contextListener>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<appender name="STDOUT" target="System.out" class="ch.qos.logback.core.ConsoleAppender">
<withJansi>true</withJansi>
<encoder>
<pattern>%green(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %cyan(%logger{36}) - %msg%n</pattern>