From e15a72eab1193221a68c3aef161ea5444a5e46e9 Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Tue, 9 Apr 2013 17:00:25 +0530 Subject: [PATCH] wip: repayment/disbursal apis support optional payment details --- .../core_db/V11__add-payment-details.sql | 27 ++++ .../loanaccount/data/LoanTransactionData.java | 9 +- .../data/LoanTransactionEnumData.java | 11 ++ .../loanaccount/data/PaymentDetailData.java | 41 ++++++ .../portfolio/loanaccount/domain/Loan.java | 26 ++-- .../loanaccount/domain/LoanTransaction.java | 32 ++++- .../loanaccount/domain/PaymentDetail.java | 128 ++++++++++++++++++ .../domain/PaymentDetailRepository.java | 13 ++ .../loanaccount/domain/PaymentType.java | 81 +++++++++++ .../LoanEventApiJsonValidator.java | 30 +++- .../service/LoanReadPlatformServiceImpl.java | 72 ++++++---- ...WritePlatformServiceJpaRepositoryImpl.java | 64 ++++++--- .../loanproduct/service/LoanEnumerations.java | 8 ++ .../loanaccount/LoanTransactionBuilder.java | 56 ++++---- 14 files changed, 503 insertions(+), 95 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V11__add-payment-details.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/PaymentDetailData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetail.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetailRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentType.java diff --git a/mifosng-db/migrations/core_db/V11__add-payment-details.sql b/mifosng-db/migrations/core_db/V11__add-payment-details.sql new file mode 100755 index 000000000..02fc1f5e3 --- /dev/null +++ b/mifosng-db/migrations/core_db/V11__add-payment-details.sql @@ -0,0 +1,27 @@ +/* +New table for storing transaction details (for both loan and savings) +*/ + +CREATE TABLE `m_payment_detail` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `payment_type_enum` SMALLINT(5) NOT NULL, + `account_number` VARCHAR(100) NULL DEFAULT NULL, + `check_number` VARCHAR(100) NULL DEFAULT NULL, + `receipt_number` VARCHAR(100) NULL DEFAULT NULL, + `bank_number` VARCHAR(100) NULL DEFAULT NULL, + `routing_code` VARCHAR(100) NULL DEFAULT NULL, + PRIMARY KEY (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +/* +Update loan transaction to add a link to transaction detail table +*/ + +ALTER TABLE `m_loan_transaction` + ADD COLUMN `payment_detail_id` BIGINT(20) NULL AFTER `loan_id`, + ADD CONSTRAINT `FK_m_loan_transaction_m_payment_detail` FOREIGN KEY (`payment_detail_id`) REFERENCES `m_payment_detail` (`id`); + + + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java index e4291d7a1..1276fabbd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionData.java @@ -24,6 +24,8 @@ public class LoanTransactionData { @SuppressWarnings("unused") private final CurrencyData currency; + @SuppressWarnings("unused") + private final PaymentDetailData paymentDetailData; @SuppressWarnings("unused") private final BigDecimal amount; @@ -36,11 +38,12 @@ public class LoanTransactionData { @SuppressWarnings("unused") private final BigDecimal penaltyChargesPortion; - public LoanTransactionData(final Long id, final LoanTransactionEnumData transactionType, final CurrencyData currency, - final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, - final BigDecimal feeChargesPortion, final BigDecimal penaltyChargesPortion) { + 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) { this.id = id; this.type = transactionType; + this.paymentDetailData = paymentDetailData; this.currency = currency; this.date = date; this.amount = amount; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java index f11e63f18..b1cf8e3e2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/LoanTransactionEnumData.java @@ -46,6 +46,17 @@ public class LoanTransactionEnumData { return this.id; } + /** + * Returns true if the transaction involves either a payment (disbursement) + * or a receipt (repayments, repayments during disbursement etc) + * + * @return + */ + public boolean isPaymentOrReceipt() { + if (isDisbursement() || isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment()) { return true; } + return false; + } + public boolean isDisbursement() { return this.disbursement; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/PaymentDetailData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/PaymentDetailData.java new file mode 100755 index 000000000..55ba9c1b4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/data/PaymentDetailData.java @@ -0,0 +1,41 @@ +/** + * 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.data; + +import org.mifosplatform.infrastructure.core.data.EnumOptionData; + +/** + * Immutable data object representing a payment. + */ +public class PaymentDetailData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final EnumOptionData paymentType; + @SuppressWarnings("unused") + private final String accountNumber; + @SuppressWarnings("unused") + private final String checkNumber; + @SuppressWarnings("unused") + private final String routingCode; + @SuppressWarnings("unused") + private final String receiptNumber; + @SuppressWarnings("unused") + private final String bankNumber; + + public PaymentDetailData(Long id, EnumOptionData paymentType, String accountNumber, String checkNumber, String routingCode, + String receiptNumber, String bankNumber) { + this.id = id; + this.paymentType = paymentType; + this.accountNumber = accountNumber; + this.checkNumber = checkNumber; + this.routingCode = routingCode; + this.receiptNumber = receiptNumber; + this.bankNumber = bankNumber; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index bc77fb267..a42c0777b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -810,7 +810,8 @@ public class Loan extends AbstractPersistable { } final String clientIdParamName = "clientId"; - //FIXME AA : Client is null for group loans. Group loan modifications require group validation. + // FIXME AA : Client is null for group loans. Group loan modifications + // require group validation. if (this.client != null) { if (command.isChangeInLongParameterNamed(clientIdParamName, this.client.getId())) { final Long newValue = command.longValueOfParameterNamed(clientIdParamName); @@ -1227,11 +1228,12 @@ public class Loan extends AbstractPersistable { this.actualDisbursementDate = expectedDisbursedOnLocalDate.toDate(); updateLoanScheduleDependentDerivedFields(); /**** - * Vishwas TODO: Refactor to skip unnecessary creation of Disbursement + * Vishwas TODO: Re-factor to skip unnecessary creation of Disbursement * and fees applied transactions (though they are not persisted) on * modifications to loan application modification *****/ - handleDisbursementTransaction(expectedDisbursedOnLocalDate); + PaymentDetail paymentDetail = null; + handleDisbursementTransaction(paymentDetail, expectedDisbursedOnLocalDate); } private Collection findExistingTransactionIds() { @@ -1259,9 +1261,8 @@ public class Loan extends AbstractPersistable { } public Map disburse(final AppUser currentUser, final JsonCommand command, final ApplicationCurrency currency, - final List existingTransactionIds, final List existingReversedTransactionIds) { - - final Map actualChanges = new LinkedHashMap(); + final List existingTransactionIds, final List existingReversedTransactionIds, + final Map actualChanges, final PaymentDetail paymentDetail) { updateLoanToPreDisbursalState(); @@ -1284,7 +1285,7 @@ public class Loan extends AbstractPersistable { actualChanges.put("dateFormat", command.dateFormat()); actualChanges.put("actualDisbursementDate", command.stringValueOfParameterNamed("actualDisbursementDate")); - handleDisbursementTransaction(actualDisbursementDate); + handleDisbursementTransaction(paymentDetail, actualDisbursementDate); if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate)) { regenerateRepaymentSchedule(currency); @@ -1349,19 +1350,24 @@ public class Loan extends AbstractPersistable { updateLoanSchedule(generatedData); } - private LoanTransaction handleDisbursementTransaction(final LocalDate disbursedOn) { + private LoanTransaction handleDisbursementTransaction(final PaymentDetail paymentDetail, final LocalDate disbursedOn) { // track disbursement transaction final LoanTransaction disbursementTransaction = LoanTransaction.disbursement(this.loanRepaymentScheduleDetail.getPrincipal(), - disbursedOn); + paymentDetail, disbursedOn); disbursementTransaction.updateLoan(this); this.loanTransactions.add(disbursementTransaction); // add repayment transaction to track incoming money from client to mfi // for (charges due at time of disbursement) + + /*** + * TODO Vishwas: do we need to be able to pass in payment type details + * for repayments at disbursements too? + ***/ final Money totalFeeChargesDueAtDisbursement = this.summary.getTotalFeeChargesDueAtDisbursement(loanCurrency()); if (totalFeeChargesDueAtDisbursement.isGreaterThanZero()) { - LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, disbursedOn); + LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(totalFeeChargesDueAtDisbursement, null, disbursedOn); Money zero = Money.zero(getCurrency()); chargesPayment.updateComponents(zero, zero, totalFeeChargesDueAtDisbursement, zero); chargesPayment.updateLoan(this); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java index c828e8c7e..4bc814745 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanTransaction.java @@ -24,6 +24,7 @@ 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.springframework.data.jpa.domain.AbstractPersistable; @@ -39,6 +40,10 @@ public final class LoanTransaction extends AbstractPersistable { @JoinColumn(name = "loan_id", nullable = false) private Loan loan; + @ManyToOne(optional = true) + @JoinColumn(name = "payment_detail_id", nullable = true) + private PaymentDetail paymentDetail; + @Column(name = "transaction_type_enum", nullable = false) private final Integer typeOf; @@ -70,16 +75,16 @@ public final class LoanTransaction extends AbstractPersistable { this.typeOf = null; } - public static LoanTransaction disbursement(final Money amount, final LocalDate disbursementDate) { - return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, amount.getAmount(), disbursementDate); + public static LoanTransaction disbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate disbursementDate) { + return new LoanTransaction(null, LoanTransactionType.DISBURSEMENT, paymentDetail, amount.getAmount(), disbursementDate); } - public static LoanTransaction repayment(final Money amount, final LocalDate paymentDate) { - return new LoanTransaction(null, LoanTransactionType.REPAYMENT, amount.getAmount(), paymentDate); + public static LoanTransaction repayment(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) { + return new LoanTransaction(null, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate); } - public static LoanTransaction repaymentAtDisbursement(final Money amount, final LocalDate paymentDate) { - return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, amount.getAmount(), paymentDate); + public static LoanTransaction repaymentAtDisbursement(final Money amount, final PaymentDetail paymentDetail, final LocalDate paymentDate) { + return new LoanTransaction(null, LoanTransactionType.REPAYMENT_AT_DISBURSEMENT, paymentDetail, amount.getAmount(), paymentDate); } public static LoanTransaction waiver(final Loan loan, final Money waived, final LocalDate waiveDate) { @@ -146,6 +151,15 @@ public final class LoanTransaction extends AbstractPersistable { this.dateOf = date.toDateMidnight().toDate(); } + private LoanTransaction(final Loan loan, final LoanTransactionType type, final PaymentDetail paymentDetail, final BigDecimal amount, + final LocalDate date) { + this.loan = loan; + this.typeOf = type.getValue(); + this.paymentDetail = paymentDetail; + this.amount = amount; + this.dateOf = date.toDateMidnight().toDate(); + } + public void reverse() { this.reversed = true; } @@ -315,7 +329,11 @@ public final class LoanTransaction extends AbstractPersistable { public LoanTransactionData toData(final CurrencyData currencyData) { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf); - return new LoanTransactionData(this.getId(), transactionType, currencyData, getTransactionDate(), this.amount, + PaymentDetailData paymentDetailData = null; + if (this.paymentDetail != null) { + paymentDetailData = paymentDetail.toData(); + } + return new LoanTransactionData(this.getId(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion, this.penaltyChargesPortion); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetail.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetail.java new file mode 100755 index 000000000..9acdedca8 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetail.java @@ -0,0 +1,128 @@ +/** + * 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 { + + @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 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; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetailRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetailRepository.java new file mode 100755 index 000000000..90353b22d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentDetailRepository.java @@ -0,0 +1,13 @@ +/** + * 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 org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface PaymentDetailRepository extends JpaRepository, JpaSpecificationExecutor { + // no added behaviour +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentType.java new file mode 100755 index 000000000..3042a59be --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/PaymentType.java @@ -0,0 +1,81 @@ +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 intToEnumMap = new HashMap(); + 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); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index 652290cc6..b6ba762e6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -21,6 +21,7 @@ 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; @@ -47,7 +48,7 @@ public final class LoanEventApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Set disbursementParameters = new HashSet(Arrays.asList("actualDisbursementDate", "note", "locale", - "dateFormat")); + "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber")); final Type typeOfMap = new TypeToken>() {}.getType(); fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); @@ -62,6 +63,8 @@ public final class LoanEventApiJsonValidator { final String note = fromApiJsonHelper.extractStringNamed("note", element); baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); + validatePaymentDetails(baseDataValidator, element); + throwExceptionIfValidationWarningsExist(dataValidationErrors); } @@ -69,11 +72,11 @@ public final class LoanEventApiJsonValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - final Set disbursementParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "note", - "locale", "dateFormat")); + final Set transactionParameters = new HashSet(Arrays.asList("transactionDate", "transactionAmount", "note", + "locale", "dateFormat", "paymentTypeId", "accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber")); final Type typeOfMap = new TypeToken>() {}.getType(); - fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, disbursementParameters); + fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters); final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); @@ -88,9 +91,28 @@ public final class LoanEventApiJsonValidator { final String note = fromApiJsonHelper.extractStringNamed("note", element); baseDataValidator.reset().parameter("note").value(note).notExceedingLengthOf(1000); + validatePaymentDetails(baseDataValidator, element); throwExceptionIfValidationWarningsExist(dataValidationErrors); } + /** + * @param baseDataValidator + * @param element + */ + 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()); + final Set paymentDetailParameters = new HashSet(Arrays.asList("accountNumber", "checkNumber", "routingCode", + "receiptNumber", "bankNumber")); + for (String paymentDetailParameterName : paymentDetailParameters) { + final String paymentDetailParameterValue = fromApiJsonHelper.extractStringNamed(paymentDetailParameterName, element); + baseDataValidator.reset().parameter(paymentDetailParameterName).value(paymentDetailParameterValue).ignoreIfNull() + .notExceedingLengthOf(50); + } + } + public void validateTransactionWithNoAmount(final String json) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 4606f64e3..34f7087f6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -35,6 +35,7 @@ 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; @@ -126,6 +127,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { // retrieve all loan transactions that are not invalid and have not // been 'contra'ed by another transaction // repayments at time of disbursement (e.g. charges) + + /*** TODO Vishwas: Remove references to "Contra" from the codebase ***/ String sql = "select " + rm.LoanPaymentsSchema() + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and tr.is_reversed=0 order by tr.transaction_date ASC"; @@ -158,7 +161,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { context.authenticatedUser(); - final GroupData groupAccount = this.groupReadPlatformService.retrieveGroupDetails(groupId , false); + final GroupData groupAccount = this.groupReadPlatformService.retrieveGroupDetails(groupId, false); final LocalDate expectedDisbursementDate = DateUtils.getLocalDateOfTenant(); LoanAccountData loanDetails = LoanAccountData.groupDefaults(groupAccount, expectedDisbursementDate); @@ -169,10 +172,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { return loanDetails; } - + @Override - public LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId){ - + public LoanAccountData retrieveTemplateWithCompleteGroupAndProductDetails(Long groupId, Long productId) { + context.authenticatedUser(); final GroupData groupAccount = this.groupReadPlatformService.retrieveGroup(groupId); @@ -186,8 +189,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { return loanDetails; } - - + @Override public LoanTransactionData retrieveLoanTransactionTemplate(final Long loanId) { @@ -209,7 +211,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Money possibleNextRepaymentAmount = loan.possibleNextRepaymentAmount(); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT); - return new LoanTransactionData(null, transactionType, currencyData, earliestUnpaidInstallmentDate, + return new LoanTransactionData(null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, possibleNextRepaymentAmount.getAmount(), null, null, null, null); } @@ -237,8 +239,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final MonetaryCurrency monetaryCurrency = new MonetaryCurrency(currency.getCode(), currency.getDecimalPlaces()); final BigDecimal amount = waiveOfInterest.getAmount(monetaryCurrency).getAmount(); - return new LoanTransactionData(null, transactionType, currencyData, waiveOfInterest.getTransactionDate(), amount, null, null, null, - null); + return new LoanTransactionData(null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, null, null, + null, null); } @Override @@ -247,7 +249,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { context.authenticatedUser(); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); - return new LoanTransactionData(null, transactionType, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null); + return new LoanTransactionData(null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, null, null); } @Override @@ -516,19 +518,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { } GroupData groupData = null; - - if(groupId != null){ - + + if (groupId != null) { + groupData = new GroupData(groupId, groupName, groupExternalId, groupOfficeId, groupStaffId, groupParentId, groupHierarchy); } - + return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData, - loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, - loanPurposeName, loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, - termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, - amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, - interestCalculationPeriodType, expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, loanSummary, - feeChargesDueAtDisbursementCharged); + loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, + loanOfficerId, loanOfficerName, currencyData, principal, inArrearsTolerance, termFrequency, termPeriodFrequencyType, + numberOfRepayments, repaymentEvery, repaymentFrequencyType, transactionStrategyId, amortizationType, + interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, + expectedFirstRepaymentOnDate, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged); } } @@ -689,12 +690,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { public String LoanPaymentsSchema() { return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as `date`, tr.amount as total, " - + "tr.principal_portion_derived as principal, " + "tr.interest_portion_derived as interest, " - + "tr.fee_charges_portion_derived as fees, " + "tr.penalty_charges_portion_derived as penalties, " + + " tr.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.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" - + " join m_currency rc on rc.`code` = l.currency_code "; + + " 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"; } @Override @@ -711,6 +715,22 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Long id = rs.getLong("id"); final int transactionTypeInt = JdbcSupport.getInteger(rs, "transactionType"); final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(transactionTypeInt); + + PaymentDetailData paymentDetailData = null; + + if (transactionType.isPaymentOrReceipt()) { + final Integer paymentTypeId = JdbcSupport.getInteger(rs, "paymentType"); + if (paymentTypeId != null) { + EnumOptionData paymentType = LoanEnumerations.paymentType(paymentTypeId); + final String accountNumber = rs.getString("accountNumber"); + final String checkNumber = rs.getString("checkNumber"); + final String routingCode = rs.getString("routingCode"); + final String receiptNumber = rs.getString("receiptNumber"); + final String bankNumber = rs.getString("bankNumber"); + paymentDetailData = new PaymentDetailData(id, paymentType, accountNumber, checkNumber, routingCode, receiptNumber, + bankNumber); + } + } final LocalDate date = JdbcSupport.getLocalDate(rs, "date"); final BigDecimal totalAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "total"); final BigDecimal principalPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "principal"); @@ -718,8 +738,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); - return new LoanTransactionData(id, transactionType, currencyData, date, totalAmount, principalPortion, interestPortion, - feeChargesPortion, penaltyChargesPortion); + return new LoanTransactionData(id, transactionType, paymentDetailData, currencyData, date, totalAmount, principalPortion, + interestPortion, feeChargesPortion, penaltyChargesPortion); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index a6f1f9601..ded89037c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -49,6 +49,8 @@ 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.LoanNotFoundException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException; import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException; @@ -79,6 +81,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final LoanSummaryWrapper loanSummaryWrapper; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; + private final PaymentDetailRepository paymentDetailRepository; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -88,7 +91,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final NoteRepository noteRepository, final ChargeRepositoryWrapper chargeRepository, final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepository applicationCurrencyRepository, final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper, - final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) { + final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, + final PaymentDetailRepository paymentDetailRepository) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -102,6 +106,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer; this.loanSummaryWrapper = loanSummaryWrapper; this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory; + this.paymentDetailRepository = paymentDetailRepository; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -123,8 +128,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); - final Map changes = loan.disburse(currentUser, command, currency, existingTransactionIds, - existingReversedTransactionIds); + + final Map changes = new LinkedHashMap(); + PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes); + if (paymentDetail != null) { + paymentDetailRepository.save(paymentDetail); + } + + loan.disburse(currentUser, command, currency, existingTransactionIds, existingReversedTransactionIds, changes, paymentDetail); + if (!changes.isEmpty()) { this.loanRepository.save(loan); @@ -153,6 +165,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public Map bulkLoanDisbursal(JsonCommand command, CollectionSheetBulkDisbursalCommand bulkDisbursalCommand) { final AppUser currentUser = context.authenticatedUser(); + /**** + * TODO Vishwas: Pair with Ashok and re-factor collection sheet + * code-base + *****/ + SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions(); Map changes = new LinkedHashMap(); if (disbursalCommand == null) { return changes; } @@ -167,7 +184,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); - changes.putAll(loan.disburse(currentUser, command, currency, existingTransactionIds, existingReversedTransactionIds)); + PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes); + if (paymentDetail != null) { + paymentDetailRepository.save(paymentDetail); + } + + loan.disburse(currentUser, command, currency, existingTransactionIds, existingReversedTransactionIds, changes, paymentDetail); if (!changes.isEmpty()) { this.loanRepository.save(loan); @@ -190,8 +212,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf context.authenticatedUser(); final Loan loan = retrieveLoanBy(loanId); - final BigDecimal totalInterestCharged = loan.getSummary().getTotalInterestCharged(); - final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); final Map changes = loan.undoDisbursal(existingTransactionIds, existingReversedTransactionIds); @@ -207,11 +227,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final ApplicationCurrency currency = this.applicationCurrencyRepository.findOneByCode(loan.getCurrencyCode()); final Map accountingBridgeData = loan.deriveAccountingBridgeData(currency.toData(), existingTransactionIds, existingReversedTransactionIds); - // add the previously calculated net charged interest back to - // accountingBridgedate, this would not work if interest - // recalculation in introduced - accountingBridgeData.put("calculatedInterest", totalInterestCharged); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } @@ -248,8 +263,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf changes.put("note", noteText); } - CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, transactionAmount, transactionDate, - noteText); + PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes); + if (paymentDetail != null) { + paymentDetailRepository.save(paymentDetail); + } + CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount, + transactionDate, noteText); return commandProcessingResultBuilder.withCommandId(command.commandId()) // .withLoanId(loanId) // @@ -257,15 +276,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .build(); } - private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final BigDecimal transactionAmount, - final LocalDate transactionDate, final String noteText) { + private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final PaymentDetail paymentDetail, + final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText) { final Loan loan = retrieveLoanBy(loanId); final List existingTransactionIds = new ArrayList(); final List existingReversedTransactionIds = new ArrayList(); final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); - final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, transactionDate); + final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, paymentDetail, transactionDate); final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds); @@ -310,7 +329,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (repaymentCommand == null) return changes; for (SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) { - saveLoanRepayment(singleLoanRepaymentCommand.getLoanId(), singleLoanRepaymentCommand.getTransactionAmount(), + /**** + * TODO Vishwas, have a re-look at this implementation, defaulting + * it to null for now + ***/ + PaymentDetail paymentDetail = null; + saveLoanRepayment(singleLoanRepaymentCommand.getLoanId(), paymentDetail, singleLoanRepaymentCommand.getTransactionAmount(), bulkRepaymentCommand.getTransactionDate(), bulkRepaymentCommand.getNote()); changes.put("bulkTransations", singleLoanRepaymentCommand); } @@ -343,7 +367,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingReversedTransactionIds = new ArrayList(); final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); - LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, transactionDate); + PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(command, changes); + LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate); if (transactionToAdjust.isInterestWaiver()) { newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate); } @@ -352,6 +377,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds); if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) { + if (paymentDetail != null) { + paymentDetailRepository.save(paymentDetail); + } this.loanTransactionRepository.save(newTransactionDetail); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java index fccf55e52..00bb83876 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/service/LoanEnumerations.java @@ -10,6 +10,7 @@ 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.PaymentType; import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType; import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod; import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod; @@ -197,6 +198,13 @@ public class LoanEnumerations { 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)); } diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanTransactionBuilder.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanTransactionBuilder.java index e86e05a17..b75805d8d 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanTransactionBuilder.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/LoanTransactionBuilder.java @@ -2,37 +2,39 @@ package org.mifosplatform.portfolio.loanaccount; import org.joda.time.LocalDate; import org.mifosplatform.organisation.monetary.domain.Money; -import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction; public class LoanTransactionBuilder { - private Money transactionAmount = new MoneyBuilder().build(); - private LocalDate transactionDate = LocalDate.now(); - private boolean repayment = false; + @SuppressWarnings("unused") + private Money transactionAmount = new MoneyBuilder().build(); + @SuppressWarnings("unused") + private LocalDate transactionDate = LocalDate.now(); + @SuppressWarnings("unused") + private boolean repayment = false; - public LoanTransaction build() { - - LoanTransaction transaction = null; - - if (repayment) { - transaction = LoanTransaction.repayment(transactionAmount, transactionDate); - } - - return transaction; - } + /** public LoanTransaction build() { - public LoanTransactionBuilder with(final Money newAmount) { - this.transactionAmount = newAmount; - return this; - } + LoanTransaction transaction = null; - public LoanTransactionBuilder with(final LocalDate withTransactionDate) { - this.transactionDate = withTransactionDate; - return this; - } - - public LoanTransactionBuilder repayment() { - this.repayment = true; - return this; - } + if (repayment) { + transaction = LoanTransaction.repayment(transactionAmount, transactionDate); + } + + return transaction; + }**/ + + public LoanTransactionBuilder with(final Money newAmount) { + this.transactionAmount = newAmount; + return this; + } + + public LoanTransactionBuilder with(final LocalDate withTransactionDate) { + this.transactionDate = withTransactionDate; + return this; + } + + public LoanTransactionBuilder repayment() { + this.repayment = true; + return this; + } }