Merge pull request #289 from vishwasbabu/mifosx-223

user configurable payment types and related accounting changes
This commit is contained in:
Vishwas Babu A J
2013-05-13 23:42:04 -07:00
33 changed files with 698 additions and 414 deletions
@@ -0,0 +1,17 @@
INSERT INTO `m_code` (`code_name`, `is_system_defined`) VALUES
('PaymentType',1);
/*Update payment detail to add foreign key relationship to user defined Code Value*/
update m_loan_transaction set payment_detail_id=null;
ALTER TABLE `m_payment_detail`
ALTER `payment_type_enum` DROP DEFAULT;
ALTER TABLE `m_payment_detail`
CHANGE COLUMN `payment_type_enum` `payment_type_cv_id` INT(11) NULL AFTER `id`;
delete from m_payment_detail;
ALTER TABLE `m_payment_detail`
ADD CONSTRAINT `FK_m_payment_detail_m_code_value` FOREIGN KEY (`payment_type_cv_id`) REFERENCES `m_code_value` (`id`);
/*Map Different Payment Channels to payment Types*/
ALTER TABLE `acc_product_mapping`
ADD COLUMN `payment_type` INT(11) NULL DEFAULT NULL AFTER `product_type`,
ADD CONSTRAINT `FK_acc_product_mapping_m_code_value` FOREIGN KEY (`payment_type`) REFERENCES `m_code_value` (`id`);
@@ -84,7 +84,8 @@ public class AccountingConstants {
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");
"receivablePenaltyAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelFundSourceMappings"), PAYMENT_TYPE(
"paymentTypeId");
private final String value;
@@ -101,7 +102,7 @@ public class AccountingConstants {
return this.value;
}
}
public static final String ASSESTS_TAG_OPTION_CODE_NAME = "AssetAccountTags";
public static final String LIABILITIES_TAG_OPTION_CODE_NAME = "LiabilityAccountTags";
public static final String EQUITY_TAG_OPTION_CODE_NAME = "EquityAccountTags";
@@ -27,8 +27,7 @@ public class JournalEntryCommand {
private final SingleDebitOrCreditEntryCommand[] debits;
public JournalEntryCommand(final Long officeId, final LocalDate transactionDate, final String comments,
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits,
final String referenceNumber) {
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits, final String referenceNumber) {
this.officeId = officeId;
this.transactionDate = transactionDate;
this.comments = comments;
@@ -52,7 +51,7 @@ public class JournalEntryCommand {
baseDataValidator.reset().parameter("credits").value(this.credits).notNull();
baseDataValidator.reset().parameter("debits").value(this.debits).notNull();
baseDataValidator.reset().parameter("referenceNumber").value(this.referenceNumber).ignoreIfNull().notExceedingLengthOf(100);
// validation for credit array elements
@@ -118,8 +117,8 @@ public class JournalEntryCommand {
return this.debits;
}
public String getReferenceNumber() {
return this.referenceNumber;
}
public String getReferenceNumber() {
return this.referenceNumber;
}
}
@@ -14,6 +14,7 @@ public class LoanTransactionDTO {
private final String transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final LoanTransactionEnumData transactionType;
private final BigDecimal amount;
@@ -27,9 +28,10 @@ public class LoanTransactionDTO {
/*** Boolean values determines if the transaction is reversed ***/
private final boolean reversed;
public LoanTransactionDTO(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) {
public LoanTransactionDTO(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) {
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
@@ -77,4 +79,8 @@ public class LoanTransactionDTO {
return this.reversed;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
}
@@ -71,9 +71,10 @@ public class AccountingProcessorHelper {
final BigDecimal fees = (BigDecimal) map.get("feeChargesPortion");
final BigDecimal penalties = (BigDecimal) map.get("penaltyChargesPortion");
final boolean reversed = (Boolean) map.get("reversed");
final Long paymentTypeId = (Long) map.get("paymentTypeId");
final LoanTransactionDTO transaction = new LoanTransactionDTO(transactionId, transactionDate, transactionType, amount,
principal, interest, fees, penalties, reversed);
final LoanTransactionDTO transaction = new LoanTransactionDTO(paymentTypeId, transactionId, transactionDate, transactionType,
amount, principal, interest, fees, penalties, reversed);
newLoanTransactions.add(transaction);
@@ -95,6 +96,7 @@ public class AccountingProcessorHelper {
* @param accountTypeToBeCredited
* Enum of the placeholder of the GLAccount to be credited
* @param loanProductId
* @param paymentTypeId
* @param loanId
* @param transactionId
* @param transactionDate
@@ -103,8 +105,8 @@ public class AccountingProcessorHelper {
*/
public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office,
final ACCRUAL_ACCOUNTS_FOR_LOAN accountTypeToBeDebited, final ACCRUAL_ACCOUNTS_FOR_LOAN accountTypeToBeCredited,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount, final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited.getValue();
int accountTypeToCreditId = accountTypeToBeCredited.getValue();
// reverse debits and credits for reversals
@@ -112,8 +114,8 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited.getValue();
accountTypeToCreditId = accountTypeToBeDebited.getValue();
}
createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, loanId, transactionId,
transactionDate, amount);
createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, amount);
}
/**
@@ -129,6 +131,7 @@ public class AccountingProcessorHelper {
* @param accountTypeToBeCredited
* Enum of the placeholder of the GLAccount to be credited
* @param loanProductId
* @param paymentTypeId
* @param loanId
* @param transactionId
* @param transactionDate
@@ -136,8 +139,8 @@ public class AccountingProcessorHelper {
* @param isReversal
*/
public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountTypeToBeDebited,
final CASH_ACCOUNTS_FOR_LOAN accountTypeToBeCredited, final Long loanProductId, final Long loanId, final String transactionId,
final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
final CASH_ACCOUNTS_FOR_LOAN accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId, final Long loanId,
final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
int accountTypeToDebitId = accountTypeToBeDebited.getValue();
int accountTypeToCreditId = accountTypeToBeCredited.getValue();
// reverse debits and credits for reversals
@@ -145,40 +148,40 @@ public class AccountingProcessorHelper {
accountTypeToDebitId = accountTypeToBeCredited.getValue();
accountTypeToCreditId = accountTypeToBeDebited.getValue();
}
createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, loanId, transactionId,
transactionDate, amount);
createJournalEntriesForLoan(office, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, amount);
}
public void createCreditJournalEntryOrReversalForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountMappingType,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount, final Boolean isReversal) {
int accountMappingTypeId = accountMappingType.getValue();
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
amount, isReversal);
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount, isReversal);
}
public void createCreditJournalEntryOrReversalForLoan(final Office office, final ACCRUAL_ACCOUNTS_FOR_LOAN accountMappingType,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount, final Boolean isReversal) {
int accountMappingTypeId = accountMappingType.getValue();
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
amount, isReversal);
createCreditJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount, isReversal);
}
public void createDebitJournalEntryOrReversalForLoan(final Office office, final CASH_ACCOUNTS_FOR_LOAN accountMappingType,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount, final Boolean isReversal) {
int accountMappingTypeId = accountMappingType.getValue();
createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
amount, isReversal);
createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount, isReversal);
}
public void createDebitJournalEntryOrReversalForLoan(final Office office, final ACCRUAL_ACCOUNTS_FOR_LOAN accountMappingType,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount, final Boolean isReversal) {
int accountMappingTypeId = accountMappingType.getValue();
createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, loanId, transactionId, transactionDate,
amount, isReversal);
createDebitJournalEntryOrReversalForLoan(office, accountMappingTypeId, loanProductId, paymentTypeId, loanId, transactionId,
transactionDate, amount, isReversal);
}
/**
@@ -205,16 +208,18 @@ public class AccountingProcessorHelper {
}
private void createJournalEntriesForLoan(final Office office, final int accountTypeToDebitId, final int accountTypeToCreditId,
final Long loanProductId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
GLAccount debitAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToDebitId);
GLAccount creditAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToCreditId);
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
final BigDecimal amount) {
GLAccount debitAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToDebitId, paymentTypeId);
GLAccount creditAccount = getLinkedGLAccountForLoanProduct(loanProductId, accountTypeToCreditId, paymentTypeId);
createDebitJournalEntryForLoan(office, debitAccount, loanId, transactionId, transactionDate, amount);
createCreditJournalEntryForLoan(office, creditAccount, loanId, transactionId, transactionDate, amount);
}
private void createDebitJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId);
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId);
if (isReversal) {
createCreditJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
} else {
@@ -223,8 +228,9 @@ public class AccountingProcessorHelper {
}
private void createCreditJournalEntryOrReversalForLoan(final Office office, final int accountMappingTypeId, final Long loanProductId,
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId);
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
final Boolean isReversal) {
GLAccount account = getLinkedGLAccountForLoanProduct(loanProductId, accountMappingTypeId, paymentTypeId);
if (isReversal) {
createDebitJournalEntryForLoan(office, account, loanId, transactionId, transactionDate, amount);
} else {
@@ -248,11 +254,22 @@ public class AccountingProcessorHelper {
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
private GLAccount getLinkedGLAccountForLoanProduct(final Long loanProductId, final int accountMappingTypeId) {
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountType(loanProductId, PortfolioProductType.LOAN.getValue(),
accountMappingTypeId);
private GLAccount getLinkedGLAccountForLoanProduct(final Long loanProductId, final int accountMappingTypeId, final Long paymentTypeId) {
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId,
PortfolioProductType.LOAN.getValue(), accountMappingTypeId);
/****
* Get more specific mapping for FUND source accounts (based on payment
* channels). Note that fund source placeholder ID would be same for
* both cash and accrual accounts
***/
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue()) {
ProductToGLAccountMapping paymentChannelSpecificAccountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentType(loanProductId,
PortfolioProductType.LOAN.getValue(), accountMappingTypeId, paymentTypeId);
if (paymentChannelSpecificAccountMapping != null) {
accountMapping = paymentChannelSpecificAccountMapping;
}
}
return accountMapping.getGlAccount();
}
}
@@ -86,11 +86,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
final boolean isReversed = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for the disbursement (or disbursement
// reversal)
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversed);
ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
disbursalAmount, isReversed);
}
@@ -114,11 +116,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
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)) {
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE,
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId, transactionId, transactionDate, amount, isReversed);
ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
amount, isReversed);
}
}
@@ -176,6 +180,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal interestAmount = loanTransactionDTO.getInterest();
final BigDecimal feesAmount = loanTransactionDTO.getFees();
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
final boolean isReversal = loanTransactionDTO.isReversed();
BigDecimal totalDebitAmount = new BigDecimal(0);
@@ -183,15 +188,15 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
// handle principal payment or writeOff (and reversals)
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
transactionId, transactionDate, principalAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, principalAmount, isReversal);
}
// handle interest payment of writeOff (and reversals)
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, loanProductId, loanId,
transactionId, transactionDate, interestAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.INTEREST_RECEIVABLE, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversal);
}
// handle fees payment of writeOff (and reversals)
@@ -204,15 +209,15 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
feeAccountToCredit = ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES;
}
helper.createCreditJournalEntryOrReversalForLoan(office, feeAccountToCredit, loanProductId, loanId, transactionId,
transactionDate, feesAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, feeAccountToCredit, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, feesAmount, isReversal);
}
// handle penalties payment of writeOff (and reversals)
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE, loanProductId, loanId,
transactionId, transactionDate, penaltiesAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, penaltiesAmount, isReversal);
}
/**
@@ -220,11 +225,11 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
* reversals)
***/
if (writeOff) {
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF, loanProductId, loanId,
transactionId, transactionDate, totalDebitAmount, isReversal);
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
} else {
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId,
transactionId, transactionDate, totalDebitAmount, isReversal);
helper.createDebitJournalEntryOrReversalForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, totalDebitAmount, isReversal);
}
}
@@ -256,19 +261,19 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
final BigDecimal feesAmount = loanTransactionDTO.getFees();
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
final boolean isReversed = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
// create journal entries for the fees application (or reversal)
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.FEES_RECEIVABLE,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, loanId, transactionId, transactionDate, feesAmount,
isReversed);
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
feesAmount, isReversed);
}
// create journal entries for the penalties application (or reversal)
else if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
helper.createAccrualBasedJournalEntriesAndReversalsForLoan(office, ACCRUAL_ACCOUNTS_FOR_LOAN.PENALTIES_RECEIVABLE,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, loanId, transactionId, transactionDate,
ACCRUAL_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
penaltiesAmount, isReversed);
}
}
}
@@ -34,6 +34,7 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
for (final LoanTransactionDTO loanTransactionDTO : loanDTO.getNewLoanTransactions()) {
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final String transactionId = loanTransactionDTO.getTransactionId();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
final Long loanId = loanDTO.getLoanId();
helper.checkForBranchClosures(latestGLClosure, transactionDate);
@@ -58,8 +59,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
else if (loanTransactionDTO.getTransactionType().isWriteOff() && !loanTransactionDTO.isReversed()) {
final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF,
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId, transactionId, transactionDate, principalAmount,
false);
CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate,
principalAmount, false);
}
}
}
@@ -86,9 +87,11 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final Date transactionDate = loanTransactionDTO.getTransactionDate();
final BigDecimal disbursalAmount = loanTransactionDTO.getAmount();
final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO,
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId, transactionDate, disbursalAmount, isReversal);
CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, disbursalAmount,
isReversal);
}
/**
@@ -113,35 +116,36 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF
final BigDecimal feesAmount = loanTransactionDTO.getFees();
final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
final boolean isReversal = loanTransactionDTO.isReversed();
final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
BigDecimal totalDebitAmount = new BigDecimal(0);
if (principalAmount != null && !(principalAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(principalAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, loanId,
transactionId, transactionDate, principalAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, principalAmount, isReversal);
}
if (interestAmount != null && !(interestAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(interestAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId, loanId,
transactionId, transactionDate, interestAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INTEREST_ON_LOANS, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, interestAmount, isReversal);
}
if (feesAmount != null && !(feesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(feesAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, loanId,
transactionId, transactionDate, feesAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES, loanProductId, paymentTypeId,
loanId, transactionId, transactionDate, feesAmount, isReversal);
}
if (penaltiesAmount != null && !(penaltiesAmount.compareTo(BigDecimal.ZERO) == 0)) {
totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId, loanId,
transactionId, transactionDate, penaltiesAmount, isReversal);
helper.createCreditJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_PENALTIES, loanProductId,
paymentTypeId, loanId, transactionId, transactionDate, penaltiesAmount, isReversal);
}
/*** create a single debit entry (or reversal) for the entire amount **/
helper.createDebitJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, loanId, transactionId,
transactionDate, totalDebitAmount, isReversal);
helper.createDebitJournalEntryOrReversalForLoan(office, CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE, loanProductId, paymentTypeId, loanId,
transactionId, transactionDate, totalDebitAmount, isReversal);
}
}
@@ -14,11 +14,12 @@ import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "acc_product_mapping", uniqueConstraints = { @UniqueConstraint(columnNames = { "product_id", "product_type",
"financial_account_type" }, name = "financial_action") })
"financial_account_type", "payment_type" }, name = "financial_action") })
public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@@ -28,6 +29,10 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
@Column(name = "product_id", nullable = false)
private Long productId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "payment_type", nullable = true)
private CodeValue paymentType;
@Column(name = "product_type", nullable = false)
private int productType;
@@ -44,10 +49,16 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
}
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType, final int financialAccountType) {
this(glAccount, productId, productType, financialAccountType, null);
}
public ProductToGLAccountMapping(final GLAccount glAccount, final Long productId, final int productType,
final int financialAccountType, final CodeValue paymentType) {
this.glAccount = glAccount;
this.productId = productId;
this.productType = productType;
this.financialAccountType = financialAccountType;
this.paymentType = paymentType;
}
public GLAccount getGlAccount() {
@@ -82,4 +93,12 @@ public class ProductToGLAccountMapping extends AbstractPersistable<Long> {
this.financialAccountType = financialAccountType;
}
public CodeValue getPaymentType() {
return this.paymentType;
}
public void setPaymentType(CodeValue paymentType) {
this.paymentType = paymentType;
}
}
@@ -9,11 +9,23 @@ import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface ProductToGLAccountMappingRepository extends JpaRepository<ProductToGLAccountMapping, Long>,
JpaSpecificationExecutor<ProductToGLAccountMapping> {
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountType(Long productId, int productType, int financialAccountType);
ProductToGLAccountMapping findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentType(Long productId, int productType,
int financialAccountType, Long paymentType);
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=:financialAccountType and mapping.paymentType is NULL")
ProductToGLAccountMapping findCoreProductToFinAccountMapping(@Param("productId") Long productId, @Param("productType") int productType,
@Param("financialAccountType") int financialAccountType);
/*** The financial Account Type for a fund source will always be an asset (1) ***/
@Query("from ProductToGLAccountMapping mapping where mapping.productId =:productId and mapping.productType =:productType and mapping.financialAccountType=1 and mapping.paymentType is not NULL")
List<ProductToGLAccountMapping> findAllPaymentTypeToFundSourceMappings(@Param("productId") Long productId,
@Param("productType") int productType);
List<ProductToGLAccountMapping> findByProductIdAndProductType(Long productId, int productType);
}
@@ -10,4 +10,6 @@ import java.util.Map;
public interface ProductToGLAccountMappingReadPlatformService {
Map<String, Object> fetchAccountMappingDetailsForLoanProduct(final Long loanProductId, final Integer accountingType);
Map<Long, Long> fetchPaymentTypeToFundSourceMappingsForLoanProduct(final Long loanProductId);
}
@@ -34,8 +34,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
private static final class LoanProductToGLAccountMappingMapper implements RowMapper<Map<String, Object>> {
public String schema() {
return " id as id, gl_account_id as glAccountId,product_id as productId,product_type as productType,financial_account_type as financialAccountType"
+ " from acc_product_mapping " + " where product_type=1 ";
return " id as id, gl_account_id as glAccountId,product_id as productId,product_type as productType,financial_account_type as financialAccountType,"
+ " payment_type as paymentTypeId from acc_product_mapping " + " where product_type=1 ";
}
@Override
@@ -44,6 +44,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
final Long id = rs.getLong("id");
final Long glAccountId = rs.getLong("glAccountId");
final Long productId = rs.getLong("productId");
final Long paymentTypeId = rs.getLong("paymentTypeId");
final Integer productType = rs.getInt("productType");
final Integer financialAccountType = rs.getInt("financialAccountType");
@@ -53,6 +54,7 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
loanProductToGLAccountMap.put("productId", productId);
loanProductToGLAccountMap.put("productType", productType);
loanProductToGLAccountMap.put("financialAccountType", financialAccountType);
loanProductToGLAccountMap.put("paymentTypeId", paymentTypeId);
return loanProductToGLAccountMap;
}
@@ -124,4 +126,25 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
return accountMappingDetails;
}
@Override
public Map<Long, Long> fetchPaymentTypeToFundSourceMappingsForLoanProduct(Long loanProductId) {
Map<Long, Long> paymentTypeToFundSourceMappingDetails = null;
final LoanProductToGLAccountMappingMapper rm = new LoanProductToGLAccountMappingMapper();
final String sql = "select " + rm.schema() + " and product_id = ? and payment_type is not null";
final List<Map<String, Object>> paymentTypeToFundSourceMappingsList = this.jdbcTemplate.query(sql, rm,
new Object[] { loanProductId });
for (final Map<String, Object> productToGLAccountMap : paymentTypeToFundSourceMappingsList) {
if (paymentTypeToFundSourceMappingDetails == null) {
paymentTypeToFundSourceMappingDetails = new LinkedHashMap<Long, Long>();
}
final Long glAccountId = (Long) productToGLAccountMap.get("glAccountId");
final Long paymentTypeId = (Long) productToGLAccountMap.get("paymentTypeId");
paymentTypeToFundSourceMappingDetails.put(paymentTypeId, glAccountId);
}
return paymentTypeToFundSourceMappingDetails;
}
}
@@ -6,9 +6,11 @@
package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
@@ -23,14 +25,19 @@ import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAc
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingInvalidException;
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.accounting.producttoaccountmapping.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@Service
public class ProductToGLAccountMappingWritePlatformServiceImpl implements ProductToGLAccountMappingWritePlatformService {
@@ -39,15 +46,17 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final FromJsonHelper fromApiJsonHelper;
private final ProductToGLAccountMappingFromApiJsonDeserializer deserializer;
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
@Autowired
public ProductToGLAccountMappingWritePlatformServiceImpl(final GLAccountRepository glAccountRepository,
final ProductToGLAccountMappingRepository glAccountMappingRepository, final FromJsonHelper fromApiJsonHelper,
final ProductToGLAccountMappingFromApiJsonDeserializer deserializer) {
final ProductToGLAccountMappingFromApiJsonDeserializer deserializer, final CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
this.accountRepository = glAccountRepository;
this.accountMappingRepository = glAccountMappingRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
this.deserializer = deserializer;
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
}
@Override
@@ -78,6 +87,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
// expenses
saveLoanToExpenseAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
CASH_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
// advanced accounting mappings
savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
break;
case ACCRUAL_BASED:
@@ -104,6 +116,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
// expenses
saveLoanToExpenseAccountMapping(element, LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
ACCRUAL_ACCOUNTS_FOR_LOAN.LOSSES_WRITTEN_OFF.getValue());
// advanced accounting mappings
savePaymentChannelToFundSourceMappings(command, element, loanProductId, null);
break;
}
}
@@ -127,9 +142,11 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
if (accountingRuleChanged) {
this.deserializer.validateForCreate(command.json());
handleChangesToAccountingRuleType(loanProductId, command, changes, element, accountingRuleType);
savePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
}/*** else examine and update individual changes ***/
else {
handleChangesToLoanProductToGLAccountMappings(loanProductId, changes, element, accountingRuleType);
updatePaymentChannelToFundSourceMappings(command, element, loanProductId, changes);
}
return changes;
}
@@ -143,6 +160,129 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
}
}
/**
* Saves payment type to Fund source mappings (also populates the changes
* array if passed in)
*
* @param command
* @param element
* @param productId
* @param changes
*/
private void savePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
Map<String, Object> changes) {
JsonArray paymentChannelMappingArray = fromApiJsonHelper.extractJsonArrayNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
if (paymentChannelMappingArray != null) {
if (changes != null) {
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
command.jsonFragment(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue()));
}
for (int i = 0; i < paymentChannelMappingArray.size(); i++) {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).getAsLong();
savePaymentChannelToFundSourceMapping(productId, paymentTypeId, paymentSpecificFundAccountId);
}
}
}
/**
* Finds
*
* @param command
* @param element
* @param productId
* @param changes
*/
private void updatePaymentChannelToFundSourceMappings(final JsonCommand command, final JsonElement element, final Long productId,
Map<String, Object> changes) {
// find all existing payment Channel to Fund source Mappings
List<ProductToGLAccountMapping> existingPaymentChannelToFundSourceMappings = accountMappingRepository
.findAllPaymentTypeToFundSourceMappings(productId, PortfolioProductType.LOAN.getValue());
JsonArray paymentChannelMappingArray = fromApiJsonHelper.extractJsonArrayNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
/**
* Variable stores a map representation of Payment channels (key) and
* their fund sources (value) extracted from the passed in Jsoncommand
**/
Map<Long, Long> inputPaymentChannelFundSourceMap = new HashMap<Long, Long>();
/***
* Variable stores all payment types which hae already been mapped to
* Fund Sources in the system
**/
Set<Long> existingPaymentTypes = new HashSet<Long>();
if (paymentChannelMappingArray != null) {
if (changes != null) {
changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
command.jsonFragment(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue()));
}
for (int i = 0; i < paymentChannelMappingArray.size(); i++) {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).getAsLong();
inputPaymentChannelFundSourceMap.put(paymentTypeId, paymentSpecificFundAccountId);
}
// If input map is empty, delete all existing mappings
if (inputPaymentChannelFundSourceMap.size() == 0) {
accountMappingRepository.deleteInBatch(existingPaymentChannelToFundSourceMappings);
}/**
* Else, <br/>
* update existing mappings OR <br/>
* delete old mappings (which re already present, but not passed in
* as a part of Jsoncommand)<br/>
* Create new mappings for payment types that are passed in as a
* part of the Jsoncommand but not already present
*
**/
else {
for (ProductToGLAccountMapping existingPaymentChannelToFundSourceMapping : existingPaymentChannelToFundSourceMappings) {
Long currentPaymentChannelId = existingPaymentChannelToFundSourceMapping.getId();
existingPaymentTypes.add(currentPaymentChannelId);
// update existing mappings (if required)
if (inputPaymentChannelFundSourceMap.containsKey(existingPaymentChannelToFundSourceMapping.getId())) {
Long newGLAccountId = inputPaymentChannelFundSourceMap.get(existingPaymentChannelToFundSourceMapping.getId());
if (newGLAccountId != existingPaymentChannelToFundSourceMapping.getGlAccount().getId()) {
final GLAccount glAccount = getAccountByIdAndType(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(),
GLAccountType.ASSET, newGLAccountId);
existingPaymentChannelToFundSourceMapping.setGlAccount(glAccount);
accountMappingRepository.save(existingPaymentChannelToFundSourceMapping);
}
}// deleted payment type
else {
accountMappingRepository.delete(existingPaymentChannelToFundSourceMapping);
}
}
// create new mappings
Set<Long> incomingPaymentTypes = inputPaymentChannelFundSourceMap.keySet();
boolean newPaymentTypesAdded = incomingPaymentTypes.removeAll(existingPaymentTypes);
if (newPaymentTypesAdded) {
for (Long newPaymentType : incomingPaymentTypes) {
Long newGLAccountId = inputPaymentChannelFundSourceMap.get(newPaymentType);
savePaymentChannelToFundSourceMapping(productId, newPaymentType, newGLAccountId);
}
}
}
}
}
/**
* @param productId
* @param jsonObject
*/
private void savePaymentChannelToFundSourceMapping(final Long productId, final Long paymentTypeId,
final Long paymentTypeSpecificFundAccountId) {
CodeValue paymentType = codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
PaymentDetailConstants.paymentTypeCodeName, paymentTypeId);
final GLAccount glAccount = getAccountByIdAndType(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), GLAccountType.ASSET,
paymentTypeSpecificFundAccountId);
final ProductToGLAccountMapping accountMapping = new ProductToGLAccountMapping(glAccount, productId,
PortfolioProductType.LOAN.getValue(), CASH_ACCOUNTS_FOR_LOAN.FUND_SOURCE.getValue(), paymentType);
this.accountMappingRepository.save(accountMapping);
}
/**
* Examines and updates each account mapping for given loan product with
* changes passed in from the Json element
@@ -396,8 +536,8 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
// get the existing product
if (accountId != null) {
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository
.findByProductIdAndProductTypeAndFinancialAccountType(productId, PortfolioProductType.LOAN.getValue(), accountTypeId);
final ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(productId,
PortfolioProductType.LOAN.getValue(), accountTypeId);
if (accountMapping == null) { throw new ProductToGLAccountMappingNotFoundException(PortfolioProductType.LOAN, productId,
accountTypeName); }
if (accountMapping.getGlAccount().getId() != accountId) {
@@ -411,7 +551,7 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
/**
* Fetches account with a particular Id and throws and Exception it is not
* of the expected Account Category ('DEBIT','CREDIT' etc)
* of the expected Account Category ('ASSET','liability' etc)
*
* @param paramName
* @param expectedAccountType
@@ -6,8 +6,8 @@
package org.mifosplatform.portfolio.loanaccount.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.Consumes;
@@ -25,17 +25,17 @@ import org.apache.commons.lang.StringUtils;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentType;
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -54,17 +54,20 @@ public class LoanTransactionsApiResource {
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
@Autowired
public LoanTransactionsApiResource(final PlatformSecurityContext context, final LoanReadPlatformService loanReadPlatformService,
final ApiRequestParameterHelper apiRequestParameterHelper,
final DefaultToApiJsonSerializer<LoanTransactionData> toApiJsonSerializer,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context;
this.loanReadPlatformService = loanReadPlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.toApiJsonSerializer = toApiJsonSerializer;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
}
private boolean is(final String commandParam, final String commandValue) {
@@ -113,7 +116,8 @@ public class LoanTransactionsApiResource {
LoanTransactionData transactionData = this.loanReadPlatformService.retrieveLoanTransaction(loanId, transactionId);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) {
final List<EnumOptionData> paymentTypeOptions = LoanEnumerations.paymentTypes(PaymentType.values());
final Collection<CodeValueData> paymentTypeOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
transactionData = LoanTransactionData.templateOnTop(transactionData, paymentTypeOptions);
}
@@ -6,11 +6,12 @@
package org.mifosplatform.portfolio.loanaccount.data;
import java.math.BigDecimal;
import java.util.List;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
/**
* Immutable data object representing a loan transaction.
@@ -33,9 +34,9 @@ public class LoanTransactionData {
private final BigDecimal penaltyChargesPortion;
// templates
final List<EnumOptionData> paymentTypeOptions;
final Collection<CodeValueData> paymentTypeOptions;
public static LoanTransactionData templateOnTop(LoanTransactionData loanTransactionData, List<EnumOptionData> paymentTypeOptions) {
public static LoanTransactionData templateOnTop(LoanTransactionData loanTransactionData, Collection<CodeValueData> paymentTypeOptions) {
return new LoanTransactionData(loanTransactionData.id, loanTransactionData.type, loanTransactionData.paymentDetailData,
loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion,
loanTransactionData.interestPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion,
@@ -52,7 +53,7 @@ public class LoanTransactionData {
public LoanTransactionData(final Long id, 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 List<EnumOptionData> paymentTypeOptions) {
final Collection<CodeValueData> paymentTypeOptions) {
this.id = id;
this.type = transactionType;
this.paymentDetailData = paymentDetailData;
@@ -75,6 +75,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -24,8 +24,9 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.data.PaymentDetailData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.springframework.data.jpa.domain.AbstractPersistable;
/**
@@ -353,6 +354,10 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
thisTransactionData.put("feeChargesPortion", this.feeChargesPortion);
thisTransactionData.put("penaltyChargesPortion", this.penaltyChargesPortion);
if (this.paymentDetail != null) {
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
}
return thisTransactionData;
}
@@ -1,128 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.domain;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanaccount.data.PaymentDetailData;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_payment_detail")
public final class PaymentDetail extends AbstractPersistable<Long> {
@Column(name = "payment_type_enum", nullable = false)
private Integer paymentType;
@Column(name = "account_number", length = 50)
private String accountNumber;
@Column(name = "check_number", length = 50)
private String checkNumber;
@Column(name = "routing_code", length = 50)
private String routingCode;
@Column(name = "receipt_number", length = 50)
private String receiptNumber;
@Column(name = "bank_number", length = 50)
private String bankNumber;
protected PaymentDetail() {
}
public static PaymentDetail generatePaymentDetail(final JsonCommand command, final Map<String, Object> changes) {
// create a payment detail
final Integer paymentTypeId = command.integerValueSansLocaleOfParameterNamed("paymentTypeId");
if (paymentTypeId == null) { return null; }
changes.put("paymentTypeId", paymentTypeId);
final String accountNumber = command.stringValueOfParameterNamed("accountNumber");
final String checkNumber = command.stringValueOfParameterNamed("checkNumber");
final String routingCode = command.stringValueOfParameterNamed("routingCode");
final String receiptNumber = command.stringValueOfParameterNamed("receiptNumber");
final String bankNumber = command.stringValueOfParameterNamed("bankNumber");
final PaymentType paymentType = PaymentType.fromInt(paymentTypeId);
PaymentDetail paymentDetail = null;
if (paymentType.isCheckPayment()) {
paymentDetail = PaymentDetail.checkPaymentDetail(accountNumber, checkNumber, routingCode);
if (StringUtils.isNotBlank(accountNumber)) {
changes.put("accountNumber", accountNumber);
}
if (StringUtils.isNotBlank(checkNumber)) {
changes.put("checkNumber", checkNumber);
}
if (StringUtils.isNotBlank(routingCode)) {
changes.put("routingCode", routingCode);
}
} else if (paymentType.isEFTPayment()) {
paymentDetail = PaymentDetail.eftPaymentDetail(accountNumber, routingCode);
if (StringUtils.isNotBlank(accountNumber)) {
changes.put("accountNumber", accountNumber);
}
if (StringUtils.isNotBlank(routingCode)) {
changes.put("routingCode", routingCode);
}
} else if (paymentType.isReceiptPayment()) {
paymentDetail = PaymentDetail.receiptPaymentDetail(receiptNumber, bankNumber);
if (StringUtils.isNotBlank(receiptNumber)) {
changes.put("receiptNumber", receiptNumber);
}
if (StringUtils.isNotBlank(bankNumber)) {
changes.put("bankNumber", bankNumber);
}
} else {
paymentDetail = PaymentDetail.cashPaymentDetail();
}
return paymentDetail;
}
public static PaymentDetail cashPaymentDetail() {
return new PaymentDetail(PaymentType.CASH, null, null, null, null, null);
}
public static PaymentDetail checkPaymentDetail(final String accountNumber, final String checkNumber, final String routingCode) {
return new PaymentDetail(PaymentType.CHECK, accountNumber, checkNumber, routingCode, null, null);
}
public static PaymentDetail receiptPaymentDetail(final String receiptNumber, final String bankNumber) {
return new PaymentDetail(PaymentType.RECEIPT, null, null, null, receiptNumber, bankNumber);
}
public static PaymentDetail eftPaymentDetail(final String accountNumber, final String routingCode) {
return new PaymentDetail(PaymentType.ELECTRONIC_FUND_TRANSFER, accountNumber, null, routingCode, null, null);
}
private PaymentDetail(final PaymentType paymentType, final String accountNumber, final String checkNumber, final String routingCode,
final String receiptNumber, final String bankNumber) {
this.paymentType = paymentType.getValue();
this.accountNumber = accountNumber;
this.checkNumber = checkNumber;
this.routingCode = routingCode;
this.receiptNumber = receiptNumber;
this.bankNumber = bankNumber;
}
public PaymentDetailData toData() {
final EnumOptionData paymentTypeData = LoanEnumerations.paymentType(this.paymentType);
final PaymentDetailData paymentDetailData = new PaymentDetailData(getId(), paymentTypeData, this.accountNumber, this.checkNumber,
this.routingCode, this.receiptNumber, this.bankNumber);
return paymentDetailData;
}
}
@@ -1,86 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.domain;
import java.util.HashMap;
import java.util.Map;
public enum PaymentType {
CASH(1, "paymentType.cash"), CHECK(2, "paymentType.check"), RECEIPT(3, "paymentType.receipt"), //
ELECTRONIC_FUND_TRANSFER(4, "paymentType.eft");// Electronic Funds Transfer
private final Integer value;
private final String code;
private PaymentType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
private static final Map<Integer, PaymentType> intToEnumMap = new HashMap<Integer, PaymentType>();
private static int minValue;
private static int maxValue;
static {
int i = 0;
for (final PaymentType type : PaymentType.values()) {
if (i == 0) {
minValue = type.value;
}
intToEnumMap.put(type.value, type);
if (minValue >= type.value) {
minValue = type.value;
}
if (maxValue < type.value) {
maxValue = type.value;
}
i = i + 1;
}
}
public static PaymentType fromInt(final int i) {
final PaymentType type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
public static int getMinValue() {
return minValue;
}
public static int getMaxValue() {
return maxValue;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public boolean isCashPayment() {
return this.value.equals(PaymentType.CASH.value);
}
public boolean isCheckPayment() {
return this.value.equals(PaymentType.CHECK.value);
}
public boolean isReceiptPayment() {
return this.value.equals(PaymentType.RECEIPT.value);
}
public boolean isEFTPayment() {
return this.value.equals(PaymentType.ELECTRONIC_FUND_TRANSFER.value);
}
}
@@ -21,7 +21,6 @@ import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -102,8 +101,7 @@ public final class LoanEventApiJsonValidator {
private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
// Validate all string payment detail fields for max length
final Integer paymentTypeId = fromApiJsonHelper.extractIntegerWithLocaleNamed("paymentTypeId", element);
baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId)
.inMinMaxRange(PaymentType.getMinValue(), PaymentType.getMaxValue());
baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).ignoreIfNull().integerGreaterThanZero();
final Set<String> paymentDetailParameters = new HashSet<String>(Arrays.asList("accountNumber", "checkNumber", "routingCode",
"receiptNumber", "bankNumber"));
for (String paymentDetailParameterName : paymentDetailParameters) {
@@ -11,7 +11,6 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.joda.time.Days;
@@ -50,7 +49,6 @@ import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
import org.mifosplatform.portfolio.loanaccount.data.LoanSummaryData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.data.PaymentDetailData;
import org.mifosplatform.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
@@ -66,6 +64,8 @@ import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrateg
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
@@ -92,10 +92,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
private final ChargeReadPlatformService chargeReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final CalendarReadPlatformService calendarReadPlatformService;
private final PaginationHelper<LoanAccountData> paginationHelper = new PaginationHelper<LoanAccountData>();
private final LoanMapper loaanLoanMapper = new LoanMapper();
@Autowired
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository,
@@ -313,7 +313,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount();
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
final List<EnumOptionData> paymentOptions = loanDropdownReadPlatformService.retrievePaymentTypeOptions();
final Collection<CodeValueData> paymentOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
return new LoanTransactionData(null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions);
}
@@ -356,7 +357,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
final Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) { throw new LoanNotFoundException(loanId); }
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.DISBURSEMENT);
final List<EnumOptionData> paymentOptions = loanDropdownReadPlatformService.retrievePaymentTypeOptions();
final Collection<CodeValueData> paymentOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
return new LoanTransactionData(null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, null, null,
null, paymentOptions);
}
@@ -806,13 +808,14 @@ 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, "
+ " pd.payment_type_enum as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "
+ " 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, rc.`name` as currencyName, "
+ " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode "
+ " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
+ " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
+ " cv.code_value as paymentTypeName " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
+ " join m_currency rc on rc.`code` = l.currency_code "
+ " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id";
+ " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id"
+ " left join m_code_value cv on pd.payment_type_cv_id = cv.id";
}
@Override
@@ -833,9 +836,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
PaymentDetailData paymentDetailData = null;
if (transactionType.isPaymentOrReceipt()) {
final Integer paymentTypeId = JdbcSupport.getInteger(rs, "paymentType");
final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentType");
if (paymentTypeId != null) {
EnumOptionData paymentType = LoanEnumerations.paymentType(paymentTypeId);
final String typeName = rs.getString("paymentTypeName");
CodeValueData paymentType = CodeValueData.instance(paymentTypeId, typeName);
final String accountNumber = rs.getString("accountNumber");
final String checkNumber = rs.getString("checkNumber");
final String routingCode = rs.getString("routingCode");
@@ -860,6 +864,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@Override
public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId) {
context.authenticatedUser();
final LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
@@ -57,8 +57,6 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentDetail;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentDetailRepository;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
@@ -69,6 +67,8 @@ import org.mifosplatform.portfolio.loanaccount.serialization.LoanUpdateCommandFr
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -91,8 +91,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final PaymentDetailRepository paymentDetailRepository;
private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -103,7 +103,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final PaymentDetailRepository paymentDetailRepository, final CalendarInstanceRepository calendarInstanceRepository) {
final CalendarInstanceRepository calendarInstanceRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler;
@@ -117,8 +118,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
this.paymentDetailRepository = paymentDetailRepository;
this.calendarInstanceRepository = calendarInstanceRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -135,11 +136,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanEventApiJsonValidator.validateDisbursement(command.json());
final Loan loan = retrieveLoanBy(loanId);
//validate actual disbursement date against meeting date
Collection<CalendarInstance> calendarInstances = this.calendarInstanceRepository.findByEntityIdAndEntityTypeId(loan.getId(), CalendarEntityType.LOANS.getValue());
// validate actual disbursement date against meeting date
Collection<CalendarInstance> calendarInstances = this.calendarInstanceRepository.findByEntityIdAndEntityTypeId(loan.getId(),
CalendarEntityType.LOANS.getValue());
validateDisbursementDate(command.localDateValueOfParameterNamed("actualDisbursementDate"), calendarInstances);
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
@@ -147,10 +149,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes);
if (paymentDetail != null) {
paymentDetailRepository.save(paymentDetail);
}
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
loan.disburse(currentUser, command, applicationCurrency, existingTransactionIds, existingReversedTransactionIds, changes,
paymentDetail);
@@ -192,13 +192,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (disbursementDate.isBefore(calendar.getStartDateLocalDate())) {
final String errorMessage = "Actual disbursement date '" + disbursementDate.toString()
+ "' cannot be before meeting start date '" + calendar.getStartDate().toString() + "' ";
throw new LoanApplicationDateException("actual.disbursement.date.cannot.be.before.meeting.start.date", errorMessage, disbursementDate.toString(),
calendar.getStartDate());
throw new LoanApplicationDateException("actual.disbursement.date.cannot.be.before.meeting.start.date", errorMessage,
disbursementDate.toString(), calendar.getStartDate());
}
// Disbursement date should fall on a meeting date
if (!CalendarHelper.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), disbursementDate)) {
final String errorMessage = "Expected disbursement date '" + disbursementDate.toString() + "' does not fall on a meeting date.";
final String errorMessage = "Expected disbursement date '" + disbursementDate.toString()
+ "' does not fall on a meeting date.";
throw new NotValidRecurringDateException("loan.actual.disbursement.date", errorMessage, disbursementDate.toString(),
calendar.getTitle());
}
@@ -231,10 +232,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes);
if (paymentDetail != null) {
paymentDetailRepository.save(paymentDetail);
}
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
loan.disburse(currentUser, command, applicationCurrency, existingTransactionIds, existingReversedTransactionIds, changes,
paymentDetail);
@@ -314,10 +312,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("note", noteText);
}
PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes);
if (paymentDetail != null) {
paymentDetailRepository.save(paymentDetail);
}
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount,
transactionDate, noteText);
@@ -418,7 +414,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes);
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createPaymentDetail(command, changes);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate);
if (transactionToAdjust.isInterestWaiver()) {
newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate);
@@ -429,7 +425,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
if (paymentDetail != null) {
paymentDetailRepository.save(paymentDetail);
paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
}
this.loanTransactionRepository.save(newTransactionDetail);
}
@@ -31,6 +31,8 @@ import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLA
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
@@ -47,6 +49,7 @@ import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -61,10 +64,10 @@ public class LoanProductsApiResource {
"maxNumberOfRepayments", "repaymentEvery", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod",
"maxInterestRatePerPeriod", "interestRateFrequencyType", "annualInterestRate", "amortizationType", "interestType",
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
"charges", "accountingRule", "accountingMappings", "fundOptions", "currencyOptions", "repaymentFrequencyTypeOptions",
"interestRateFrequencyTypeOptions", "amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
"accountingMappingOptions"));
"charges", "accountingRule", "accountingMappings", "paymentChannelToFundSourceMappings", "fundOptions", "paymentTypeOptions",
"currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "amortizationTypeOptions",
"interestTypeOptions", "interestCalculationPeriodTypeOptions", "transactionProcessingStrategyOptions", "chargeOptions",
"accountingOptions", "accountingRuleOptions", "accountingMappingOptions"));
private final String resourceNameForPermissions = "LOANPRODUCT";
@@ -79,6 +82,7 @@ public class LoanProductsApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final GLAccountReadPlatformService accountReadPlatformService;
private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
@Autowired
public LoanProductsApiResource(final PlatformSecurityContext context, final LoanProductReadPlatformService readPlatformService,
@@ -88,7 +92,8 @@ public class LoanProductsApiResource {
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final GLAccountReadPlatformService accountReadPlatformService,
final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService) {
final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context;
this.loanProductReadPlatformService = readPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
@@ -100,6 +105,7 @@ public class LoanProductsApiResource {
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.accountReadPlatformService = accountReadPlatformService;
this.accountMappingReadPlatformService = accountMappingReadPlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
}
@POST
@@ -136,7 +142,7 @@ public class LoanProductsApiResource {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermissions);
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveNewLoanProductDetails();
loanProduct = handleTemplate(loanProduct, new HashMap<String, Object>());
loanProduct = handleTemplate(loanProduct, new HashMap<String, Object>(), null);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, loanProduct, LOAN_PRODUCT_DATA_PARAMETERS);
@@ -155,14 +161,17 @@ public class LoanProductsApiResource {
LoanProductData loanProduct = this.loanProductReadPlatformService.retrieveLoanProduct(productId);
Map<String, Object> accountingMappings = null;
Map<Long, Long> paymentChannelToFundSourceMappings = null;
if (loanProduct.hasAccountingEnabled()) {
accountingMappings = accountMappingReadPlatformService.fetchAccountMappingDetailsForLoanProduct(productId, loanProduct
.accountingRuleType().getId().intValue());
loanProduct = LoanProductData.withAccountingMappings(loanProduct, accountingMappings);
paymentChannelToFundSourceMappings = accountMappingReadPlatformService
.fetchPaymentTypeToFundSourceMappingsForLoanProduct(productId);
loanProduct = LoanProductData.withAccountingDetails(loanProduct, accountingMappings, paymentChannelToFundSourceMappings);
}
if (settings.isTemplate()) {
loanProduct = handleTemplate(loanProduct, accountingMappings);
loanProduct = handleTemplate(loanProduct, accountingMappings, paymentChannelToFundSourceMappings);
}
return this.toApiJsonSerializer.serialize(settings, loanProduct, LOAN_PRODUCT_DATA_PARAMETERS);
}
@@ -181,7 +190,8 @@ public class LoanProductsApiResource {
return this.toApiJsonSerializer.serialize(result);
}
private LoanProductData handleTemplate(final LoanProductData productData, final Map<String, Object> accountingMappings) {
private LoanProductData handleTemplate(final LoanProductData productData, final Map<String, Object> accountingMappings,
final Map<Long, Long> paymentChannelToFundSourceMappings) {
final boolean feeChargesOnly = true;
Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveLoanApplicableCharges(feeChargesOnly);
@@ -197,6 +207,8 @@ public class LoanProductsApiResource {
final List<EnumOptionData> repaymentFrequencyTypeOptions = dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
final List<EnumOptionData> interestRateFrequencyTypeOptions = dropdownReadPlatformService
.retrieveInterestRateFrequencyTypeOptions();
final Collection<CodeValueData> paymentTypeOptions = codeValueReadPlatformService
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
if (fundOptions.isEmpty()) {
@@ -226,8 +238,9 @@ public class LoanProductsApiResource {
List<EnumOptionData> accountingRuleTypeOptions = dropdownReadPlatformService.retrieveAccountingRuleTypeOptions();
return new LoanProductData(productData, chargeOptions, currencyOptions, amortizationTypeOptions, interestTypeOptions,
interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions, fundOptions,
transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, accountingMappings);
return new LoanProductData(productData, chargeOptions, paymentTypeOptions, currencyOptions, amortizationTypeOptions,
interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions, interestRateFrequencyTypeOptions,
fundOptions, transactionProcessingStrategyOptions, accountOptions, accountingRuleTypeOptions, accountingMappings,
paymentChannelToFundSourceMappings);
}
}
@@ -12,6 +12,7 @@ import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.charge.data.ChargeData;
@@ -65,9 +66,12 @@ public class LoanProductData {
private final EnumOptionData accountingRule;
@SuppressWarnings("unused")
private final Map<String, Object> accountingMappings;
@SuppressWarnings("unused")
private final Map<Long, Long> paymentChannelToFundSourceMappings;
// template related
private final Collection<FundData> fundOptions;
private final Collection<CodeValueData> paymentTypeOptions;
private final Collection<CurrencyData> currencyOptions;
private final List<EnumOptionData> repaymentFrequencyTypeOptions;
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
@@ -186,13 +190,14 @@ public class LoanProductData {
transactionProcessingStrategyName, charges, accountingType);
}
public static LoanProductData withAccountingMappings(final LoanProductData productData, final Map<String, Object> accountingMappings) {
public static LoanProductData withAccountingDetails(final LoanProductData productData, final Map<String, Object> accountingMappings,
final Map<Long, Long> paymentChannelToFundSourceMappings) {
return new LoanProductData(productData, productData.chargeOptions, productData.currencyOptions,
return new LoanProductData(productData, productData.chargeOptions, productData.paymentTypeOptions, productData.currencyOptions,
productData.amortizationTypeOptions, productData.interestTypeOptions, productData.interestCalculationPeriodTypeOptions,
productData.repaymentFrequencyTypeOptions, productData.interestRateFrequencyTypeOptions, productData.fundOptions,
productData.transactionProcessingStrategyOptions, productData.accountingMappingOptions, productData.accountingRuleOptions,
accountingMappings);
accountingMappings, paymentChannelToFundSourceMappings);
}
public LoanProductData(final Long id, final String name, final String description, final CurrencyData currency,
@@ -242,6 +247,7 @@ public class LoanProductData {
this.accountingRule = accountingType;
this.chargeOptions = null;
this.paymentTypeOptions = null;
this.currencyOptions = null;
this.fundOptions = null;
this.transactionProcessingStrategyOptions = null;
@@ -254,15 +260,17 @@ public class LoanProductData {
this.accountingMappingOptions = null;
this.accountingRuleOptions = null;
this.accountingMappings = null;
this.paymentChannelToFundSourceMappings = null;
}
public LoanProductData(final LoanProductData productData, final Collection<ChargeData> chargeOptions,
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> amortizationTypeOptions,
final List<EnumOptionData> interestTypeOptions, final List<EnumOptionData> interestCalculationPeriodTypeOptions,
final List<EnumOptionData> repaymentFrequencyTypeOptions, final List<EnumOptionData> interestRateFrequencyTypeOptions,
final Collection<FundData> fundOptions, final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Collection<CodeValueData> paymentTypeOptions, final Collection<CurrencyData> currencyOptions,
final List<EnumOptionData> amortizationTypeOptions, final List<EnumOptionData> interestTypeOptions,
final List<EnumOptionData> interestCalculationPeriodTypeOptions, final List<EnumOptionData> repaymentFrequencyTypeOptions,
final List<EnumOptionData> interestRateFrequencyTypeOptions, final Collection<FundData> fundOptions,
final Collection<TransactionProcessingStrategyData> transactionStrategyOptions,
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
final Map<String, Object> accountingMappings) {
final Map<String, Object> accountingMappings, final Map<Long, Long> paymentChannelToFundSourceMappings) {
this.id = productData.id;
this.name = productData.name;
this.description = productData.description;
@@ -291,6 +299,7 @@ public class LoanProductData {
this.accountingRule = productData.accountingRule;
this.chargeOptions = chargeOptions;
this.paymentTypeOptions = paymentTypeOptions;
this.currencyOptions = currencyOptions;
this.currency = productData.currency;
this.fundOptions = fundOptions;
@@ -320,6 +329,8 @@ public class LoanProductData {
} else {
this.accountingMappings = accountingMappings;
}
this.paymentChannelToFundSourceMappings = paymentChannelToFundSourceMappings;
}
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
@@ -27,7 +27,9 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
@Component
@@ -45,7 +47,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
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.LOSSES_WRITTEN_OFF.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue()));
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue()));
private final FromJsonHelper fromApiJsonHelper;
@@ -250,6 +253,8 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId)
.notNull().integerGreaterThanZero();
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
}
if (isAccrualBasedAccounting(accountingRuleType)) {
@@ -461,11 +466,48 @@ public final class LoanProductCommandFromApiJsonDeserializer {
baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue())
.value(receivablePenaltyAccountId).ignoreIfNull().integerGreaterThanZero();
validatePaymentChannelFundSourceMappings(baseDataValidator, element);
validateMinMaxConstraints(element, baseDataValidator, loanProduct);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
/**
* Validation for advanced accounting options
*
* @param baseDataValidator
* @param element
*/
private void validatePaymentChannelFundSourceMappings(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
if (fromApiJsonHelper.parameterExists(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element)) {
JsonArray paymentChannelMappingArray = fromApiJsonHelper.extractJsonArrayNamed(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(), element);
if (paymentChannelMappingArray != null) {
int i = 0;
do {
final JsonObject jsonObject = paymentChannelMappingArray.get(i).getAsJsonObject();
final Long paymentTypeId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.getValue()).getAsLong();
final Long paymentSpecificFundAccountId = jsonObject.get(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue())
.getAsLong();
baseDataValidator
.reset()
.parameter(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]."
+ LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_TYPE.toString()).value(paymentTypeId).notNull()
.integerGreaterThanZero();
baseDataValidator
.reset()
.parameter(
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue() + "[" + i + "]."
+ LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue()).value(paymentSpecificFundAccountId)
.notNull().integerGreaterThanZero();
i++;
} while (i < paymentChannelMappingArray.size());
}
}
}
public void validateMinMaxConstraints(final JsonElement element, final DataValidatorBuilder baseDataValidator,
final LoanProduct loanProduct) {
@@ -29,5 +29,4 @@ public interface LoanDropdownReadPlatformService {
List<EnumOptionData> retrieveAccountingRuleTypeOptions();
List<EnumOptionData> retrievePaymentTypeOptions();
}
@@ -5,13 +5,13 @@
*/
package org.mifosplatform.portfolio.loanproduct.service;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.accountingRuleType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.amortizationType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestCalculationPeriodType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestRateFrequencyType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.loanTermFrequencyType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.accountingRuleType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -20,7 +20,6 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentType;
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
@@ -111,8 +110,4 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
return accountingRuleTypeOptions;
}
@Override
public List<EnumOptionData> retrievePaymentTypeOptions() {
return LoanEnumerations.paymentTypes(PaymentType.values());
}
}
@@ -5,16 +5,12 @@
*/
package org.mifosplatform.portfolio.loanproduct.service;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.loanaccount.data.LoanStatusEnumData;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
import org.mifosplatform.portfolio.loanaccount.domain.LoanType;
import org.mifosplatform.portfolio.loanaccount.domain.PaymentType;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
@@ -49,8 +45,6 @@ public class LoanEnumerations {
return interestType(id);
} else if (typeName.equals(INTEREST_CALCULATION_PERIOD_TYPE)) {
return interestCalculationPeriodType(id);
} else if (typeName.equals(PAYMENT_TYPE)) {
return paymentType(id);
} else if (typeName.equals(ACCOUNTING_RULE_TYPE)) {
return accountingRuleType(id);
} else if (typeName.equals(LOAN_TYPE)) { return loanType(id); }
@@ -236,27 +230,6 @@ public class LoanEnumerations {
return optionData;
}
public static List<EnumOptionData> paymentTypes(final PaymentType[] paymentTypes) {
final List<EnumOptionData> optionDatas = new ArrayList<EnumOptionData>();
for (final PaymentType paymentType : paymentTypes) {
optionDatas.add(paymentType(paymentType));
}
return optionDatas;
}
public static EnumOptionData paymentType(final PaymentType paymentType) {
final EnumOptionData optionData = new EnumOptionData(paymentType.getValue().longValue(), paymentType.getCode(),
paymentType.toString());
return optionData;
}
public static EnumOptionData paymentType(final Integer id) {
PaymentType paymentType = PaymentType.fromInt(id);
EnumOptionData enumOptionData = new EnumOptionData(paymentType.getValue().longValue(), paymentType.getCode(), paymentType
.getValue().toString());
return enumOptionData;
}
public static LoanTransactionEnumData transactionType(final Integer id) {
return transactionType(LoanTransactionType.fromInt(id));
}
@@ -0,0 +1,31 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.paymentdetail;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class PaymentDetailConstants {
// Code representing Payment Details
public static final String paymentTypeCodeName = "PaymentType";
// request parameters
public static final String paymentTypeParamName = "paymentTypeId";
public static final String accountNumberParamName = "accountNumber";
public static final String checkNumberParamName = "checkNumber";
public static final String routingCodeParamName = "routingCode";
public static final String receiptNumberParamName = "receiptNumber";
public static final String bankNumberParamName = "bankNumber";
// template related part of response
public static final String officeOptionsParamName = "paymentTypeOptions";
public static final Set<String> PAYMENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(accountNumberParamName,
checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName));
}
@@ -3,9 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.data;
package org.mifosplatform.portfolio.paymentdetail.data;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
/**
* Immutable data object representing a payment.
@@ -15,7 +15,7 @@ public class PaymentDetailData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final EnumOptionData paymentType;
private final CodeValueData paymentType;
@SuppressWarnings("unused")
private final String accountNumber;
@SuppressWarnings("unused")
@@ -27,7 +27,7 @@ public class PaymentDetailData {
@SuppressWarnings("unused")
private final String bankNumber;
public PaymentDetailData(Long id, EnumOptionData paymentType, String accountNumber, String checkNumber, String routingCode,
public PaymentDetailData(Long id, CodeValueData paymentType, String accountNumber, String checkNumber, String routingCode,
String receiptNumber, String bankNumber) {
this.id = id;
this.paymentType = paymentType;
@@ -0,0 +1,99 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.paymentdetail.domain;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "m_payment_detail")
public final class PaymentDetail extends AbstractPersistable<Long> {
@ManyToOne
@JoinColumn(name = "payment_type_cv_id", nullable = false)
private CodeValue paymentType;
@Column(name = "account_number", length = 50)
private String accountNumber;
@Column(name = "check_number", length = 50)
private String checkNumber;
@Column(name = "routing_code", length = 50)
private String routingCode;
@Column(name = "receipt_number", length = 50)
private String receiptNumber;
@Column(name = "bank_number", length = 50)
private String bankNumber;
protected PaymentDetail() {
}
public static PaymentDetail generatePaymentDetail(final CodeValue paymentType, final JsonCommand command,
final Map<String, Object> changes) {
final String accountNumber = command.stringValueOfParameterNamed(PaymentDetailConstants.accountNumberParamName);
final String checkNumber = command.stringValueOfParameterNamed(PaymentDetailConstants.checkNumberParamName);
final String routingCode = command.stringValueOfParameterNamed(PaymentDetailConstants.routingCodeParamName);
final String receiptNumber = command.stringValueOfParameterNamed(PaymentDetailConstants.receiptNumberParamName);
final String bankNumber = command.stringValueOfParameterNamed(PaymentDetailConstants.bankNumberParamName);
if (StringUtils.isNotBlank(accountNumber)) {
changes.put(PaymentDetailConstants.accountNumberParamName, accountNumber);
}
if (StringUtils.isNotBlank(checkNumber)) {
changes.put(PaymentDetailConstants.checkNumberParamName, checkNumber);
}
if (StringUtils.isNotBlank(routingCode)) {
changes.put(PaymentDetailConstants.routingCodeParamName, routingCode);
}
if (StringUtils.isNotBlank(receiptNumber)) {
changes.put(PaymentDetailConstants.receiptNumberParamName, receiptNumber);
}
if (StringUtils.isNotBlank(bankNumber)) {
changes.put(PaymentDetailConstants.bankNumberParamName, bankNumber);
}
PaymentDetail paymentDetail = new PaymentDetail(paymentType, accountNumber, checkNumber, routingCode, receiptNumber, bankNumber);
return paymentDetail;
}
private PaymentDetail(final CodeValue paymentType, final String accountNumber, final String checkNumber, final String routingCode,
final String receiptNumber, final String bankNumber) {
this.paymentType = paymentType;
this.accountNumber = accountNumber;
this.checkNumber = checkNumber;
this.routingCode = routingCode;
this.receiptNumber = receiptNumber;
this.bankNumber = bankNumber;
}
public PaymentDetailData toData() {
CodeValueData paymentTypeData = paymentType.toData();
final PaymentDetailData paymentDetailData = new PaymentDetailData(getId(), paymentTypeData, this.accountNumber, this.checkNumber,
this.routingCode, this.receiptNumber, this.bankNumber);
return paymentDetailData;
}
public CodeValue getPaymentType() {
return this.paymentType;
}
}
@@ -3,8 +3,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.loanaccount.domain;
package org.mifosplatform.portfolio.paymentdetail.domain;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -0,0 +1,20 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.paymentdetail.service;
import java.util.Map;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
public interface PaymentDetailWritePlatformService {
PaymentDetail createAndPersistPaymentDetail(final JsonCommand command, Map<String, Object> changes);
PaymentDetail createPaymentDetail(final JsonCommand command, Map<String, Object> changes);
PaymentDetail persistPaymentDetail(PaymentDetail paymentDetail);
}
@@ -0,0 +1,59 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.paymentdetail.service;
import java.util.Map;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.portfolio.paymentdetail.PaymentDetailConstants;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetailRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class PaymentDetailWritePlatformServiceJpaRepositoryImpl implements PaymentDetailWritePlatformService {
private final PaymentDetailRepository paymentDetailRepository;
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
@Autowired
public PaymentDetailWritePlatformServiceJpaRepositoryImpl(final PaymentDetailRepository paymentDetailRepository,
final CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
this.paymentDetailRepository = paymentDetailRepository;
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
}
@Override
@Transactional
public PaymentDetail createPaymentDetail(JsonCommand command, Map<String, Object> changes) {
final Long paymentTypeId = command.longValueOfParameterNamed(PaymentDetailConstants.paymentTypeParamName);
if (paymentTypeId == null) { return null; }
CodeValue paymentType = codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
PaymentDetailConstants.paymentTypeCodeName, paymentTypeId);
PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(paymentType, command, changes);
return paymentDetail;
}
@Override
@Transactional
public PaymentDetail persistPaymentDetail(PaymentDetail paymentDetail) {
return paymentDetailRepository.save(paymentDetail);
}
@Override
@Transactional
public PaymentDetail createAndPersistPaymentDetail(JsonCommand command, Map<String, Object> changes) {
PaymentDetail paymentDetail = createPaymentDetail(command, changes);
if (paymentDetail != null) { return persistPaymentDetail(paymentDetail); }
return paymentDetail;
}
}