MIFOSX-595: support savings account to loan account transfers for purposes of making loan repayment.

This commit is contained in:
Keith Woodlock
2013-08-15 11:05:27 +01:00
parent eee431d3b0
commit 50823d061b
31 changed files with 1472 additions and 1354 deletions
+245 -867
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,2 @@
ALTER TABLE `m_savings_account_transfer`
ADD COLUMN `currency_multiplesof` SMALLINT(5) NULL DEFAULT NULL AFTER `currency_digits`;
@@ -18,27 +18,21 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
@Table(name = "m_organisation_currency")
public class OrganisationCurrency extends AbstractPersistable<Long> {
@SuppressWarnings("unused")
@Column(name = "code", nullable = false, length = 3)
private final String code;
@SuppressWarnings("unused")
@Column(name = "decimal_places", nullable = false)
private final Integer decimalPlaces;
@SuppressWarnings("unused")
@Column(name = "currency_multiplesof")
private final Integer inMultiplesOf;
@SuppressWarnings("unused")
@Column(name = "name", nullable = false, length = 50)
private final String name;
@SuppressWarnings("unused")
@Column(name = "internationalized_name_code", nullable = false, length = 50)
private final String nameCode;
@SuppressWarnings("unused")
@Column(name = "display_symbol", nullable = true, length = 10)
private final String displaySymbol;
@@ -29,10 +29,8 @@ public class WorkingDaysRepositoryWrapper {
public WorkingDays findOne() {
final List<WorkingDays> workingDaysList = this.repository.findAll();
if (workingDaysList == null || workingDaysList.isEmpty()) {
throw new WorkingDaysNotFoundException();
}
if (workingDaysList == null || workingDaysList.isEmpty()) { throw new WorkingDaysNotFoundException(); }
return workingDaysList.get(0);
}
@@ -3,13 +3,13 @@
* 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.accounttransfers;
package org.mifosplatform.portfolio.account;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
public class AccountTransfersApiConstants {
@@ -0,0 +1,67 @@
/**
* 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.account;
import java.util.ArrayList;
import java.util.List;
public enum PortfolioAccountType {
INVALID(0, "accountType.invalid"), //
LOAN(1, "accountType.loan"), //
SAVINGS(2, "accountType.savings");
private final Integer value;
private final String code;
private PortfolioAccountType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public static Object[] integerValues() {
final List<Integer> values = new ArrayList<Integer>();
for (final PortfolioAccountType enumType : values()) {
if (enumType.getValue() > 0) {
values.add(enumType.getValue());
}
}
return values.toArray();
}
public static PortfolioAccountType fromInt(final Integer type) {
PortfolioAccountType enumType = PortfolioAccountType.INVALID;
if (type != null) {
switch (type) {
case 1:
enumType = LOAN;
break;
case 2:
enumType = SAVINGS;
break;
}
}
return enumType;
}
public boolean isSavingsAccount() {
return this.value == Integer.valueOf(2);
}
public boolean isLoanAccount() {
return this.value == Integer.valueOf(1);
}
}
@@ -3,7 +3,7 @@
* 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.accounttransfers.api;
package org.mifosplatform.portfolio.account.api;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -25,9 +25,9 @@ import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSeriali
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.account.AccountTransfersApiConstants;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.account.service.AccountTransfersReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
@@ -3,16 +3,16 @@
* 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.accounttransfers.data;
package org.mifosplatform.portfolio.account.data;
import java.math.BigDecimal;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
/**
* Immutable data object representing a savings account.
@@ -28,25 +28,30 @@ public class AccountTransferData {
private final String transferDescription;
private final OfficeData fromOffice;
private final ClientData fromClient;
private final SavingsAccountData fromAccount;
private final EnumOptionData fromAccountType;
private final PortfolioAccountData fromAccount;
private final OfficeData toOffice;
private final ClientData toClient;
private final SavingsAccountData toAccount;
private final EnumOptionData toAccountType;
private final PortfolioAccountData toAccount;
// template
private final Collection<OfficeData> fromOfficeOptions;
private final Collection<ClientData> fromClientOptions;
private final Collection<SavingsAccountData> fromAccountOptions;
private final Collection<EnumOptionData> fromAccountTypeOptions;
private final Collection<PortfolioAccountData> fromAccountOptions;
private final Collection<OfficeData> toOfficeOptions;
private final Collection<ClientData> toClientOptions;
private final Collection<SavingsAccountData> toAccountOptions;
private final Collection<EnumOptionData> toAccountTypeOptions;
private final Collection<PortfolioAccountData> toAccountOptions;
public static AccountTransferData template(final OfficeData fromOffice, final ClientData fromClient,
final SavingsAccountData fromAccount, final LocalDate transferDate, final OfficeData toOffice, final ClientData toClient,
final SavingsAccountData toAccount, final Collection<OfficeData> fromOfficeOptions,
final Collection<ClientData> fromClientOptions, final Collection<SavingsAccountData> fromAccountOptions,
final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final LocalDate transferDate,
final OfficeData toOffice, final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount,
final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions,
final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions,
final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions,
final Collection<SavingsAccountData> toAccountOptions) {
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions) {
final Long id = null;
CurrencyData currency = null;
if (fromAccount != null) {
@@ -56,41 +61,48 @@ public class AccountTransferData {
final BigDecimal transferAmount = BigDecimal.ZERO;
final String transferDescription = null;
final Boolean reversed = null;
return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccount, currency, transferAmount, transferDate,
transferDescription, toOffice, toClient, toAccount, fromOfficeOptions, fromClientOptions, fromAccountOptions,
toOfficeOptions, toClientOptions, toAccountOptions);
return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccountType, fromAccount, currency, transferAmount,
transferDate, transferDescription, toOffice, toClient, toAccountType, toAccount, fromOfficeOptions, fromClientOptions,
fromAccountTypeOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions);
}
public static AccountTransferData instance(final Long id, final Boolean reversed, final LocalDate transferDate,
final CurrencyData currency, final BigDecimal transferAmount, final String transferDescription, final OfficeData fromOffice,
final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final SavingsAccountData fromSavingsAccount,
final SavingsAccountData toSavingsAccount) {
final OfficeData toOffice, final ClientData fromClient, final ClientData toClient, final EnumOptionData fromAccountType,
final PortfolioAccountData fromAccount, final EnumOptionData toAccountType, final PortfolioAccountData toAccount) {
return new AccountTransferData(id, reversed, fromOffice, fromClient, fromSavingsAccount, currency, transferAmount, transferDate,
transferDescription, toOffice, toClient, toSavingsAccount, null, null, null, null, null, null);
return new AccountTransferData(id, reversed, fromOffice, fromClient, fromAccountType, fromAccount, currency, transferAmount,
transferDate, transferDescription, toOffice, toClient, toAccountType, toAccount, null, null, null, null, null, null,
null, null);
}
public static AccountTransferData transferBasicDetails(final Long id, final CurrencyData currency, final BigDecimal transferAmount,
final LocalDate transferDate, final String description, final Boolean reversed) {
return new AccountTransferData(id, reversed, null, null, null, currency, transferAmount, transferDate, description, null, null,
null, null, null, null, null, null, null);
final EnumOptionData fromAccountType = null;
final EnumOptionData toAccountType = null;
return new AccountTransferData(id, reversed, null, null, fromAccountType, null, currency, transferAmount, transferDate,
description, null, null, toAccountType, null, null, null, null, null, null, null, null, null);
}
private AccountTransferData(final Long id, final Boolean reversed, final OfficeData fromOffice, final ClientData fromClient,
final SavingsAccountData fromAccount, final CurrencyData currency, final BigDecimal transferAmount,
final LocalDate transferDate, final String transferDescription, final OfficeData toOffice, final ClientData toClient,
final SavingsAccountData toAccount, final Collection<OfficeData> fromOfficeOptions,
final Collection<ClientData> fromClientOptions, final Collection<SavingsAccountData> fromAccountOptions,
final EnumOptionData fromAccountType, final PortfolioAccountData fromAccount, final CurrencyData currency,
final BigDecimal transferAmount, final LocalDate transferDate, final String transferDescription, final OfficeData toOffice,
final ClientData toClient, final EnumOptionData toAccountType, final PortfolioAccountData toAccount,
final Collection<OfficeData> fromOfficeOptions, final Collection<ClientData> fromClientOptions,
final Collection<EnumOptionData> fromAccountTypeOptions, final Collection<PortfolioAccountData> fromAccountOptions,
final Collection<OfficeData> toOfficeOptions, final Collection<ClientData> toClientOptions,
final Collection<SavingsAccountData> toAccountOptions) {
final Collection<EnumOptionData> toAccountTypeOptions, final Collection<PortfolioAccountData> toAccountOptions) {
this.id = id;
this.reversed = reversed;
this.fromOffice = fromOffice;
this.fromClient = fromClient;
this.fromAccountType = fromAccountType;
this.fromAccount = fromAccount;
this.toOffice = toOffice;
this.toClient = toClient;
this.toAccountType = toAccountType;
this.toAccount = toAccount;
this.currency = currency;
@@ -100,9 +112,11 @@ public class AccountTransferData {
this.fromOfficeOptions = fromOfficeOptions;
this.fromClientOptions = fromClientOptions;
this.fromAccountTypeOptions = fromAccountTypeOptions;
this.fromAccountOptions = fromAccountOptions;
this.toOfficeOptions = toOfficeOptions;
this.toClientOptions = toClientOptions;
this.toAccountTypeOptions = toAccountTypeOptions;
this.toAccountOptions = toAccountOptions;
}
}
@@ -3,21 +3,21 @@
* 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.accounttransfers.data;
package org.mifosplatform.portfolio.account.data;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountTypeParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountTypeParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@@ -0,0 +1,78 @@
/**
* 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.account.data;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
/**
* Immutable data object representing a savings account.
*/
@SuppressWarnings("unused")
public class PortfolioAccountData {
private final Long id;
private final String accountNo;
private final String externalId;
private final Long groupId;
private final String groupName;
private final Long clientId;
private final String clientName;
private final Long productId;
private final String productName;
private final Long fieldOfficerId;
private final String fieldOfficerName;
private final CurrencyData currency;
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);
}
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) {
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;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) { return false; }
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();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(this.id).append(this.accountNo).append(this.productId).append(this.productName).toHashCode();
}
public Long clientId() {
return this.clientId;
}
public CurrencyData currency() {
return this.currency;
}
public String currencyCode() {
return this.currency.code();
}
}
@@ -3,7 +3,7 @@
* 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.accounttransfers.domain;
package org.mifosplatform.portfolio.account.domain;
import java.math.BigDecimal;
import java.util.Date;
@@ -22,6 +22,8 @@ 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.client.domain.Client;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.springframework.data.jpa.domain.AbstractPersistable;
@@ -52,7 +54,7 @@ public class AccountTransfer extends AbstractPersistable<Long> {
@ManyToOne
@JoinColumn(name = "to_savings_account_id", nullable = true)
private SavingsAccount toAccount;
private SavingsAccount toSavingsAccount;
@ManyToOne
@JoinColumn(name = "from_savings_transaction_id", nullable = true)
@@ -62,6 +64,14 @@ public class AccountTransfer extends AbstractPersistable<Long> {
@JoinColumn(name = "to_savings_transaction_id", nullable = true)
private SavingsAccountTransaction toSavingsTransaction;
@ManyToOne
@JoinColumn(name = "to_loan_account_id", nullable = true)
private Loan toLoanAccount;
@ManyToOne
@JoinColumn(name = "to_loan_transaction_id", nullable = true)
private LoanTransaction toLoanTransaction;
@Column(name = "is_reversed", nullable = false)
private final boolean reversed = false;
@@ -83,8 +93,16 @@ 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, withdrawal, deposit,
transactionDate, transactionAmount, description);
return new AccountTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount, null, withdrawal,
deposit, 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);
}
protected AccountTransfer() {
@@ -92,17 +110,20 @@ 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 SavingsAccountTransaction withdrawal, final SavingsAccountTransaction deposit, final LocalDate transactionDate,
final Money transactionAmount, final String description) {
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) {
this.fromOffice = fromOffice;
this.fromClient = fromClient;
this.fromAccount = fromSavingsAccount;
this.toOffice = toOffice;
this.toClient = toClient;
this.toAccount = toSavingsAccount;
this.toSavingsAccount = toSavingsAccount;
this.fromTransaction = withdrawal;
this.toSavingsTransaction = deposit;
this.toLoanAccount = toLoanAccount;
this.toLoanTransaction = loanRepaymentTransaction;
this.date = transactionDate.toDate();
this.currency = transactionAmount.getCurrency();
this.amount = transactionAmount.getAmountDefaultedToNullIfZero();
@@ -3,17 +3,17 @@
* 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.accounttransfers.domain;
package org.mifosplatform.portfolio.account.domain;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDescriptionParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromClientIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromOfficeIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toClientIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toOfficeIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromClientIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromOfficeIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toClientIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toOfficeIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDescriptionParamName;
import java.math.BigDecimal;
@@ -25,6 +25,8 @@ import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountAssembler;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
@@ -55,19 +57,19 @@ public class AccountTransferAssembler {
final JsonElement element = command.parsedJson();
final Long fromOfficeId = fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Long fromOfficeId = this.fromApiJsonHelper.extractLongNamed(fromOfficeIdParamName, element);
final Office fromOffice = this.officeRepository.findOne(fromOfficeId);
final Long fromClientId = fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Long fromClientId = this.fromApiJsonHelper.extractLongNamed(fromClientIdParamName, element);
final Client fromClient = this.clientRepository.findOneWithNotFoundDetection(fromClientId);
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId);
final Long toOfficeId = fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Long toOfficeId = this.fromApiJsonHelper.extractLongNamed(toOfficeIdParamName, element);
final Office toOffice = this.officeRepository.findOne(toOfficeId);
final Long toClientId = fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
final Long toClientId = this.fromApiJsonHelper.extractLongNamed(toClientIdParamName, element);
final Client toClient = this.clientRepository.findOneWithNotFoundDetection(toClientId);
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
@@ -82,4 +84,31 @@ public class AccountTransferAssembler {
return AccountTransfer.savingsToSavingsTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toSavingsAccount,
withdrawal, deposit, transactionDate, transactionMonetaryAmount, description);
}
public AccountTransfer assembleSavingsToLoanTransfer(final JsonCommand command, final SavingsAccount fromSavingsAccount,
final Loan toLoanAccount, final SavingsAccountTransaction withdrawal, final LoanTransaction loanRepaymentTransaction) {
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(fromSavingsAccount.getCurrency(), transactionAmount);
final String description = command.stringValueOfParameterNamed(transferDescriptionParamName);
return AccountTransfer.savingsToLoanTransfer(fromOffice, fromClient, fromSavingsAccount, toOffice, toClient, toLoanAccount,
withdrawal, loanRepaymentTransaction, transactionDate, transactionMonetaryAmount, description);
}
}
@@ -3,7 +3,7 @@
* 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.accounttransfers.domain;
package org.mifosplatform.portfolio.account.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -3,7 +3,7 @@
* 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.accounttransfers.exception;
package org.mifosplatform.portfolio.account.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
@@ -3,12 +3,12 @@
* 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.accounttransfers.handler;
package org.mifosplatform.portfolio.account.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.portfolio.accounttransfers.service.AccountTransfersWritePlatformService;
import org.mifosplatform.portfolio.account.service.AccountTransfersWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -0,0 +1,38 @@
/**
* 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.account.service;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
public class AccountTransferEnumerations {
public static EnumOptionData accountType(final Integer type) {
return accountType(PortfolioAccountType.fromInt(type));
}
public static EnumOptionData accountType(final PortfolioAccountType type) {
EnumOptionData optionData = null;
if (type != null) {
switch (type) {
case INVALID:
break;
case LOAN:
optionData = new EnumOptionData(PortfolioAccountType.LOAN.getValue().longValue(), PortfolioAccountType.LOAN.getCode(),
"Loan Account");
break;
case SAVINGS:
optionData = new EnumOptionData(PortfolioAccountType.SAVINGS.getValue().longValue(),
PortfolioAccountType.SAVINGS.getCode(), "Savings Account");
break;
}
}
return optionData;
}
}
@@ -3,10 +3,10 @@
* 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.accounttransfers.service;
package org.mifosplatform.portfolio.account.service;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.group.service.SearchParameters;
public interface AccountTransfersReadPlatformService {
@@ -3,7 +3,7 @@
* 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.accounttransfers.service;
package org.mifosplatform.portfolio.account.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
@@ -14,6 +14,7 @@ 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;
import org.mifosplatform.infrastructure.core.service.Page;
@@ -22,18 +23,19 @@ import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.office.data.OfficeData;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.accounttransfers.exception.AccountTransferNotFoundException;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.service.SearchParameters;
import org.mifosplatform.portfolio.savings.data.SavingsAccountData;
import org.mifosplatform.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService {
@@ -41,7 +43,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService;
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
private final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService;
// mapper
private final AccountTransfersMapper accountTransfersMapper;
@@ -52,11 +54,11 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
@Autowired
public AccountTransfersReadPlatformServiceImpl(final RoutingDataSource dataSource,
final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService,
final SavingsAccountReadPlatformService savingsAccountReadPlatformService) {
final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.clientReadPlatformService = clientReadPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
this.savingsAccountReadPlatformService = savingsAccountReadPlatformService;
this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService;
this.accountTransfersMapper = new AccountTransfersMapper();
}
@@ -65,30 +67,38 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
public AccountTransferData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId,
final Integer fromAccountType, final Long toOfficeId, final Long toClientId, final Long toAccountId, final Integer toAccountType) {
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
}
Integer mostRelevantToAccountType = toAccountType;
if (mostRelevantToAccountType == null) {
mostRelevantToAccountType = 2; // savings
}
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("transferFromLoanAccount"); }
if (mostRelevantFromAccountType != 2) { throw new UnsupportedCommandException("transferFromLoanAccountNotSupported"); }
// from settings
OfficeData fromOffice = null;
ClientData fromClient = null;
SavingsAccountData fromAccount = null;
PortfolioAccountData fromAccount = null;
OfficeData toOffice = null;
ClientData toClient = null;
SavingsAccountData toAccount = null;
PortfolioAccountData toAccount = null;
// template
Collection<SavingsAccountData> fromAccountOptions = null;
Collection<SavingsAccountData> toAccountOptions = null;
Collection<PortfolioAccountData> fromAccountOptions = null;
Collection<PortfolioAccountData> toAccountOptions = null;
Long mostRelevantFromOfficeId = fromOfficeId;
Long mostRelevantFromClientId = fromClientId;
@@ -96,20 +106,21 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
Long mostRelevantToOfficeId = toOfficeId;
Long mostRelevantToClientId = toClientId;
if (fromAccountId != null && fromAccountType == 2) {
fromAccount = this.savingsAccountReadPlatformService.retrieveOne(fromAccountId);
if (fromAccountId != null && mostRelevantFromAccountType == 2) {
fromAccount = this.portfolioAccountReadPlatformService.retrieveOne(fromAccountId, PortfolioAccountType.SAVINGS.getValue());
// 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();
toClient = fromClient;
fromAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromClientId);
toAccountOptions = new ArrayList<SavingsAccountData>(fromAccountOptions);
toAccountOptions.remove(fromAccount);
fromAccountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(mostRelevantFromAccountType,
mostRelevantFromClientId);
}
Collection<OfficeData> fromOfficeOptions = null;
@@ -121,29 +132,25 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
}
// defaults
LocalDate transferDate = DateUtils.getLocalDateOfTenant();
toOffice = fromOffice;
final LocalDate transferDate = DateUtils.getLocalDateOfTenant();
Collection<OfficeData> toOfficeOptions = fromOfficeOptions;
Collection<ClientData> toClientOptions = fromClientOptions;
Collection<ClientData> toClientOptions = null;
if (toAccountId != null) {
toAccount = this.savingsAccountReadPlatformService.retrieveOne(toAccountId);
// override provided toClient with client of account
if (toAccountId != null && fromAccount != null) {
toAccount = this.portfolioAccountReadPlatformService.retrieveOne(toAccountId, mostRelevantToAccountType, fromAccount.currencyCode());
mostRelevantToClientId = toAccount.clientId();
}
if (mostRelevantToClientId != null && mostRelevantToClientId != mostRelevantFromClientId) {
if (mostRelevantToClientId != null) {
toClient = this.clientReadPlatformService.retrieveOne(mostRelevantToClientId);
mostRelevantToOfficeId = toClient.officeId();
toClientOptions = this.clientReadPlatformService.retrieveAllForLookupByOfficeId(mostRelevantToOfficeId);
toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId);
toAccountOptions.remove(fromAccount);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId);
}
if (mostRelevantToOfficeId != null && mostRelevantToOfficeId != mostRelevantFromOfficeId) {
if (mostRelevantToOfficeId != null) {
toOffice = this.officeReadPlatformService.retrieveOffice(mostRelevantToOfficeId);
toOfficeOptions = this.officeReadPlatformService.retrieveAllOfficesForDropdown();
@@ -151,21 +158,37 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
if (toClientOptions != null && toClientOptions.size() == 1) {
toClient = new ArrayList<ClientData>(toClientOptions).get(0);
toAccountOptions = this.savingsAccountReadPlatformService.retrieveAllForLookup(mostRelevantToClientId);
toAccountOptions.remove(fromAccount);
toAccountOptions = retrieveToAccounts(fromAccount, mostRelevantToAccountType, mostRelevantToClientId);
}
}
return AccountTransferData.template(fromOffice, fromClient, fromAccount, transferDate, toOffice, toClient, toAccount,
fromOfficeOptions, fromClientOptions, fromAccountOptions, toOfficeOptions, toClientOptions, toAccountOptions);
return AccountTransferData.template(fromOffice, fromClient, fromAccountTypeData, fromAccount, transferDate, toOffice, toClient,
toAccountTypeData, toAccount, fromOfficeOptions, fromClientOptions, fromAccountTypeOptions, fromAccountOptions,
toOfficeOptions, toClientOptions, toAccountTypeOptions, toAccountOptions);
}
private Collection<PortfolioAccountData> retrieveToAccounts(final PortfolioAccountData excludeThisAccountFromOptions,
final Integer toAccountType, final Long toClientId) {
final String currencyCode = excludeThisAccountFromOptions != null ? excludeThisAccountFromOptions.currencyCode() : null;
Collection<PortfolioAccountData> accountOptions = this.portfolioAccountReadPlatformService.retrieveAllForLookup(toAccountType,
toClientId, currencyCode);
if (!CollectionUtils.isEmpty(accountOptions)) {
accountOptions.remove(excludeThisAccountFromOptions);
} else {
accountOptions = null;
}
return accountOptions;
}
@Override
public Page<AccountTransferData> retrieveAll(final SearchParameters searchParameters) {
StringBuilder sqlBuilder = new StringBuilder(200);
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
sqlBuilder.append(accountTransfersMapper.schema());
sqlBuilder.append(this.accountTransfersMapper.schema());
if (searchParameters.isOrderByRequested()) {
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
@@ -183,7 +206,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
}
final Object[] objectArray = new Object[2];
int arrayPos = 0;
final int arrayPos = 0;
final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
final String sqlCountRows = "SELECT FOUND_ROWS()";
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlCountRows, sqlBuilder.toString(), finalObjectArray,
@@ -197,7 +220,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
final String sql = "select " + this.accountTransfersMapper.schema() + " where sat.id = ?";
return this.jdbcTemplate.queryForObject(sql, this.accountTransfersMapper, new Object[] { transferId });
} catch (EmptyResultDataAccessException e) {
} catch (final EmptyResultDataAccessException e) {
throw new AccountTransferNotFoundException(transferId);
}
}
@@ -206,13 +229,13 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
private final String schemaSql;
public AccountTransfersMapper() {
public AccountTransfersMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("sat.id as id, sat.is_reversed as isReversed,");
sqlBuilder.append("sat.transaction_date as transferDate, sat.amount as transferAmount,");
sqlBuilder.append("sat.description as transferDescription,");
sqlBuilder
.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits, sat.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("sat.currency_code as currencyCode, sat.currency_digits as currencyDigits,");
sqlBuilder.append("sat.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,");
@@ -221,6 +244,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,");
sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,");
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,");
sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,");
sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,");
@@ -233,6 +257,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
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 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 ");
sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = sat.to_savings_transaction_id ");
@@ -264,30 +289,43 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers
final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId");
final String fromOfficeName = rs.getString("fromOfficeName");
OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null);
final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null);
final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId");
final String toOfficeName = rs.getString("toOfficeName");
OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null);
final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null);
final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId");
final String fromClientName = rs.getString("fromClientName");
ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName);
final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName);
final Long toClientId = JdbcSupport.getLong(rs, "toClientId");
final String toClientName = rs.getString("toClientName");
ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName);
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");
SavingsAccountData fromSavingsAccount = SavingsAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo);
final PortfolioAccountData fromSavingsAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo);
PortfolioAccountData toAccount = null;
EnumOptionData toAccountType = null;
final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId");
final String toSavingsAccountNo = rs.getString("toSavingsAccountNo");
SavingsAccountData toSavingsAccount = SavingsAccountData.lookup(toSavingsAccountId, toSavingsAccountNo);
final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId");
final String toLoanAccountNo = rs.getString("toLoanAccountNo");
if (toSavingsAccountId != null) {
toAccount = PortfolioAccountData.lookup(toSavingsAccountId, toSavingsAccountNo);
toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS);
} else if (toLoanAccountId != null) {
toAccount = PortfolioAccountData.lookup(toLoanAccountId, toLoanAccountNo);
toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN);
}
return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice,
toOffice, fromClient, toClient, fromSavingsAccount, toSavingsAccount);
toOffice, fromClient, toClient, fromAccountType, fromSavingsAccount, toAccountType, toAccount);
}
}
}
@@ -3,7 +3,7 @@
* 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.accounttransfers.service;
package org.mifosplatform.portfolio.account.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
@@ -0,0 +1,153 @@
/**
* 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.account.service;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.fromAccountTypeParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.toAccountTypeParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.account.AccountTransfersApiConstants.transferDateParamName;
import java.math.BigDecimal;
import java.util.Locale;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.AccountTransfersDataValidator;
import org.mifosplatform.portfolio.account.domain.AccountTransfer;
import org.mifosplatform.portfolio.account.domain.AccountTransferAssembler;
import org.mifosplatform.portfolio.account.domain.AccountTransferRepository;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService {
private final AccountTransfersDataValidator accountTransfersDataValidator;
private final AccountTransferAssembler accountTransferAssembler;
private final AccountTransferRepository accountTransferRepository;
private final SavingsAccountAssembler savingsAccountAssembler;
private final SavingsAccountDomainService savingsAccountDomainService;
private final LoanAssembler loanAccountAssembler;
private final LoanAccountDomainService loanAccountDomainService;
@Autowired
public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator,
final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository,
final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService,
final LoanAssembler loanAssembler, final LoanAccountDomainService loanAccountDomainService) {
this.accountTransfersDataValidator = accountTransfersDataValidator;
this.accountTransferAssembler = accountTransferAssembler;
this.accountTransferRepository = accountTransferRepository;
this.savingsAccountAssembler = savingsAccountAssembler;
this.savingsAccountDomainService = savingsAccountDomainService;
this.loanAccountAssembler = loanAssembler;
this.loanAccountDomainService = loanAccountDomainService;
}
@Transactional
@Override
public CommandProcessingResult create(final JsonCommand command) {
this.accountTransfersDataValidator.validate(command);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName);
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
final Integer fromAccountTypeId = command.integerValueSansLocaleOfParameterNamed(fromAccountTypeParamName);
final PortfolioAccountType fromAccountType = PortfolioAccountType.fromInt(fromAccountTypeId);
final Integer toAccountTypeId = command.integerValueSansLocaleOfParameterNamed(toAccountTypeParamName);
final PortfolioAccountType toAccountType = PortfolioAccountType.fromInt(toAccountTypeId);
final PaymentDetail paymentDetail = null;
Long fromSavingsAccountId = null;
Long transferTransactionId = null;
if (isSavingsToSavingsAccountTransfer(fromAccountType, toAccountType)) {
fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId);
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail);
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId);
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail);
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal,
deposit);
this.accountTransferRepository.saveAndFlush(transferTransaction);
transferTransactionId = transferTransaction.getId();
} else if (isSavingsToLoanAccountTransfer(fromAccountType, toAccountType)) {
//
fromSavingsAccountId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsAccountId);
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail);
final Long toLoanAccountId = command.longValueOfParameterNamed(toAccountIdParamName);
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(toLoanAccountId);
final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccountId,
new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null);
final AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command,
fromSavingsAccount, toLoanAccount, withdrawal, loanRepaymentTransaction);
this.accountTransferRepository.saveAndFlush(transferTransaction);
transferTransactionId = transferTransaction.getId();
} else if (isLoanToSavingsAccountTransfer(fromAccountType, toAccountType)) {
// FIXME - kw - ADD overpaid loan to savings account transfer support.
} else {
}
final CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder().withEntityId(transferTransactionId);
if (fromAccountType.isSavingsAccount()) {
builder.withSavingsId(fromSavingsAccountId);
}
return builder.build();
}
private boolean isLoanToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
return fromAccountType.isLoanAccount() && toAccountType.isSavingsAccount();
}
private boolean isSavingsToLoanAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
return fromAccountType.isSavingsAccount() && toAccountType.isLoanAccount();
}
private boolean isSavingsToSavingsAccountTransfer(final PortfolioAccountType fromAccountType, final PortfolioAccountType toAccountType) {
return fromAccountType.isSavingsAccount() && toAccountType.isSavingsAccount();
}
}
@@ -0,0 +1,21 @@
/**
* 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.account.service;
import java.util.Collection;
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
public interface PortfolioAccountReadPlatformService {
PortfolioAccountData retrieveOne(Long accountId, Integer accountTypeId);
PortfolioAccountData retrieveOne(Long accountId, Integer accountTypeId, String currencyCode);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId);
Collection<PortfolioAccountData> retrieveAllForLookup(Integer toAccountType, Long toClientId, String currencyCode);
}
@@ -0,0 +1,248 @@
/**
* 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.account.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.portfolio.account.PortfolioAccountType;
import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
import org.mifosplatform.portfolio.account.exception.AccountTransferNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class PortfolioAccountReadPlatformServiceImpl implements PortfolioAccountReadPlatformService {
private final JdbcTemplate jdbcTemplate;
// mapper
private final PortfolioSavingsAccountMapper savingsAccountMapper;
private final PortfolioLoanAccountMapper loanAccountMapper;
@Autowired
public PortfolioAccountReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.savingsAccountMapper = new PortfolioSavingsAccountMapper();
this.loanAccountMapper = new PortfolioLoanAccountMapper();
}
@Override
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId) {
return retrieveOne(accountId, accountTypeId, null);
}
@Override
public PortfolioAccountData retrieveOne(final Long accountId, final Integer accountTypeId, final String currencyCode) {
Object[] sqlParams = new Object[] {accountId};
PortfolioAccountData accountData = null;
try {
String sql = null;
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
switch (accountType) {
case INVALID:
break;
case LOAN:
sql = "select " + this.loanAccountMapper.schema() + " where la.id = ?";
if (currencyCode != null) {
sql += " and la.currency_code = ?";
sqlParams = new Object[] {accountId, currencyCode};
}
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
break;
case SAVINGS:
sql = "select " + this.savingsAccountMapper.schema() + " where sa.id = ?";
if (currencyCode != null) {
sql += " and la.currency_code = ?";
sqlParams = new Object[] {accountId, currencyCode};
}
accountData = this.jdbcTemplate.queryForObject(sql, this.savingsAccountMapper, sqlParams);
break;
default:
break;
}
} catch (final EmptyResultDataAccessException e) {
throw new AccountTransferNotFoundException(accountId);
}
return accountData;
}
@Override
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId) {
return retrieveAllForLookup(accountTypeId, clientId, null);
}
@Override
public Collection<PortfolioAccountData> retrieveAllForLookup(final Integer accountTypeId, final Long clientId, final String currencyCode) {
Object[] sqlParams = new Object[] {clientId};
Collection<PortfolioAccountData> accounts = null;
String sql = null;
final PortfolioAccountType accountType = PortfolioAccountType.fromInt(accountTypeId);
switch (accountType) {
case INVALID:
break;
case LOAN:
sql = "select " + this.loanAccountMapper.schema() + " where la.client_id = ? and la.loan_status_id = 300";
if (currencyCode != null) {
sql += " and la.currency_code = ?";
sqlParams = new Object[] {clientId, currencyCode};
}
accounts = this.jdbcTemplate.query(sql, this.loanAccountMapper, sqlParams);
break;
case SAVINGS:
sql = "select " + this.savingsAccountMapper.schema() + " where sa.client_id = ? and sa.status_enum = 300";
if (currencyCode != null) {
sql += " and sa.currency_code = ?";
sqlParams = new Object[] {clientId, currencyCode};
}
accounts = this.jdbcTemplate.query(sql, this.savingsAccountMapper, sqlParams);
break;
default:
break;
}
return accounts;
}
private static final class PortfolioSavingsAccountMapper implements RowMapper<PortfolioAccountData> {
private final String schemaSql;
public PortfolioSavingsAccountMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("sa.id as id, sa.account_no as accountNo, sa.external_id as externalId, ");
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
sqlBuilder.append("s.id as fieldOfficerId, s.display_name as fieldOfficerName, ");
sqlBuilder.append("sa.currency_code as currencyCode, sa.currency_digits as currencyDigits,");
sqlBuilder.append("sa.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
sqlBuilder.append("from m_savings_account sa ");
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
sqlBuilder.append("left join m_client c ON c.id = sa.client_id ");
sqlBuilder.append("left join m_group g ON g.id = sa.group_id ");
sqlBuilder.append("left join m_staff s ON s.id = sa.field_officer_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo");
final String externalId = rs.getString("externalId");
final Long groupId = JdbcSupport.getLong(rs, "groupId");
final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
final String clientName = rs.getString("clientName");
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
final String fieldOfficerName = rs.getString("fieldOfficerName");
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 inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
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);
}
}
private static final class PortfolioLoanAccountMapper implements RowMapper<PortfolioAccountData> {
private final String schemaSql;
public PortfolioLoanAccountMapper() {
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("la.id as id, la.account_no as accountNo, la.external_id as externalId, ");
sqlBuilder.append("c.id as clientId, c.display_name as clientName, ");
sqlBuilder.append("g.id as groupId, g.display_name as groupName, ");
sqlBuilder.append("lp.id as productId, lp.name as productName, ");
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("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol ");
sqlBuilder.append("from m_loan la ");
sqlBuilder.append("join m_product_loan lp ON la.product_id = lp.id ");
sqlBuilder.append("join m_currency curr on curr.code = la.currency_code ");
sqlBuilder.append("left join m_client c ON c.id = la.client_id ");
sqlBuilder.append("left join m_group g ON g.id = la.group_id ");
sqlBuilder.append("left join m_staff s ON s.id = la.loan_officer_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public PortfolioAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String accountNo = rs.getString("accountNo");
final String externalId = rs.getString("externalId");
final Long groupId = JdbcSupport.getLong(rs, "groupId");
final String groupName = rs.getString("groupName");
final Long clientId = JdbcSupport.getLong(rs, "clientId");
final String clientName = rs.getString("clientName");
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final Long fieldOfficerId = rs.getLong("fieldOfficerId");
final String fieldOfficerName = rs.getString("fieldOfficerName");
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 inMulitplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
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);
}
}
}
@@ -1,91 +0,0 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.accounttransfers.service;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.fromAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.toAccountIdParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferAmountParamName;
import static org.mifosplatform.portfolio.accounttransfers.AccountTransfersApiConstants.transferDateParamName;
import java.math.BigDecimal;
import java.util.Locale;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransfersDataValidator;
import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransfer;
import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferAssembler;
import org.mifosplatform.portfolio.accounttransfers.domain.AccountTransferRepository;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountTransfersWritePlatformServiceImpl implements AccountTransfersWritePlatformService {
private final AccountTransfersDataValidator accountTransfersDataValidator;
private final AccountTransferAssembler accountTransferAssembler;
private final AccountTransferRepository accountTransferRepository;
private final SavingsAccountAssembler savingsAccountAssembler;
private final SavingsAccountDomainService savingsAccountDomainService;
@Autowired
public AccountTransfersWritePlatformServiceImpl(final AccountTransfersDataValidator accountTransfersDataValidator,
final AccountTransferAssembler accountTransferAssembler, final AccountTransferRepository accountTransferRepository,
final SavingsAccountAssembler savingsAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService) {
this.accountTransfersDataValidator = accountTransfersDataValidator;
this.accountTransferAssembler = accountTransferAssembler;
this.accountTransferRepository = accountTransferRepository;
this.savingsAccountAssembler = savingsAccountAssembler;
this.savingsAccountDomainService = savingsAccountDomainService;
}
@Transactional
@Override
public CommandProcessingResult create(final JsonCommand command) {
this.accountTransfersDataValidator.validate(command);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(transferDateParamName);
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(transferAmountParamName);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
// check fromAccountType settings to see if its a savings account
// withdrawal or a loan account transfer
final Long fromSavingsId = command.longValueOfParameterNamed(fromAccountIdParamName);
final SavingsAccount fromSavingsAccount = this.savingsAccountAssembler.assembleFrom(fromSavingsId);
final PaymentDetail paymentDetail = null;
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount, fmt,
transactionDate, transactionAmount, paymentDetail);
final Long toSavingsId = command.longValueOfParameterNamed(toAccountIdParamName);
final SavingsAccount toSavingsAccount = this.savingsAccountAssembler.assembleFrom(toSavingsId);
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount, fmt, transactionDate,
transactionAmount, paymentDetail);
AccountTransfer transferTransaction = this.accountTransferAssembler.assembleSavingsToSavingsTransfer(command, withdrawal, deposit);
this.accountTransferRepository.saveAndFlush(transferTransaction);
return new CommandProcessingResultBuilder() //
.withSavingsId(fromSavingsId) //
.withEntityId(transferTransaction.getId()) //
.build();
}
}
@@ -16,7 +16,6 @@ import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.client.command.ClientCommand;
import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -0,0 +1,18 @@
/**
* 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.math.BigDecimal;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
public interface LoanAccountDomainService {
LoanTransaction makeRepayment(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
}
@@ -0,0 +1,166 @@
/**
* 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.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.organisation.holiday.domain.Holiday;
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
import org.mifosplatform.organisation.monetary.domain.Money;
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
import org.mifosplatform.portfolio.loanaccount.service.LoanAssembler;
import org.mifosplatform.portfolio.note.domain.Note;
import org.mifosplatform.portfolio.note.domain.NoteRepository;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private final LoanAssembler loanAccountAssembler;
private final LoanRepository loanRepository;
private final LoanTransactionRepository loanTransactionRepository;
private final ConfigurationDomainService configurationDomainService;
private final HolidayRepository holidayRepository;
private final WorkingDaysRepositoryWrapper workingDaysRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final NoteRepository noteRepository;
@Autowired
public LoanAccountDomainServiceJpa(final LoanAssembler loanAccountAssembler, final LoanRepository loanRepository,
final LoanTransactionRepository loanTransactionRepository, final NoteRepository noteRepository,
final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
final WorkingDaysRepositoryWrapper workingDaysRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService) {
this.loanAccountAssembler = loanAccountAssembler;
this.loanRepository = loanRepository;
this.loanTransactionRepository = loanTransactionRepository;
this.noteRepository = noteRepository;
this.configurationDomainService = configurationDomainService;
this.holidayRepository = holidayRepository;
this.workingDaysRepository = workingDaysRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
}
@Transactional
@Override
public LoanTransaction makeRepayment(final Long accountId, final CommandProcessingResultBuilder builderResult,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
final String txnExternalId) {
final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
checkClientOrGroupActive(loan);
// TODO: Is it required to validate transaction date with meeting dates
// if repayments is synced with meeting?
/*
* if(loan.isSyncDisbursementWithMeeting()){ // validate actual
* disbursement date against meeting date CalendarInstance
* calendarInstance =
* this.calendarInstanceRepository.findCalendarInstaneByLoanId
* (loan.getId(), CalendarEntityType.LOANS.getValue());
* this.loanEventApiJsonValidator
* .validateRepaymentDateWithMeetingDate(transactionDate,
* calendarInstance); }
*/
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, 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();
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday,
holidays, workingDays, allowTransactionsOnNonWorkingDay);
this.loanTransactionRepository.save(newRepaymentTransaction);
/***
* TODO Vishwas Batch save is giving me a
* HibernateOptimisticLockingFailureException, looping and saving for
* the time being, not a major issue for now as this loop is entered
* only in edge cases (when a payment is made before the latest payment
* recorded against the loan)
***/
if (changedTransactionDetail != null) {
for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
this.loanTransactionRepository.save(loanTransaction);
}
}
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
final Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText);
this.noteRepository.save(note);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
builderResult.withEntityId(newRepaymentTransaction.getId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()); //
return newRepaymentTransaction;
}
private void postJournalEntries(final Loan loanAccount, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = loanAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = loanAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
private void checkClientOrGroupActive(final Loan loan) {
final Client client = loan.client();
if (client != null) {
if (client.isNotActive()) { throw new ClientNotActiveException(client.getId()); }
}
final Group group = loan.group();
if (group != null) {
if (group.isNotActive()) { throw new GroupNotActiveException(group.getId()); }
}
}
}
@@ -43,6 +43,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
@@ -65,6 +66,8 @@ import com.google.gson.JsonElement;
@Service
public class LoanAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final LoanRepositoryWrapper loanRepository;
private final LoanProductRepository loanProductRepository;
private final ClientRepositoryWrapper clientRepository;
private final GroupRepository groupRepository;
@@ -75,7 +78,6 @@ public class LoanAssembler {
private final LoanScheduleAssembler loanScheduleAssembler;
private final LoanChargeAssembler loanChargeAssembler;
private final CollateralAssembler loanCollateralAssembler;
private final FromJsonHelper fromApiJsonHelper;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final HolidayRepository holidayRepository;
@@ -83,8 +85,9 @@ public class LoanAssembler {
private final WorkingDaysRepositoryWrapper workingDaysRepository;
@Autowired
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
final ClientRepositoryWrapper clientRepository, final GroupRepository groupRepository, final FundRepository fundRepository,
public LoanAssembler(final FromJsonHelper fromApiJsonHelper, final LoanRepositoryWrapper loanRepository,
final LoanProductRepository loanProductRepository, final ClientRepositoryWrapper clientRepository,
final GroupRepository groupRepository, final FundRepository fundRepository,
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
final StaffRepository staffRepository, final CodeValueRepositoryWrapper codeValueRepository,
final LoanScheduleAssembler loanScheduleAssembler, final LoanChargeAssembler loanChargeAssembler,
@@ -93,6 +96,7 @@ public class LoanAssembler {
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
final WorkingDaysRepositoryWrapper workingDaysRepository) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.loanRepository = loanRepository;
this.loanProductRepository = loanProductRepository;
this.clientRepository = clientRepository;
this.groupRepository = groupRepository;
@@ -110,24 +114,32 @@ public class LoanAssembler {
this.workingDaysRepository = workingDaysRepository;
}
public Loan assembleFrom(final Long accountId) {
final Loan loanAccount = this.loanRepository.findOneWithNotFoundDetection(accountId);
loanAccount.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
this.loanRepaymentScheduleTransactionProcessorFactory);
return loanAccount;
}
public Loan assembleFrom(final JsonCommand command, final AppUser currentUser) {
final JsonElement element = command.parsedJson();
final Long clientId = fromApiJsonHelper.extractLongNamed("clientId", element);
final Long groupId = fromApiJsonHelper.extractLongNamed("groupId", element);
final Long clientId = this.fromApiJsonHelper.extractLongNamed("clientId", element);
final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element);
return assembleApplication(element, clientId, groupId, currentUser);
}
private Loan assembleApplication(final JsonElement element, final Long clientId, final Long groupId, final AppUser currentUser) {
final String accountNo = fromApiJsonHelper.extractStringNamed("accountNo", element);
final Long productId = fromApiJsonHelper.extractLongNamed("productId", element);
final Long fundId = fromApiJsonHelper.extractLongNamed("fundId", element);
final Long loanOfficerId = fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
final Long transactionProcessingStrategyId = fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
final Long loanPurposeId = fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
final Boolean syncDisbursementWithMeeting = fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
final String accountNo = this.fromApiJsonHelper.extractStringNamed("accountNo", element);
final Long productId = this.fromApiJsonHelper.extractLongNamed("productId", element);
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
final Long loanOfficerId = this.fromApiJsonHelper.extractLongNamed("loanOfficerId", element);
final Long transactionProcessingStrategyId = this.fromApiJsonHelper.extractLongNamed("transactionProcessingStrategyId", element);
final Long loanPurposeId = this.fromApiJsonHelper.extractLongNamed("loanPurposeId", element);
final Boolean syncDisbursementWithMeeting = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
@@ -137,11 +149,11 @@ public class LoanAssembler {
final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy = findStrategyByIdIfProvided(transactionProcessingStrategyId);
CodeValue loanPurpose = null;
if (loanPurposeId != null) {
loanPurpose = codeValueRepository.findOneWithNotFoundDetection(loanPurposeId);
loanPurpose = this.codeValueRepository.findOneWithNotFoundDetection(loanPurposeId);
}
final Set<LoanCollateral> collateral = this.loanCollateralAssembler.fromParsedJson(element);
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element);
for (LoanCharge loanCharge : loanCharges) {
for (final LoanCharge loanCharge : loanCharges) {
if (!loanProduct.hasCurrencyCodeOf(loanCharge.currencyCode())) {
final String errorMessage = "Charge and Loan must have the same currency.";
throw new InvalidCurrencyException("loanCharge", "attach.to.loan", errorMessage);
@@ -155,14 +167,12 @@ public class LoanAssembler {
final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
final String loanTypeParameterName = "loanType";
final String loanTypeStr = fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
final EnumOptionData loanType = AccountEnumerations.loanType(loanTypeStr);
if (clientId != null) {
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
if (client.isNotActive()) {
throw new ClientNotActiveException(clientId);
}
if (client.isNotActive()) { throw new ClientNotActiveException(clientId); }
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral);
@@ -171,9 +181,7 @@ public class LoanAssembler {
if (groupId != null) {
group = this.groupRepository.findOne(groupId);
if (group == null) { throw new GroupNotFoundException(groupId); }
if (group.isNotActive()) {
throw new GroupNotActiveException(groupId);
}
if (group.isNotActive()) { throw new GroupNotActiveException(groupId); }
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting);
@@ -184,11 +192,12 @@ public class LoanAssembler {
if (!group.hasClientAsMember(client)) { throw new ClientNotInGroupException(clientId, groupId); }
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, syncDisbursementWithMeeting);
loanProduct, fund, loanOfficer, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
syncDisbursementWithMeeting);
}
final String externalId = fromApiJsonHelper.extractStringNamed("externalId", element);
final LocalDate submittedOnDate = fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
final String externalId = this.fromApiJsonHelper.extractStringNamed("externalId", element);
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed("submittedOnDate", element);
if (loanApplication == null) { throw new IllegalStateException("No loan application exists for either a client or group (or both)."); }
loanApplication.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper,
@@ -196,11 +205,13 @@ public class LoanAssembler {
final LoanApplicationTerms loanApplicationTerms = this.loanScheduleAssembler.assembleLoanTerms(element);
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(), loanApplicationTerms.getExpectedDisbursementDate().toDate());
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loanApplication.getOfficeId(),
loanApplicationTerms.getExpectedDisbursementDate().toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms, isHolidayEnabled, holidays, workingDays, element);
final LoanScheduleModel loanScheduleModel = this.loanScheduleAssembler.assembleLoanScheduleFrom(loanApplicationTerms,
isHolidayEnabled, holidays, workingDays, element);
loanApplication.loanApplicationSubmittal(currentUser, loanScheduleModel, loanApplicationTerms, defaultLoanLifecycleStateMachine(),
submittedOnDate, externalId, allowTransactionsOnHoliday, holidays, workingDays, allowTransactionsOnNonWorkingDay);
@@ -208,7 +219,7 @@ public class LoanAssembler {
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
@@ -256,7 +267,7 @@ public class LoanAssembler {
loanApplication.getExpectedDisbursedOnLocalDate().toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
loanApplication.validateExpectedDisbursementForHolidayAndNonWorkingDay(workingDays, allowTransactionsOnHoliday, holidays,
allowTransactionsOnNonWorkingDay);
}
@@ -61,17 +61,15 @@ import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand;
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
import org.mifosplatform.portfolio.loanaccount.domain.DefaultLoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
import org.mifosplatform.portfolio.loanaccount.domain.LoanChargeRepository;
import org.mifosplatform.portfolio.loanaccount.domain.LoanLifecycleStateMachine;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
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.exception.LoanDisbursalException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
@@ -99,6 +97,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanEventApiJsonValidator loanEventApiJsonValidator;
private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
private final LoanRepository loanRepository;
private final LoanAccountDomainService loanAccountDomainService;
private final NoteRepository noteRepository;
private final LoanTransactionRepository loanTransactionRepository;
private final LoanAssembler loanAssembler;
@@ -106,8 +105,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanChargeRepository loanChargeRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository;
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final LoanSummaryWrapper loanSummaryWrapper;
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
private final LoanScheduleGeneratorFactory loanScheduleFactory;
private final CalendarInstanceRepository calendarInstanceRepository;
private final PaymentDetailWritePlatformService paymentDetailWritePlatformService;
@@ -120,20 +117,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
final LoanEventApiJsonValidator loanEventApiJsonValidator,
final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, final LoanAssembler loanAssembler,
final LoanRepository loanRepository, final LoanTransactionRepository loanTransactionRepository,
final NoteRepository noteRepository, final ChargeRepositoryWrapper chargeRepository,
final LoanChargeRepository loanChargeRepository, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanSummaryWrapper loanSummaryWrapper,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final LoanRepository loanRepository, final LoanAccountDomainService loanAccountDomainService,
final LoanTransactionRepository loanTransactionRepository, final NoteRepository noteRepository,
final ChargeRepositoryWrapper chargeRepository, final LoanChargeRepository loanChargeRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService,
final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository,
final PaymentDetailWritePlatformService paymentDetailWritePlatformService, final HolidayRepository holidayRepository,
final ConfigurationDomainService configurationDomainService, final WorkingDaysRepositoryWrapper workingDaysRepository,
final LoanProductReadPlatformService loanProductReadPlatformService) {
this.context = context;
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
this.loanAssembler = loanAssembler;
this.loanRepository = loanRepository;
this.loanAccountDomainService = loanAccountDomainService;
this.loanTransactionRepository = loanTransactionRepository;
this.noteRepository = noteRepository;
this.chargeRepository = chargeRepository;
@@ -141,8 +138,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.applicationCurrencyRepository = applicationCurrencyRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.loanUpdateCommandFromApiJsonDeserializer = loanUpdateCommandFromApiJsonDeserializer;
this.loanSummaryWrapper = loanSummaryWrapper;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
this.loanScheduleFactory = loanScheduleFactory;
this.calendarInstanceRepository = calendarInstanceRepository;
this.paymentDetailWritePlatformService = paymentDetailWritePlatformService;
@@ -153,7 +148,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
}
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
final List<LoanStatus> allowedLoanStatuses = Arrays.asList(LoanStatus.values());
return new DefaultLoanLifecycleStateMachine(allowedLoanStatuses);
}
@@ -161,14 +156,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult disburseLoan(final Long loanId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateDisbursement(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
//check for product mix validations
// check for product mix validations
checkForProductMixRestrictions(loan);
// validate actual disbursement date against meeting date
@@ -188,13 +183,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
// Recalculate first repayment date based in actual disbursement date.
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final LocalDate calculatedRepaymentsStartingFromDate = getCalculatedRepaymentsStartingFromDate(actualDisbursementDate, loan, calendarInstance);
final LocalDate calculatedRepaymentsStartingFromDate = getCalculatedRepaymentsStartingFromDate(actualDisbursementDate, loan,
calendarInstance);
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate());
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
actualDisbursementDate.toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
@@ -209,7 +206,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
final Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
@@ -229,7 +226,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private LocalDate getCalculatedRepaymentsStartingFromDate(final LocalDate actualDisbursementDate, final Loan loan,
final CalendarInstance calendarInstance) {
final Calendar calendar = (calendarInstance == null) ? null : calendarInstance.getCalendar();
final Calendar calendar = calendarInstance == null ? null : calendarInstance.getCalendar();
LocalDate calculatedRepaymentsStartingFromDate = loan.getExpectedFirstRepaymentOnDate();
if (calendar != null) {// sync repayments
@@ -246,8 +243,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Integer repayEvery = repaymentScheduleDetails.getRepayEvery();
final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails
.getRepaymentPeriodFrequencyType());
calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, repayEvery,
frequency);
calculatedRepaymentsStartingFromDate = CalendarHelper.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate,
repayEvery, frequency);
}
}
}
@@ -260,16 +257,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Transactional
@Override
public Map<String, Object> bulkLoanDisbursal(final JsonCommand command, final CollectionSheetBulkDisbursalCommand bulkDisbursalCommand) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
Map<String, Object> changes = new LinkedHashMap<String, Object>();
final SingleDisbursalCommand[] disbursalCommand = bulkDisbursalCommand.getDisburseTransactions();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
if (disbursalCommand == null) { return changes; }
for (int i = 0; i < disbursalCommand.length; i++) {
SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i];
final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i];
final Loan loan = retrieveLoanBy(singleLoanDisbursalCommand.getLoanId());
final Loan loan = this.loanAssembler.assembleFrom(singleLoanDisbursalCommand.getLoanId());
checkClientOrGroupActive(loan);
final MonetaryCurrency currency = loan.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
@@ -277,7 +274,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
// Bulk disbursement should happen on meeting date (mostly from
// collection sheet).
@@ -288,7 +285,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate firstRepaymentOnDate = null;
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate");
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), actualDisbursementDate.toDate());
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
actualDisbursementDate.toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
@@ -303,7 +301,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
final Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
@@ -317,9 +315,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult undoLoanDisbursal(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
removeLoanCycle(loan);
final List<Long> existingTransactionIds = new ArrayList<Long>();
@@ -330,7 +326,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanNote(loan, noteText);
final Note note = Note.loanNote(loan, noteText);
this.noteRepository.save(note);
}
@@ -339,7 +335,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
return new CommandProcessingResultBuilder() //
@@ -357,7 +353,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult makeLoanRepayment(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json());
@@ -376,10 +372,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("note", noteText);
}
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
CommandProcessingResultBuilder commandProcessingResultBuilder = saveLoanRepayment(loanId, paymentDetail, transactionAmount,
transactionDate, noteText, txnExternalId);
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
this.loanAccountDomainService.makeRepayment(loanId, commandProcessingResultBuilder, transactionDate, transactionAmount,
paymentDetail, noteText, txnExternalId);
return commandProcessingResultBuilder.withCommandId(command.commandId()) //
.withLoanId(loanId) //
@@ -387,86 +384,27 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.build();
}
private CommandProcessingResultBuilder saveLoanRepayment(final Long loanId, final PaymentDetail paymentDetail,
final BigDecimal transactionAmount, final LocalDate transactionDate, final String noteText, final String txnExternalId) {
final Loan loan = retrieveLoanBy(loanId);
checkClientOrGroupActive(loan);
// TODO: Is it required to validate transaction date with meeting dates
// if repayments is synced with meeting?
/*
* if(loan.isSyncDisbursementWithMeeting()){ // validate actual
* disbursement date against meeting date CalendarInstance
* calendarInstance =
* this.calendarInstanceRepository.findCalendarInstaneByLoanId
* (loan.getId(), CalendarEntityType.LOANS.getValue());
* this.loanEventApiJsonValidator
* .validateRepaymentDateWithMeetingDate(transactionDate,
* calendarInstance); }
*/
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount);
final LoanTransaction newRepaymentTransaction = LoanTransaction.repayment(repaymentAmount, 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();
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, allowTransactionsOnHoliday,
holidays, workingDays, allowTransactionsOnNonWorkingDay);
this.loanTransactionRepository.save(newRepaymentTransaction);
/***
* TODO Vishwas Batch save is giving me a
* HibernateOptimisticLockingFailureException, looping and saving for
* the time being, not a major issue for now as this loop is entered
* only in edge cases (when a payment is made before the latest payment
* recorded against the loan)
***/
if (changedTransactionDetail != null) {
for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
this.loanTransactionRepository.save(loanTransaction);
}
}
this.loanRepository.save(loan);
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.loanTransactionNote(loan, newRepaymentTransaction, noteText);
this.noteRepository.save(note);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
return new CommandProcessingResultBuilder() //
.withEntityId(newRepaymentTransaction.getId()) //
.withOfficeId(loan.getOfficeId()) //
.withClientId(loan.getClientId()) //
.withGroupId(loan.getGroupId()); //
}
@Transactional
@Override
public Map<String, Object> makeLoanBulkRepayment(final CollectionSheetBulkRepaymentCommand bulkRepaymentCommand) {
context.authenticatedUser();
this.context.authenticatedUser();
final SingleRepaymentCommand[] repaymentCommand = bulkRepaymentCommand.getLoanTransactions();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
if (repaymentCommand == null) return changes;
if (repaymentCommand == null) { return changes; }
for (SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) {
for (final SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) {
/****
* 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(), null);
final PaymentDetail paymentDetail = null;
final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
this.loanAccountDomainService.makeRepayment(singleLoanRepaymentCommand.getLoanId(), commandProcessingResultBuilder,
bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail,
bulkRepaymentCommand.getNote(), null);
changes.put("bulkTransations", singleLoanRepaymentCommand);
}
return changes;
@@ -476,11 +414,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult adjustLoanTransaction(final Long loanId, final Long transactionId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateTransaction(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final LoanTransaction transactionToAdjust = this.loanTransactionRepository.findOne(transactionId);
@@ -500,14 +438,16 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
PaymentDetail paymentDetail = paymentDetailWritePlatformService.createPaymentDetail(command, changes);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate, txnExternalId);
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createPaymentDetail(command, changes);
LoanTransaction newTransactionDetail = LoanTransaction.repayment(transactionAmountAsMoney, paymentDetail, transactionDate,
txnExternalId);
if (transactionToAdjust.isInterestWaiver()) {
newTransactionDetail = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate);
}
final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate());
final List<Holiday> holidays = this.holidayRepository
.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate.toDate());
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final boolean allowTransactionsOnNonWorkingDay = this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled();
@@ -517,7 +457,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
if (paymentDetail != null) {
paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
}
this.loanTransactionRepository.save(newTransactionDetail);
}
@@ -530,7 +470,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
* payment recorded against the loan)
***/
if (changedTransactionDetail != null) {
for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
this.loanTransactionRepository.save(loanTransaction);
}
}
@@ -570,7 +510,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult waiveInterestOnLoan(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateTransaction(command.json());
@@ -582,14 +522,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final List<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate);
final LoanTransaction waiveInterestTransaction = LoanTransaction.waiver(loan, transactionAmountAsMoney, transactionDate);
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds);
@@ -604,7 +544,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
* recorded against the loan)
***/
if (changedTransactionDetail != null) {
for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
this.loanTransactionRepository.save(loanTransaction);
}
}
@@ -643,7 +583,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
changes.put("locale", command.locale());
changes.put("dateFormat", command.dateFormat());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
removeLoanCycle(loan);
@@ -679,11 +619,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Override
public CommandProcessingResult closeLoan(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
@@ -742,11 +682,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
@Transactional
@Override
public CommandProcessingResult closeAsRescheduled(final Long loanId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.loanEventApiJsonValidator.validateTransactionWithNoAmount(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
removeLoanCycle(loan);
@@ -785,7 +725,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanEventApiJsonValidator.validateAddLoanCharge(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final Long chargeDefinitionId = command.longValueOfParameterNamed("chargeId");
@@ -818,7 +758,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
* latest payment recorded against the loan)
***/
if (changedTransactionDetail != null) {
for (LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
for (final LoanTransaction loanTransaction : changedTransactionDetail.getNewTransactions()) {
this.loanTransactionRepository.save(loanTransaction);
}
}
@@ -846,7 +786,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanEventApiJsonValidator.validateUpdateOfLoanCharge(command.json());
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
@@ -876,7 +816,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.context.authenticatedUser();
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
@@ -921,7 +861,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.context.authenticatedUser();
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
final LoanCharge loanCharge = retrieveLoanChargeBy(loanId, loanChargeId);
@@ -943,13 +883,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
.build();
}
private Loan retrieveLoanBy(final Long loanId) {
final Loan loan = this.loanRepository.findOne(loanId);
if (loan == null) { throw new LoanNotFoundException(loanId); }
loan.setHelpers(defaultLoanLifecycleStateMachine(), this.loanSummaryWrapper, this.loanRepaymentScheduleTransactionProcessorFactory);
return loan;
}
private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) {
final LoanCharge loanCharge = this.loanChargeRepository.findOne(loanChargeId);
if (loanCharge == null) { throw new LoanChargeNotFoundException(loanChargeId); }
@@ -969,11 +902,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Long fromLoanOfficerId = command.longValueOfParameterNamed("fromLoanOfficerId");
final Long toLoanOfficerId = command.longValueOfParameterNamed("toLoanOfficerId");
final Staff fromLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId);
final Staff toLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId);
final Staff fromLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId);
final Staff toLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId);
final LocalDate dateOfLoanOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate");
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loanId, fromLoanOfficerId); }
@@ -1004,12 +937,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate dateOfLoanOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate");
final Staff fromLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId);
final Staff toLoanOfficer = loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId);
final Staff fromLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(fromLoanOfficerId);
final Staff toLoanOfficer = this.loanAssembler.findLoanOfficerByIdIfProvided(toLoanOfficerId);
for (final String loanIdString : loanIds) {
final Long loanId = Long.valueOf(loanIdString);
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
if (!loan.hasLoanOfficer(fromLoanOfficer)) { throw new LoanOfficerAssignmentException(loanId, fromLoanOfficerId); }
@@ -1037,7 +970,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final LocalDate dateOfLoanOfficerunAssigned = command.localDateValueOfParameterNamed("unassignedDate");
final Loan loan = retrieveLoanBy(loanId);
final Loan loan = this.loanAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan);
if (loan.getLoanOfficer() == null) { throw new LoanOfficerUnassignmentException(loanId); }
@@ -1064,31 +997,33 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
final Map<String, Object> accountingBridgeData = loan.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
}
@Transactional
@Override
public void applyMeetingDateChanges(final Calendar calendar, Collection<CalendarInstance> loanCalendarInstances) {
public void applyMeetingDateChanges(final Calendar calendar, final Collection<CalendarInstance> loanCalendarInstances) {
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final WorkingDays workingDays = this.workingDaysRepository.findOne();
final Collection<Integer> loanStatuses = new ArrayList<Integer>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
final Collection<Integer> loanTypes = new ArrayList<Integer>(Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue()));
final Collection<Integer> loanTypes = new ArrayList<Integer>(
Arrays.asList(AccountType.GROUP.getValue(), AccountType.JLG.getValue()));
final Collection<Long> loanIds = new ArrayList<Long>(loanCalendarInstances.size());
// loop through loanCalendarInstances to get loan ids
for (CalendarInstance calendarInstance : loanCalendarInstances) {
for (final CalendarInstance calendarInstance : loanCalendarInstances) {
loanIds.add(calendarInstance.getEntityId());
}
final List<Loan> loans = this.loanRepository.findByIdsAndLoanStatusAndLoanType(loanIds, loanStatuses, loanTypes);
List<Holiday> holidays = null;
// loop through each loan to reschedule the repayment dates
for (Loan loan : loans) {
for (final Loan loan : loans) {
if (loan != null) {
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, holidays, workingDays);
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled,
holidays, workingDays);
this.loanRepository.save(loan);
}
}
@@ -1247,32 +1182,33 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
public void applyHolidaysToLoans() {
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
if(!isHolidayEnabled) return;
if (!isHolidayEnabled) { return; }
final Collection<Integer> loanStatuses = new ArrayList<Integer>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
// Get all Holidays which are yet to applied to loans
List<Holiday> holidays = this.holidayRepository.findUnprocessed();
final List<Holiday> holidays = this.holidayRepository.findUnprocessed();
// Loop through all holidays
for (Holiday holiday : holidays) {
for (final Holiday holiday : holidays) {
// All offices to which holiday is applied
final Set<Office> offices = holiday.getOffices();
Collection<Long> officeIds = new ArrayList<Long>(offices.size());
for (Office office : offices) {
final Collection<Long> officeIds = new ArrayList<Long>(offices.size());
for (final Office office : offices) {
officeIds.add(office.getId());
}
// get all loans
List<Loan> loans = new ArrayList<Loan>();
final List<Loan> loans = new ArrayList<Loan>();
// get all individual and jlg loans
loans.addAll(this.loanRepository.findByClientOfficeIdsAndLoanStatus(officeIds, loanStatuses));
//FIXME: AA optimize to get all client and group loans belongs to a office id
//get all group loans
// FIXME: AA optimize to get all client and group loans belongs to a
// office id
// get all group loans
loans.addAll(this.loanRepository.findByGroupOfficeIdsAndLoanStatus(officeIds, loanStatuses));
for (Loan loan : loans) {
for (final Loan loan : loans) {
// apply holiday
loan.applyHolidayToRepaymentScheduleDates(holiday);
}
@@ -1302,7 +1238,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (!CollectionUtils.isEmpty(activeLoansLoanProductIds)) {
final Collection<LoanProductData> restrictedPrdouctsList = this.loanProductReadPlatformService
.retrieveRestrictedProductsForMix(productId);
for (LoanProductData restrictedProduct : restrictedPrdouctsList) {
for (final LoanProductData restrictedProduct : restrictedPrdouctsList) {
if (activeLoansLoanProductIds.contains(restrictedProduct.getId())) { throw new LoanDisbursalException(productName,
restrictedProduct.getName()); }
}
@@ -11,7 +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.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
/**
@@ -26,7 +26,7 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
import org.mifosplatform.organisation.monetary.data.CurrencyData;
import org.mifosplatform.organisation.staff.data.StaffData;
import org.mifosplatform.organisation.staff.service.StaffReadPlatformService;
import org.mifosplatform.portfolio.accounttransfers.data.AccountTransferData;
import org.mifosplatform.portfolio.account.data.AccountTransferData;
import org.mifosplatform.portfolio.client.data.ClientData;
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
@@ -151,14 +151,14 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final Long savingsId = account.getId();
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
} catch (DataAccessException dve) {
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
} catch (final DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
@@ -230,15 +230,15 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
} catch (DataAccessException dve) {
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
} catch (final DataAccessException dve) {
handleDataIntegrityIssues(command, dve);
return new CommandProcessingResult(Long.valueOf(-1));
}
@@ -254,33 +254,33 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
if (account.isNotSubmittedAndPendingApproval()) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.deleteApplicationAction);
.resource(SAVINGS_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.deleteApplicationAction);
baseDataValidator.reset().parameter(SavingsApiConstants.activatedOnDateParamName)
.failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state");
.failWithCodeNoParameterAddedToErrorCode("not.in.submittedandpendingapproval.state");
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
List<Note> relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId);
final List<Note> relatedNotes = this.noteRepository.findBySavingsAccountId(savingsId);
this.noteRepository.deleteInBatch(relatedNotes);
this.savingAccountRepository.delete(account);
return new CommandProcessingResultBuilder() //
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
.withEntityId(savingsId) //
.withOfficeId(account.officeId()) //
.withClientId(account.clientId()) //
.withGroupId(account.groupId()) //
.withSavingsId(savingsId) //
.build();
}
@Transactional
@Override
public CommandProcessingResult approveApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateApproval(command.json());
@@ -293,28 +293,28 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
final Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult undoApplicationApproval(final Long savingsId, final JsonCommand command) {
context.authenticatedUser();
this.context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateForUndo(command.json());
@@ -327,28 +327,28 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
final Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult rejectApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateRejection(command.json());
@@ -361,27 +361,27 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
final Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
@Transactional
@Override
public CommandProcessingResult applicantWithdrawsFromApplication(final Long savingsId, final JsonCommand command) {
final AppUser currentUser = context.authenticatedUser();
final AppUser currentUser = this.context.authenticatedUser();
this.savingsAccountApplicationTransitionApiJsonValidator.validateApplicantWithdrawal(command.json());
@@ -395,21 +395,21 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) {
Note note = Note.savingNote(savingsAccount, noteText);
final Note note = Note.savingNote(savingsAccount, noteText);
changes.put("note", noteText);
this.noteRepository.save(note);
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
.withCommandId(command.commandId()) //
.withEntityId(savingsId) //
.withOfficeId(savingsAccount.officeId()) //
.withClientId(savingsAccount.clientId()) //
.withGroupId(savingsAccount.groupId()) //
.withSavingsId(savingsId) //
.with(changes) //
.build();
}
private void postJournalEntries(final SavingsAccount savingsAccount, final List<Long> existingTransactionIds,
@@ -420,7 +420,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
private void checkClientOrGroupActive(final SavingsAccount account) {