MIFOSX-865 introduce new transaction type for accrual and deprecate interest applied and charge applied transaction types

This commit is contained in:
Vishwas Babu A J
2014-04-13 01:08:27 -07:00
parent bf04f97333
commit 739ca7897a
9 changed files with 92 additions and 102 deletions
@@ -39,14 +39,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
createJournalEntriesForDisbursements(loanDTO, loanTransactionDTO, office);
}
/*** Handle Interest Postings ***/
if (loanTransactionDTO.getTransactionType().isApplyInterest()) {
createJournalEntriesForInterestPostings(loanDTO, loanTransactionDTO, office);
}
/*** Handle Apply charges ***/
else if (loanTransactionDTO.getTransactionType().isApplyCharges()) {
createJournalEntriesForApplyCharges(loanDTO, loanTransactionDTO, office);
/*** Handle Accruals ***/
if (loanTransactionDTO.getTransactionType().isAccrual()) {
createJournalEntriesForAccruals(loanDTO, loanTransactionDTO, office);
}
/***
@@ -98,37 +93,6 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
/**
* Recognise the receivable interest <br/>
* Debit "Interest Receivable" and Credit "Income from Interest"
*
* @param loanDTO
* @param loanTransactionDTO
* @param office
*/
private void createJournalEntriesForInterestPostings(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
final Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
final Long loanId = loanDTO.getLoanId();
final String currencyCode = loanDTO.getCurrencyCode();
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal amount = loanTransactionDTO.getAmount();
final boolean isReversed = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for recognising interest (or reversal)
if (amount != null && !(amount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, amount, isReversed);
}
}
/**
*
* Handles repayments using the following posting rules <br/>
@@ -238,8 +202,8 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
}
/**
* Create Journal Entries for application of charges (fees and payments)
* using the following rules <br/>
* Recognize the receivable interest <br/>
* Debit "Interest Receivable" and Credit "Income from Interest"
*
* <b>Fees:</b> Debit <i>Fees Receivable</i> and credit <i>Income from
* Fees</i> <br/>
@@ -253,7 +217,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* @param loanTransactionDTO
* @param office
*/
private void createJournalEntriesForApplyCharges(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
private void createJournalEntriesForAccruals(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
// loan properties
final Long loanProductId = loanDTO.getLoanProductId();
@@ -263,11 +227,18 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// transaction properties
final String transactionId = loanTransactionDTO.getTransactionId();
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
final BigDecimal feesAmount = loanTransactionDTO.getFees();
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
final boolean isReversed = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for recognizing interest (or reversal)
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversed);
}
// create journal entries for the fees application (or reversal)
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
@@ -275,7 +246,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
loanId, transactionId, transactionDate, feesAmount, isReversed);
}
// create journal entries for the penalties application (or reversal)
else if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
this.helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, currencyCode,
ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE, ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, penaltiesAmount, isReversed);
@@ -19,10 +19,9 @@ public class LoanTransactionEnumData {
private final boolean repaymentAtDisbursement;
private final boolean repayment;
private final boolean contra;
private final boolean applyInterest;
private final boolean waiveInterest;
private final boolean waiveCharges;
private final boolean applyCharges;
private final boolean accrual;
private final boolean writeOff;
private final boolean recoveryRepayment;
private final boolean initiateTransfer;
@@ -42,8 +41,7 @@ public class LoanTransactionEnumData {
this.contra = Long.valueOf(3).equals(this.id);
this.waiveInterest = Long.valueOf(4).equals(this.id);
this.waiveCharges = Long.valueOf(9).equals(this.id);
this.applyCharges = Long.valueOf(10).equals(this.id);
this.applyInterest = Long.valueOf(11).equals(this.id);
this.accrual = Long.valueOf(10).equals(this.id);
this.writeOff = Long.valueOf(6).equals(this.id);
this.recoveryRepayment = Long.valueOf(8).equals(this.id);
this.initiateTransfer = Long.valueOf(12).equals(this.id);
@@ -105,12 +103,8 @@ public class LoanTransactionEnumData {
return this.recoveryRepayment;
}
public boolean isApplyCharges() {
return this.applyCharges;
}
public boolean isApplyInterest() {
return this.applyInterest;
public boolean isAccrual() {
return this.accrual;
}
public boolean isInitiateTransfer() {
@@ -555,7 +555,7 @@ public class Loan extends AbstractPersistable<Long> {
}
}
final LoanTransaction applyLoanChargeTransaction = LoanTransaction.applyLoanCharge(this, getOffice(), chargeAmount,
final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount,
transactionDate, feeCharges, penaltyCharges);
this.loanTransactions.add(applyLoanChargeTransaction);
return applyLoanChargeTransaction;
@@ -1787,9 +1787,17 @@ public class Loan extends AbstractPersistable<Long> {
}
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
final LoanTransaction interestAppliedTransaction = LoanTransaction.applyInterest(getOffice(), this, interestApplied,
actualDisbursementDate);
this.loanTransactions.add(interestAppliedTransaction);
/**
* Add an interest applied transaction of the interest is accrued
* upfront (Up front accrual)
**/
if (isUpfrontAccrualAccountingEnabledOnLoanProduct()) {
final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(getOffice(), this, interestApplied,
actualDisbursementDate);
this.loanTransactions.add(interestAppliedTransaction);
}
// changedTransactionDetail =
// reprocessTransactionForDisbursement(changedTransactionDetail);
@@ -1990,7 +1998,13 @@ public class Loan extends AbstractPersistable<Long> {
disbursentMoney = disbursentMoney.plus(charge.amount());
}
} else {
handleChargeAppliedTransaction(charge, disbursedOn);
/**
* create a Charge applied transaction if Upfront Accrual is
* enabled
**/
if (isUpfrontAccrualAccountingEnabledOnLoanProduct()) {
handleChargeAppliedTransaction(charge, disbursedOn);
}
}
}
if (disbursentMoney.isGreaterThanZero()) {
@@ -3208,8 +3222,12 @@ public class Loan extends AbstractPersistable<Long> {
return this.loanProduct.isCashBasedAccountingEnabled();
}
private Boolean isAccrualBasedAccountingEnabledOnLoanProduct() {
return this.loanProduct.isAccrualBasedAccountingEnabled();
public Boolean isUpfrontAccrualAccountingEnabledOnLoanProduct() {
return this.loanProduct.isUpfrontAccrualAccountingEnabled();
}
private Boolean isPeriodicAccrualAccountingEnabledOnLoanProduct() {
return this.loanProduct.isPeriodicAccrualAccountingEnabled();
}
private Long productId() {
@@ -3238,8 +3256,8 @@ public class Loan extends AbstractPersistable<Long> {
accountingBridgeData.put("currency", currencyData);
accountingBridgeData.put("calculatedInterest", this.summary.getTotalInterestCharged());
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnLoanProduct());
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnLoanProduct());
accountingBridgeData.put("periodicAccrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnLoanProduct());
accountingBridgeData.put("upfrontAccrualBasedAccountingEnabled", isUpfrontAccrualAccountingEnabledOnLoanProduct());
accountingBridgeData.put("periodicAccrualBasedAccountingEnabled", isPeriodicAccrualAccountingEnabledOnLoanProduct());
final List<Map<String, Object>> newLoanTransactions = new ArrayList<Map<String, Object>>();
for (final LoanTransaction transaction : this.loanTransactions) {
@@ -127,9 +127,18 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
return new LoanTransaction(loan, office, LoanTransactionType.WAIVE_INTEREST, waived.getAmount(), waiveDate, null);
}
public static LoanTransaction applyInterest(final Office office, final Loan loan, final Money amount,
public static LoanTransaction accrueInterest(final Office office, final Loan loan, final Money amount,
final LocalDate interestAppliedDate) {
return new LoanTransaction(loan, office, LoanTransactionType.APPLY_INTEREST, amount.getAmount(), interestAppliedDate, null);
BigDecimal principalPortion = null;
BigDecimal feesPortion = null;
BigDecimal penaltiesPortion = null;
BigDecimal interestPortion = amount.getAmount();
BigDecimal overPaymentPortion = null;
boolean reversed = false;
PaymentDetail paymentDetail = null;
String externalId = null;
return new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL.getValue(), interestAppliedDate.toDate(), interestPortion,
principalPortion, interestPortion, feesPortion, penaltiesPortion, overPaymentPortion, reversed, paymentDetail, externalId);
}
public static LoanTransaction initiateTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
@@ -165,10 +174,11 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
loanTransaction.reversed, loanTransaction.paymentDetail, loanTransaction.externalId);
}
public static LoanTransaction applyLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
public static LoanTransaction accrueLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate,
final Money feeCharges, final Money penaltyCharges) {
final LoanTransaction applyCharge = new LoanTransaction(loan, office, LoanTransactionType.APPLY_CHARGES, amount.getAmount(),
applyDate, null);
String externalId = null;
final LoanTransaction applyCharge = new LoanTransaction(loan, office, LoanTransactionType.ACCRUAL, amount.getAmount(), applyDate,
externalId);
applyCharge.updateChargesComponents(feeCharges, penaltyCharges);
return applyCharge;
}
@@ -21,8 +21,11 @@ public enum LoanTransactionType {
*/
RECOVERY_REPAYMENT(8, "loanTransactionType.recoveryRepayment"), //
WAIVE_CHARGES(9, "loanTransactionType.waiveCharges"), //
APPLY_CHARGES(10, "loanTransactionType.applyCharges"), //
APPLY_INTEREST(11, "loanTransactionType.applyInterest"),
/**
* Transaction represents an Accrual (For either interest, charge or a
* penalty
**/
ACCRUAL(10, "loanTransactionType.accrual"), //
/***
* A Loan Transfer involves two steps, first a "initiate" Loan transfer
@@ -32,10 +35,9 @@ public enum LoanTransactionType {
INITIATE_TRANSFER(12, "loanTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "loanTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "loanTransactionType.withdrawTransfer"), //
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer"),
REFUND(16, "loanTransactionType.refund"), CHARGE_PAYMENT(17, "loanTransactionType.chargePayment"),
ACCRUAL_ACCOUNTING(17,"loanTransactionType.accrualAccounting");
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer"), //
REFUND(16, "loanTransactionType.refund"), //
CHARGE_PAYMENT(17, "loanTransactionType.chargePayment");
private final Integer value;
private final String code;
@@ -87,10 +89,7 @@ public enum LoanTransactionType {
loanTransactionType = LoanTransactionType.WAIVE_CHARGES;
break;
case 10:
loanTransactionType = LoanTransactionType.APPLY_CHARGES;
break;
case 11:
loanTransactionType = LoanTransactionType.APPLY_INTEREST;
loanTransactionType = LoanTransactionType.ACCRUAL;
break;
case 12:
loanTransactionType = LoanTransactionType.INITIATE_TRANSFER;
@@ -141,8 +140,8 @@ public enum LoanTransactionType {
return this.value.equals(LoanTransactionType.WAIVE_CHARGES.getValue());
}
public boolean isApplyCharges() {
return this.value.equals(LoanTransactionType.APPLY_CHARGES.getValue());
public boolean isAccrual() {
return this.value.equals(LoanTransactionType.ACCRUAL.getValue());
}
public boolean isWriteOff() {
@@ -116,8 +116,8 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
TransactionStatus transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
this.jdbcTemplate.update(transactionSql, scheduleAccrualData.getLoanId(), scheduleAccrualData.getOfficeId(),
LoanTransactionType.ACCRUAL_ACCOUNTING.getValue(), scheduleAccrualData.getDueDate(), amount, interestportion,
feeportion, penaltyportion);
LoanTransactionType.ACCRUAL.getValue(), scheduleAccrualData.getDueDate(), amount, interestportion, feeportion,
penaltyportion);
final Long transactonId = this.jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()");
Map<String, Object> transactionMap = toMapData(transactonId, amount, interestportion, feeportion, penaltyportion,
@@ -158,7 +158,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
final BigDecimal feeportion, final BigDecimal penaltyportion, final LoanScheduleAccrualData loanScheduleAccrualData) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<String, Object>();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.ACCRUAL_ACCOUNTING);
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.ACCRUAL);
thisTransactionData.put("id", id);
thisTransactionData.put("officeId", loanScheduleAccrualData.getOfficeId());
@@ -985,9 +985,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
existingReversedTransactionIds);
// we want to apply charge transactions only for those loans charges
// that are applied when a loan is active
if (loan.status().isActive()) {
/**
* we want to apply charge transactions only for those loans charges
* that are applied when a loan is active and the loan product uses
* Upfront Accruals
**/
if (loan.isUpfrontAccrualAccountingEnabledOnLoanProduct() && (loan.status().isActive())) {
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
this.loanTransactionRepository.save(applyLoanChargeTransaction);
/***
@@ -1847,9 +1850,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final ChangedTransactionDetail changedTransactionDetail = loan.addLoanCharge(loanCharge, existingTransactionIds,
existingReversedTransactionIds);
// we want to apply charge transactions only for those loans charges
// that are applied when a loan is active
if (loan.status().isActive()) {
/**
* we want to apply charge transactions only for those loans charges
* that are applied when a loan is active and the loan product has
* Upfront Accrual accounting enabled
**/
if (loan.isUpfrontAccrualAccountingEnabledOnLoanProduct() && loan.status().isActive()) {
final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
this.loanTransactionRepository.save(applyLoanChargeTransaction);
this.loanRepository.saveAndFlush(loan);
@@ -614,14 +614,14 @@ public class LoanProduct extends AbstractPersistable<Long> {
}
public boolean isAccrualBasedAccountingEnabled() {
return isUpfrontAccrualAccounting() || isPeriodicAccrualAccounting();
return isUpfrontAccrualAccountingEnabled() || isPeriodicAccrualAccountingEnabled();
}
public boolean isUpfrontAccrualAccounting() {
public boolean isUpfrontAccrualAccountingEnabled() {
return AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(this.accountingRule);
}
public boolean isPeriodicAccrualAccounting() {
public boolean isPeriodicAccrualAccountingEnabled() {
return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(this.accountingRule);
}
@@ -277,13 +277,9 @@ public class LoanEnumerations {
optionData = new LoanTransactionEnumData(LoanTransactionType.WAIVE_CHARGES.getValue().longValue(),
LoanTransactionType.WAIVE_CHARGES.getCode(), "Waive loan charges");
break;
case APPLY_CHARGES:
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_CHARGES.getValue().longValue(),
LoanTransactionType.APPLY_CHARGES.getCode(), "Apply Charge");
break;
case APPLY_INTEREST:
optionData = new LoanTransactionEnumData(LoanTransactionType.APPLY_INTEREST.getValue().longValue(),
LoanTransactionType.APPLY_INTEREST.getCode(), "Apply Interest");
case ACCRUAL:
optionData = new LoanTransactionEnumData(LoanTransactionType.ACCRUAL.getValue().longValue(),
LoanTransactionType.ACCRUAL.getCode(), "Accrual");
break;
case APPROVE_TRANSFER:
optionData = new LoanTransactionEnumData(LoanTransactionType.APPROVE_TRANSFER.getValue().longValue(),
@@ -309,10 +305,6 @@ public class LoanEnumerations {
optionData = new LoanTransactionEnumData(LoanTransactionType.CHARGE_PAYMENT.getValue().longValue(),
LoanTransactionType.CHARGE_PAYMENT.getCode(), "Charge Payment");
break;
case ACCRUAL_ACCOUNTING:
optionData = new LoanTransactionEnumData(LoanTransactionType.ACCRUAL_ACCOUNTING.getValue().longValue(),
LoanTransactionType.ACCRUAL_ACCOUNTING.getCode(), "Accrual Accounting");
break;
default:
break;
}