diff --git a/CHANGELOG.md b/CHANGELOG.md index 0af2d098e..250a4d5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ See VERSIONING (https://github.com/openMF/mifosx/blob/master/VERSIONING.md) for Releases =============== +1. 10 Sep 2013 - 1.9.1.RELEASE 1. 8 Sep 2013 - 1.9.0.RELEASE 1. 21 Aug 2013 - 1.8.1.RELEASE 1. 20 Aug 2013 - 1.8.0.RELEASE @@ -34,6 +35,21 @@ Releases 1. 05 April 2013 - 0.11.1.beta 1. 05 April 2013 - 0.11.0.beta +1.9.1.RELEASE +============= +Bug Release + +Platform & API + +Bug + - [MIFOSX-647] - Transfered amount in Loan account transaction page should not be allowed to Edit/Delete + - [MIFOSX-648] - User with no read permissions should still be able to fetch their own user account data + - [MIFOSX-658] - Unable to update existing Loan Products in demo server + +Reference App + - [MIFOSX-655] - Client Closure reason added in code values is not displayed in dropdon list of Client Close page + + 1.9.0.RELEASE ============= Feature Release diff --git a/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql b/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql new file mode 100644 index 000000000..55e60416b --- /dev/null +++ b/mifosng-db/migrations/core_db/V111_1__set default_transfers_in_suspense_account_for_existing_loan_products.sql @@ -0,0 +1,4 @@ +INSERT INTO `acc_product_mapping` (`gl_account_id`,`product_id`,`product_type`,`payment_type`,`charge_id`,`financial_account_type`) +select mapping.gl_account_id,mapping.product_id,mapping.product_type,mapping.payment_type,mapping.charge_id, 10 +from acc_product_mapping mapping +where mapping.financial_account_type = 1 and mapping.product_type=1; \ No newline at end of file diff --git a/mifosng-provider/gradle.properties b/mifosng-provider/gradle.properties index 705b732fa..435ec0cb3 100644 --- a/mifosng-provider/gradle.properties +++ b/mifosng-provider/gradle.properties @@ -1 +1,2 @@ releaseVersion=1.10.0.DEVELOP + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java index e2f69f4df..05c71e2c4 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java @@ -64,7 +64,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalAmount, false); } else if (loanTransactionDTO.getTransactionType().isInitiateTransfer() - || loanTransactionDTO.getTransactionType().isApproveTransfer()) { + || loanTransactionDTO.getTransactionType().isApproveTransfer() + || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { createJournalEntriesForTransfers(loanDTO, loanTransactionDTO, office); } } @@ -184,7 +185,8 @@ public class CashBasedAccountingProcessorForLoan implements AccountingProcessorF helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, loanProductId, null, loanId, transactionId, transactionDate, principalAmount, isReversal); - } else if (loanTransactionDTO.getTransactionType().isApproveTransfer()) { + } else if (loanTransactionDTO.getTransactionType().isApproveTransfer() + || loanTransactionDTO.getTransactionType().isWithdrawTransfer()) { helper.createCashBasedJournalEntriesAndReversalsForLoan(office, CASH_ACCOUNTS_FOR_LOAN.LOAN_PORTFOLIO, CASH_ACCOUNTS_FOR_LOAN.TRANSFERS_SUSPENSE, loanProductId, null, loanId, transactionId, transactionDate, principalAmount, isReversal); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java index 669d661af..56daa93a0 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/serialization/ProductToGLAccountMappingFromApiJsonDeserializer.java @@ -107,6 +107,11 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer { baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue()).value(writeOffAccountId) .notNull().integerGreaterThanZero(); + final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); + baseDataValidator.reset().parameter(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue()) + .value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero(); + } if (isAccrualBasedAccounting(accountingRuleType)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java index e3b12c28f..74de8f839 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java @@ -112,6 +112,8 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), element); final Long writeOffAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), element); + final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed( + LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element); final Long receivableInterestAccountId = this.fromApiJsonHelper.extractLongNamed( LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), element); @@ -130,6 +132,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeeId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), incomeFromPenaltyId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), writeOffAccountId); + changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); break; case ACCRUAL_BASED: changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), fundAccountId); @@ -141,6 +144,7 @@ public class LoanProductToGLAccountMappingHelper extends ProductToGLAccountMappi changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), receivableInterestAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(), receivableFeeAccountId); changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(), receivablePenaltyAccountId); + changes.put(LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId); break; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java index a039841b1..2967fa1a6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformService.java @@ -6,6 +6,7 @@ package org.mifosplatform.portfolio.account.service; import org.mifosplatform.infrastructure.core.service.Page; +import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.group.service.SearchParameters; @@ -17,4 +18,6 @@ public interface AccountTransfersReadPlatformService { Page retrieveAll(SearchParameters searchParameters); AccountTransferData retrieveOne(Long transferId); + + boolean isAccountTransfer(Long transactionId,PortfolioAccountType accountType); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index e5135302d..bc00341fd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -247,6 +247,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); @@ -260,6 +261,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers sqlBuilder.append("join m_client fromclient on fromclient.id = sat.from_client_id "); sqlBuilder.append("join m_client toclient on toclient.id = sat.to_client_id "); sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = sat.from_savings_account_id "); + sqlBuilder.append("left join m_savings_account fromloanacc on fromloanacc.id = sat.from_loan_account_id "); sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = sat.to_savings_account_id "); sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = sat.to_loan_account_id "); sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = sat.from_savings_transaction_id "); @@ -307,11 +309,20 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers final String toClientName = rs.getString("toClientName"); final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); - final EnumOptionData fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); - final PortfolioAccountData fromSavingsAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); + final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); + PortfolioAccountData fromAccount = null; + EnumOptionData fromAccountType = null; + if(fromSavingsAccountId != null){ + fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + }else if(fromLoanAccountId != null){ + fromAccount = PortfolioAccountData.lookup(fromLoanAccountId, fromLoanAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + } PortfolioAccountData toAccount = null; EnumOptionData toAccountType = null; @@ -329,7 +340,21 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers } return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, - toOffice, fromClient, toClient, fromAccountType, fromSavingsAccount, toAccountType, toAccount); + toOffice, fromClient, toClient, fromAccountType, fromAccount, toAccountType, toAccount); } } + + @Override + public boolean isAccountTransfer(Long transactionId, PortfolioAccountType accountType) { + StringBuilder sql = new StringBuilder("select count(*) from m_savings_account_transfer at where "); + if (accountType.isLoanAccount()) { + sql.append("at.from_loan_transaction_id=").append(transactionId).append(" or at.to_loan_transaction_id=").append(transactionId); + } else { + sql.append("at.from_savings_transaction_id=").append(transactionId).append(" or at.to_savings_transaction_id=").append(transactionId); + } + + int count = this.jdbcTemplate.queryForInt(sql.toString()); + return count > 0; + } + } \ No newline at end of file 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 4b34efa97..8837c9eab 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 @@ -11,6 +11,7 @@ import java.util.Collection; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.organisation.monetary.data.CurrencyData; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData; /** @@ -35,6 +36,7 @@ public class LoanTransactionData { private final BigDecimal feeChargesPortion; private final BigDecimal penaltyChargesPortion; private final String externalId; + private final AccountTransferData transfer; // templates final Collection paymentTypeOptions; @@ -44,21 +46,22 @@ public class LoanTransactionData { loanTransactionData.type, loanTransactionData.paymentDetailData, loanTransactionData.currency, loanTransactionData.date, loanTransactionData.amount, loanTransactionData.principalPortion, loanTransactionData.interestPortion, loanTransactionData.feeChargesPortion, loanTransactionData.penaltyChargesPortion, paymentTypeOptions, - loanTransactionData.externalId); + loanTransactionData.externalId,loanTransactionData.transfer); } public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final String externalId) { + final BigDecimal penaltyChargesPortion, final String externalId, AccountTransferData transfer) { this(id, officeId, officeName, transactionType, paymentDetailData, currency, date, amount, principalPortion, interestPortion, - feeChargesPortion, penaltyChargesPortion, null, externalId); + feeChargesPortion, penaltyChargesPortion, null, externalId,transfer); } public LoanTransactionData(final Long id, final Long officeId, final String officeName, final LoanTransactionEnumData transactionType, final PaymentDetailData paymentDetailData, final CurrencyData currency, final LocalDate date, final BigDecimal amount, final BigDecimal principalPortion, final BigDecimal interestPortion, final BigDecimal feeChargesPortion, - final BigDecimal penaltyChargesPortion, final Collection paymentTypeOptions, final String externalId) { + final BigDecimal penaltyChargesPortion, final Collection paymentTypeOptions, final String externalId, + final AccountTransferData transfer) { this.id = id; this.officeId = officeId; this.officeName = officeName; @@ -73,6 +76,7 @@ public class LoanTransactionData { this.penaltyChargesPortion = penaltyChargesPortion; this.paymentTypeOptions = paymentTypeOptions; this.externalId = externalId; + this.transfer = transfer; } public LocalDate dateOf() { 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 f937d5f81..cbbbc73db 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 @@ -2090,7 +2090,7 @@ public class Loan extends AbstractPersistable { return !isDisbursed(); } - private boolean isDisbursed() { + public boolean isDisbursed() { return hasDisbursementTransaction(); } 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 b93e54df9..64e4e311e 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 @@ -32,6 +32,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionData; import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; @@ -395,7 +396,7 @@ public final class LoanTransaction extends AbstractPersistable { return result; } - public LoanTransactionData toData(final CurrencyData currencyData) { + public LoanTransactionData toData(final CurrencyData currencyData,final AccountTransferData transfer) { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(this.typeOf); PaymentDetailData paymentDetailData = null; if (this.paymentDetail != null) { @@ -403,7 +404,7 @@ public final class LoanTransaction extends AbstractPersistable { } return new LoanTransactionData(this.getId(), this.office.getId(), this.office.getName(), transactionType, paymentDetailData, currencyData, getTransactionDate(), this.amount, this.principalPortion, this.interestPortion, this.feeChargesPortion, - this.penaltyChargesPortion, this.externalId); + this.penaltyChargesPortion, this.externalId, transfer); } public Map toMapData(final CurrencyData currencyData) { 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 11dadbc75..2a93650f2 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 @@ -31,6 +31,7 @@ import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; +import org.mifosplatform.portfolio.account.data.AccountTransferData; import org.mifosplatform.portfolio.accountdetails.service.AccountEnumerations; import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; @@ -329,7 +330,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate, - possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null); + possibleNextRepaymentAmount.getAmount(), null, null, null, null, paymentOptions, null,null); } @Override @@ -353,7 +354,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal amount = waiveOfInterest.getAmount(currency).getAmount(); return new LoanTransactionData(null, null, null, transactionType, null, currencyData, waiveOfInterest.getTransactionDate(), amount, - null, null, null, null, null); + null, null, null, null, null, null); } @Override @@ -363,7 +364,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.WRITEOFF); return new LoanTransactionData(null, null, null, transactionType, null, null, DateUtils.getLocalDateOfTenant(), null, null, null, - null, null, null); + null, null, null, null); } @Override @@ -374,7 +375,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final Collection paymentOptions = codeValueReadPlatformService .retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName); return new LoanTransactionData(null, null, null, transactionType, null, null, loan.getExpectedDisbursedOnLocalDate(), null, null, - null, null, null, paymentOptions, null); + null, null, null, paymentOptions, null,null); } @Override @@ -393,8 +394,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { if (transaction == null) { throw new LoanTransactionNotFoundException(transactionId); } if (transaction.isNotBelongingToLoanOf(loan)) { throw new LoanTransactionNotFoundException(transactionId, loanId); } - - return transaction.toData(currencyData); + + LoanTransactionsAccountTransferMapper trasfermapper = new LoanTransactionsAccountTransferMapper(); + String sql = "select " + + trasfermapper.accountTransferSchema() + + " where tr.loan_id = ? and tr.id = ?"; + AccountTransferData accountTransferData = this.jdbcTemplate.queryForObject(sql, trasfermapper, loanId, transactionId); + return transaction.toData(currencyData,accountTransferData); } private static final class LoanMapper implements RowMapper { @@ -861,12 +867,20 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, " + " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " - + " cv.code_value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName " + + " cv.code_value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName, " + + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed," + + " fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount," + + " fromtran.description as fromTransferDescription," + + " totran.id as toTransferId, totran.is_reversed as toTransferReversed," + + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount," + + " totran.description as toTransferDescription " + " 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_code_value cv on pd.payment_type_cv_id = cv.id" - + " left join m_office office on office.id=tr.office_id"; + + " left join m_office office on office.id=tr.office_id" + + " left join m_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; } @Override @@ -910,12 +924,89 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final BigDecimal feeChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fees"); final BigDecimal penaltyChargesPortion = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "penalties"); final String externalId = rs.getString("externalId"); + + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currencyData, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currencyData, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount, - principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, externalId); + principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, externalId, transfer); } } + private static final class LoanTransactionsAccountTransferMapper implements RowMapper { + + public String accountTransferSchema() { + + return " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, " + + " rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, " + + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed," + + " fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount," + + " fromtran.description as fromTransferDescription," + + " totran.id as toTransferId, totran.is_reversed as toTransferReversed," + + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount," + + " totran.description as toTransferDescription " + + " 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_savings_account_transfer fromtran on fromtran.from_loan_transaction_id = tr.id " + + " left join m_savings_account_transfer totran on totran.to_loan_transaction_id = tr.id "; + } + + @Override + public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currencyData = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, + currencyDisplaySymbol, currencyNameCode); + + AccountTransferData transfer = null; + final Long fromTransferId = JdbcSupport.getLong(rs, "fromTransferId"); + final Long toTransferId = JdbcSupport.getLong(rs, "toTransferId"); + if (fromTransferId != null) { + final LocalDate fromTransferDate = JdbcSupport.getLocalDate(rs, "fromTransferDate"); + final BigDecimal fromTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "fromTransferAmount"); + final boolean fromTransferReversed = rs.getBoolean("fromTransferReversed"); + final String fromTransferDescription = rs.getString("fromTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(fromTransferId, currencyData, fromTransferAmount, fromTransferDate, + fromTransferDescription, fromTransferReversed); + } else if (toTransferId != null) { + final LocalDate toTransferDate = JdbcSupport.getLocalDate(rs, "toTransferDate"); + final BigDecimal toTransferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "toTransferAmount"); + final boolean toTransferReversed = rs.getBoolean("toTransferReversed"); + final String toTransferDescription = rs.getString("toTransferDescription"); + + transfer = AccountTransferData.transferBasicDetails(toTransferId, currencyData, toTransferAmount, toTransferDate, + toTransferDescription, toTransferReversed); + } + return transfer; + } + } + + @Override public LoanAccountData retrieveLoanProductDetailsTemplate(final Long productId) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java index f8d78d084..c4c7050c3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformService.java @@ -59,12 +59,12 @@ public interface LoanWritePlatformService { void applyHolidaysToLoans(); - LoanTransaction initiateLoanTransfer(Long accountId, LocalDate TransferDate); + LoanTransaction initiateLoanTransfer(Long accountId, LocalDate transferDate); - LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate); + LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate transferDate); void rejectLoanTransfer(Long accountId); - LoanTransaction acceptLoanTransfer(Long accountId, LocalDate TransferDate, Office acceptedInOffice, Staff loanOfficer); + LoanTransaction acceptLoanTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff loanOfficer); } \ 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 d6ed5530e..abe1ae2a2 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 @@ -21,6 +21,7 @@ import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomain import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -35,6 +36,7 @@ import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService; import org.mifosplatform.portfolio.accountdetails.domain.AccountType; import org.mifosplatform.portfolio.calendar.domain.Calendar; @@ -115,6 +117,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final WorkingDaysRepositoryWrapper workingDaysRepository; private final LoanProductReadPlatformService loanProductReadPlatformService; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; @Autowired public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -129,7 +132,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanProductReadPlatformService loanProductReadPlatformService, - final AccountTransfersWritePlatformService accountTransfersWritePlatformService) { + final AccountTransfersWritePlatformService accountTransfersWritePlatformService, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { this.context = context; this.loanEventApiJsonValidator = loanEventApiJsonValidator; this.loanAssembler = loanAssembler; @@ -150,6 +154,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.workingDaysRepository = workingDaysRepository; this.loanProductReadPlatformService = loanProductReadPlatformService; this.accountTransfersWritePlatformService = accountTransfersWritePlatformService; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -428,6 +433,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId); if (transactionToAdjust == null) { throw new LoanTransactionNotFoundException(transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)){ + throw new PlatformServiceUnavailableException("error.msg.loan.transfer.transaction.update.not.allowed","Loan transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate"); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount"); @@ -899,7 +908,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override - public LoanTransaction initiateLoanTransfer(final Long accountId, final LocalDate TransferDate) { + public LoanTransaction initiateLoanTransfer(final Long accountId, final LocalDate transferDate) { final Loan loan = this.loanAssembler.assembleFrom(accountId); checkClientOrGroupActive(loan); @@ -907,7 +916,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final List existingTransactionIds = new ArrayList(loan.findExistingTransactionIds()); final List existingReversedTransactionIds = new ArrayList(loan.findExistingReversedTransactionIds()); - final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, TransferDate); + final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate); loan.getLoanTransactions().add(newTransferTransaction); loan.setLoanStatus(LoanStatus.TRANSFER_IN_PROGRESS.getValue()); @@ -946,13 +955,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf @Transactional @Override - public LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate TransferDate) { + public LoanTransaction withdrawLoanTransfer(Long accountId, LocalDate transferDate) { final Loan loan = this.loanAssembler.assembleFrom(accountId); final List existingTransactionIds = new ArrayList(loan.findExistingTransactionIds()); final List existingReversedTransactionIds = new ArrayList(loan.findExistingReversedTransactionIds()); - final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, TransferDate); + final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate); loan.getLoanTransactions().add(newTransferAcceptanceTransaction); loan.setLoanStatus(LoanStatus.ACTIVE.getValue()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index e67a6c136..d65852b78 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -27,6 +27,7 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.exception.PlatformServiceUnavailableException; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.jobs.annotation.CronTarget; import org.mifosplatform.infrastructure.jobs.service.JobName; @@ -36,6 +37,8 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyReposit import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.portfolio.account.PortfolioAccountType; +import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.exception.ClientNotActiveException; import org.mifosplatform.portfolio.group.domain.Group; @@ -74,6 +77,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final SavingsAccountDomainService savingsAccountDomainService; private final NoteRepository noteRepository; + private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; @Autowired public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -84,7 +88,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, final JournalEntryWritePlatformService journalEntryWritePlatformService, - final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository) { + final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository, + final AccountTransfersReadPlatformService accountTransfersReadPlatformService) { this.context = context; this.savingAccountRepository = savingAccountRepository; this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; @@ -95,6 +100,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.savingsAccountDomainService = savingsAccountDomainService; this.noteRepository = noteRepository; + this.accountTransfersReadPlatformService = accountTransfersReadPlatformService; } @Transactional @@ -301,6 +307,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( transactionId, savingsId); if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){ + throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed","Savings account transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); @@ -339,6 +349,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( savingsId, transactionId); } + + if(accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)){ + throw new PlatformServiceUnavailableException("error.msg.saving.account.transfer.transaction.update.not.allowed","Savings account transaction:"+transactionId+" update not allowed as it involves in account transfer",transactionId); + } this.savingsAccountTransactionDataValidator.validate(command); final LocalDate today = DateUtils.getLocalDateOfTenant(); @@ -362,9 +376,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi // for undo withdrawal fee SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId( - transactionId + 1, savingsId); - if (nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()) { - account.undoTransaction(transactionId + 1, existingReversedTransactionIds); + transactionId+1, savingsId); + if(nextSavingsAccountTransaction!=null && nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()){ + account.undoTransaction(transactionId+1, existingReversedTransactionIds); } SavingsAccountTransaction transaction = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java index 7673d561b..361577168 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/transfer/service/TransferWritePlatformServiceJpaRepositoryImpl.java @@ -330,7 +330,11 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr if (loanRepository.doNonClosedLoanAccountsExistForClient(client.getId())) { // get each individual loan for the client for (Loan loan : loanRepository.findLoanByClientId(client.getId())) { - if (!loan.isClosed()) { + /** + * We need to create transactions etc only for loans which are + * disbursed and not yet closed + **/ + if (loan.isDisbursed() && !loan.isClosed()) { switch (transferEventType) { case ACCEPTANCE: this.loanWritePlatformService.acceptLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant(), @@ -340,7 +344,7 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr this.loanWritePlatformService.initiateLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant()); break; case REJECTION: - // do nothing + this.loanWritePlatformService.rejectLoanTransfer(loan.getId()); break; case WITHDRAWAL: this.loanWritePlatformService.withdrawLoanTransfer(loan.getId(), DateUtils.getLocalDateOfTenant());