diff --git a/mifosng-db/migrations/core_db/V118_8__track_overpayments_seperately_in_loan_transactions.sql b/mifosng-db/migrations/core_db/V118_8__track_overpayments_seperately_in_loan_transactions.sql new file mode 100644 index 000000000..81ca984df --- /dev/null +++ b/mifosng-db/migrations/core_db/V118_8__track_overpayments_seperately_in_loan_transactions.sql @@ -0,0 +1,16 @@ +/***Set defaults for income from Penalties for existing Saving Products**/ +ALTER TABLE `m_loan_transaction` + ADD COLUMN `overpayment_portion_derived` DECIMAL(19,6) NULL DEFAULT NULL AFTER `penalty_charges_portion_derived`; + +/**Add dummy liability account if organization already has a loan product with accounting enabled**/ +INSERT INTO `acc_gl_account` (`name`, `hierarchy`, `gl_code`, `classification_enum`,`description`) +select 'Loan Overpayments (Temp)', '.', '22000-Temp', 2,'Temporary account to track Loan overpayments Liabilities' +FROM m_product_loan WHERE accounting_type != 1 +limit 1; + + +/**Map a liability account for every loan which has accounting enabled**/ +INSERT INTO `acc_product_mapping` (`gl_account_id`,`product_id`,`product_type`,`financial_account_type`) +select (select id from acc_gl_account where classification_enum=2 and account_usage=1 LIMIT 1), mapping.product_id, mapping.product_type, 11 +from acc_product_mapping mapping +where mapping.financial_account_type = 2 and mapping.product_type=1; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java index 92604063b..c2b2e6f9e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingConstants.java @@ -13,7 +13,7 @@ public class AccountingConstants { /*** Accounting placeholders for cash based accounting for loan products ***/ public static enum CASH_ACCOUNTS_FOR_LOAN { FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), TRANSFERS_SUSPENSE( - 10); + 10), OVERPAYMENT(11); private final Integer value; @@ -46,7 +46,7 @@ public class AccountingConstants { /*** Accounting placeholders for accrual based accounting for loan products ***/ public static enum ACCRUAL_ACCOUNTS_FOR_LOAN { FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE( - 7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10); + 7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11); private final Integer value; @@ -83,12 +83,12 @@ public class AccountingConstants { ***/ public static enum LOAN_PRODUCT_ACCOUNTING_PARAMS { FUND_SOURCE("fundSourceAccountId"), LOAN_PORTFOLIO("loanPortfolioAccountId"), INTEREST_ON_LOANS("interestOnLoanAccountId"), INCOME_FROM_FEES( - "incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), INTEREST_RECEIVABLE( - "receivableInterestAccountId"), FEES_RECEIVABLE("receivableFeeAccountId"), PENALTIES_RECEIVABLE( - "receivablePenaltyAccountId"), TRANSFERS_SUSPENSE("transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING( - "paymentChannelToFundSourceMappings"), PAYMENT_TYPE("paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING( - "feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING("penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID( - "incomeAccountId"); + "incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), OVERPAYMENT( + "overpaymentLiabilityAccountId"), INTEREST_RECEIVABLE("receivableInterestAccountId"), FEES_RECEIVABLE( + "receivableFeeAccountId"), PENALTIES_RECEIVABLE("receivablePenaltyAccountId"), TRANSFERS_SUSPENSE( + "transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE( + "paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING( + "penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId"); private final String value; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingDropdownReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingDropdownReadPlatformServiceImpl.java index 98306ebb8..a91d1188a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingDropdownReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/common/AccountingDropdownReadPlatformServiceImpl.java @@ -69,6 +69,13 @@ public class AccountingDropdownReadPlatformServiceImpl implements AccountingDrop expenseAccountOptions = null; } accountOptions.put("expenseAccountOptions", expenseAccountOptions); + + List liabilityAccountOptions = this.accountReadPlatformService + .retrieveAllEnabledDetailGLAccounts(GLAccountType.LIABILITY); + if (liabilityAccountOptions.isEmpty()) { + liabilityAccountOptions = null; + } + accountOptions.put("liabilityAccountOptions", liabilityAccountOptions); return accountOptions; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/data/LoanTransactionDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/data/LoanTransactionDTO.java index 61679d171..73ea17ecf 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/data/LoanTransactionDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/data/LoanTransactionDTO.java @@ -27,6 +27,7 @@ public class LoanTransactionDTO { private final BigDecimal interest; private final BigDecimal fees; private final BigDecimal penalties; + private final BigDecimal overPayment; /*** Boolean values determines if the transaction is reversed ***/ private final boolean reversed; @@ -37,8 +38,8 @@ public class LoanTransactionDTO { public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate, final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest, - final BigDecimal fees, final BigDecimal penalties, final boolean reversed, final List feePayments, - final List penaltyPayments) { + final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed, + final List feePayments, final List penaltyPayments) { this.paymentTypeId = paymentTypeId; this.transactionId = transactionId; this.transactionDate = transactionDate; @@ -51,6 +52,7 @@ public class LoanTransactionDTO { this.transactionType = transactionType; this.feePayments = feePayments; this.penaltyPayments = penaltyPayments; + this.overPayment = overPayment; this.officeId = officeId; } @@ -90,6 +92,10 @@ public class LoanTransactionDTO { return this.penalties; } + public BigDecimal getOverPayment() { + return this.overPayment; + } + public boolean isReversed() { return this.reversed; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java index 07f62a68c..3abf9f41f 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/AccountingProcessorHelper.java @@ -32,6 +32,7 @@ import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidEx import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType; import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping; import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; +import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.office.domain.Office; @@ -81,11 +82,12 @@ public class AccountingProcessorHelper { final BigDecimal interest = (BigDecimal) map.get("interestPortion"); final BigDecimal fees = (BigDecimal) map.get("feeChargesPortion"); final BigDecimal penalties = (BigDecimal) map.get("penaltyChargesPortion"); + final BigDecimal overPayments = (BigDecimal) map.get("overPaymentPortion"); final boolean reversed = (Boolean) map.get("reversed"); final Long paymentTypeId = (Long) map.get("paymentTypeId"); - final List feePayments = new ArrayList(); - final List penaltyPayments = new ArrayList(); + final List feePaymentDetails = new ArrayList(); + final List penaltyPaymentDetails = new ArrayList(); // extract charge payment details (if exists) if (map.containsKey("loanChargesPaid")) { @SuppressWarnings("unchecked") @@ -97,15 +99,16 @@ public class AccountingProcessorHelper { final BigDecimal chargeAmountPaid = (BigDecimal) loanChargePaid.get("amount"); final ChargePaymentDTO chargePaymentDTO = new ChargePaymentDTO(chargeId, loanChargeId, chargeAmountPaid); if (isPenalty) { - penaltyPayments.add(chargePaymentDTO); + penaltyPaymentDetails.add(chargePaymentDTO); } else { - feePayments.add(chargePaymentDTO); + feePaymentDetails.add(chargePaymentDTO); } } } final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionOfficeId, paymentTypeId, transactionId, - transactionDate, transactionType, amount, principal, interest, fees, penalties, reversed, feePayments, penaltyPayments); + transactionDate, transactionType, amount, principal, interest, fees, penalties, overPayments, reversed, + feePaymentDetails, penaltyPaymentDetails); newLoanTransactions.add(transaction); @@ -513,6 +516,9 @@ public class AccountingProcessorHelper { } } + if (accountMapping == null) { throw new ProductToGLAccountMappingNotFoundException(PortfolioProductType.LOAN, loanProductId, + ACCRUAL_ACCOUNTS_FOR_LOAN.OVERPAYMENT.toString()); } + return accountMapping.getGlAccount(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java index 66da5ee67..138f8cbbd 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java @@ -55,6 +55,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF || loanTransactionDTO.getTransactionType().isChargePayment()) { createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office); } + + /** Logic for Refunds of Overpayments **/ + else if (loanTransactionDTO.getTransactionType().isRefund()) { + createJournalEntriesForRefund(loanDTO, loanTransactionDTO, office); + } /*** * Only principal write off affects cash based accounting (interest * and fee write off need not be considered). Debit losses written @@ -103,6 +108,35 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF isReversal); } + /** + * Debit loan Portfolio and credit Fund source for a Disbursement
+ * + * All debits are turned into credits and vice versa in case of disbursement + * reversals + * + * + * @param loanDTO + * @param loanTransactionDTO + * @param office + */ + private void createJournalEntriesForRefund(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 refundAmount = loanTransactionDTO.getAmount(); + final boolean isReversal = loanTransactionDTO.isReversed(); + final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); + + this.helper.createCashBasedJournalEntriesAndReversalsForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT, + CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, refundAmount, + isReversal); + } + /** * Create a single Debit to fund source and multiple credits if applicable * (loan portfolio for principal repayments, Interest on loans for interest @@ -125,6 +159,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF final BigDecimal interestAmount = loanTransactionDTO.getInterest(); final BigDecimal feesAmount = loanTransactionDTO.getFees(); final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties(); + final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment(); final boolean isReversal = loanTransactionDTO.isReversed(); final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId(); @@ -156,6 +191,12 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF penaltiesAmount, isReversal, loanTransactionDTO.getPenaltyPayments()); } + if (overPaymentAmount != null && !(overPaymentAmount.compareTo(BigDecimal.ZERO) == 0)) { + totalDebitAmount = totalDebitAmount.add(overPaymentAmount); + this.helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT, loanProductId, + paymentTypeId, loanId, transactionId, transactionDate, overPaymentAmount, isReversal); + } + /*** create a single debit entry (or reversal) for the entire amount **/ this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/exception/ProductToGLAccountMappingNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/exception/ProductToGLAccountMappingNotFoundException.java index 5a4d41207..330d0077e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/exception/ProductToGLAccountMappingNotFoundException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/exception/ProductToGLAccountMappingNotFoundException.java @@ -15,7 +15,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceN public class ProductToGLAccountMappingNotFoundException extends AbstractPlatformResourceNotFoundException { public ProductToGLAccountMappingNotFoundException(final PortfolioProductType type, final Long productId, final String accountType) { - super("error.msg.productTyAccountMapping.not.found", "Mapping for product of type" + type.toString() + " with Id " + productId + super("error.msg.productToAccountMapping.not.found", "Mapping for product of type " + type.toString() + " with Id " + productId + " does not exist for an account of type " + accountType, type.toString(), productId, accountType); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java index 978597db2..7aa16db63 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java @@ -107,6 +107,11 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer { baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) .notNull().integerGreaterThanZero(); + final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), element); + baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue()).value(overpaymentAccountId) + .notNull().integerGreaterThanZero(); + final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue()) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java index 74de8f839..9f72f2fbd 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java @@ -52,6 +52,11 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.EXPENSE, PortfolioProductType.LOAN); } + public void saveLoanToLiabilityAccountMapping(final JsonElement element, final String paramName, final Long productId, + final int placeHolderTypeId) { + saveProductToAccountMapping(element, paramName, productId, placeHolderTypeId, GLAccountType.LIABILITY, PortfolioProductType.LOAN); + } + /*** Set of abstractions for merging Savings Products to GL Account Mappings ***/ public void mergeLoanToAssetAccountMappingChanges(final JsonElement element, final String paramName, final Long productId, final int accountTypeId, final String accountTypeName, final Map changes) { @@ -71,6 +76,12 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi PortfolioProductType.LOAN); } + public void mergeLoanToLiabilityAccountMappingChanges(final JsonElement element, final String paramName, final Long productId, + final int accountTypeId, final String accountTypeName, final Map changes) { + mergeProductToAccountMappingChanges(element, paramName, productId, accountTypeId, accountTypeName, changes, + GLAccountType.LIABILITY, PortfolioProductType.LOAN); + } + /*** Abstractions for payments channel related to loan products ***/ public void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId, @@ -112,6 +123,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element); final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element); + final Long overPaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), + element); final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); @@ -132,19 +145,13 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId); + changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), overPaymentAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); - break; + //$FALL-THROUGH$ case ACCRUAL_BASED: - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), loanPortfolioAccountId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), incomeFromInterestId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), receivableFeeAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId); - changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); break; } @@ -187,6 +194,10 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(), changes); + + // liabilities + mergeLoanToLiabilityAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId, + CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue(), CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.toString(), changes); break; case ACCRUAL_BASED: // assets (including receivables) @@ -221,6 +232,10 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi mergeLoanToExpenseAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue(), ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.toString(), changes); + + // liabilities + mergeLoanToLiabilityAccountMappingChanges(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId, + CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue(), CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.toString(), changes); break; } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java index cc59f506b..2ce50d032 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java @@ -106,6 +106,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), glAccountId); } else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) { accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), glAccountId); + } else if (glAccountForLoan.equals(CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT)) { + accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), glAccountId); } } } else if (AccountingRuleType.ACCRUAL_BASED.getValue().equals(accountingType)) { @@ -126,6 +128,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), glAccountId); } else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO)) { accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), glAccountId); + } else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.OVERPAYMENT)) { + accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), glAccountId); } else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE)) { accountMappingDetails.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), glAccountId); } else if (glAccountForLoan.equals(ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java index 88e62779f..9ec60469e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java @@ -79,9 +79,11 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES.getValue()); // expenses - this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element, - LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId, - CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue()); + this.loanProductToGLAccountMappingHelper + .saveLoanToLiabilityAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId, + CASH_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue()); + + // liabilities // advanced accounting mappings this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null); @@ -125,6 +127,11 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue()); + // liabilities + this.loanProductToGLAccountMappingHelper.saveLoanToLiabilityAccountMapping(element, + LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), loanProductId, + ACCRUAL_ACCOUNTS_FOR_LOAN.OVERPAYMENT.getValue()); + // advanced accounting mappings this.loanProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, loanProductId, null); this.loanProductToGLAccountMappingHelper.saveChargesToIncomeAccountMappings(command, element, loanProductId, null); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java index db19d8b74..657815a5c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java @@ -35,6 +35,7 @@ public class LoanTransactionData { private final BigDecimal interestPortion; private final BigDecimal feeChargesPortion; private final BigDecimal penaltyChargesPortion; + private final BigDecimal overpaymentPortion; private final String externalId; private final AccountTransferData transfer; @@ -46,23 +47,24 @@ public class LoanTransactionData { return new LoanTransactionData(loanTransactionData.id, loanTransactionData.officeId, loanTransactionData.officeName, loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion, - loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, paymentTypeOptions, - loanTransactionData.externalId, loanTransactionData.transfer); + loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, loanTransactionData.overpaymentPortion, + paymentTypeOptions, loanTransactionData.externalId, loanTransactionData.transfer); } public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final String externalId, final AccountTransferData transfer) { - this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, - feeChargesPortion, penaltyChargesPortion, null, externalId, transfer); - } - - public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, - final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, - final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final Collection paymentTypeOptions, final String externalId, + final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, final String externalId, final AccountTransferData transfer) { + this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, + feeChargesPortion, penaltyChargesPortion, overpaymentPortion, null, externalId, transfer); + } + + public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, + final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, + final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, + final BigDecimal penaltyChargesPortion, final BigDecimal overpaymentPortion, + final Collection paymentTypeOptions, final String externalId, final AccountTransferData transfer) { this.id = id; this.officeId = officeId; this.officeName = officeName; @@ -78,6 +80,7 @@ public class LoanTransactionData { this.paymentTypeOptions = paymentTypeOptions; this.externalId = externalId; this.transfer = transfer; + this.overpaymentPortion = overpaymentPortion; } public LocalDate dateOf() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index 212c5de8c..db8a2f419 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -82,6 +82,9 @@ public final class LoanTransaction extends AbstractPersistable { @Column(name = "penalty_charges_portion_derived", scale = 6, precision = 19, nullable = true) private BigDecimal penaltyChargesPortion; + @Column(name = "overpayment_portion_derived", scale = 6, precision = 19, nullable = true) + private BigDecimal overPaymentPortion; + @Column(name = "is_reversed", nullable = false) private boolean reversed; @@ -133,21 +136,21 @@ public final class LoanTransaction extends AbstractPersistable { return new LoanTransaction(loan, office, LoanTransactionType.INITIATE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() - .getTotalPenaltyChargesOutstanding(), false, null); + .getTotalPenaltyChargesOutstanding(), null, false, null); } public static LoanTransaction approveTransfer(final Office office, final Loan loan, final LocalDate transferDate) { return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() - .getTotalPenaltyChargesOutstanding(), false, null); + .getTotalPenaltyChargesOutstanding(), null, false, null); } public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate) { return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(), loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary() .getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary() - .getTotalPenaltyChargesOutstanding(), false, null); + .getTotalPenaltyChargesOutstanding(), null, false, null); } public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail, @@ -158,8 +161,8 @@ public final class LoanTransaction extends AbstractPersistable { 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.reversed, - loanTransaction.paymentDetail); + loanTransaction.feeChargesPortion, loanTransaction.penaltyChargesPortion, loanTransaction.overPaymentPortion, + loanTransaction.reversed, loanTransaction.paymentDetail); } public static LoanTransaction applyLoanCharge(final Loan loan, final Office office, final Money amount, final LocalDate applyDate, @@ -176,13 +179,15 @@ public final class LoanTransaction extends AbstractPersistable { && (loanTransaction.getPrincipalPortion(currency).isEqualTo(newLoanTransaction.getPrincipalPortion(currency))) && (loanTransaction.getInterestPortion(currency).isEqualTo(newLoanTransaction.getInterestPortion(currency))) && (loanTransaction.getFeeChargesPortion(currency).isEqualTo(newLoanTransaction.getFeeChargesPortion(currency))) - && (loanTransaction.getPenaltyChargesPortion(currency).isEqualTo(newLoanTransaction.getPenaltyChargesPortion(currency)))) { return true; } + && (loanTransaction.getPenaltyChargesPortion(currency).isEqualTo(newLoanTransaction.getPenaltyChargesPortion(currency))) + && (loanTransaction.getOverPaymentPortion(currency).isEqualTo(newLoanTransaction.getOverPaymentPortion(currency)))) { return true; } return false; } private LoanTransaction(final Loan loan, final Office office, final Integer typeOf, final Date dateOf, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final boolean reversed, final PaymentDetail paymentDetail) { + final BigDecimal penaltyChargesPortion, final BigDecimal overPaymentPortion, final boolean reversed, + final PaymentDetail paymentDetail) { super(); this.loan = loan; this.typeOf = typeOf; @@ -192,6 +197,7 @@ public final class LoanTransaction extends AbstractPersistable { this.interestPortion = interestPortion; this.feeChargesPortion = feeChargesPortion; this.penaltyChargesPortion = penaltyChargesPortion; + this.overPaymentPortion = overPaymentPortion; this.reversed = reversed; this.paymentDetail = paymentDetail; this.office = office; @@ -274,6 +280,11 @@ public final class LoanTransaction extends AbstractPersistable { .plus(getPenaltyChargesPortion(currency)).getAmount(); } + public void updateOverPayments(final Money overPayment) { + final MonetaryCurrency currency = overPayment.getCurrency(); + this.overPaymentPortion = defaultToNullIfZero(getOverPaymentPortion(currency).plus(overPayment).getAmount()); + } + public Money getPrincipalPortion(final MonetaryCurrency currency) { return Money.of(currency, this.principalPortion); } @@ -294,6 +305,10 @@ public final class LoanTransaction extends AbstractPersistable { return Money.of(currency, this.penaltyChargesPortion); } + public Money getOverPaymentPortion(final MonetaryCurrency currency) { + return Money.of(currency, this.overPaymentPortion); + } + public Money getAmount(final MonetaryCurrency currency) { return Money.of(currency, this.amount); } @@ -425,7 +440,7 @@ public final class LoanTransaction extends AbstractPersistable { } return new LoanTransactionData(getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion, - this.penaltyChargesPortion, this.externalId, transfer); + this.penaltyChargesPortion, this.overPaymentPortion, this.externalId, transfer); } public Map toMapData(final CurrencyData currencyData) { @@ -444,6 +459,7 @@ public final class LoanTransaction extends AbstractPersistable { thisTransactionData.put("interestPortion", this.interestPortion); thisTransactionData.put("feeChargesPortion", this.feeChargesPortion); thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion); + thisTransactionData.put("overPaymentPortion", this.overPaymentPortion); if (this.paymentDetail != null) { thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 4fca20850..244f87bfd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -186,6 +186,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (transactionAmountUnprocessed.isGreaterThanZero()) { onLoanOverpayment(loanTransaction, transactionAmountUnprocessed); + loanTransaction.updateOverPayments(transactionAmountUnprocessed); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 4fadd17b0..2efe26092 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -330,7 +330,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = this.codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, - possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null, null); + possibleNextRepaymentAmount.getAmount(), null, null, null, null, null, paymentOptions, null, null); } @Override @@ -354,7 +354,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount(); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, - null, null, null, null, null, null); + null, null, null, null, null, null, null); } @Override @@ -364,7 +364,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); return new LoanTransactionData(null, null, null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, - null, null, null, null); + null, null, null, null, null); } @Override @@ -375,7 +375,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = this.codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, - null, null, null, paymentOptions, null, null); + null, null, null, null, paymentOptions, null, null); } @Override @@ -861,6 +861,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, " + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, " + + " tr.overpayment_portion_derived as overpayment, " + " pd.payment_type_cv_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, " + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " @@ -920,6 +921,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal interestPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "interest"); final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); + final BigDecimal overPaymentPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "overpayment"); final String externalId = rs.getString("externalId"); AccountTransferData transfer = null; @@ -944,7 +946,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount, - principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, externalId, transfer); + principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overPaymentPortion, externalId, transfer); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java index fd018242d..1aa3d6ab1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -48,8 +48,8 @@ public final class LoanProductDataValidator { LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), - LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), + LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue())); @@ -282,6 +282,11 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) .notNull().integerGreaterThanZero(); + final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), element); + baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue()).value(overpaymentAccountId) + .notNull().integerGreaterThanZero(); + validatePaymentChannelFundSourceMappings(baseDataValidator, element); validateChargeToIncomeAccountMappings(baseDataValidator, element); @@ -520,6 +525,11 @@ public final class LoanProductDataValidator { baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) .ignoreIfNull().integerGreaterThanZero(); + final Long overpaymentAccountId = this.fromApiJsonHelper.extractLongNamed(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), + element); + baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue()).value(overpaymentAccountId) + .ignoreIfNull().integerGreaterThanZero(); + final Long receivableInterestAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element); baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue())