wip:enabling transfer of saving accounts

This commit is contained in:
Vishwas Babu A J
2013-09-10 17:13:53 +05:30
parent b9f929aec2
commit 190b9a58b3
22 changed files with 333 additions and 48 deletions
@@ -108,7 +108,7 @@ public class AccountingConstants {
/*** Accounting placeholders for cash based accounting for savings products ***/
public static enum CASH_ACCOUNTS_FOR_SAVINGS {
SAVINGS_REFERENCE(1), SAVINGS_CONTROL(2), INTEREST_ON_SAVINGS(3), INCOME_FROM_FEES(4);
SAVINGS_REFERENCE(1), SAVINGS_CONTROL(2), INTEREST_ON_SAVINGS(3), INCOME_FROM_FEES(4), TRANSFERS_SUSPENSE(10);
private final Integer value;
@@ -145,7 +145,8 @@ public class AccountingConstants {
public static enum SAVINGS_PRODUCT_ACCOUNTING_PARAMS {
SAVINGS_REFERENCE("savingsReferenceAccountId"), SAVINGS_CONTROL("savingsControlAccountId"), INCOME_FROM_FEES(
"incomeFromFeeAccountId"), INTEREST_ON_SAVINGS("interestOnSavingsAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING(
"paymentChannelToFundSourceMappings"), PAYMENT_TYPE("paymentTypeId"), FUND_SOURCE("fundSourceAccountId");
"paymentChannelToFundSourceMappings"), PAYMENT_TYPE("paymentTypeId"), FUND_SOURCE("fundSourceAccountId"), TRANSFERS_SUSPENSE(
"transfersInSuspenseAccountId");
private final String value;
@@ -72,6 +72,21 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
transactionDate, amount, isReversal);
}
/** Handle Transfers proposal **/
else if (savingsTransactionDTO.getTransactionType().isInitiateTransfer()) {
helper.createCashBasedJournalEntriesAndReversalsForSavings(office, CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL,
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE, savingsProductId, paymentTypeId, savingsId, transactionId,
transactionDate, amount, isReversal);
}
/** Handle Transfer Withdrawal or Acceptance **/
else if (savingsTransactionDTO.getTransactionType().isWithdrawTransfer()
|| savingsTransactionDTO.getTransactionType().isApproveTransfer()) {
helper.createCashBasedJournalEntriesAndReversalsForSavings(office, CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL, savingsProductId, paymentTypeId, savingsId, transactionId,
transactionDate, amount, isReversal);
}
}
}
}
@@ -156,6 +156,11 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue())
.value(savingsReferenceAccountId).notNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue())
@@ -169,6 +169,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), glAccountId);
} else if (glAccountForSavings.equals(CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES)) {
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), glAccountId);
} else if (glAccountForSavings.equals(CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE)) {
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), glAccountId);
} else if (glAccountForSavings.equals(CASH_ACCOUNTS_FOR_SAVINGS.INTEREST_ON_SAVINGS)) {
accountMappingDetails.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), glAccountId);
}
@@ -163,6 +163,9 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue());
savingsProductToGLAccountMappingHelper.saveSavingsToLiabilityAccountMapping(element,
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), savingProductId,
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.getValue());
// advanced accounting mappings
savingsProductToGLAccountMappingHelper.savePaymentChannelToFundSourceMappings(command, element, savingProductId, null);
@@ -106,6 +106,8 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element);
final Long savingsControlId = this.fromApiJsonHelper.extractLongNamed(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(),
element);
final Long transfersInSuspenseAccountId = this.fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
switch (accountingRuleType) {
case NONE:
@@ -115,6 +117,7 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
changes.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(), savingsReferenceId);
changes.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), interestOnSavingsId);
changes.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(), incomeFromFeesId);
changes.put(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), transfersInSuspenseAccountId);
break;
case ACCRUAL_BASED:
break;
@@ -156,6 +159,9 @@ public class SavingsProductToGLAccountMappingHelper extends ProductToGLAccountMa
mergeSavingsToLiabilityAccountMappingChanges(element, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(),
savingsProductId, CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.toString(), changes);
mergeSavingsToLiabilityAccountMappingChanges(element, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
savingsProductId, CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.TRANSFERS_SUSPENSE.toString(), changes);
break;
case ACCRUAL_BASED:
break;
@@ -125,18 +125,18 @@ public enum LoanStatus {
}
public boolean isTransferInProgress() {
return this.isTransferInProgress();
return this.value.equals(LoanStatus.TRANSFER_IN_PROGRESS.getValue());
}
public boolean isTransferOnHold() {
return this.isTransferOnHold();
return this.value.equals(LoanStatus.TRANSFER_ON_HOLD.getValue());
}
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
public boolean isOverpaid(){
public boolean isOverpaid() {
return this.value.equals(LoanStatus.OVERPAID.getValue());
}
}
@@ -138,7 +138,7 @@ public final class LoanTransaction extends AbstractPersistable<Long> {
}
public static LoanTransaction withdrawTransfer(final Office office, final Loan loan, final LocalDate transferDate) {
return new LoanTransaction(loan, office, LoanTransactionType.APPROVE_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(),
return new LoanTransaction(loan, office, LoanTransactionType.WITHDRAW_TRANSFER.getValue(), transferDate.toDateMidnight().toDate(),
loan.getSummary().getTotalOutstanding(), loan.getSummary().getTotalPrincipalOutstanding(), loan.getSummary()
.getTotalInterestOutstanding(), loan.getSummary().getTotalFeeChargesOutstanding(), loan.getSummary()
.getTotalPenaltyChargesOutstanding(), false, null);
@@ -357,7 +357,7 @@ public class LoanEnumerations {
break;
case TRANSFER_ON_HOLD:
optionData = new LoanStatusEnumData(LoanStatus.TRANSFER_ON_HOLD.getValue().longValue(),
LoanStatus.TRANSFER_ON_HOLD.getCode(), "Transfer in hold");
LoanStatus.TRANSFER_ON_HOLD.getCode(), "Transfer on hold");
break;
default:
break;
@@ -18,7 +18,11 @@ public enum SavingsAccountTransactionType {
WITHDRAWAL(2, "savingsAccountTransactionType.withdrawal"), //
INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), //
WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"), //
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee");
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee"), //
INITIATE_TRANSFER(12, "savingsAccountTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
REJECT_TRANSFER(15, "savingsAccountTransactionType.rejectTransfer");
private final Integer value;
private final String code;
@@ -57,6 +61,18 @@ public enum SavingsAccountTransactionType {
case 5:
loanTransactionType = SavingsAccountTransactionType.ANNUAL_FEE;
break;
case 12:
loanTransactionType = SavingsAccountTransactionType.INITIATE_TRANSFER;
break;
case 13:
loanTransactionType = SavingsAccountTransactionType.APPROVE_TRANSFER;
break;
case 14:
loanTransactionType = SavingsAccountTransactionType.WITHDRAW_TRANSFER;
break;
case 15:
loanTransactionType = SavingsAccountTransactionType.REJECT_TRANSFER;
break;
}
return loanTransactionType;
}
@@ -105,7 +105,8 @@ public class SavingsApiConstants {
annualFeeOnMonthDayParamName, accountingRuleParamName, SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(),
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue()));
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue()));
/**
* These parameters will match the class level parameters of
@@ -27,9 +27,14 @@ public class SavingsAccountStatusEnumData {
private final boolean active;
@SuppressWarnings("unused")
private final boolean closed;
@SuppressWarnings("unused")
private final boolean transferInProgress;
@SuppressWarnings("unused")
private final boolean transferOnHold;
public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval,
final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed) {
final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed,
final boolean transferInProgress, final boolean transferOnHold) {
this.id = id;
this.code = code;
this.value = value;
@@ -39,6 +44,8 @@ public class SavingsAccountStatusEnumData {
this.withdrawnByApplicant = withdrawnByApplicant;
this.active = active;
this.closed = closed;
this.transferInProgress = transferInProgress;
this.transferOnHold = transferOnHold;
}
public Long id() {
@@ -22,6 +22,10 @@ public class SavingsAccountTransactionEnumData {
private final boolean withdrawal;
private final boolean interestPosting;
private final boolean feeDeduction;
private final boolean initiateTransfer;
private final boolean approveTransfer;
private final boolean withdrawTransfer;
private final boolean rejectTransfer;
public SavingsAccountTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
@@ -32,6 +36,10 @@ public class SavingsAccountTransactionEnumData {
this.interestPosting = Long.valueOf(SavingsAccountTransactionType.INTEREST_POSTING.getValue()).equals(this.id);
this.feeDeduction = Long.valueOf(SavingsAccountTransactionType.ANNUAL_FEE.getValue()).equals(this.id)
|| Long.valueOf(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue()).equals(this.id);
this.initiateTransfer = Long.valueOf(SavingsAccountTransactionType.INITIATE_TRANSFER.getValue()).equals(this.id);
this.approveTransfer = Long.valueOf(SavingsAccountTransactionType.APPROVE_TRANSFER.getValue()).equals(this.id);
this.withdrawTransfer = Long.valueOf(SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue()).equals(this.id);
this.rejectTransfer = Long.valueOf(SavingsAccountTransactionType.REJECT_TRANSFER.getValue()).equals(this.id);
}
public boolean isDeposit() {
@@ -54,4 +62,20 @@ public class SavingsAccountTransactionEnumData {
return this.feeDeduction;
}
public boolean isInitiateTransfer() {
return this.initiateTransfer;
}
public boolean isApproveTransfer() {
return this.approveTransfer;
}
public boolean isWithdrawTransfer() {
return this.withdrawTransfer;
}
public boolean isRejectTransfer() {
return this.rejectTransfer;
}
}
@@ -174,11 +174,12 @@ public class SavingsProductDataValidator {
.zeroOrPositiveAmount();
}
}
if(this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers).ignoreIfNull()
.validateForBooleanValue();
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName,
element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
@@ -216,6 +217,11 @@ public class SavingsProductDataValidator {
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue())
.value(savingsReferenceAccountId).notNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).notNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue())
@@ -326,11 +332,12 @@ public class SavingsProductDataValidator {
baseDataValidator.reset().parameter(withdrawalFeeTypeParamName).value(withdrawalFeeType).ignoreIfNull()
.isOneOfTheseValues(1, 2);
}
if(this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName, element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers).ignoreIfNull()
.validateForBooleanValue();
if (this.fromApiJsonHelper.parameterExists(withdrawalFeeForTransfersParamName, element)) {
final Boolean isWithdrawalFeeApplicableForTransfers = fromApiJsonHelper.extractBooleanNamed(withdrawalFeeForTransfersParamName,
element);
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
.ignoreIfNull().validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(annualFeeAmountParamName, element)) {
@@ -353,6 +360,11 @@ public class SavingsProductDataValidator {
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue())
.value(savingsReferenceAccountId).ignoreIfNull().integerGreaterThanZero();
final Long transfersInSuspenseAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue())
.value(transfersInSuspenseAccountId).ignoreIfNull().integerGreaterThanZero();
final Long interestOnSavingsAccountId = fromApiJsonHelper.extractLongNamed(
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue(), element);
baseDataValidator.reset().parameter(SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_SAVINGS.getValue())
@@ -1178,7 +1178,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return accountingBridgeData;
}
private Collection<Long> findExistingTransactionIds() {
public Collection<Long> findExistingTransactionIds() {
final Collection<Long> ids = new ArrayList<Long>();
@@ -1189,7 +1189,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return ids;
}
private Collection<Long> findExistingReversedTransactionIds() {
public Collection<Long> findExistingReversedTransactionIds() {
final Collection<Long> ids = new ArrayList<Long>();
@@ -1787,4 +1787,17 @@ public class SavingsAccount extends AbstractPersistable<Long> {
public LocalDate getClosedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.closedOnDate), null);
}
public SavingsAccountSummary getSummary() {
return this.summary;
}
public List<SavingsAccountTransaction> getTransactions() {
return this.transactions;
}
public void setStatus(Integer status) {
this.status = status;
}
}
@@ -14,6 +14,8 @@ public enum SavingsAccountStatusType {
SUBMITTED_AND_PENDING_APPROVAL(100, "savingsAccountStatusType.submitted.and.pending.approval"), //
APPROVED(200, "savingsAccountStatusType.approved"), //
ACTIVE(300, "savingsAccountStatusType.active"), //
TRANSFER_IN_PROGRESS(303, "savingsAccountStatusType.transfer.in.progress"), //
TRANSFER_ON_HOLD(304, "savingsAccountStatusType.transfer.on.hold"), //
WITHDRAWN_BY_APPLICANT(400, "savingsAccountStatusType.withdrawn.by.applicant"), //
REJECTED(500, "savingsAccountStatusType.rejected"), //
CLOSED(600, "savingsAccountStatusType.closed");
@@ -34,6 +36,12 @@ public enum SavingsAccountStatusType {
case 300:
enumeration = SavingsAccountStatusType.ACTIVE;
break;
case 303:
enumeration = SavingsAccountStatusType.TRANSFER_IN_PROGRESS;
break;
case 304:
enumeration = SavingsAccountStatusType.TRANSFER_ON_HOLD;
break;
case 400:
enumeration = SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT;
break;
@@ -87,4 +95,16 @@ public enum SavingsAccountStatusType {
public boolean isClosed() {
return this.value.equals(SavingsAccountStatusType.CLOSED.getValue()) || isRejected() || isApplicationWithdrawnByApplicant();
}
public boolean isTransferInProgress() {
return this.value.equals(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
}
public boolean isTransferOnHold() {
return this.value.equals(SavingsAccountStatusType.TRANSFER_ON_HOLD.getValue());
}
public boolean isUnderTransfer() {
return isTransferInProgress() || isTransferOnHold();
}
}
@@ -79,4 +79,9 @@ public final class SavingsAccountSummary {
public Money getAccountBalance(final MonetaryCurrency currency) {
return Money.of(currency, this.accountBalance);
}
public BigDecimal getAccountBalance() {
return this.accountBalance;
}
}
@@ -117,6 +117,30 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
isReversed);
}
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed);
}
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed);
}
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, amount, isReversed);
@@ -124,11 +148,16 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Money amount, final boolean isReversed) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, amount.getAmount(), isReversed);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final BigDecimal amount, final boolean isReversed) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
this.dateOf = transactionLocalDate.toDate();
this.amount = amount.getAmount();
this.amount = amount;
this.reversed = isReversed;
this.paymentDetail = paymentDetail;
@@ -8,6 +8,9 @@ package org.mifosplatform.portfolio.savings.service;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
public interface SavingsAccountWritePlatformService {
@@ -28,6 +31,14 @@ public interface SavingsAccountWritePlatformService {
void postInterestForAccounts();
CommandProcessingResult adjustSavingsTransaction(Long savingsId, Long transactionId, JsonCommand command);
CommandProcessingResult close(Long savingsId, JsonCommand command);
SavingsAccountTransaction initiateSavingsTransfer(Long accountId, LocalDate transferDate);
SavingsAccountTransaction withdrawSavingsTransfer(Long accountId, LocalDate transferDate);
void rejectSavingsTransfer(Long accountId);
SavingsAccountTransaction acceptSavingsTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice, Staff staff);
}
@@ -34,6 +34,8 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext
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.office.domain.Office;
import org.mifosplatform.organisation.staff.domain.Staff;
import org.mifosplatform.portfolio.client.domain.Client;
import org.mifosplatform.portfolio.client.exception.ClientNotActiveException;
import org.mifosplatform.portfolio.group.domain.Group;
@@ -304,7 +306,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN);
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
if(account.isNotActive()){
if (account.isNotActive()) {
throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction);
}
account.undoTransaction(transactionId, reversedTransactionIds);
@@ -342,7 +344,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate today = DateUtils.getLocalDateOfTenant();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
if(account.isNotActive()){
if (account.isNotActive()) {
throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction);
}
@@ -357,23 +359,23 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN);
account.undoTransaction(transactionId, existingReversedTransactionIds);
//for undo withdrawal fee
// for undo withdrawal fee
SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository.findOneByIdAndSavingsAccountId(
transactionId+1, savingsId);
if(nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()){
account.undoTransaction(transactionId+1, existingReversedTransactionIds);
transactionId + 1, savingsId);
if (nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()) {
account.undoTransaction(transactionId + 1, existingReversedTransactionIds);
}
SavingsAccountTransaction transaction = null;
if (savingsAccountTransaction.isDeposit()) {
SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
existingTransactionIds, existingReversedTransactionIds,paymentDetail);
SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
existingTransactionIds, existingReversedTransactionIds, paymentDetail);
transaction = account.deposit(transactionDTO);
} else {
SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
existingTransactionIds, existingReversedTransactionIds, paymentDetail);
transaction = account.withdraw(transactionDTO , true);
transaction = account.withdraw(transactionDTO, true);
}
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
@@ -455,4 +457,75 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
.with(changes) //
.build();
}
@Override
public SavingsAccountTransaction initiateSavingsTransfer(Long accountId, LocalDate transferDate) {
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final List<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount,
savingsAccount.office(), transferDate);
savingsAccount.getTransactions().add(newTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
this.savingsAccountTransactionRepository.save(newTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
return newTransferTransaction;
}
@Override
public SavingsAccountTransaction withdrawSavingsTransfer(Long accountId, LocalDate transferDate) {
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final List<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount,
savingsAccount.office(), transferDate);
savingsAccount.getTransactions().add(withdrawtransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
this.savingsAccountTransactionRepository.save(withdrawtransferTransaction);
this.savingAccountRepository.save(savingsAccount);
postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
return withdrawtransferTransaction;
}
@Override
public void rejectSavingsTransfer(Long accountId) {
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_ON_HOLD.getValue());
this.savingAccountRepository.save(savingsAccount);
}
@Override
public SavingsAccountTransaction acceptSavingsTransfer(Long accountId, LocalDate transferDate, Office acceptedInOffice,
Staff fieldOfficer) {
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId);
final List<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount,
acceptedInOffice, transferDate);
savingsAccount.getTransactions().add(acceptTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
if (fieldOfficer != null) {
savingsAccount.update(fieldOfficer);
}
this.savingsAccountTransactionRepository.save(acceptTransferTransaction);
this.savingAccountRepository.save(savingsAccount);
postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
return acceptTransferTransaction;
}
}
@@ -100,46 +100,61 @@ public class SavingsEnumerations {
final boolean isWithdrawnByApplicant = type.isApplicationWithdrawnByApplicant();
final boolean isActive = type.isActive();
final boolean isClosed = type.isClosed();
final boolean isTransferInProgress = type.isTransferInProgress();
final boolean isTransferOnHold = type.isTransferOnHold();
SavingsAccountStatusEnumData optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
switch (type) {
case INVALID:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case SUBMITTED_AND_PENDING_APPROVAL:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue()
.longValue(), SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval",
submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed);
submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed,
isTransferInProgress, isTransferOnHold);
break;
case REJECTED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.REJECTED.getValue().longValue(),
SavingsAccountStatusType.REJECTED.getCode(), "Rejected", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case WITHDRAWN_BY_APPLICANT:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue().longValue(),
SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getCode(), "Withdrawn by applicant", submittedAndPendingApproval,
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed);
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case APPROVED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.APPROVED.getValue().longValue(),
SavingsAccountStatusType.APPROVED.getCode(), "Approved", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case ACTIVE:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(),
SavingsAccountStatusType.ACTIVE.getCode(), "Active", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case CLOSED:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(),
SavingsAccountStatusType.CLOSED.getCode(), "Closed", submittedAndPendingApproval, isApproved, isRejected,
isWithdrawnByApplicant, isActive, isClosed);
isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case TRANSFER_IN_PROGRESS:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(),
SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getCode(), "Transfer in progress", submittedAndPendingApproval,
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
case TRANSFER_ON_HOLD:
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(),
SavingsAccountStatusType.TRANSFER_ON_HOLD.getCode(), "Transfer in progress", submittedAndPendingApproval,
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isTransferInProgress, isTransferOnHold);
break;
default:
break;
}
return optionData;
@@ -35,7 +35,9 @@ import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
import org.mifosplatform.portfolio.note.service.NoteWritePlatformService;
import org.mifosplatform.portfolio.savings.domain.SavingsAccount;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository;
import org.mifosplatform.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.mifosplatform.portfolio.transfer.api.TransferApiConstants;
import org.mifosplatform.portfolio.transfer.data.TransfersDataValidator;
import org.mifosplatform.portfolio.transfer.exception.ClientNotAwaitingTransferApprovalException;
@@ -58,6 +60,7 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
private final CalendarInstanceRepository calendarInstanceRepository;
private final GroupRepositoryWrapper groupRepository;
private final LoanWritePlatformService loanWritePlatformService;
private final SavingsAccountWritePlatformService savingsAccountWritePlatformService;
private final LoanRepository loanRepository;
private final SavingsAccountRepository savingsAccountRepository;
private final TransfersDataValidator transfersDataValidator;
@@ -70,7 +73,8 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
final LoanWritePlatformService loanWritePlatformService, final GroupRepositoryWrapper groupRepository,
final LoanRepository loanRepository, final TransfersDataValidator transfersDataValidator,
final NoteWritePlatformService noteWritePlatformService, final StaffRepositoryWrapper staffRepositoryWrapper,
final SavingsAccountRepository savingsAccountRepository) {
final SavingsAccountRepository savingsAccountRepository,
final SavingsAccountWritePlatformService savingsAccountWritePlatformService) {
this.clientRepository = clientRepository;
this.officeRepository = officeRepository;
this.calendarInstanceRepository = calendarInstanceRepository;
@@ -81,6 +85,7 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
this.noteWritePlatformService = noteWritePlatformService;
this.staffRepositoryWrapper = staffRepositoryWrapper;
this.savingsAccountRepository = savingsAccountRepository;
this.savingsAccountWritePlatformService = savingsAccountWritePlatformService;
}
@Override
@@ -345,8 +350,30 @@ public class TransferWritePlatformServiceJpaRepositoryImpl implements TransferWr
}
/*** Handle Active Savings (Currently throw and exception) ***/
if (savingsAccountRepository.doNonClosedSavingAccountsExistForClient(client.getId())) { throw new TransferNotSupportedException(
TRANSFER_NOT_SUPPORTED_REASON.ACTIVE_SAVINGS_ACCOUNT, client.getId()); }
if (savingsAccountRepository.doNonClosedSavingAccountsExistForClient(client.getId())) {
// get each individual saving account for the client
for (SavingsAccount savingsAccount : savingsAccountRepository.findSavingAccountByClientId(client.getId())) {
if (!savingsAccount.isClosed()) {
switch (transferEventType) {
case ACCEPTANCE:
this.savingsAccountWritePlatformService.acceptSavingsTransfer(savingsAccount.getId(),
DateUtils.getLocalDateOfTenant(), destinationOffice, staff);
break;
case PROPOSAL:
this.savingsAccountWritePlatformService.initiateSavingsTransfer(savingsAccount.getId(),
DateUtils.getLocalDateOfTenant());
break;
case REJECTION:
this.savingsAccountWritePlatformService.withdrawSavingsTransfer(savingsAccount.getId(),
DateUtils.getLocalDateOfTenant());
break;
case WITHDRAWAL:
this.savingsAccountWritePlatformService.withdrawSavingsTransfer(savingsAccount.getId(),
DateUtils.getLocalDateOfTenant());
}
}
}
}
switch (transferEventType) {
case ACCEPTANCE: