FINERACT-1670: Add auditable fields to Loan Repayment Schedule installment and history

This commit is contained in:
Adam Saghy
2022-08-09 10:42:21 +02:00
committed by Aleksandar Vidakovic
parent cad37ed956
commit 4029464e32
8 changed files with 165 additions and 36 deletions
@@ -31,14 +31,14 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.repaymentwithpostdatedchecks.domain.PostDatedChecks;
@Entity
@Table(name = "m_loan_repayment_schedule")
public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCustom
public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom
implements Comparable<LoanRepaymentScheduleInstallment> {
@ManyToOne(optional = false)
@@ -135,7 +135,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "installment")
private Set<LoanInstallmentCharge> installmentCharges = new HashSet<>();
LoanRepaymentScheduleInstallment() {
public LoanRepaymentScheduleInstallment() {
this.installmentNumber = null;
this.fromDate = null;
this.dueDate = null;
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
@@ -28,6 +29,8 @@ import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE_DB_FIELD;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
@@ -65,7 +68,7 @@ public class LoanRepaymentScheduleHistory extends AbstractPersistableCustom {
private BigDecimal penaltyCharges;
@Column(name = "created_date")
private LocalDateTime createdOnDate;
private LocalDateTime oldCreatedOnDate;
@Column(name = "createdby_id")
private Long createdByUser;
@@ -74,7 +77,13 @@ public class LoanRepaymentScheduleHistory extends AbstractPersistableCustom {
private Long lastModifiedByUser;
@Column(name = "lastmodified_date")
private LocalDateTime lastModifiedOnDate;
private LocalDateTime oldLastModifiedOnDate;
@Column(name = CREATED_DATE_DB_FIELD)
private OffsetDateTime createdDate;
@Column(name = LAST_MODIFIED_DATE_DB_FIELD)
private OffsetDateTime lastModifiedDate;
@Column(name = "version")
private Integer version;
@@ -90,8 +99,8 @@ public class LoanRepaymentScheduleHistory extends AbstractPersistableCustom {
private LoanRepaymentScheduleHistory(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
final Integer installmentNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principal,
final BigDecimal interestCharged, final BigDecimal feeChargesCharged, final BigDecimal penaltyCharges,
final LocalDateTime createdOnDate, final Long createdByUser, final Long lastModifiedByUser,
final LocalDateTime lastModifiedOnDate, final Integer version) {
final LocalDateTime oldCreatedOnDate, final Long createdByUser, final Long lastModifiedByUser,
final LocalDateTime oldLastModifiedOnDate, final Integer version, final OffsetDateTime createdDate, final OffsetDateTime lastModifiedDate) {
this.loan = loan;
this.loanRescheduleRequest = loanRescheduleRequest;
@@ -102,11 +111,13 @@ public class LoanRepaymentScheduleHistory extends AbstractPersistableCustom {
this.interestCharged = interestCharged;
this.feeChargesCharged = feeChargesCharged;
this.penaltyCharges = penaltyCharges;
this.createdOnDate = createdOnDate;
this.oldCreatedOnDate = oldCreatedOnDate;
this.createdByUser = createdByUser;
this.lastModifiedByUser = lastModifiedByUser;
this.lastModifiedOnDate = lastModifiedOnDate;
this.oldLastModifiedOnDate = oldLastModifiedOnDate;
this.version = version;
this.createdDate = createdDate;
this.lastModifiedDate = lastModifiedDate;
}
/**
@@ -115,12 +126,12 @@ public class LoanRepaymentScheduleHistory extends AbstractPersistableCustom {
public static LoanRepaymentScheduleHistory instance(final Loan loan, final LoanRescheduleRequest loanRescheduleRequest,
final Integer installmentNumber, final LocalDate fromDate, final LocalDate dueDate, final BigDecimal principal,
final BigDecimal interestCharged, final BigDecimal feeChargesCharged, final BigDecimal penaltyCharges,
final LocalDateTime createdOnDate, final Long createdByUser, final Long lastModifiedByUser,
final LocalDateTime lastModifiedOnDate, final Integer version) {
final LocalDateTime oldCreatedOnDate, final Long createdByUser, final Long lastModifiedByUser,
final LocalDateTime oldLastModifiedOnDate, final Integer version, final OffsetDateTime createdDate, final OffsetDateTime lastModifiedDate) {
return new LoanRepaymentScheduleHistory(loan, loanRescheduleRequest, installmentNumber, fromDate, dueDate, principal,
interestCharged, feeChargesCharged, penaltyCharges, createdOnDate, createdByUser, lastModifiedByUser, lastModifiedOnDate,
version);
interestCharged, feeChargesCharged, penaltyCharges, oldCreatedOnDate, createdByUser, lastModifiedByUser, oldLastModifiedOnDate,
version, createdDate, lastModifiedDate);
}
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.loanschedule.service;
import java.util.Collection;
import java.util.Map;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
@@ -29,4 +30,6 @@ public interface LoanScheduleHistoryReadPlatformService {
LoanScheduleData retrieveRepaymentArchiveSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
Collection<DisbursementData> disbursementData);
Map<String, Object> fetchOldAuditDates(Long id);
}
@@ -25,6 +25,7 @@ import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
@@ -86,6 +87,12 @@ public class LoanScheduleHistoryReadPlatformServiceImpl implements LoanScheduleH
}
}
@Override
public Map<String, Object> fetchOldAuditDates(Long id) {
final String sql = "select lrs.created_date, lrs.lastmodified_date from m_loan_repayment_schedule lrs where lrs.id = ?";
return this.jdbcTemplate.queryForMap(sql, id);
}
private static final class LoanScheduleArchiveResultSetExtractor implements ResultSetExtractor<LoanScheduleData> {
private final CurrencyData currency;
@@ -21,32 +21,31 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistoryRepository;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
@RequiredArgsConstructor
public class LoanScheduleHistoryWritePlatformServiceImpl implements LoanScheduleHistoryWritePlatformService {
private final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService;
private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository;
@Autowired
public LoanScheduleHistoryWritePlatformServiceImpl(final LoanScheduleHistoryReadPlatformService loanScheduleHistoryReadPlatformService,
final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository) {
this.loanScheduleHistoryReadPlatformService = loanScheduleHistoryReadPlatformService;
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
}
private final PlatformSecurityContext platformSecurityContext;
@Override
public List<LoanRepaymentScheduleHistory> createLoanScheduleArchive(
@@ -73,23 +72,33 @@ public class LoanScheduleHistoryWritePlatformServiceImpl implements LoanSchedule
final BigDecimal feeChargesCharged = repaymentScheduleInstallment.getFeeChargesCharged(currency).getAmount();
final BigDecimal penaltyCharges = repaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount();
LocalDateTime createdOnDate = null;
Map<String, Object> oldDates = null;
OffsetDateTime createdOnDate = DateUtils.getOffsetDateTimeOfTenant();
LocalDateTime oldCreatedOnDate = null;
LocalDateTime oldLastModifiedOnDate = null;
if (repaymentScheduleInstallment.getCreatedDate().isPresent()) {
createdOnDate = repaymentScheduleInstallment.getCreatedDate().get(); // NOSONAR
createdOnDate = repaymentScheduleInstallment.getCreatedDate().get();
} else if (repaymentScheduleInstallment.getId() != null) {
oldDates = loanScheduleHistoryReadPlatformService.fetchOldAuditDates(repaymentScheduleInstallment.getId());
oldCreatedOnDate = (LocalDateTime) oldDates.get("created_date");
oldLastModifiedOnDate = (LocalDateTime) oldDates.get("lastmodified_date");
}
final Long createdByUser = repaymentScheduleInstallment.getCreatedBy().orElse(null);
final Long lastModifiedByUser = repaymentScheduleInstallment.getLastModifiedBy().orElse(null);
LocalDateTime lastModifiedOnDate = null;
final Long createdByUser = repaymentScheduleInstallment.getCreatedBy().orElse(platformSecurityContext.authenticatedUser().getId());
final Long lastModifiedByUser = repaymentScheduleInstallment.getLastModifiedBy().orElse(platformSecurityContext.authenticatedUser().getId());
OffsetDateTime lastModifiedOnDate = DateUtils.getOffsetDateTimeOfTenant();
if (repaymentScheduleInstallment.getLastModifiedDate().isPresent()) {
lastModifiedOnDate = repaymentScheduleInstallment.getLastModifiedDate().get(); // NOSONAR
lastModifiedOnDate = repaymentScheduleInstallment.getLastModifiedDate().get();
} else if (repaymentScheduleInstallment.getId() != null && oldDates == null) {
oldDates = loanScheduleHistoryReadPlatformService.fetchOldAuditDates(repaymentScheduleInstallment.getId());
oldCreatedOnDate = (LocalDateTime) oldDates.get("created_date");
oldLastModifiedOnDate = (LocalDateTime) oldDates.get("lastmodified_date");
}
LoanRepaymentScheduleHistory loanRepaymentScheduleHistory = LoanRepaymentScheduleHistory.instance(loan, loanRescheduleRequest,
installmentNumber, fromDate, dueDate, principal, interestCharged, feeChargesCharged, penaltyCharges, createdOnDate,
createdByUser, lastModifiedByUser, lastModifiedOnDate, version);
installmentNumber, fromDate, dueDate, principal, interestCharged, feeChargesCharged, penaltyCharges, oldCreatedOnDate,
createdByUser, lastModifiedByUser, oldLastModifiedOnDate, version, createdOnDate, lastModifiedOnDate);
loanRepaymentScheduleHistoryList.add(loanRepaymentScheduleHistory);
}
@@ -475,9 +475,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
loan.recalculateAllCharges();
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
for (LoanRepaymentScheduleHistory loanRepaymentScheduleHistory : loanRepaymentScheduleHistoryList) {
this.loanRepaymentScheduleHistoryRepository.save(loanRepaymentScheduleHistory);
}
this.loanRepaymentScheduleHistoryRepository.saveAll(loanRepaymentScheduleHistoryList);
loan.updateRescheduledByUser(appUser);
loan.updateRescheduledOnDate(DateUtils.getBusinessLocalDate());
@@ -1943,9 +1943,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// Subtract the amount waived from the existing fee charges waived amount.
chargePerInstallment.getInstallment().setFeeChargesWaived(feeChargesWaivedAmount.subtract(amountWaived));
// Set the last modification date.
chargePerInstallment.getInstallment().setLastModifiedDate(DateUtils.getLocalDateTimeOfSystem());
// Update loan charge.
loanCharge.setInstallmentLoanCharge(chargePerInstallment, chargePerInstallment.getInstallment().getInstallmentNumber());
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet author="fineract" id="loan_repayment_schedule-1" context="mysql">
<addColumn tableName="m_loan_repayment_schedule">
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</addColumn>
</changeSet>
<changeSet author="fineract" id="loan_repayment_schedule-1" context="postgresql">
<addColumn tableName="m_loan_repayment_schedule">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<changeSet id="loan_repayment_schedule-2" author="fineract">
<dropNotNullConstraint tableName="m_loan_repayment_schedule" columnName="created_date" columnDataType="datetime"/>
<dropNotNullConstraint tableName="m_loan_repayment_schedule" columnName="lastmodified_date" columnDataType="datetime"/>
</changeSet>
<changeSet id="loan_repayment_schedule-3" author="fineract">
<renameColumn tableName="m_loan_repayment_schedule" oldColumnName="createdby_id" newColumnName="created_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_loan_repayment_schedule" oldColumnName="lastmodifiedby_id" newColumnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet author="fineract" id="loan_repayment_schedule-4">
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_loan_repayment_schedule"
constraintName="FK_loan_repayment_schedule_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_loan_repayment_schedule"
constraintName="FK_loan_repayment_schedule_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
</changeSet>
<changeSet id="loan_repayment_schedule-5" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_repayment_schedule</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
</changeSet>
<changeSet id="loan_repayment_schedule-5" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_repayment_schedule</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
</changeSet>
<changeSet id="loan_repayment_schedule-6" author="fineract">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_repayment_schedule where created_by is null or last_modified_by is null</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_loan_repayment_schedule" columnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet author="fineract" id="loan_repayment_schedule_history-1" context="mysql">
<addColumn tableName="m_loan_repayment_schedule_history">
<column name="created_on_utc" type="DATETIME"/>
<column name="last_modified_on_utc" type="DATETIME"/>
</addColumn>
</changeSet>
<changeSet author="fineract" id="loan_repayment_schedule_history-1" context="postgresql">
<addColumn tableName="m_loan_repayment_schedule_history">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<changeSet id="loan_repayment_schedule_history-2" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_repayment_schedule_history</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_repayment_schedule_history" columnName="created_on_utc" columnDataType="DATETIME"/>
<addNotNullConstraint tableName="m_loan_repayment_schedule_history" columnName="last_modified_on_utc" columnDataType="DATETIME"/>
</changeSet>
<changeSet id="loan_repayment_schedule_history-2" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_loan_repayment_schedule_history</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_loan_repayment_schedule_history" columnName="created_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
<addNotNullConstraint tableName="m_loan_repayment_schedule_history" columnName="last_modified_on_utc" columnDataType="TIMESTAMP WITH TIME ZONE"/>
</changeSet>
</databaseChangeLog>