changes to transfer funds from overpaid account

This commit is contained in:
Pramod Nuthakki
2013-08-29 16:17:43 +05:30
parent 8286801eac
commit 82821cedec
18 changed files with 378 additions and 64 deletions
@@ -54,11 +54,13 @@ public class AccountTransferData {
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions) {
final Long id = null;
CurrencyData currency = null;
BigDecimal transferAmount = BigDecimal.ZERO;
if (fromAccount != null) {
currency = fromAccount.currency();
if(fromAccount.getAmtForTransfer() != null){
transferAmount = fromAccount.getAmtForTransfer();
}
}
final BigDecimal transferAmount = BigDecimal.ZERO;
final String transferDescription = null;
final Boolean reversed = null;
return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccountType, fromAccount, currency, transferAmount,
@@ -5,6 +5,8 @@
*/
package org.mifosplatform.portfolio.account.data;
import java.math.BigDecimal;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
@@ -27,9 +29,10 @@ public class PortfolioAccountData {
private final Long fieldOfficerId;
private final String fieldOfficerName;
private final CurrencyData currency;
private final BigDecimal amtForTransfer;
public static PortfolioAccountData lookup(final Long accountId, final String accountNo) {
return new PortfolioAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null);
return new PortfolioAccountData(accountId, accountNo, null, null, null, null, null, null, null, null, null, null, null);
}
public PortfolioAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
@@ -47,6 +50,25 @@ public class PortfolioAccountData {
this.fieldOfficerId = fieldofficerId;
this.fieldOfficerName = fieldofficerName;
this.currency = currency;
this.amtForTransfer = null;
}
public PortfolioAccountData(final Long id, final String accountNo, final String externalId, final Long groupId, final String groupName,
final Long clientId, final String clientName, final Long productId, final String productName, final Long fieldofficerId,
final String fieldofficerName, final CurrencyData currency, final BigDecimal amtForTransfer) {
this.id = id;
this.accountNo = accountNo;
this.externalId = externalId;
this.groupId = groupId;
this.groupName = groupName;
this.clientId = clientId;
this.clientName = clientName;
this.productId = productId;
this.productName = productName;
this.fieldOfficerId = fieldofficerId;
this.fieldOfficerName = fieldofficerName;
this.currency = currency;
this.amtForTransfer = amtForTransfer;
}
@Override
@@ -56,12 +78,14 @@ public class PortfolioAccountData {
if (obj == this) { return true; }
if (obj.getClass() != getClass()) { return false; }
final PortfolioAccountData rhs = (PortfolioAccountData) obj;
return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).append(this.productId, rhs.productId).append(this.productName, rhs.productName).isEquals();
return new EqualsBuilder().append(this.id, rhs.id).append(this.accountNo, rhs.accountNo).append(this.productId, rhs.productId)
.append(this.productName, rhs.productName).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).append(this.productId).append(this.productName).toHashCode();
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).append(this.productId).append(this.productName)
.toHashCode();
}
public Long clientId() {
@@ -75,4 +99,8 @@ public class PortfolioAccountData {
public String currencyCode() {
return this.currency.code();
}
public BigDecimal getAmtForTransfer() {
return this.amtForTransfer;
}
}
@@ -68,12 +68,20 @@ public class AccountTransfer extends AbstractPersistable<Long> {
@JoinColumn(name = "to_loan_account_id", nullable = true)
private Loan toLoanAccount;
@ManyToOne
@JoinColumn(name = "from_loan_account_id", nullable = true)
private Loan fromLoanAccount;
@ManyToOne
@JoinColumn(name = "to_loan_transaction_id", nullable = true)
private LoanTransaction toLoanTransaction;
@ManyToOne
@JoinColumn(name = "from_loan_transaction_id", nullable = true)
private LoanTransaction fromLoanTransaction;
@Column(name = "is_reversed", nullable = false)
private final boolean reversed = false;
private boolean reversed = false;
@Temporal(TemporalType.DATE)
@Column(name = "transaction_date")
@@ -93,16 +101,23 @@ public class AccountTransfer extends AbstractPersistable<Long> {
final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate,
final Money transactionAmount, final String description) {
return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, null, withdrawal,
deposit, null, transactionDate, transactionAmount, description);
return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, toSavingsAccount, null,
withdrawal, deposit, null, null, transactionDate, transactionAmount, description);
}
public static AccountTransfer savingsToLoanTransfer(final Office fromOffice, final Client fromClient,
final SavingsAccount fromSavingsAccount, final Office toOffice, final Client toClient, final Loan toLoanAccount,
final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate,
final Money transactionAmount, final String description) {
return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, null, toLoanAccount, withdrawal, null,
loanRepaymentTransaction, transactionDate, transactionAmount, description);
return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, null, toOffice, toClient, null, toLoanAccount, withdrawal,
null, loanRepaymentTransaction, null, transactionDate, transactionAmount, description);
}
public static AccountTransfer LoanTosavingsTransfer(final Office fromOffice, final Client fromClient, final Loan fromLoanAccount,
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, final SavingsAccountTransaction deposit,
final LoanTransaction loanRefundTransaction, final LocalDate transactionDate, final Money transactionAmount, final String description) {
return new AccountTransfer(fromOffice, fromClient, null, fromLoanAccount, toOffice, toClient, toSavingsAccount, null, null,
deposit, null, loanRefundTransaction, transactionDate, transactionAmount, description);
}
protected AccountTransfer() {
@@ -110,13 +125,15 @@ public class AccountTransfer extends AbstractPersistable<Long> {
}
private AccountTransfer(final Office fromOffice, final Client fromClient, final SavingsAccount fromSavingsAccount,
final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount, final Loan toLoanAccount,
final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit,
final LoanTransaction loanRepaymentTransaction, final LocalDate transactionDate, final Money transactionAmount,
final String description) {
final Loan fromLoanAccount, final Office toOffice, final Client toClient, final SavingsAccount toSavingsAccount,
final Loan toLoanAccount, final SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit,
final LoanTransaction loanRepaymentTransaction, LoanTransaction loanRefundTransaction, final LocalDate transactionDate,
final Money transactionAmount, final String description) {
this.fromOffice = fromOffice;
this.fromClient = fromClient;
this.fromAccount = fromSavingsAccount;
this.fromLoanAccount = fromLoanAccount;
this.fromLoanTransaction = loanRefundTransaction;
this.toOffice = toOffice;
this.toClient = toClient;
this.toSavingsAccount = toSavingsAccount;
@@ -129,4 +146,38 @@ public class AccountTransfer extends AbstractPersistable<Long> {
this.amount = transactionAmount.getAmountDefaultedToNullIfZero();
this.description = description;
}
public LoanTransaction getFromLoanTransaction() {
return this.fromLoanTransaction;
}
public SavingsAccountTransaction getFromTransaction() {
return this.fromTransaction;
}
public LoanTransaction getToLoanTransaction() {
return this.toLoanTransaction;
}
public SavingsAccountTransaction getToSavingsTransaction() {
return this.toSavingsTransaction;
}
public void reverse() {
this.reversed = true;
}
public SavingsAccount getToSavingsAccount() {
return this.toSavingsAccount;
}
public SavingsAccount getFromAccount() {
return this.fromAccount;
}
}
@@ -111,4 +111,32 @@ public class AccountTransferAssembler {
return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
withdrawal, loanRepaymentTransaction, transactionDate, transactionMonetaryAmount, description);
}
public AccountTransfer assembleLoanToSavingsTransfer(final JsonCommand command, final Loan fromLoanAccount,
final SavingsAccount toSavingsAccount, final SavingsAccountTransaction deposit, final LoanTransaction loanRefundTransaction) {
final JsonElement element = command.parsedJson();
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Office fromOffice = this.officeRepository.findOne(fromOfficeId);
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Office toOffice = this.officeRepository.findOne(toOfficeId);
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName);
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName);
final Money transactionMonetaryAmount = Money.of(toSavingsAccount.getCurrency(), transactionAmount);
final String description = command.stringValueOfParameterNamed(transferDescriptionParamName);
return AccountTransfer.LoanTosavingsTransfer(fromOffice, fromClient, fromLoanAccount, toOffice, toClient, toSavingsAccount,
deposit, loanRefundTransaction, transactionDate, transactionMonetaryAmount, description);
}
}
@@ -5,9 +5,15 @@
*/
package org.mifosplatform.portfolio.account.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface AccountTransferRepository extends JpaRepository<AccountTransfer, Long>, JpaSpecificationExecutor<AccountTransfer> {
@Query("from AccountTransfer at where at.fromLoanAccount.id= :accountNumber and at.reversed=false")
List<AccountTransfer> findByLoanId(@Param("accountNumber") Long accountNumber);
}
@@ -13,7 +13,6 @@ import java.util.Arrays;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.commands.exception.UnsupportedCommandException;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.DateUtils;
@@ -70,23 +69,20 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
final EnumOptionData loanAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN);
final EnumOptionData savingsAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS);
final Collection<EnumOptionData> fromAccountTypeOptions = Arrays.asList(savingsAccountType);
final Collection<EnumOptionData> toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType);
// TODO - remove this after doing overpaid loan to savings transfers
Integer mostRelevantFromAccountType = fromAccountType;
if (mostRelevantFromAccountType == null) {
mostRelevantFromAccountType = 2; // savings
final Collection<EnumOptionData> fromAccountTypeOptions = Arrays.asList(savingsAccountType, loanAccountType);
final Collection<EnumOptionData> toAccountTypeOptions;
if (mostRelevantFromAccountType == 1) {
// overpaid loan amt transfer to savings account
toAccountTypeOptions = Arrays.asList(savingsAccountType);
} else {
toAccountTypeOptions = Arrays.asList(loanAccountType, savingsAccountType);
}
final Integer mostRelevantToAccountType = toAccountType;
final EnumOptionData fromAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantFromAccountType);
final EnumOptionData toAccountTypeData = AccountTransferEnumerations.accountType(mostRelevantToAccountType);
// check for now that only savings account type is passed and used.
if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccountNotSupported"); }
// from settings
OfficeData fromOffice = null;
ClientData fromClient = null;
@@ -106,21 +102,28 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
Long mostRelevantToOfficeId = toOfficeId;
Long mostRelevantToClientId = toClientId;
if (fromAccountId != null && mostRelevantFromAccountType == 2) {
fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, PortfolioAccountType.SAVINGS.getValue());
if (fromAccountId != null) {
Integer accountType;
if (mostRelevantFromAccountType == 1) {
accountType = PortfolioAccountType.LOAN.getValue();
} else {
accountType = PortfolioAccountType.SAVINGS.getValue();
}
fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, accountType);
// override provided fromClient with client of account
mostRelevantFromClientId = fromAccount.clientId();
} else if (fromAccountId != null && mostRelevantFromAccountType == 1) { // loans
// fetch loan account
}
if (mostRelevantFromClientId != null) {
fromClient = this.clientReadPlatformService.retrieveOne(mostRelevantFromClientId);
mostRelevantFromOfficeId = fromClient.officeId();
long[] loanStatus = null;
if (mostRelevantFromAccountType == 1) {
loanStatus = new long[] { 300, 700 };
}
fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromAccountType,
mostRelevantFromClientId);
mostRelevantFromClientId, loanStatus);
}
Collection<OfficeData> fromOfficeOptions = null;
@@ -137,7 +140,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
Collection<ClientData> toClientOptions = null;
if (toAccountId != null && fromAccount != null) {
toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, fromAccount.currencyCode());
toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType,
fromAccount.currencyCode());
mostRelevantToClientId = toAccount.clientId();
}
@@ -173,7 +177,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.currencyCode() : null;
Collection<PortfolioAccountData> accountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(toAccountType,
toClientId, currencyCode);
toClientId, currencyCode, null);
if (!CollectionUtils.isEmpty(accountOptions)) {
accountOptions.remove(excludeThisAccountFromOptions);
} else {
@@ -7,8 +7,11 @@ package org.mifosplatform.portfolio.account.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
public interface AccountTransfersWritePlatformService {
CommandProcessingResult create(JsonCommand command);
void reverseTransfers(Long accountNumber, PortfolioAccountType accountTypeId);
}
@@ -13,6 +13,7 @@ import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.t
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName;
import java.math.BigDecimal;
import java.util.List;
import java.util.Locale;
import org.joda.time.LocalDate;
@@ -35,6 +36,7 @@ import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountDomainService;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -49,12 +51,14 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
private final SavingsAccountDomainService savingsAccountDomainService;
private final LoanAssembler loanAccountAssembler;
private final LoanAccountDomainService loanAccountDomainService;
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
@Autowired
public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator,
final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository,
final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService,
final LoanAssembler loanAssembler, final LoanAccountDomainService loanAccountDomainService) {
final LoanAssembler loanAssembler, final LoanAccountDomainService loanAccountDomainService,
final SavingsAccountWritePlatformService savingsAccountWritePlatformService) {
this.accountTransfersDataValidator = accountTransfersDataValidator;
this.accountTransferAssembler = accountTransferAssembler;
this.accountTransferRepository = accountTransferRepository;
@@ -62,6 +66,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
this.savingsAccountDomainService = savingsAccountDomainService;
this.loanAccountAssembler = loanAssembler;
this.loanAccountDomainService = loanAccountDomainService;
this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
}
@Transactional
@@ -124,7 +129,27 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
transferTransactionId = transferTransaction.getId();
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
// FIXME - kw - ADD overpaid loan to savings account transfer support.
// FIXME - kw - ADD overpaid loan to savings account transfer
// support.
//
final Long fromLoanAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final Loan fromLoanAccount = this.loanAccountAssembler.assembleFrom(fromLoanAccountId);
final LoanTransaction loanRefundTransaction = this.loanAccountDomainService.makeRefund(fromLoanAccountId,
new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null);
final Long toSavingsAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsAccountId);
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail);
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleLoanToSavingsTransfer(command,
fromLoanAccount, toSavingsAccount, deposit, loanRefundTransaction);
this.accountTransferRepository.saveAndFlush(transferTransaction);
transferTransactionId = transferTransaction.getId();
} else {
}
@@ -139,6 +164,25 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
return builder.build();
}
@Override
@Transactional
public void reverseTransfers(Long accountNumber, PortfolioAccountType accountTypeId) {
List<AccountTransfer> acccountTransfers = null;
if (accountTypeId.isLoanAccount()) {
acccountTransfers = this.accountTransferRepository.findByLoanId(accountNumber);
}
if (acccountTransfers != null && acccountTransfers.size() > 0) {
for (AccountTransfer accountTransfer : acccountTransfers) {
this.loanAccountDomainService.reverseTransfer(accountTransfer.getFromLoanTransaction());
this.savingsAccountWritePlatformService.undoTransaction(accountTransfer.getToSavingsAccount().getId(), accountTransfer
.getToSavingsTransaction().getId());
accountTransfer.reverse();
this.accountTransferRepository.save(accountTransfer);
}
}
}
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
}
@@ -15,7 +15,7 @@ public interface PortfolioAccountReadPlatformService {
PortfolioAccountData retrieveOne(Long accountId, Integer accountTypeId, String currencyCode);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId, long[] accountStatus);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId, String currencyCode);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId, String currencyCode, long[] accountStatus);
}
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.account.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
@@ -45,24 +46,24 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
@Override
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
Object[] sqlParams = new Object[] {accountId};
Object[] sqlParams = new Object[] { accountId };
PortfolioAccountData accountData = null;
try {
String sql = null;
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
switch (accountType) {
case INVALID:
break;
break;
case LOAN:
sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?";
if (currencyCode != null) {
sql += " and la.currency_code = ?";
sqlParams = new Object[] {accountId, currencyCode};
sqlParams = new Object[] { accountId, currencyCode };
}
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
break;
accountData = this.jdbcTemplate.queryForObject(sql, this.loanAccountMapper, sqlParams);
break;
case SAVINGS:
sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?";
if (currencyCode != null) {
@@ -71,9 +72,9 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
}
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
break;
break;
default:
break;
break;
}
} catch (final EmptyResultDataAccessException e) {
throw new AccountTransferNotFoundException(accountId);
@@ -83,40 +84,50 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
}
@Override
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId) {
return retrieveAllForLookup(accountTypeId, clientId, null);
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId, long[] accountStatus) {
return retrieveAllForLookup(accountTypeId, clientId, null, accountStatus);
}
@Override
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId, final String currencyCode) {
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId,
final String currencyCode, long[] accountStatus) {
Object[] sqlParams = new Object[] {clientId};
Object[] sqlParams = new Object[] { clientId };
Collection<PortfolioAccountData> accounts = null;
String sql = null;
String defaultAccountStatus = "300";
if (accountStatus != null) {
for (long status : accountStatus) {
defaultAccountStatus += ", " + status;
}
defaultAccountStatus = defaultAccountStatus.substring(defaultAccountStatus.indexOf(",")+1);
}
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
switch (accountType) {
case INVALID:
break;
break;
case LOAN:
sql = "select " + this.loanAccountMapper.schema() + " where la.client_id = ? and la.loan_status_id = 300";
sql = "select " + this.loanAccountMapper.schema() + " where la.client_id = ? and la.loan_status_id in ("
+ defaultAccountStatus.toString() + ")";
if (currencyCode != null) {
sql += " and la.currency_code = ?";
sqlParams = new Object[] {clientId, currencyCode};
sqlParams = new Object[] { clientId, currencyCode };
}
accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams);
break;
break;
case SAVINGS:
sql = "select " + this.savingsAccountMapper.schema() + " where sa.client_id = ? and sa.status_enum = 300";
sql = "select " + this.savingsAccountMapper.schema() + " where sa.client_id = ? and sa.status_enum in ("
+ defaultAccountStatus.toString() + ")";
if (currencyCode != null) {
sql += " and sa.currency_code = ?";
sqlParams = new Object[] {clientId, currencyCode};
sqlParams = new Object[] { clientId, currencyCode };
}
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams);
break;
break;
default:
break;
break;
}
return accounts;
@@ -198,6 +209,7 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
sqlBuilder.append("la.currency_code as currencyCode, la.currency_digits as currencyDigits,");
sqlBuilder.append("la.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("la.total_overpaid_derived as totalOverpaid, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
sqlBuilder.append("from m_loan la ");
@@ -238,11 +250,12 @@ public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccount
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final BigDecimal amtForTransfer = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverpaid");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMulitplesOf,
currencyDisplaySymbol, currencyNameCode);
return new PortfolioAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
fieldOfficerId, fieldOfficerName, currency);
fieldOfficerId, fieldOfficerName, currency, amtForTransfer);
}
}
}
@@ -1528,6 +1528,42 @@ public class Loan extends AbstractPersistable<Long> {
return changedTransactionDetail;
}
public void makeRefund(final LoanTransaction loanTransaction, final LoanLifecycleStateMachine loanLifecycleStateMachine,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds,
final boolean allowTransactionsOnHoliday, final List<Holiday> holidays, final WorkingDays workingDays,
final boolean allowTransactionsOnNonWorkingDay) {
validateRepaymentDateIsOnHoliday(loanTransaction.getTransactionDate(), allowTransactionsOnHoliday, holidays);
validateRepaymentDateIsOnNonWorkingDay(loanTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay);
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
if (status().isOverpaid()) {
if (this.totalOverpaid.compareTo(loanTransaction.getAmount(getCurrency()).getAmount()) == -1) {
final String errorMessage = "The refund amount must be less than or equal to overpaid amount ";
throw new InvalidLoanStateTransitionException("transaction", "is.exceeding.overpaid.amount", errorMessage, totalOverpaid,
loanTransaction.getAmount(getCurrency()).getAmount());
} else if (!isAfterLatRepayment(loanTransaction, loanTransactions)) {
final String errorMessage = "Transfer funds is allowed only after last repayment date";
throw new InvalidLoanStateTransitionException("transaction", "is.not.after.repayment.date", errorMessage);
}
} else {
final String errorMessage = "Transfer funds is allowed only for loan accounts with overpaid status ";
throw new InvalidLoanStateTransitionException("transaction", "is.not.a.overpaid.loan", errorMessage);
}
this.totalOverpaid = this.totalOverpaid.subtract(loanTransaction.getAmount(getCurrency()).getAmount());
if (this.totalOverpaid.compareTo(BigDecimal.ZERO) == 0) {
handleLoanRepaymentInFull(loanTransaction.getTransactionDate(), loanLifecycleStateMachine);
}
loanTransaction.updateLoan(this);
if (loanTransaction.isNotZero(loanCurrency())) {
this.loanTransactions.add(loanTransaction);
}
}
private ChangedTransactionDetail handleRepaymentOrWaiverTransaction(final LoanTransaction loanTransaction,
final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransaction adjustedTransaction) {
@@ -1656,6 +1692,22 @@ public class Loan extends AbstractPersistable<Long> {
return isChronologicallyLatestRepaymentOrWaiver;
}
private boolean isAfterLatRepayment(final LoanTransaction loanTransaction, final List<LoanTransaction> loanTransactions) {
boolean isAfterLatRepayment = true;
final LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
for (final LoanTransaction previousTransaction : loanTransactions) {
if (previousTransaction.isRepayment() && previousTransaction.isNotReversed()) {
if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
isAfterLatRepayment = false;
break;
}
}
}
return isAfterLatRepayment;
}
private boolean isChronologicallyLatestTransaction(final LoanTransaction loanTransaction, final List<LoanTransaction> loanTransactions) {
boolean isChronologicallyLatestRepaymentOrWaiver = true;
@@ -1794,7 +1846,7 @@ public class Loan extends AbstractPersistable<Long> {
private Money calculateTotalOverpayment() {
final Money totalPaidInRepayments = getTotalPaidInRepayments();
Money totalPaidInRepayments = getTotalPaidInRepayments();
final MonetaryCurrency currency = loanCurrency();
Money cumulativeTotalPaidOnInstallments = Money.zero(currency);
@@ -1809,6 +1861,12 @@ public class Loan extends AbstractPersistable<Long> {
cumulativeTotalWaivedOnInstallments = cumulativeTotalWaivedOnInstallments.plus(scheduledRepayment.getInterestWaived(currency));
}
for (LoanTransaction loanTransaction : this.loanTransactions) {
if (loanTransaction.isRefund() && !loanTransaction.isReversed()) {
totalPaidInRepayments = totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
}
}
// if total paid in transactions doesnt match repayment schedule then
// theres an overpayment.
return totalPaidInRepayments.minus(cumulativeTotalPaidOnInstallments);
@@ -2578,11 +2636,11 @@ public class Loan extends AbstractPersistable<Long> {
public Integer getTermPeriodFrequencyType() {
return this.termPeriodFrequencyType;
}
public List<LoanTransaction> getLoanTransactions() {
return this.loanTransactions;
}
public void setLoanStatus(Integer loanStatus) {
this.loanStatus = loanStatus;
}
@@ -15,4 +15,9 @@ public interface LoanAccountDomainService {
LoanTransaction makeRepayment(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
}
LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
void reverseTransfer(LoanTransaction loanTransaction);
}
@@ -163,4 +163,49 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
if (group.isNotActive()) { throw new GroupNotActiveException(group.getId()); }
}
}
@Override
public LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId) {
final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
checkClientOrGroupActive(loan);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRefundTransaction = LoanTransaction.refund(loan.getOffice(), refundAmount, paymentDetail, transactionDate,
txnExternalId);
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final List<Holiday> holidays = this.holidayRepository
.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
loan.makeRefund(newRefundTransaction, defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds,
allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
this.loanTransactionRepository.save(newRefundTransaction);
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
final Note note = Note.loanTransactionNote(loan, newRefundTransaction, noteText);
this.noteRepository.save(note);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
builderResult.withEntityId(newRefundTransaction.getId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()); //
return newRefundTransaction;
}
@Override
public void reverseTransfer(LoanTransaction loanTransaction) {
loanTransaction.reverse();
this.loanTransactionRepository.save(loanTransaction);
}
}
@@ -135,4 +135,8 @@ public enum LoanStatus {
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
public boolean isOverpaid(){
return this.value.equals(LoanStatus.OVERPAID.getValue());
}
}
@@ -137,6 +137,11 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
.getTotalPenaltyChargesOutstanding(), false, null);
}
public static LoanTransaction refund(final Office office, final Money amount, final PaymentDetail paymentDetail,
final LocalDate paymentDate, final String externalId) {
return new LoanTransaction(null, office, LoanTransactionType.REFUND, paymentDetail, amount.getAmount(), paymentDate, externalId);
}
public static LoanTransaction copyTransactionProperties(LoanTransaction loanTransaction) {
return new LoanTransaction(loanTransaction.loan, loanTransaction.office, loanTransaction.typeOf, loanTransaction.dateOf,
loanTransaction.amount, loanTransaction.principalPortion, loanTransaction.interestPortion,
@@ -446,5 +451,9 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
public String getExternalId() {
return externalId;
}
public boolean isRefund(){
return LoanTransactionType.REFUND.equals(getTypeOf()) && isNotReversed();
}
}
@@ -32,7 +32,9 @@ public enum LoanTransactionType {
INITIATE_TRANSFER(12, "loanTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "loanTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "loanTransactionType.withdrawTransfer"), //
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer");
REJECT_TRANSFER(15, "loanTransactionType.rejectTransfer"),
REFUND(16,"loanTransactionType.refund");
private final Integer value;
private final String code;
@@ -101,6 +103,9 @@ public enum LoanTransactionType {
case 15:
loanTransactionType = LoanTransactionType.REJECT_TRANSFER;
break;
case 16:
loanTransactionType = LoanTransactionType.REFUND;
break;
default:
loanTransactionType = LoanTransactionType.INVALID;
break;
@@ -34,6 +34,8 @@ import org.mifosplatform.organisation.office.domain.Office;
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.AccountTransfersWritePlatformService;
import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
import org.mifosplatform.portfolio.calendar.domain.Calendar;
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
@@ -113,6 +115,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final ConfigurationDomainService configurationDomainService;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final LoanProductReadPlatformService loanProductReadPlatformService;
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
@Autowired
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -126,7 +129,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final HolidayRepository holidayRepository,
final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository,
final LoanProductReadPlatformService loanProductReadPlatformService) {
final LoanProductReadPlatformService loanProductReadPlatformService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler;
@@ -146,6 +150,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.configurationDomainService = configurationDomainService;
this.workingDaysRepository = workingDaysRepository;
this.loanProductReadPlatformService = loanProductReadPlatformService;
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
@@ -477,7 +482,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
this.loanRepository.save(loan);
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
changes.put("note", noteText);
@@ -493,6 +497,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
this.noteRepository.save(note);
}
this.accountTransfersWritePlatformService.reverseTransfers(loanId, PortfolioAccountType.LOAN);
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
@@ -298,6 +298,10 @@ public class LoanEnumerations {
optionData = new LoanTransactionEnumData(LoanTransactionType.REJECT_TRANSFER.getValue().longValue(),
LoanTransactionType.REJECT_TRANSFER.getCode(), "Transfer Rejected");
break;
case REFUND:
optionData = new LoanTransactionEnumData(LoanTransactionType.REFUND.getValue().longValue(),
LoanTransactionType.REFUND.getCode(), "Transfer Refund");
break;
default:
break;
}