Loan transaction date handling enhancement + Make Loan transaction auditable

This commit is contained in:
Adam Saghy
2022-07-07 15:56:43 +02:00
committed by Arnold Gálovics
parent ad4fa563de
commit b30d5c5a16
14 changed files with 259 additions and 299 deletions
@@ -664,7 +664,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
sqlBuilder.append(" left join m_loan loan on loan_txn.loan_id = loan.id ");
sqlBuilder.append(" left join m_client cl on loan.client_id = cl.id ");
sqlBuilder.append(" left join m_office o on cl.office_id = o.id ");
sqlBuilder.append(" left join m_appuser user on loan_txn.appuser_id = user.id ");
sqlBuilder.append(" left join m_appuser user on loan_txn.createdby_id = user.id ");
sqlBuilder.append(" left join m_staff staff on user.staff_id = staff.id ");
sqlBuilder.append(" left join m_cashiers c on c.staff_id = staff.id ");
sqlBuilder.append(" left join m_payment_detail payDetails on payDetails.id = loan_txn.payment_detail_id ");
@@ -829,7 +829,7 @@ public class TellerManagementReadPlatformServiceImpl implements TellerManagement
sqlBuilder.append(" left join m_loan loan on loan_txn.loan_id = loan.id ");
sqlBuilder.append(" left join m_client cl on loan.client_id = cl.id ");
sqlBuilder.append(" left join m_office o on cl.office_id = o.id ");
sqlBuilder.append(" left join m_appuser user on loan_txn.appuser_id = user.id ");
sqlBuilder.append(" left join m_appuser user on loan_txn.createdby_id = user.id ");
sqlBuilder.append(" left join m_staff staff on user.staff_id = staff.id ");
sqlBuilder.append(" left join m_cashiers c on c.staff_id = staff.id ");
sqlBuilder.append(" left join m_payment_detail payDetails on payDetails.id = loan_txn.payment_detail_id ");
@@ -688,8 +688,7 @@ public class Loan extends AbstractPersistableCustom {
* @param suppliedTransactionDate
* @return
*/
public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate,
final AppUser currentUser) {
public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
final Money chargeAmount = loanCharge.getAmount(getCurrency());
Money feeCharges = chargeAmount;
Money penaltyCharges = Money.zero(loanCurrency());
@@ -714,7 +713,7 @@ public class Loan extends AbstractPersistableCustom {
}
final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount,
transactionDate, feeCharges, penaltyCharges, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, feeCharges, penaltyCharges);
Integer installmentNumber = null;
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge,
@@ -1009,8 +1008,7 @@ public class Loan extends AbstractPersistableCustom {
public LoanTransaction waiveLoanCharge(final LoanCharge loanCharge, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final Integer loanInstallmentNumber, final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge,
final AppUser currentUser) {
final Integer loanInstallmentNumber, final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge) {
validateLoanIsNotClosed(loanCharge);
@@ -1062,14 +1060,14 @@ public class Loan extends AbstractPersistableCustom {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived, transactionDate,
feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), currentUser);
feeChargesWaived, penaltyChargesWaived, unrecognizedIncome);
final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge,
waiveLoanChargeTransaction.getAmount(getCurrency()).getAmount(), loanInstallmentNumber);
waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy);
addLoanTransaction(waiveLoanChargeTransaction);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
&& (loanCharge.getDueLocalDate() == null || DateUtils.getBusinessLocalDate().isAfter(loanCharge.getDueLocalDate()))) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
}
// Waive of charges whose due date falls after latest 'repayment'
// transaction dont require entire loan schedule to be reprocessed.
@@ -1225,7 +1223,7 @@ public class Loan extends AbstractPersistableCustom {
this.rates.addAll(loanRates);
}
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule, AppUser currentUser) {
public void updateLoanSchedule(final LoanScheduleModel modifiedLoanSchedule) {
this.repaymentScheduleInstallments.clear();
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
@@ -1242,11 +1240,11 @@ public class Loan extends AbstractPersistableCustom {
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields();
applyAccurals(currentUser);
applyAccurals();
}
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments, AppUser currentUser) {
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments) {
List<LoanRepaymentScheduleInstallment> existingInstallments = new ArrayList<>(this.repaymentScheduleInstallments);
repaymentScheduleInstallments.clear();
for (final LoanRepaymentScheduleInstallment installment : installments) {
@@ -1262,7 +1260,7 @@ public class Loan extends AbstractPersistableCustom {
}
updateLoanScheduleDependentDerivedFields();
updateLoanSummaryDerivedFields();
applyAccurals(currentUser);
applyAccurals();
}
@@ -1279,13 +1277,13 @@ public class Loan extends AbstractPersistableCustom {
/**
* method updates accrual derived fields on installments and reverse the unprocessed transactions
*/
private void applyAccurals(AppUser currentUser) {
private void applyAccurals() {
Collection<LoanTransaction> accruals = retreiveListOfAccrualTransactions();
if (accruals.size() > 0) {
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
applyPeriodicAccruals(accruals);
} else if (isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
updateAccrualsForNonPeriodicAccruals(accruals, currentUser);
updateAccrualsForNonPeriodicAccruals(accruals);
}
}
}
@@ -1325,7 +1323,7 @@ public class Loan extends AbstractPersistableCustom {
}
}
private void updateAccrualsForNonPeriodicAccruals(final Collection<LoanTransaction> accruals, final AppUser currentUser) {
private void updateAccrualsForNonPeriodicAccruals(final Collection<LoanTransaction> accruals) {
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
for (LoanTransaction loanTransaction : accruals) {
@@ -1334,7 +1332,7 @@ public class Loan extends AbstractPersistableCustom {
loanTransaction.reverse();
final LocalDateTime currentDateTime = DateUtils.getLocalDateTimeOfTenant();
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
getDisbursementDate(), currentDateTime, currentUser);
getDisbursementDate());
addLoanTransaction(interestAppliedTransaction);
}
} else {
@@ -1344,7 +1342,7 @@ public class Loan extends AbstractPersistableCustom {
Money chargeAmount = loanCharge.getAmount(getCurrency());
if (chargeAmount.isNotEqualTo(loanTransaction.getAmount(getCurrency()))) {
loanTransaction.reverse();
handleChargeAppliedTransaction(loanCharge, loanTransaction.getTransactionDate(), currentUser);
handleChargeAppliedTransaction(loanCharge, loanTransaction.getTransactionDate());
}
}
@@ -2068,7 +2066,7 @@ public class Loan extends AbstractPersistableCustom {
final LocalDate submittedOn, final String externalId, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
updateLoanSchedule(loanSchedule, currentUser);
updateLoanSchedule(loanSchedule);
LoanStatus from = null;
if (this.loanStatus != null) {
@@ -2483,12 +2481,11 @@ public class Loan extends AbstractPersistableCustom {
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
isDisbursementMissed(), scheduleGeneratorDTO, currentUser);
isDisbursementMissed(), scheduleGeneratorDTO);
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
handleDisbursementTransaction(actualDisbursementDate, createdDate, currentUser, paymentDetail);
handleDisbursementTransaction(actualDisbursementDate, paymentDetail);
updateLoanSummaryDerivedFields();
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
@@ -2500,7 +2497,7 @@ public class Loan extends AbstractPersistableCustom {
if (((isMultiDisburmentLoan() && getDisbursedLoanDisbursementDetails().size() == 1) || !isMultiDisburmentLoan())
&& isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
actualDisbursementDate, createdDate, currentUser);
actualDisbursementDate);
addLoanTransaction(interestAppliedTransaction);
}
@@ -2509,12 +2506,12 @@ public class Loan extends AbstractPersistableCustom {
}
private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO, AppUser currentUser) {
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO) {
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
if (interestRecalculationEnabledParam
&& (firstInstallmentDueDate.isBefore(DateUtils.getBusinessLocalDate()) || disbursementMissedParam)) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
}
}
@@ -2532,7 +2529,7 @@ public class Loan extends AbstractPersistableCustom {
}
public void regenerateScheduleOnDisbursement(final ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean recalculateSchedule,
final LocalDate actualDisbursementDate, BigDecimal emiAmount, final AppUser currentUser, LocalDate nextPossibleRepaymentDate,
final LocalDate actualDisbursementDate, BigDecimal emiAmount, LocalDate nextPossibleRepaymentDate,
Date rescheduledRepaymentDate) {
boolean isEmiAmountChanged = false;
if ((this.loanProduct.isMultiDisburseLoan() || this.loanProduct.canDefineInstallmentAmount()) && emiAmount != null
@@ -2565,9 +2562,9 @@ public class Loan extends AbstractPersistableCustom {
if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
|| rescheduledRepaymentDate != null) {
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
} else {
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
}
}
@@ -2789,12 +2786,12 @@ public class Loan extends AbstractPersistableCustom {
/*
* Ability to regenerate the repayment schedule based on the loans current details/state.
*/
public void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
public void regenerateRepaymentSchedule(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final LoanScheduleModel loanSchedule = regenerateScheduleModel(scheduleGeneratorDTO);
if (loanSchedule == null) {
return;
}
updateLoanSchedule(loanSchedule, currentUser);
updateLoanSchedule(loanSchedule);
final Set<LoanCharge> charges = this.charges();
for (final LoanCharge loanCharge : charges) {
if (!loanCharge.isWaived()) {
@@ -2855,8 +2852,7 @@ public class Loan extends AbstractPersistableCustom {
return interestRate;
}
private void handleDisbursementTransaction(final LocalDate disbursedOn, final LocalDateTime createdDate, final AppUser currentUser,
final PaymentDetail paymentDetail) {
private void handleDisbursementTransaction(final LocalDate disbursedOn, final PaymentDetail paymentDetail) {
// add repayment transaction to track incoming money from client to mfi
// for (charges due at time of disbursement)
@@ -2873,7 +2869,7 @@ public class Loan extends AbstractPersistableCustom {
Money disbursentMoney = Money.zero(getCurrency());
final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, paymentDetail,
disbursedOn, null, createdDate, currentUser);
disbursedOn, null);
final Integer installmentNumber = null;
for (final LoanCharge charge : charges()) {
Date actualDisbursementDate = getActualDisbursementDate(charge);
@@ -2898,7 +2894,7 @@ public class Loan extends AbstractPersistableCustom {
* create a Charge applied transaction if Up front Accrual, None or Cash based accounting is enabled
**/
if (isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
handleChargeAppliedTransaction(charge, disbursedOn, currentUser);
handleChargeAppliedTransaction(charge, disbursedOn);
}
}
}
@@ -2968,7 +2964,7 @@ public class Loan extends AbstractPersistableCustom {
}
public Map<String, Object> undoDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, AppUser currentUser) {
final List<Long> existingReversedTransactionIds) {
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO);
@@ -3003,7 +2999,7 @@ public class Loan extends AbstractPersistableCustom {
// clear off actual disbusrement date so schedule regeneration
// uses expected date.
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
regenerateRepaymentSchedule(scheduleGeneratorDTO);
if (isDisbursedAmountChanged) {
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
}
@@ -3070,7 +3066,7 @@ public class Loan extends AbstractPersistableCustom {
public ChangedTransactionDetail waiveInterest(final LoanTransaction waiveInterestTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
final List<Long> existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
validateAccountStatus(LoanEvent.LOAN_REPAYMENT_OR_WAIVER);
@@ -3082,7 +3078,7 @@ public class Loan extends AbstractPersistableCustom {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(waiveInterestTransaction,
loanLifecycleStateMachine, null, scheduleGeneratorDTO, currentUser);
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
return changedTransactionDetail;
}
@@ -3091,7 +3087,7 @@ public class Loan extends AbstractPersistableCustom {
public ChangedTransactionDetail makeRepayment(final LoanTransaction repaymentTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds, boolean isRecoveryRepayment, final ScheduleGeneratorDTO scheduleGeneratorDTO,
final AppUser currentUser, Boolean isHolidayValidationDone) {
Boolean isHolidayValidationDone) {
HolidayDetailDTO holidayDetailDTO = null;
LoanEvent event = null;
if (isRecoveryRepayment) {
@@ -3115,7 +3111,7 @@ public class Loan extends AbstractPersistableCustom {
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
final ChangedTransactionDetail changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction,
loanLifecycleStateMachine, null, scheduleGeneratorDTO, currentUser);
loanLifecycleStateMachine, null, scheduleGeneratorDTO);
return changedTransactionDetail;
}
@@ -3184,7 +3180,7 @@ public class Loan extends AbstractPersistableCustom {
private ChangedTransactionDetail handleRepaymentOrRecoveryOrWaiverTransaction(final LoanTransaction loanTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction,
final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
ChangedTransactionDetail changedTransactionDetail = null;
@@ -3289,7 +3285,7 @@ public class Loan extends AbstractPersistableCustom {
}
if (reprocess) {
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
}
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
@@ -3457,8 +3453,8 @@ public class Loan extends AbstractPersistableCustom {
BigDecimal penaltyToPost = cumulativeIncomeFromInstallments.get("penalty")
.subtract(cumulativeIncomeFromIncomePosting.get("penalty"));
BigDecimal amountToPost = interestToPost.add(feeToPost).add(penaltyToPost);
LoanTransaction finalIncomeTransaction = LoanTransaction.incomePosting(this, this.getOffice(), closedDate, amountToPost,
interestToPost, feeToPost, penaltyToPost, null);
LoanTransaction finalIncomeTransaction = LoanTransaction.incomePosting(this, this.getOffice(), closedLocalDate, amountToPost,
interestToPost, feeToPost, penaltyToPost);
addLoanTransaction(finalIncomeTransaction);
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
List<LoanTransaction> updatedAccrualTransactions = retreiveListOfAccrualTransactions();
@@ -3469,7 +3465,7 @@ public class Loan extends AbstractPersistableCustom {
HashMap<String, Object> feeDetails = new HashMap<>();
determineFeeDetails(lastAccruedDate, closedLocalDate, feeDetails);
LoanTransaction finalAccrual = LoanTransaction.accrueTransaction(this, this.getOffice(), closedLocalDate, amountToPost,
interestToPost, feeToPost, penaltyToPost, null);
interestToPost, feeToPost, penaltyToPost);
updateLoanChargesPaidBy(finalAccrual, feeDetails, null);
addLoanTransaction(finalAccrual);
}
@@ -3607,7 +3603,7 @@ public class Loan extends AbstractPersistableCustom {
return loanRepaymentScheduleInstallment;
}
public LoanTransaction deriveDefaultInterestWaiverTransaction(final LocalDateTime createdDate, final AppUser currentUser) {
public LoanTransaction deriveDefaultInterestWaiverTransaction() {
final Money totalInterestOutstanding = getTotalInterestOutstandingOnLoan();
Money possibleInterestToWaive = totalInterestOutstanding.copy();
@@ -3630,13 +3626,13 @@ public class Loan extends AbstractPersistableCustom {
}
return LoanTransaction.waiver(getOffice(), this, possibleInterestToWaive, transactionDate, possibleInterestToWaive,
possibleInterestToWaive.zero(), createdDate, currentUser);
possibleInterestToWaive.zero());
}
public ChangedTransactionDetail adjustExistingTransaction(final LoanTransaction newTransactionDetail,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction transactionForAdjustment,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
validateActivityNotBeforeLastTransactionDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, transactionForAdjustment.getTransactionDate());
@@ -3675,14 +3671,14 @@ public class Loan extends AbstractPersistableCustom {
if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) {
changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine,
transactionForAdjustment, scheduleGeneratorDTO, currentUser);
transactionForAdjustment, scheduleGeneratorDTO);
}
return changedTransactionDetail;
}
public ChangedTransactionDetail undoWrittenOff(final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO);
existingTransactionIds.addAll(findExistingTransactionIds());
@@ -3694,7 +3690,7 @@ public class Loan extends AbstractPersistableCustom {
.determineProcessor(this.transactionProcessingStrategy);
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
}
ChangedTransactionDetail changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(
getDisbursementDate(), allNonContraTransactionsPostDisbursement, getCurrency(), getRepaymentScheduleInstallments(),
@@ -3766,7 +3762,7 @@ public class Loan extends AbstractPersistableCustom {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
loanRepaymentScheduleTransactionProcessor, currentUser);
loanRepaymentScheduleTransactionProcessor);
validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING);
@@ -3805,7 +3801,7 @@ public class Loan extends AbstractPersistableCustom {
}
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId, createdDate, currentUser);
loanTransaction = LoanTransaction.writeoff(this, getOffice(), writtenOffOnLocalDate, txnExternalId);
LocalDate lastTransactionDate = getLastUserTransactionDate();
if (lastTransactionDate.isAfter(writtenOffOnLocalDate)) {
final String errorMessage = "The date of the writeoff transaction must occur on or before previous transactions.";
@@ -3826,14 +3822,14 @@ public class Loan extends AbstractPersistableCustom {
}
private ChangedTransactionDetail closeDisbursements(final ScheduleGeneratorDTO scheduleGeneratorDTO,
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final AppUser currentUser) {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
ChangedTransactionDetail changedTransactionDetail = null;
if (isDisbursementAllowed() && atleastOnceDisbursed()) {
this.loanRepaymentScheduleDetail.setPrincipal(getDisbursedAmount());
removeDisbursementDetail();
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
regenerateRepaymentSchedule(scheduleGeneratorDTO);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
}
final List<LoanTransaction> allNonContraTransactionsPostDisbursement = retreiveListOfTransactionsPostDisbursement();
changedTransactionDetail = loanRepaymentScheduleTransactionProcessor.handleTransaction(getDisbursementDate(),
@@ -3862,7 +3858,7 @@ public class Loan extends AbstractPersistableCustom {
public ChangedTransactionDetail close(final JsonCommand command, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final Map<String, Object> changes, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) {
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
validateAccountStatus(LoanEvent.LOAN_CLOSED);
@@ -3890,7 +3886,7 @@ public class Loan extends AbstractPersistableCustom {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
.determineProcessor(this.transactionProcessingStrategy);
ChangedTransactionDetail changedTransactionDetail = closeDisbursements(scheduleGeneratorDTO,
loanRepaymentScheduleTransactionProcessor, currentUser);
loanRepaymentScheduleTransactionProcessor);
LoanTransaction loanTransaction = null;
if (isOpen()) {
@@ -3904,8 +3900,7 @@ public class Loan extends AbstractPersistableCustom {
changes.put("status", LoanEnumerations.status(this.loanStatus));
}
this.closedOnDate = Date.from(closureDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
loanTransaction = LoanTransaction.writeoff(this, getOffice(), closureDate, txnExternalId,
DateUtils.getLocalDateTimeOfTenant(), currentUser);
loanTransaction = LoanTransaction.writeoff(this, getOffice(), closureDate, txnExternalId);
final boolean isLastTransaction = isChronologicallyLatestTransaction(loanTransaction, getLoanTransactions());
if (!isLastTransaction) {
final String errorMessage = "The closing date of the loan must be on or after latest transaction date.";
@@ -5239,8 +5234,7 @@ public class Loan extends AbstractPersistableCustom {
}
public ChangedTransactionDetail updateDisbursementDateAndAmountForTranche(final LoanDisbursementDetails disbursementDetails,
final JsonCommand command, final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO,
final AppUser currentUser) {
final JsonCommand command, final Map<String, Object> actualChanges, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final Locale locale = command.extractLocale();
validateAccountStatus(LoanEvent.LOAN_EDIT_MULTI_DISBURSE_DATE);
final BigDecimal principal = command.bigDecimalValueOfParameterNamed(LoanApiConstants.updatedDisbursementPrincipalParameterName,
@@ -5266,9 +5260,9 @@ public class Loan extends AbstractPersistableCustom {
this.loanRepaymentScheduleDetail.setPrincipal(setPrincipalAmount);
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
} else {
regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
@@ -5365,7 +5359,7 @@ public class Loan extends AbstractPersistableCustom {
}
public ChangedTransactionDetail recalculateScheduleFromLastTransaction(final ScheduleGeneratorDTO generatorDTO,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds, final AppUser currentUser) {
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
/*
@@ -5376,17 +5370,16 @@ public class Loan extends AbstractPersistableCustom {
* loanTransaction.getTransactionDate(); } } generatorDTO.setRecalculateFrom(recalculateFrom);
*/
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO, currentUser);
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
} else {
regenerateRepaymentSchedule(generatorDTO, currentUser);
regenerateRepaymentSchedule(generatorDTO);
}
return processTransactions();
}
public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO,
final AppUser currentUser) {
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO, currentUser);
public ChangedTransactionDetail handleRegenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
return processTransactions();
}
@@ -5413,14 +5406,14 @@ public class Loan extends AbstractPersistableCustom {
return changedTransactionDetail;
}
public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO, final AppUser currentUser) {
public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
LocalDate lastTransactionDate = getLastUserTransactionDate();
final LoanScheduleDTO loanSchedule = getRecalculatedSchedule(generatorDTO);
if (loanSchedule == null) {
return;
}
updateLoanSchedule(loanSchedule.getInstallments(), currentUser);
updateLoanSchedule(loanSchedule.getInstallments());
this.interestRecalculatedOn = DateUtils.getBusinessDate();
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate(true);
Set<LoanCharge> charges = this.charges();
@@ -5440,7 +5433,7 @@ public class Loan extends AbstractPersistableCustom {
}
processPostDisbursementTransactions();
processIncomeTransactions(currentUser);
processIncomeTransactions();
}
private void updateLoanChargesPaidBy(LoanTransaction accrual, HashMap<String, Object> feeDetails,
@@ -5468,7 +5461,7 @@ public class Loan extends AbstractPersistableCustom {
}
}
public void processIncomeTransactions(AppUser currentUser) {
public void processIncomeTransactions() {
if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()) {
LocalDate lastCompoundingDate = this.getDisbursementDate();
List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = extractInterestRecalculationAdditionalDetails();
@@ -5480,8 +5473,7 @@ public class Loan extends AbstractPersistableCustom {
}
LoanTransaction incomeTransaction = getTransactionForDate(incomeTransactions, compoundingDetail.getEffectiveDate());
LoanTransaction accrualTransaction = getTransactionForDate(accrualTransactions, compoundingDetail.getEffectiveDate());
addUpdateIncomeAndAccrualTransaction(compoundingDetail, lastCompoundingDate, currentUser, incomeTransaction,
accrualTransaction);
addUpdateIncomeAndAccrualTransaction(compoundingDetail, lastCompoundingDate, incomeTransaction, accrualTransaction);
lastCompoundingDate = compoundingDetail.getEffectiveDate();
}
List<LoanRepaymentScheduleInstallment> installments = getRepaymentScheduleInstallments();
@@ -5501,8 +5493,7 @@ public class Loan extends AbstractPersistableCustom {
}
private void addUpdateIncomeAndAccrualTransaction(LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
LocalDate lastCompoundingDate, AppUser currentUser, LoanTransaction existingIncomeTransaction,
LoanTransaction existingAccrualTransaction) {
LocalDate lastCompoundingDate, LoanTransaction existingIncomeTransaction, LoanTransaction existingAccrualTransaction) {
BigDecimal interest = BigDecimal.ZERO;
BigDecimal fee = BigDecimal.ZERO;
BigDecimal penalties = BigDecimal.ZERO;
@@ -5525,28 +5516,26 @@ public class Loan extends AbstractPersistableCustom {
}
if (existingIncomeTransaction == null) {
LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(),
Date.from(compoundingDetail.getEffectiveDate().atStartOfDay(ZoneId.systemDefault()).toInstant()),
compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
compoundingDetail.getAmount(), interest, fee, penalties);
addLoanTransaction(transaction);
} else if (existingIncomeTransaction.getAmount(getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
existingIncomeTransaction.reverse();
LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(),
Date.from(compoundingDetail.getEffectiveDate().atStartOfDay(ZoneId.systemDefault()).toInstant()),
compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
compoundingDetail.getAmount(), interest, fee, penalties);
addLoanTransaction(transaction);
}
if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
if (existingAccrualTransaction == null) {
LoanTransaction accrual = LoanTransaction.accrueTransaction(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
compoundingDetail.getAmount(), interest, fee, penalties);
updateLoanChargesPaidBy(accrual, feeDetails, null);
addLoanTransaction(accrual);
} else if (existingAccrualTransaction.getAmount(getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
existingAccrualTransaction.reverse();
LoanTransaction accrual = LoanTransaction.accrueTransaction(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
compoundingDetail.getAmount(), interest, fee, penalties);
updateLoanChargesPaidBy(accrual, feeDetails, null);
addLoanTransaction(accrual);
}
@@ -6293,7 +6282,7 @@ public class Loan extends AbstractPersistableCustom {
}
public Map<String, Object> undoLastDisbursal(ScheduleGeneratorDTO scheduleGeneratorDTO, List<Long> existingTransactionIds,
List<Long> existingReversedTransactionIds, AppUser currentUser, Loan loan) {
List<Long> existingReversedTransactionIds, Loan loan) {
validateAccountStatus(LoanEvent.LOAN_DISBURSAL_UNDO_LAST);
existingTransactionIds.addAll(findExistingTransactionIds());
@@ -6331,8 +6320,7 @@ public class Loan extends AbstractPersistableCustom {
}
}
reverseExistingTransactionsTillLastDisbursal(actualDisbursementDate);
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds,
currentUser);
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds);
actualChanges.put("undolastdisbursal", "true");
actualChanges.put("disbursedAmount", this.getDisbursedAmount());
updateLoanSummaryDerivedFields();
@@ -6671,16 +6659,14 @@ public class Loan extends AbstractPersistableCustom {
}
public ChangedTransactionDetail handleForeClosureTransactions(final LoanTransaction repaymentTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO,
final AppUser appUser) {
final LoanLifecycleStateMachine loanLifecycleStateMachine, final ScheduleGeneratorDTO scheduleGeneratorDTO) {
LoanEvent event = LoanEvent.LOAN_FORECLOSURE;
validateAccountStatus(event);
validateForForeclosure(repaymentTransaction.getTransactionDate());
this.loanSubStatus = LoanSubStatus.FORECLOSED.getValue();
applyAccurals(appUser);
return handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction, loanLifecycleStateMachine, null, scheduleGeneratorDTO,
appUser);
applyAccurals();
return handleRepaymentOrRecoveryOrWaiverTransaction(repaymentTransaction, loanLifecycleStateMachine, null, scheduleGeneratorDTO);
}
public Money retrieveAccruedAmountAfterDate(final LocalDate tillDate) {
@@ -178,13 +178,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
LoanTransaction newRepaymentTransaction = null;
final LocalDateTime currentDateTime = DateUtils.getLocalDateTimeOfTenant();
if (isRecoveryRepayment) {
newRepaymentTransaction = LoanTransaction.recoveryRepayment(loan.getOffice(), repaymentAmount, paymentDetail, transactionDate,
txnExternalId, currentDateTime, currentUser);
txnExternalId);
} else {
newRepaymentTransaction = LoanTransaction.repaymentType(repaymentTransactionType, loan.getOffice(), repaymentAmount,
paymentDetail, transactionDate, txnExternalId, currentDateTime, currentUser);
paymentDetail, transactionDate, txnExternalId);
}
LocalDate recalculateFrom = null;
@@ -196,7 +195,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
scheduleGeneratorDTO, currentUser, isHolidayValidationDone);
scheduleGeneratorDTO, isHolidayValidationDone);
saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction);
@@ -361,7 +360,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
public LoanTransaction makeChargePayment(final Loan loan, final Long chargeId, final LocalDate transactionDate,
final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final String txnExternalId,
final Integer transactionType, Integer installmentNumber) {
AppUser currentUser = getAppUserIfPresent();
boolean isAccountTransfer = true;
checkClientOrGroupActive(loan);
businessEventNotifierService.notifyPreBusinessEvent(new LoanChargePaymentPreBusinessEvent(loan));
@@ -373,7 +371,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final LoanTransactionType loanTransactionType = LoanTransactionType.fromInt(transactionType);
final LoanTransaction newPaymentTransaction = LoanTransaction.loanPayment(null, loan.getOffice(), paymentAmout, paymentDetail,
transactionDate, txnExternalId, loanTransactionType, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, txnExternalId, loanTransactionType);
if (loanTransactionType.isRepaymentAtDisbursement()) {
loan.handlePayDisbursementTransaction(chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds);
@@ -445,7 +443,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
public LoanTransaction makeRefund(final Long accountId, final CommandProcessingResultBuilder builderResult,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
final String txnExternalId) {
AppUser currentUser = getAppUserIfPresent();
boolean isAccountTransfer = true;
final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
checkClientOrGroupActive(loan);
@@ -455,7 +452,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRefundTransaction = LoanTransaction.refund(loan.getOffice(), refundAmount, paymentDetail, transactionDate,
txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
txnExternalId);
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
Date.from(transactionDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), HolidayStatusType.ACTIVE.getValue());
@@ -492,7 +489,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override
public LoanTransaction makeDisburseTransaction(final Long loanId, final LocalDate transactionDate, final BigDecimal transactionAmount,
final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isLoanToLoanTransfer) {
AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
boolean isAccountTransfer = true;
@@ -500,7 +496,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final List<Long> existingReversedTransactionIds = new ArrayList<>();
final Money amount = Money.of(loan.getCurrency(), transactionAmount);
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amount, paymentDetail, transactionDate,
txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
txnExternalId);
// Subtract Previous loan outstanding balance from netDisbursalAmount
loan.deductFromNetDisbursalAmount(transactionAmount);
@@ -646,11 +642,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
businessEventNotifierService.notifyPreBusinessEvent(new LoanCreditBalanceRefundPreBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
AppUser currentUser = getAppUserIfPresent();
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newCreditBalanceRefundTransaction = LoanTransaction.creditBalanceRefund(loan, loan.getOffice(), refundAmount,
transactionDate, externalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, externalId);
loan.creditBalanceRefund(newCreditBalanceRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds,
existingReversedTransactionIds);
@@ -681,11 +676,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
businessEventNotifierService.notifyPreBusinessEvent(new LoanRefundPreBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
AppUser currentUser = getAppUserIfPresent();
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRefundTransaction = LoanTransaction.refundForActiveLoan(loan.getOffice(), refundAmount, paymentDetail,
transactionDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, txnExternalId);
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
Date.from(transactionDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), HolidayStatusType.ACTIVE.getValue());
@@ -716,7 +710,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
public Map<String, Object> foreCloseLoan(final Loan loan, final LocalDate foreClosureDate, final String noteText) {
businessEventNotifierService.notifyPreBusinessEvent(new LoanForeClosurePreBusinessEvent(loan));
MonetaryCurrency currency = loan.getCurrency();
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
final Map<String, Object> changes = new LinkedHashMap<>();
List<LoanTransaction> newTransactions = new ArrayList<>();
@@ -737,12 +730,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
Money total = interestPortion.plus(feePortion).plus(penaltyPortion);
if (total.isGreaterThanZero()) {
LoanTransaction accrualTransaction = LoanTransaction.accrueTransaction(loan, loan.getOffice(), foreClosureDate,
total.getAmount(), interestPortion.getAmount(), feePortion.getAmount(), penaltyPortion.getAmount(), appUser);
total.getAmount(), interestPortion.getAmount(), feePortion.getAmount(), penaltyPortion.getAmount());
LocalDate fromDate = loan.getDisbursementDate();
if (loan.getAccruedTill() != null) {
fromDate = loan.getAccruedTill();
}
createdDate = createdDate.plusSeconds(1);
newTransactions.add(accrualTransaction);
loan.addLoanTransaction(accrualTransaction);
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
@@ -771,16 +763,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
String externalId = null;
final LocalDateTime currentDateTime = DateUtils.getLocalDateTimeOfTenant();
payment = LoanTransaction.repayment(loan.getOffice(), payPrincipal.plus(interestPayable).plus(feePayable).plus(penaltyPayable),
paymentDetail, foreClosureDate, externalId, currentDateTime, appUser);
createdDate = createdDate.plusSeconds(1);
payment.updateCreatedDate(Date.from(createdDate.atZone(DateUtils.getDateTimeZoneOfTenant()).toInstant()));
paymentDetail, foreClosureDate, externalId);
payment.updateLoan(loan);
newTransactions.add(payment);
}
List<Long> transactionIds = new ArrayList<>();
final ChangedTransactionDetail changedTransactionDetail = loan.handleForeClosureTransactions(payment,
defaultLoanLifecycleStateMachine(), scheduleGeneratorDTO, appUser);
defaultLoanLifecycleStateMachine(), scheduleGeneratorDTO);
/***
* TODO Vishwas Batch save is giving me a HibernateOptimisticLockingFailureException, looping and saving for the
@@ -21,10 +21,8 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -38,10 +36,8 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -53,7 +49,6 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.useradministration.domain.AppUser;
/**
* All monetary transactions against a loan are modelled through this entity. Disbursements, Repayments, Waivers,
@@ -61,7 +56,7 @@ import org.apache.fineract.useradministration.domain.AppUser;
*/
@Entity
@Table(name = "m_loan_transaction", uniqueConstraints = { @UniqueConstraint(columnNames = { "external_id" }, name = "external_id_UNIQUE") })
public class LoanTransaction extends AbstractPersistableCustom {
public class LoanTransaction extends AbstractAuditableCustom {
@ManyToOne(optional = false)
@JoinColumn(name = "loan_id", nullable = false)
@@ -78,13 +73,11 @@ public class LoanTransaction extends AbstractPersistableCustom {
@Column(name = "transaction_type_enum", nullable = false)
private Integer typeOf;
@Temporal(TemporalType.DATE)
@Column(name = "transaction_date", nullable = false)
private Date dateOf;
private LocalDate dateOf;
@Temporal(TemporalType.DATE)
@Column(name = "submitted_on_date", nullable = false)
private Date submittedOnDate;
private LocalDate submittedOnDate;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@@ -113,14 +106,6 @@ public class LoanTransaction extends AbstractPersistableCustom {
@Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_date", nullable = false)
private Date createdDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "appuser_id", nullable = true)
private AppUser appUser;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "loanTransaction", orphanRemoval = true, fetch = FetchType.EAGER)
private Set<LoanChargePaidBy> loanChargesPaid = new HashSet<>();
@@ -136,44 +121,34 @@ public class LoanTransaction extends AbstractPersistableCustom {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanTransaction")
private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = new HashSet<>();
protected LoanTransaction() {
/*
* this.loan = null; this.dateOf = null; this.typeOf = null; this.submittedOnDate = DateUtils.getDateOfTenant();
* this.createdDate = new Date(); this.appUser = null;
*/
}
protected LoanTransaction() {}
public static LoanTransaction incomePosting(final Loan loan, final Office office, final Date dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion,
final AppUser appUser) {
public static LoanTransaction incomePosting(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion) {
final Integer typeOf = LoanTransactionType.INCOME_POSTING.getValue();
final BigDecimal principalPortion = BigDecimal.ZERO;
final BigDecimal overPaymentPortion = BigDecimal.ZERO;
final boolean reversed = false;
final PaymentDetail paymentDetail = null;
final String externalId = null;
final LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
return new LoanTransaction(loan, office, typeOf, dateOf, amount, principalPortion, interestPortion, feeChargesPortion,
penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId, createdDate, appUser);
penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
}
public static LoanTransaction disbursement(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate disbursementDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate disbursementDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate,
externalId, createdDate, appUser);
externalId);
}
public static LoanTransaction repayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId,
createdDate, appUser);
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction repaymentType(final LoanTransactionType repaymentType, final Office office, final Money amount,
final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate,
final AppUser appUser) {
return new LoanTransaction(null, office, repaymentType, paymentDetail, amount.getAmount(), paymentDate, externalId, createdDate,
appUser);
final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, repaymentType, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public void setLoanTransactionToRepaymentScheduleMappings(final Integer installmentId, final BigDecimal chargePerInstallment) {
@@ -189,34 +164,32 @@ public class LoanTransaction extends AbstractPersistableCustom {
}
public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.RECOVERY_REPAYMENT, paymentDetail, amount.getAmount(), paymentDate,
externalId, createdDate, appUser);
externalId);
}
public static LoanTransaction loanPayment(final Loan loan, final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LoanTransactionType transactionType,
final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(loan, office, transactionType, paymentDetail, amount.getAmount(), paymentDate, externalId, createdDate,
appUser);
final LocalDate paymentDate, final String externalId, final LoanTransactionType transactionType) {
return new LoanTransaction(loan, office, transactionType, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction repaymentAtDisbursement(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(),
paymentDate, externalId, createdDate, appUser);
paymentDate, externalId);
}
public static LoanTransaction waiver(final Office office, final Loan loan, final Money amount, final LocalDate waiveDate,
final Money waived, final Money unrecognizedPortion, final LocalDateTime createdDate, final AppUser appUser) {
final Money waived, final Money unrecognizedPortion) {
LoanTransaction loanTransaction = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_INTEREST, amount.getAmount(),
waiveDate, null, createdDate, appUser);
waiveDate, null);
loanTransaction.updateInterestComponent(waived, unrecognizedPortion);
return loanTransaction;
}
public static LoanTransaction accrueInterest(final Office office, final Loan loan, final Money amount,
final LocalDate interestAppliedDate, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate interestAppliedDate) {
BigDecimal principalPortion = null;
BigDecimal feesPortion = null;
BigDecimal penaltiesPortion = null;
@@ -225,96 +198,80 @@ public class LoanTransaction extends AbstractPersistableCustom {
boolean reversed = false;
PaymentDetail paymentDetail = null;
String externalId = null;
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(),
Date.from(interestAppliedDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), interestPortion, principalPortion,
interestPortion, feesPortion, penaltiesPortion, overPaymentPortion, reversed, paymentDetail, externalId, createdDate,
appUser);
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(), interestAppliedDate, interestPortion,
principalPortion, interestPortion, feesPortion, penaltiesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
}
public static LoanTransaction accrual(final Loan loan, final Office office, final Money amount, final Money interest,
final Money feeCharges, final Money penaltyCharges, final LocalDate transactionDate) {
final AppUser appUser = null;
return accrueTransaction(loan, office, transactionDate, amount.getAmount(), interest.getAmount(), feeCharges.getAmount(),
penaltyCharges.getAmount(), appUser);
penaltyCharges.getAmount());
}
public static LoanTransaction accrueTransaction(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion,
final AppUser appUser) {
final BigDecimal interestPortion, final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion) {
BigDecimal principalPortion = null;
BigDecimal overPaymentPortion = null;
boolean reversed = false;
PaymentDetail paymentDetail = null;
String externalId = null;
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(),
Date.from(dateOf.atStartOfDay(ZoneId.systemDefault()).toInstant()), amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId, createdDate, appUser);
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(), dateOf, amount, principalPortion, interestPortion,
feeChargesPortion, penaltyChargesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
}
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(),
Date.from(transferDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), loan.getSummary().getTotalOutstanding(),
loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary().getTotalInterestOutstanding(),
loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null,
null, createdDate, appUser);
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, null);
}
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(),
Date.from(transferDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), loan.getSummary().getTotalOutstanding(),
loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary().getTotalInterestOutstanding(),
loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null,
null, createdDate, appUser);
public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, null);
}
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate,
final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(),
Date.from(transferDate.atStartOfDay(ZoneId.systemDefault()).toInstant()), loan.getSummary().getTotalOutstanding(),
loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary().getTotalInterestOutstanding(),
loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null,
null, createdDate, appUser);
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate,
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(),
loan.getSummary().getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(),
loan.getSummary().getTotalPenaltyChargesOutstanding(), null, false, null, null);
}
public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId,
createdDate, appUser);
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction copyTransactionProperties(final LoanTransaction loanTransaction) {
return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion,
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId,
(loanTransaction.createdDate == null) ? LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant())
: LocalDateTime.ofInstant(loanTransaction.createdDate.toInstant(), DateUtils.getDateTimeZoneOfTenant()),
loanTransaction.appUser);
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId);
}
public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
final Money feeCharges, final Money penaltyCharges, final LocalDateTime createdDate, final AppUser appUser) {
final Money feeCharges, final Money penaltyCharges) {
String externalId = null;
final LoanTransaction applyCharge = new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL, amount.getAmount(), applyDate,
externalId, createdDate, appUser);
externalId);
applyCharge.updateChargesComponents(feeCharges, penaltyCharges);
return applyCharge;
}
public static LoanTransaction creditBalanceRefund(final Loan loan, final Office office, final Money amount, final LocalDate paymentDate,
final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final String externalId) {
final PaymentDetail paymentDetail = null;
return new LoanTransaction(loan, office, LoanTransactionType.CREDIT_BALANCE_REFUND, paymentDetail, amount.getAmount(), paymentDate,
externalId, createdDate, appUser);
externalId);
}
public static LoanTransaction refundForActiveLoan(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.REFUND_FOR_ACTIVE_LOAN, paymentDetail, amount.getAmount(), paymentDate,
externalId, createdDate, appUser);
externalId);
}
public static boolean transactionAmountsMatch(final MonetaryCurrency currency, final LoanTransaction loanTransaction,
@@ -330,10 +287,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
return false;
}
private LoanTransaction(final Loan loan, final Office office, final Integer typeOf, final Date dateOf, final BigDecimal amount,
private LoanTransaction(final Loan loan, final Office office, final Integer typeOf, final LocalDate dateOf, final BigDecimal amount,
final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion,
final BigDecimal penaltyChargesPortion, final BigDecimal overPaymentPortion, final boolean reversed,
final PaymentDetail paymentDetail, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final PaymentDetail paymentDetail, final String externalId) {
this.loan = loan;
this.typeOf = typeOf;
@@ -348,52 +305,43 @@ public class LoanTransaction extends AbstractPersistableCustom {
this.paymentDetail = paymentDetail;
this.office = office;
this.externalId = externalId;
this.submittedOnDate = DateUtils.getBusinessDate();
this.createdDate = Date.from(createdDate.atZone(DateUtils.getDateTimeZoneOfTenant()).toInstant());
this.appUser = appUser;
this.submittedOnDate = DateUtils.getBusinessLocalDate();
}
public static LoanTransaction waiveLoanCharge(final Loan loan, final Office office, final Money waived, final LocalDate waiveDate,
final Money feeChargesWaived, final Money penaltyChargesWaived, final Money unrecognizedCharge, final LocalDateTime createdDate,
final AppUser appUser) {
final Money feeChargesWaived, final Money penaltyChargesWaived, final Money unrecognizedCharge) {
final LoanTransaction waiver = new LoanTransaction(loan, office, LoanTransactionType.WAIVE_CHARGES, waived.getAmount(), waiveDate,
null, createdDate, appUser);
null);
waiver.updateChargesComponents(feeChargesWaived, penaltyChargesWaived, unrecognizedCharge);
return waiver;
}
public static LoanTransaction writeoff(final Loan loan, final Office office, final LocalDate writeOffDate, final String externalId,
final LocalDateTime createdDate, final AppUser appUser) {
return new LoanTransaction(loan, office, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId, createdDate, appUser);
public static LoanTransaction writeoff(final Loan loan, final Office office, final LocalDate writeOffDate, final String externalId) {
return new LoanTransaction(loan, office, LoanTransactionType.WRITEOFF, null, writeOffDate, externalId);
}
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final BigDecimal amount,
final LocalDate date, final String externalId, final LocalDateTime createdDate, final AppUser appUser) {
final LocalDate date, final String externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.amount = amount;
this.dateOf = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
this.dateOf = date;
this.externalId = externalId;
this.office = office;
this.submittedOnDate = DateUtils.getBusinessDate();
this.createdDate = Date.from(createdDate.atZone(DateUtils.getDateTimeZoneOfTenant()).toInstant());
this.appUser = appUser;
this.submittedOnDate = DateUtils.getBusinessLocalDate();
}
private LoanTransaction(final Loan loan, final Office office, final LoanTransactionType type, final PaymentDetail paymentDetail,
final BigDecimal amount, final LocalDate date, final String externalId, final LocalDateTime createdDate,
final AppUser appUser) {
final BigDecimal amount, final LocalDate date, final String externalId) {
this.loan = loan;
this.typeOf = type.getValue();
this.paymentDetail = paymentDetail;
this.amount = amount;
this.dateOf = Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
this.dateOf = date;
this.externalId = externalId;
this.office = office;
this.submittedOnDate = DateUtils.getBusinessDate();
this.createdDate = Date.from(createdDate.atZone(DateUtils.getDateTimeZoneOfTenant()).toInstant());
this.appUser = appUser;
this.submittedOnDate = DateUtils.getBusinessLocalDate();
}
public void reverse() {
@@ -504,10 +452,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
}
public LocalDate getTransactionDate() {
return LocalDate.ofInstant(this.dateOf.toInstant(), DateUtils.getDateTimeZoneOfTenant());
return this.dateOf;
}
public Date getDateOf() {
public LocalDate getDateOf() {
return this.dateOf;
}
@@ -778,14 +726,10 @@ public class LoanTransaction extends AbstractPersistableCustom {
this.manuallyAdjustedOrReversed = true;
}
private LocalDate getCreatedDate() {
return (this.createdDate == null) ? LocalDate.now(DateUtils.getDateTimeZoneOfTenant())
: LocalDate.ofInstant(this.createdDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
}
public LocalDateTime getCreatedDateTime() {
return (this.createdDate == null) ? LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant())
: LocalDateTime.ofInstant(this.createdDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
return (this.getCreatedDate().isPresent()
? LocalDateTime.ofInstant(this.getCreatedDate().get(), DateUtils.getDateTimeZoneOfTenant())
: DateUtils.getLocalDateTimeOfTenant());
}
public boolean isLastTransaction(final LoanTransaction loanTransaction) {
@@ -793,7 +737,7 @@ public class LoanTransaction extends AbstractPersistableCustom {
if (loanTransaction != null) {
isLatest = this.getTransactionDate().isBefore(loanTransaction.getTransactionDate())
|| (this.getTransactionDate().isEqual(loanTransaction.getTransactionDate())
&& this.getCreatedDate().isBefore(loanTransaction.getCreatedDate()));
&& this.getCreatedDateTime().isBefore(loanTransaction.getCreatedDateTime()));
}
return isLatest;
}
@@ -801,7 +745,7 @@ public class LoanTransaction extends AbstractPersistableCustom {
public boolean isLatestTransaction(final LoanTransaction loanTransaction) {
boolean isLatest = false;
if (loanTransaction != null) {
isLatest = this.getCreatedDate().isBefore(loanTransaction.getCreatedDate());
isLatest = this.getCreatedDateTime().isBefore(loanTransaction.getCreatedDateTime());
}
return isLatest;
}
@@ -845,10 +789,6 @@ public class LoanTransaction extends AbstractPersistableCustom {
return isDisbursement() || isAccrual() || isRepaymentAtDisbursement();
}
public void updateCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public boolean isAccrualTransaction() {
return isAccrual();
}
@@ -881,14 +881,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
currentPeriodParams.minusEarlyPaidAmount(unprocessed);
updateMapWithAmount(scheduleParams.getPrincipalPortionMap(), unprocessed.negated(), applicableDate);
LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null,
transactionDate, null, DateUtils.getLocalDateTimeOfTenant(), null);
transactionDate, null);
RecalculationDetail recalculationDetail = new RecalculationDetail(transactionDate, loanTransaction);
unprocessedTransactions.add(recalculationDetail);
break;
}
}
LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, scheduledDueDate, null,
DateUtils.getLocalDateTimeOfTenant(), null);
LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, scheduledDueDate,
null);
RecalculationDetail recalculationDetail = new RecalculationDetail(scheduledDueDate, loanTransaction);
unprocessedTransactions.add(recalculationDetail);
checkForOutstanding = false;
@@ -116,7 +116,6 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductVariableInsta
import org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductNotFoundException;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -878,8 +877,7 @@ public class LoanScheduleAssembler {
}
final LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
AppUser currentUser = this.context.getAuthenticatedUserIfPresent();
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
private List<LoanTermVariations> adjustExistingVariations(List<LoanTermVariations> variations, List<LoanTermVariations> newVariations,
@@ -34,7 +34,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -104,8 +103,7 @@ public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePl
loan.getLoanTermVariations().clear();
final LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
AppUser currentUser = this.context.getAuthenticatedUserIfPresent();
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
this.loanAccountDomainService.saveLoanWithDataIntegrityViolationChecks(loan);
return new CommandProcessingResultBuilder() //
.withLoanId(loanId) //
@@ -487,7 +487,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
loan.updateLoanSchedule(loanSchedule.getInstallments(), appUser);
loan.updateLoanSchedule(loanSchedule.getInstallments());
loan.recalculateAllCharges();
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
@@ -499,7 +499,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
final List<Long> existingReversedTransactionIds = new ArrayList<>();
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
loan.processIncomeTransactions(this.userRepository.fetchSystemUser());
loan.processIncomeTransactions();
this.loanRepositoryWrapper.saveAndFlush(loan);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
@@ -1050,7 +1050,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
final JsonQuery query = JsonQuery.from(command.json(), parsedQuery, this.fromJsonHelper);
final LoanScheduleModel loanSchedule = this.calculationPlatformService.calculateLoanSchedule(query, false);
existingLoanApplication.updateLoanSchedule(loanSchedule, currentUser);
existingLoanApplication.updateLoanSchedule(loanSchedule);
existingLoanApplication.recalculateAllCharges();
}
@@ -1449,7 +1449,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|| changes.containsKey("expectedDisbursementDate")) {
LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
if (loan.isTopup() && loan.getClientId() != null) {
@@ -1553,7 +1553,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|| changes.containsKey(LoanApiConstants.disbursementPrincipalParameterName)) {
LocalDate recalculateFrom = null;
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
}
loan.adjustNetDisbursalAmount(loan.getProposedPrincipal());
@@ -503,8 +503,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
final CurrencyData currencyData = applicationCurrency.toData();
final LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction(DateUtils.getLocalDateTimeOfTenant(),
currentUser);
final LoanTransaction waiveOfInterest = loan.deriveDefaultInterestWaiverTransaction();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WAIVE_INTEREST);
@@ -448,7 +448,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), amountToDisburse, paymentDetail,
actualDisbursementDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
actualDisbursementDate, txnExternalId);
disbursementTransaction.updateLoan(loan);
loan.addLoanTransaction(disbursementTransaction);
}
@@ -713,7 +713,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
existingTransactionIds.addAll(loan.findExistingTransactionIds());
existingReversedTransactionIds.addAll(loan.findExistingReversedTransactionIds());
LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan.getOffice(), disburseAmount, paymentDetail,
actualDisbursementDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
actualDisbursementDate, txnExternalId);
disbursementTransaction.updateLoan(loan);
loan.addLoanTransaction(disbursementTransaction);
}
@@ -826,8 +826,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
// Remove post dated checks if added.
loan.removePostDatedChecks();
final Map<String, Object> changes = loan.undoDisbursal(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds,
currentUser);
final Map<String, Object> changes = loan.undoDisbursal(scheduleGeneratorDTO, existingTransactionIds,
existingReversedTransactionIds);
if (!changes.isEmpty()) {
if (loan.isTopup() && loan.getClientId() != null) {
@@ -1050,7 +1050,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createPaymentDetail(command, changes);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(loan.getOffice(), transactionAmountAsMoney, paymentDetail,
transactionDate, txnExternalId, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, txnExternalId);
if (transactionToAdjust.isInterestWaiver()) {
Money unrecognizedIncome = transactionAmountAsMoney.zero();
Money interestComponent = transactionAmountAsMoney;
@@ -1062,7 +1062,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
newTransactionDetail = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney, transactionDate,
interestComponent, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), currentUser);
interestComponent, unrecognizedIncome);
}
LocalDate recalculateFrom = null;
@@ -1076,7 +1076,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
scheduleGeneratorDTO, currentUser);
scheduleGeneratorDTO);
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
if (paymentDetail != null) {
@@ -1176,7 +1176,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
}
final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan.getOffice(), loan, transactionAmountAsMoney,
transactionDate, interestComponent, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), currentUser);
transactionDate, interestComponent, unrecognizedIncome);
businessEventNotifierService.notifyPreBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
LocalDate recalculateFrom = null;
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
@@ -1185,8 +1185,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO,
currentUser);
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
this.loanTransactionRepository.saveAndFlush(waiveInterestTransaction);
@@ -1312,7 +1311,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine(), changes,
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, currentUser);
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO);
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
if (possibleClosingTransaction != null) {
this.loanTransactionRepository.saveAndFlush(possibleClosingTransaction);
@@ -1561,11 +1560,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
public void runScheduleRecalculation(final Loan loan, final LocalDate recalculateFrom) {
AppUser currentUser = getAppUserIfPresent();
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = loan
.handleRegenerateRepaymentScheduleWithInterestRecalculation(generatorDTO, currentUser);
.handleRegenerateRepaymentScheduleWithInterestRecalculation(generatorDTO);
saveLoanWithDataIntegrityViolationChecks(loan);
if (changedTransactionDetail != null) {
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
@@ -1615,7 +1613,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
* the loan product uses Upfront Accruals
**/
if (loan.status().isActive() && loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) {
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null, currentUser);
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
}
boolean isAppliedOnBackDate = false;
@@ -1868,8 +1866,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
final LoanTransaction waiveTransaction = loan.waiveLoanCharge(loanCharge, defaultLoanLifecycleStateMachine(), changes,
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge,
currentUser);
existingTransactionIds, existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge);
this.loanTransactionRepository.saveAndFlush(waiveTransaction);
saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2113,7 +2110,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public LoanTransaction initiateLoanTransfer(final Loan loan, final LocalDate transferDate) {
AppUser currentUser = getAppUserIfPresent();
this.loanAssembler.setHelpers(loan);
checkClientOrGroupActive(loan);
validateTransactionsForTransfer(loan, transferDate);
@@ -2123,8 +2119,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate,
DateUtils.getLocalDateTimeOfTenant(), currentUser);
final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate);
loan.addLoanTransaction(newTransferTransaction);
loan.setLoanStatus(LoanStatus.TRANSFER_IN_PROGRESS.getValue());
@@ -2140,14 +2135,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public LoanTransaction acceptLoanTransfer(final Loan loan, final LocalDate transferDate, final Office acceptedInOffice,
final Staff loanOfficer) {
AppUser currentUser = getAppUserIfPresent();
this.loanAssembler.setHelpers(loan);
businessEventNotifierService.notifyPreBusinessEvent(new LoanAcceptTransferBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate,
DateUtils.getLocalDateTimeOfTenant(), currentUser);
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate);
loan.addLoanTransaction(newTransferAcceptanceTransaction);
if (loan.getTotalOverpaid() != null) {
loan.setLoanStatus(LoanStatus.OVERPAID.getValue());
@@ -2170,15 +2163,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Transactional
@Override
public LoanTransaction withdrawLoanTransfer(final Loan loan, final LocalDate transferDate) {
AppUser currentUser = getAppUserIfPresent();
this.loanAssembler.setHelpers(loan);
businessEventNotifierService.notifyPreBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate,
DateUtils.getLocalDateTimeOfTenant(), currentUser);
final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate);
loan.addLoanTransaction(newTransferAcceptanceTransaction);
loan.setLoanStatus(LoanStatus.ACTIVE.getValue());
@@ -2375,7 +2366,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
loan.setHelpers(null, this.loanSummaryWrapper, this.transactionProcessingStrategy);
loan.recalculateScheduleFromLastTransaction(scheduleGeneratorDTO, existingTransactionIds,
existingReversedTransactionIds, currentUser);
existingReversedTransactionIds);
createAndSaveLoanScheduleArchive(loan, scheduleGeneratorDTO);
} else if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
@@ -2786,7 +2777,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(existingTransactionIds, existingReversedTransactionIds,
scheduleGeneratorDTO, currentUser);
scheduleGeneratorDTO);
if (changedTransactionDetail != null) {
for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
this.loanTransactionRepository.save(mapEntry.getValue());
@@ -2914,7 +2905,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (command.entityId() != null) {
changedTransactionDetail = loan.updateDisbursementDateAndAmountForTranche(loanDisbursementDetails, command, changes,
scheduleGeneratorDTO, currentUser);
scheduleGeneratorDTO);
} else {
// BigDecimal setAmount = loan.getApprovedPrincipal();
Collection<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
@@ -2928,9 +2919,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.repaymentScheduleDetail().setPrincipal(setAmount);
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
} else {
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
loan.regenerateRepaymentSchedule(scheduleGeneratorDTO);
loan.processPostDisbursementTransactions();
}
}
@@ -3049,7 +3040,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public void recalculateInterest(final long loanId) {
Loan loan = this.loanAssembler.assembleFrom(loanId);
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
AppUser currentUser = getAppUserIfPresent();
businessEventNotifierService.notifyPreBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
final List<Long> existingTransactionIds = new ArrayList<>();
final List<Long> existingReversedTransactionIds = new ArrayList<>();
@@ -3057,7 +3047,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(generatorDTO,
existingTransactionIds, existingReversedTransactionIds, currentUser);
existingTransactionIds, existingReversedTransactionIds);
saveLoanWithDataIntegrityViolationChecks(loan);
@@ -3101,7 +3091,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
AppUser currentUser = getAppUserIfPresent();
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName);
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount, currentUser,
loan.regenerateScheduleOnDisbursement(scheduleGeneratorDTO, recalculateSchedule, actualDisbursementDate, emiAmount,
nextPossibleRepaymentDate, rescheduledRepaymentDate);
}
@@ -3213,7 +3203,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
@Transactional
public CommandProcessingResult undoLastLoanDisbursal(Long loanId, JsonCommand command) {
final AppUser currentUser = getAppUserIfPresent();
final Loan loan = this.loanAssembler.assembleFrom(loanId);
final LocalDate recalculateFromDate = loan.getLastRepaymentDate();
@@ -3229,7 +3218,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFromDate);
final Map<String, Object> changes = loan.undoLastDisbursal(scheduleGeneratorDTO, existingTransactionIds,
existingReversedTransactionIds, currentUser, loan);
existingReversedTransactionIds, loan);
if (!changes.isEmpty()) {
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
String noteText = null;
@@ -38,4 +38,5 @@
<include file="parts/0016_changed_unique_constraint_of_ref_no.xml" relativeToChangelogFile="true"/>
<include file="parts/0017_fix_stretchy_reports.xml" relativeToChangelogFile="true"/>
<include file="parts/0018_pentaho_reports_to_table.xml" relativeToChangelogFile="true"/>
<include file="parts/0019_refactor_loan_transaction.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,59 @@
<?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.1.xsd">
<changeSet author="fineract" id="1">
<addColumn tableName="m_loan_transaction">
<column name="createdby_id" type="BIGINT" valueComputed="appuser_id"/>
<column name="lastmodifiedby_id" type="BIGINT" valueComputed="appuser_id"/>
<column name="lastmodified_date" type="DATETIME" valueComputed="created_date"/>
</addColumn>
</changeSet>
<changeSet id="2" author="fineract">
<dropColumn tableName="m_loan_transaction">
<column name="appuser_id"/>
</dropColumn>
</changeSet>
<changeSet author="fineract" id="3" context="postgresql">
<update tableName="stretchy_report">
<column name="report_sql" value="SELECT mc.id AS id, mc.firstname AS firstName, mc.middlename AS middleName, mc.lastname AS lastName, mc.display_name AS fullName, mc.mobile_no AS mobileNo, ml.principal_amount AS loanAmount, (COALESCE(ml.principal_outstanding_derived, 0) + COALESCE(ml.interest_outstanding_derived, 0) + COALESCE(ml.fee_charges_outstanding_derived, 0) + COALESCE(ml.penalty_charges_outstanding_derived, 0)) AS loanOutstanding, ounder.id AS officeNumber, ml.account_no AS loanAccountNumber, SUM(lt.amount) AS repaymentAmount FROM m_office mo JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, '%') INNER JOIN m_client mc ON mc.office_id = ounder.id INNER JOIN m_loan ml ON ml.client_id = mc.id INNER JOIN r_enum_value rev ON rev.enum_id = ml.loan_status_id AND rev.enum_name = 'loan_status_id' INNER JOIN m_loan_transaction lt ON lt.loan_id = ml.id INNER JOIN m_appuser au ON au.id = lt.createdby_id LEFT JOIN m_loan_arrears_aging laa ON laa.loan_id = ml.id LEFT JOIN m_payment_detail mpd ON mpd.id = lt.payment_detail_id LEFT JOIN m_currency cur ON cur.code = ml.currency_code LEFT JOIN m_group_client gc ON gc.client_id = mc.id LEFT JOIN m_group g ON g.id = gc.group_id LEFT JOIN m_staff lo ON lo.id = ml.loan_officer_id LEFT JOIN m_guarantor gua ON gua.loan_id = ml.id WHERE ml.loan_status_id = 300 AND mo.id = '${officeId}' AND (COALESCE(ml.loan_officer_id, -10) = ${loanOfficerId} OR '-1' = ${loanOfficerId}) AND (EXTRACT(DAY FROM (CURRENT_DATE - lt.transaction_date::TIMESTAMP)) BETWEEN 9${fromX} AND ${toY}) AND lt.is_reversed = false AND lt.transaction_type_enum = 2 AND laa.loan_id IS NULL GROUP BY ml.id, mc.id, ounder.id ORDER BY ounder.hierarchy, ml.currency_code, mc.account_no, ml.account_no"/>
<where>id='174' AND report_name='Loan payments received (Active Loans)'</where>
</update>
<update tableName="stretchy_report">
<column name="report_sql" value="SELECT ml.id AS loanId, mc.id AS id, mc.firstname AS firstName, mc.middlename AS middleName, mc.lastname AS lastName, mc.display_name AS fullName, mc.mobile_no AS mobileNo, ml.principal_amount AS loanAmount, (COALESCE(ml.principal_outstanding_derived, 0) + COALESCE(ml.interest_outstanding_derived, 0) + COALESCE(ml.fee_charges_outstanding_derived, 0) + COALESCE(ml.penalty_charges_outstanding_derived, 0)) AS loanOutstanding, ounder.id AS officeNumber, ml.account_no AS loanAccountNumber, SUM(lt.amount) AS repaymentAmount FROM m_office mo JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, '%') INNER JOIN m_client mc ON mc.office_id = ounder.id INNER JOIN m_loan ml ON ml.client_id = mc.id INNER JOIN r_enum_value rev ON rev.enum_id = ml.loan_status_id AND rev.enum_name = 'loan_status_id' INNER JOIN m_loan_arrears_aging laa ON laa.loan_id = ml.id INNER JOIN m_loan_transaction lt ON lt.loan_id = ml.id INNER JOIN m_appuser au ON au.id = lt.createdby_id LEFT JOIN m_payment_detail mpd ON mpd.id = lt.payment_detail_id LEFT JOIN m_currency cur ON cur.code = ml.currency_code LEFT JOIN m_group_client gc ON gc.client_id = mc.id LEFT JOIN m_group g ON g.id = gc.group_id LEFT JOIN m_staff lo ON lo.id = ml.loan_officer_id LEFT JOIN m_guarantor gua ON gua.loan_id = ml.id WHERE ml.loan_status_id = 300 AND mo.id = '${officeId}' AND (COALESCE(ml.loan_officer_id, -10) = ${loanOfficerId} OR '-1' = ${loanOfficerId}) AND (EXTRACT(DAY FROM(CURRENT_DATE - lt.transaction_date::TIMESTAMP)) BETWEEN ${fromX} AND ${toY}) AND (EXTRACT(DAY FROM(CURRENT_DATE - laa.overdue_since_date_derived::TIMESTAMP)) BETWEEN ${overdueX} AND ${overdueY}) AND lt.is_reversed = false AND lt.transaction_type_enum = 2 GROUP BY ml.id, mc.id, ounder.id ORDER BY ounder.hierarchy, ml.currency_code, mc.account_no, ml.account_no"/>
<where>id='175' AND report_name='Loan payments received (Overdue Loans)'</where>
</update>
</changeSet>
<changeSet author="fineract" id="4" context="mysql">
<update tableName="stretchy_report">
<column name="report_sql"
value="SELECT &#13;&#10;mc.id AS &quot;id&quot;, &#13;&#10;mc.firstname AS &quot;firstName&quot;,&#13;&#10;mc.middlename AS &quot;middleName&quot;,&#13;&#10;mc.lastname AS &quot;lastName&quot;,&#13;&#10;mc.display_name AS &quot;fullName&quot;,&#13;&#10;mc.mobile_no AS &quot;mobileNo&quot;, &#13;&#10;ml.principal_amount AS &quot;loanAmount&quot;, &#13;&#10;(IFNULL(ml.principal_outstanding_derived, 0) + IFNULL(ml.interest_outstanding_derived, 0) + IFNULL(ml.fee_charges_outstanding_derived, 0) + IFNULL(ml.penalty_charges_outstanding_derived, 0)) AS &quot;loanOutstanding&quot;,&#13;&#10;ounder.id AS &quot;officeNumber&quot;, &#13;&#10;ml.account_no AS &quot;loanAccountNumber&quot;,&#13;&#10;SUM(lt.amount) AS &quot;repaymentAmount&quot;&#13;&#10;FROM m_office mo&#13;&#10;JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, '%')&#13;&#10;INNER JOIN m_client mc ON mc.office_id=ounder.id&#13;&#10;INNER JOIN m_loan ml ON ml.client_id = mc.id&#13;&#10;INNER JOIN r_enum_value rev ON rev.enum_id=ml.loan_status_id AND rev.enum_name = 'loan_status_id'&#13;&#10;INNER JOIN m_loan_transaction lt ON lt.loan_id = ml.id&#13;&#10;INNER JOIN m_appuser au ON au.id = lt.createdby_id&#13;&#10;LEFT JOIN m_loan_arrears_aging laa ON laa.loan_id=ml.id&#13;&#10;LEFT JOIN m_payment_detail mpd ON mpd.id=lt.payment_detail_id&#13;&#10;LEFT JOIN m_currency cur ON cur.code = ml.currency_code&#13;&#10;LEFT JOIN m_group_client gc ON gc.client_id = mc.id&#13;&#10;LEFT JOIN m_group g ON g.id = gc.group_id&#13;&#10;LEFT JOIN m_staff lo ON lo.id = ml.loan_officer_id&#13;&#10;LEFT JOIN m_guarantor gua ON gua.loan_id = ml.id&#13;&#10;WHERE ml.loan_status_id=300 AND mo.id=${officeId} AND (IFNULL(ml.loan_officer_id, -10) = ${loanOfficerId} OR &quot;-1&quot; = ${loanOfficerId}) AND (DATEDIFF(CURDATE(), lt.transaction_date) BETWEEN ${fromX} AND ${toY}) AND lt.is_reversed=0 AND lt.transaction_type_enum=2 AND laa.loan_id IS NULL&#13;&#10;GROUP BY ml.id&#13;&#10;ORDER BY ounder.hierarchy, ml.currency_code, mc.account_no, ml.account_no"/>
<where>id='174' AND report_name='Loan payments received (Active Loans)'</where>
</update>
<update tableName="stretchy_report">
<column name="report_sql"
value="SELECT &#13;&#10;ml.id AS &quot;loanId&quot;, &#13;&#10;mc.id AS &quot;id&quot;, &#13;&#10;mc.firstname AS &quot;firstName&quot;,&#13;&#10;mc.middlename AS &quot;middleName&quot;,&#13;&#10;mc.lastname AS &quot;lastName&quot;,&#13;&#10;mc.display_name AS &quot;fullName&quot;,&#13;&#10;mc.mobile_no AS &quot;mobileNo&quot;, &#13;&#10;ml.principal_amount AS &quot;loanAmount&quot;, &#13;&#10;(IFNULL(ml.principal_outstanding_derived, 0) + IFNULL(ml.interest_outstanding_derived, 0) + IFNULL(ml.fee_charges_outstanding_derived, 0) + IFNULL(ml.penalty_charges_outstanding_derived, 0)) AS &quot;loanOutstanding&quot;,&#13;&#10;ounder.id AS &quot;officeNumber&quot;, &#13;&#10;ml.account_no AS &quot;loanAccountNumber&quot;,&#13;&#10;SUM(lt.amount) AS &quot;repaymentAmount&quot;&#13;&#10;FROM m_office mo&#13;&#10;JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, '%')&#13;&#10;INNER JOIN m_client mc ON mc.office_id=ounder.id&#13;&#10;INNER JOIN m_loan ml ON ml.client_id = mc.id&#13;&#10;INNER JOIN r_enum_value rev ON rev.enum_id=ml.loan_status_id AND rev.enum_name = 'loan_status_id'&#13;&#10;INNER JOIN m_loan_arrears_aging laa ON laa.loan_id=ml.id&#13;&#10;INNER JOIN m_loan_transaction lt ON lt.loan_id = ml.id&#13;&#10;INNER JOIN m_appuser au ON au.id = lt.createdby_id&#13;&#10;LEFT JOIN m_payment_detail mpd ON mpd.id=lt.payment_detail_id&#13;&#10;LEFT JOIN m_currency cur ON cur.code = ml.currency_code&#13;&#10;LEFT JOIN m_group_client gc ON gc.client_id = mc.id&#13;&#10;LEFT JOIN m_group g ON g.id = gc.group_id&#13;&#10;LEFT JOIN m_staff lo ON lo.id = ml.loan_officer_id&#13;&#10;LEFT JOIN m_guarantor gua ON gua.loan_id = ml.id&#13;&#10;WHERE ml.loan_status_id=300 AND mo.id=${officeId} AND (IFNULL(ml.loan_officer_id, -10) = ${loanOfficerId} OR &quot;-1&quot; = ${loanOfficerId}) AND (DATEDIFF(CURDATE(), lt.transaction_date) BETWEEN ${fromX} AND ${toY}) AND (DATEDIFF(CURDATE(), laa.overdue_since_date_derived) BETWEEN ${overdueX} AND ${overdueY}) AND lt.is_reversed=0 AND lt.transaction_type_enum=2&#13;&#10;GROUP BY ml.id&#13;&#10;ORDER BY ounder.hierarchy, ml.currency_code, mc.account_no, ml.account_no"/>
<where>id='175' AND report_name='Loan payments received (Overdue Loans)'</where>
</update>
</changeSet>
</databaseChangeLog>