From 4ee40b7f5794a312254957700cba7f7741deaf18 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Thu, 26 Sep 2013 19:23:02 +0530 Subject: [PATCH] cleaning up accounting integration for savings --- .../data/SavingsAccountTransactionDTO.java | 16 +- .../savings/domain/SavingsAccount.java | 77 +++----- .../SavingsAccountDomainServiceJpa.java | 32 ++-- ...WritePlatformServiceJpaRepositoryImpl.java | 173 ++++++++++-------- ...WritePlatformServiceJpaRepositoryImpl.java | 28 +-- 5 files changed, 146 insertions(+), 180 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java index e6d8b49de..5fdeeb71a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/data/SavingsAccountTransactionDTO.java @@ -1,7 +1,6 @@ package org.mifosplatform.portfolio.savings.data; import java.math.BigDecimal; -import java.util.List; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; @@ -12,18 +11,13 @@ public class SavingsAccountTransactionDTO { private final DateTimeFormatter formatter; private final LocalDate transactionDate; private final BigDecimal transactionAmount; - private final List existingTransactionIds; - private final List existingReversedTransactionIds; private final PaymentDetail paymentDetail; public SavingsAccountTransactionDTO(final DateTimeFormatter formatter, final LocalDate transactionDate, - final BigDecimal transactionAmount, final List existingTransactionIds, final List existingReversedTransactionIds, - final PaymentDetail paymentDetail) { + final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { this.formatter = formatter; this.transactionDate = transactionDate; this.transactionAmount = transactionAmount; - this.existingTransactionIds = existingTransactionIds; - this.existingReversedTransactionIds = existingReversedTransactionIds; this.paymentDetail = paymentDetail; } @@ -39,14 +33,6 @@ public class SavingsAccountTransactionDTO { return this.transactionAmount; } - public List getExistingTransactionIds() { - return this.existingTransactionIds; - } - - public List getExistingReversedTransactionIds() { - return this.existingReversedTransactionIds; - } - public PaymentDetail getPaymentDetail() { return this.paymentDetail; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index b8a45edf0..7f9c390a2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -376,16 +376,13 @@ public class SavingsAccount extends AbstractPersistable { return SavingsAccountStatusType.fromInt(this.status).isClosed(); } - public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final List existingTransactionIds, - final List existingReversedTransactionIds) { + public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate) { final List postingPeriods = calculateInterestUsing(mc, interestPostingUpToDate); Money interestPostedToDate = Money.zero(this.currency); boolean recalucateDailyBalanceDetails = false; - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); for (final PostingPeriod interestPostingPeriod : postingPeriods) { @@ -608,9 +605,6 @@ public class SavingsAccount extends AbstractPersistable { } validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_DEPOSIT, transactionDTO.getTransactionDate()); - transactionDTO.getExistingTransactionIds().addAll(findExistingTransactionIds()); - transactionDTO.getExistingReversedTransactionIds().addAll(findExistingReversedTransactionIds()); - final Money amount = Money.of(this.currency, transactionDTO.getTransactionAmount()); final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(this, office(), transactionDTO.getPaymentDetail(), @@ -683,9 +677,6 @@ public class SavingsAccount extends AbstractPersistable { } validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_WITHDRAWAL, transactionDTO.getTransactionDate()); - transactionDTO.getExistingTransactionIds().addAll(findExistingTransactionIds()); - transactionDTO.getExistingReversedTransactionIds().addAll(findExistingReversedTransactionIds()); - final Money transactionAmountMoney = Money.of(this.currency, transactionDTO.getTransactionAmount()); final SavingsAccountTransaction transaction = SavingsAccountTransaction.withdrawal(this, office(), transactionDTO.getPaymentDetail(), transactionDTO.getTransactionDate(), transactionAmountMoney); @@ -733,7 +724,7 @@ public class SavingsAccount extends AbstractPersistable { return transactionBeforeLastInterestPosting; } - + public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount) { final List transactionsSortedByDate = retreiveListOfTransactions(); Money runningBalance = Money.zero(this.currency); @@ -779,8 +770,7 @@ public class SavingsAccount extends AbstractPersistable { } public SavingsAccountTransaction addAnnualFee(final MathContext mc, final DateTimeFormatter formatter, - final LocalDate annualFeeTransactionDate, final LocalDate today, final List existingTransactionIds, - final List existingReversedTransactionIds) { + final LocalDate annualFeeTransactionDate, final LocalDate today) { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -828,9 +818,6 @@ public class SavingsAccount extends AbstractPersistable { } validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_PAY_CHARGE, annualFeeTransactionDate); - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - final Money annualFee = Money.of(this.currency, this.annualFeeAmount); final SavingsAccountTransaction annualFeeTransaction = SavingsAccountTransaction.annualFee(this, office(), annualFeeTransactionDate, annualFee); @@ -1188,8 +1175,8 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - public Map deriveAccountingBridgeData(final CurrencyData currencyData, final List existingTransactionIds, - final List existingReversedTransactionIds) { + public Map deriveAccountingBridgeData(final CurrencyData currencyData, final Set existingTransactionIds, + final Set existingReversedTransactionIds) { final Map accountingBridgeData = new LinkedHashMap(); accountingBridgeData.put("savingsId", getId()); @@ -1466,7 +1453,7 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } - public void undoTransaction(final Long transactionId, final List reversedTransactionIds) { + public void undoTransaction(final Long transactionId) { SavingsAccountTransaction transactionToUndo = null; for (final SavingsAccountTransaction transaction : this.transactions) { @@ -1483,8 +1470,6 @@ public class SavingsAccount extends AbstractPersistable { validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, transactionToUndo.transactionLocalDate()); transactionToUndo.reverse(); - reversedTransactionIds.add(transactionId); - if (transactionToUndo.isAnnualFee()) { this.annualFeeNextDueDate = findLatestAnnualFeeTransactionDueDate(); if (this.annualFeeNextDueDate == null) { @@ -1499,9 +1484,9 @@ public class SavingsAccount extends AbstractPersistable { final Set chargesPaidBy = transactionToUndo.getSavingsAccountChargesPaid(); for (final SavingsAccountChargePaidBy savingsAccountChargePaidBy : chargesPaidBy) { final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge(); - if(transactionToUndo.isCharge()){ + if (transactionToUndo.isCharge()) { chargeToUndo.undoPayment(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); - }else if (transactionToUndo.isWaiveCharge()){ + } else if (transactionToUndo.isWaiveCharge()) { chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); } } @@ -1650,8 +1635,7 @@ public class SavingsAccount extends AbstractPersistable { return actualChanges; } - public Map activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, - final List existingTransactionIds, final List existingReversedTransactionIds) { + public Map activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { final Map actualChanges = new LinkedHashMap(); @@ -1734,7 +1718,7 @@ public class SavingsAccount extends AbstractPersistable { final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance); if (minRequiredOpeningBalance.isGreaterThanZero()) { final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, activationDate, - minRequiredOpeningBalance.getAmount(), existingTransactionIds, existingReversedTransactionIds, null); + minRequiredOpeningBalance.getAmount(), null); deposit(transactionDTO); // no openingBalance concept supported yet but probably will to @@ -1912,12 +1896,11 @@ public class SavingsAccount extends AbstractPersistable { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("delete.transaction.invalid.account.is.active"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + this.charges.remove(charge); } - public void waiveCharge(final Long savingsAccountChargeId, final List existingTransactionIds, - final List existingReversedTransactionIds) { + public void waiveCharge(final Long savingsAccountChargeId) { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -1944,9 +1927,6 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - // waive charge final Money amountWaived = savingsAccountCharge.waive(getCurrency()); @@ -1972,16 +1952,16 @@ public class SavingsAccount extends AbstractPersistable { final LocalDate chargeDueDate = savingsAccountCharge.getDueLocalDate(); - if (savingsAccountCharge.isOnSpecifiedDueDate()){ - if(getActivationLocalDate() != null && chargeDueDate.isBefore(getActivationLocalDate())){ + if (savingsAccountCharge.isOnSpecifiedDueDate()) { + if (getActivationLocalDate() != null && chargeDueDate.isBefore(getActivationLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); + .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); throw new PlatformApiDataValidationException(dataValidationErrors); - }else if(getSubmittedOnLocalDate() != null && chargeDueDate.isBefore(getSubmittedOnLocalDate())){ + } else if (getSubmittedOnLocalDate() != null && chargeDueDate.isBefore(getSubmittedOnLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("before.submittedOnDate"); + .failWithCodeNoParameterAddedToErrorCode("before.submittedOnDate"); throw new PlatformApiDataValidationException(dataValidationErrors); - } + } } validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLY_CHARGE, chargeDueDate); @@ -1992,7 +1972,7 @@ public class SavingsAccount extends AbstractPersistable { } public void payCharge(final SavingsAccountCharge savingsAccountCharge, final BigDecimal amountPaid, final LocalDate transactionDate, - final DateTimeFormatter formatter, final List existingTransactionIds, final List existingReversedTransactionIds) { + final DateTimeFormatter formatter) { final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -2008,21 +1988,21 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - if (savingsAccountCharge.isOnSpecifiedDueDate()){ - if(getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())){ + if (savingsAccountCharge.isOnSpecifiedDueDate()) { + if (getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); + .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); throw new PlatformApiDataValidationException(dataValidationErrors); - }else if(getSubmittedOnLocalDate() != null && transactionDate.isBefore(getSubmittedOnLocalDate())){ + } else if (getSubmittedOnLocalDate() != null && transactionDate.isBefore(getSubmittedOnLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.before.submittedOnDate"); + .failWithCodeNoParameterAddedToErrorCode("transaction.before.submittedOnDate"); throw new PlatformApiDataValidationException(dataValidationErrors); } } - - if (DateUtils.isInFuture(transactionDate)){ + + if (DateUtils.isInFuture(transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate"); + .failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate"); throw new PlatformApiDataValidationException(dataValidationErrors); } @@ -2041,9 +2021,6 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - savingsAccountCharge.pay(getCurrency(), amountPaidMoney); handlePayChargeTransactions(savingsAccountCharge, amountPaidMoney, transactionDate); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java index ac76d853c..b7c474c6a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountDomainServiceJpa.java @@ -7,9 +7,9 @@ package org.mifosplatform.portfolio.savings.domain; import java.math.BigDecimal; import java.math.MathContext; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; @@ -49,16 +49,17 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final boolean applyWithdrawFee) { - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, - existingTransactionIds, existingReversedTransactionIds, paymentDetail); + paymentDetail); final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, applyWithdrawFee); final MathContext mc = MathContext.DECIMAL64; if (account.isBeforeLastPostingPeriod(transactionDate)) { final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); } else { final LocalDate today = DateUtils.getLocalDateOfTenant(); account.calculateInterestUsing(mc, today); @@ -77,16 +78,17 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail) { - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, - existingTransactionIds, existingReversedTransactionIds, paymentDetail); + paymentDetail); final SavingsAccountTransaction deposit = account.deposit(transactionDTO); final MathContext mc = MathContext.DECIMAL64; if (account.isBeforeLastPostingPeriod(transactionDate)) { final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); } else { final LocalDate today = DateUtils.getLocalDateOfTenant(); account.calculateInterestUsing(mc, today); @@ -106,8 +108,14 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi return transaction.getId(); } - private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, - final List existingReversedTransactionIds) { + private void updateExistingTransactionsDetails(SavingsAccount account, Set existingTransactionIds, + Set existingReversedTransactionIds) { + existingTransactionIds.addAll(account.findExistingTransactionIds()); + existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds()); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final Set existingTransactionIds, + final Set existingReversedTransactionIds) { final MonetaryCurrency currency = savingsAccount.getCurrency(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 8915e1c51..f01ef4950 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -14,10 +14,12 @@ import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; @@ -144,11 +146,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); checkClientOrGroupActive(account); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); - final Map changes = account.activate(user, command, DateUtils.getLocalDateOfTenant(), - existingReversedTransactionIds, existingReversedTransactionIds); + final Map changes = account.activate(user, command, DateUtils.getLocalDateOfTenant()); if (!changes.isEmpty()) { this.savingAccountRepository.save(account); } @@ -243,14 +245,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final MathContext mc = MathContext.DECIMAL64; final LocalDate today = DateUtils.getLocalDateOfTenant(); - final SavingsAccountTransaction annualFee = account.addAnnualFee(mc, fmt, annualFeeTransactionDate, today, existingTransactionIds, - existingReversedTransactionIds); + final SavingsAccountTransaction annualFee = account.addAnnualFee(mc, fmt, annualFeeTransactionDate, today); final Long transactionId = saveTransactionToGenerateTransactionId(annualFee); this.savingAccountRepository.save(account); @@ -306,12 +308,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Transactional private void postInterest(final SavingsAccount account) { - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); this.savingAccountRepository.save(account); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); @@ -332,8 +335,10 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId, final boolean allowAccountTransferModification) { - final List newTransactionIds = new ArrayList(); - final List reversedTransactionIds = new ArrayList(); + final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransaction savingsAccountTransaction = this.savingsAccountTransactionRepository .findOneByIdAndSavingsAccountId(transactionId, savingsId); @@ -347,21 +352,20 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); - final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId); if (account.isNotActive()) { throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction); } - account.undoTransaction(transactionId, reversedTransactionIds); + account.undoTransaction(transactionId); checkClientOrGroupActive(account); if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { - account.postInterest(mc, today, newTransactionIds, reversedTransactionIds); + account.postInterest(mc, today); } else { account.calculateInterestUsing(mc, today); } account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction); account.activateAccountBasedOnBalance(); - postJournalEntries(account, newTransactionIds, reversedTransactionIds); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); return new CommandProcessingResultBuilder() // .withEntityId(savingsId) // @@ -394,41 +398,42 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi if (account.isNotActive()) { throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction); } + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final LocalDate transactionDate = command.localDateValueOfParameterNamed(SavingsApiConstants.transactionDateParamName); final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(SavingsApiConstants.transactionAmountParamName); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final Map changes = new LinkedHashMap(); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); - account.undoTransaction(transactionId, existingReversedTransactionIds); + account.undoTransaction(transactionId); // for undo withdrawal fee final SavingsAccountTransaction nextSavingsAccountTransaction = this.savingsAccountTransactionRepository .findOneByIdAndSavingsAccountId(transactionId + 1, savingsId); if (nextSavingsAccountTransaction != null && nextSavingsAccountTransaction.isWithdrawalFeeAndNotReversed()) { - account.undoTransaction(transactionId + 1, existingReversedTransactionIds); + account.undoTransaction(transactionId + 1); } SavingsAccountTransaction transaction = null; if (savingsAccountTransaction.isDeposit()) { final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, - existingTransactionIds, existingReversedTransactionIds, paymentDetail); + paymentDetail); transaction = account.deposit(transactionDTO); } else { final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, - existingTransactionIds, existingReversedTransactionIds, paymentDetail); + paymentDetail); transaction = account.withdraw(transactionDTO, true); } final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); if (account.isBeforeLastPostingPeriod(transactionDate) || account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); } else { account.calculateInterestUsing(mc, today); } @@ -456,17 +461,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi throw new PlatformApiDataValidationException(dataValidationErrors); } - private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, - final List existingReversedTransactionIds) { - - final MonetaryCurrency currency = savingsAccount.getCurrency(); - final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); - - final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), - existingTransactionIds, existingReversedTransactionIds); - this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); - } - private void checkClientOrGroupActive(final SavingsAccount account) { final Client client = account.getClient(); if (client != null) { @@ -517,8 +511,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate) { final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId); - final List existingTransactionIds = new ArrayList(savingsAccount.findExistingTransactionIds()); - final List existingReversedTransactionIds = new ArrayList(savingsAccount.findExistingReversedTransactionIds()); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount, savingsAccount.office(), transferDate); @@ -539,8 +534,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate) { final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId); - final List existingTransactionIds = new ArrayList(savingsAccount.findExistingTransactionIds()); - final List existingReversedTransactionIds = new ArrayList(savingsAccount.findExistingReversedTransactionIds()); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount, savingsAccount.office(), transferDate); @@ -569,8 +565,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final Office acceptedInOffice, final Staff fieldOfficer) { final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(accountId); - final List existingTransactionIds = new ArrayList(savingsAccount.findExistingTransactionIds()); - final List existingReversedTransactionIds = new ArrayList(savingsAccount.findExistingReversedTransactionIds()); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount, acceptedInOffice, transferDate); @@ -579,7 +576,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi if (fieldOfficer != null) { savingsAccount.update(fieldOfficer); } - + final MathContext mc = MathContext.DECIMAL64; savingsAccount.calculateInterestUsing(mc, transferDate); @@ -622,15 +619,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() && !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } } - + savingsAccount.addCharge(fmt, savingsAccountCharge, chargeDefinition); this.savingAccountRepository.saveAndFlush(savingsAccount); @@ -647,11 +644,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Transactional @Override public CommandProcessingResult updateSavingsAccountCharge(final JsonCommand command) { - + this.context.authenticatedUser(); this.savingsAccountChargeDataValidator.validateUpdate(command.json()); final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); final Long savingsAccountId = command.getSavingsId(); // SavingsAccount Charge entity @@ -676,15 +674,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() && !this.workingDaysWritePlatformService.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } } - + this.savingsAccountChargeRepository.saveAndFlush(savingsAccountCharge); return new CommandProcessingResultBuilder() // @@ -702,23 +700,24 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) { this.context.authenticatedUser(); - + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( savingsAccountChargeId, savingsAccountId); - + // Get Savings account from savings charge final SavingsAccount account = savingsAccountCharge.savingsAccount(); this.savingAccountAssembler.assignSavingAccountHelpers(account); - - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); - account.waiveCharge(savingsAccountChargeId, existingTransactionIds, existingReversedTransactionIds); + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + + account.waiveCharge(savingsAccountChargeId); final MathContext mc = MathContext.DECIMAL64; if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); } else { final LocalDate today = DateUtils.getLocalDateOfTenant(); account.calculateInterestUsing(mc, today); @@ -728,6 +727,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.savingAccountRepository.saveAndFlush(account); + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + return new CommandProcessingResultBuilder() // .withEntityId(savingsAccountChargeId) // .withOfficeId(account.officeId()) // @@ -769,28 +770,29 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName); final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName); - + final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( savingsAccountChargeId, savingsAccountId); final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); - //transaction date should not be on a holiday or non working day + // transaction date should not be on a holiday or non working day if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday() && this.holidayWritePlatformService.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + if (!this.workingDaysWritePlatformService.isTransactionAllowedOnNonWorkingDay() && !this.workingDaysWritePlatformService.isWorkingDay(transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + this.payCharge(savingsAccountCharge, transactionDate, amountPaid, fmt); return new CommandProcessingResultBuilder() // .withEntityId(savingsAccountCharge.getId()) // @@ -805,38 +807,39 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi @Override @CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES) public void processDueCharges() { - //always use current date as transaction date for batch job + // always use current date as transaction date for batch job final LocalDate transactionDate = DateUtils.getLocalDateOfTenant(); final List pendingCharges = this.savingsAccountChargeRepository.findPendingCharges(transactionDate.toDate()); - //final List paidCharges = new ArrayList(pendingCharges.size()); + // final List paidCharges = new + // ArrayList(pendingCharges.size()); final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); for (SavingsAccountCharge savingsAccountCharge : pendingCharges) { try { - payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); + payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); } catch (InsufficientAccountBalanceException isab) { // TODO: AA - provide a meaningful error with failed // transactions list } } - - // this.savingsAccountChargeRepository.save(paidCharges); + + // this.savingsAccountChargeRepository.save(paidCharges); } - + @Transactional private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid, final DateTimeFormatter formatter) { // Get Savings account from savings charge final SavingsAccount account = savingsAccountCharge.savingsAccount(); this.savingAccountAssembler.assignSavingAccountHelpers(account); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); - account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, existingTransactionIds, - existingReversedTransactionIds); - + final Set existingTransactionIds = new HashSet(); + final Set existingReversedTransactionIds = new HashSet(); + updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); + account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter); + final MathContext mc = MathContext.DECIMAL64; if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { final LocalDate today = DateUtils.getLocalDateOfTenant(); - account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + account.postInterest(mc, today); } else { final LocalDate today = DateUtils.getLocalDateOfTenant(); account.calculateInterestUsing(mc, today); @@ -845,8 +848,24 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi account.validateAccountBalanceDoesNotBecomeNegative(amountPaid); this.savingAccountRepository.save(account); - + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); } + private void updateExistingTransactionsDetails(SavingsAccount account, Set existingTransactionIds, + Set existingReversedTransactionIds) { + existingTransactionIds.addAll(account.findExistingTransactionIds()); + existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds()); + } + + private void postJournalEntries(final SavingsAccount savingsAccount, final Set existingTransactionIds, + final Set existingReversedTransactionIds) { + + final MonetaryCurrency currency = savingsAccount.getCurrency(); + final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); + + final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), + existingTransactionIds, existingReversedTransactionIds); + this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java index 9d65f9515..d5e4755a6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl.java @@ -14,7 +14,6 @@ import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; -import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; @@ -24,9 +23,6 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.core.service.DateUtils; 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.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; @@ -75,8 +71,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl private final NoteRepository noteRepository; private final StaffRepositoryWrapper staffRepository; private final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator; - private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final SavingsAccountChargeAssembler savingsAccountChargeAssembler; @Autowired @@ -87,8 +81,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl final GroupRepository groupRepository, final SavingsProductRepository savingsProductRepository, final NoteRepository noteRepository, final StaffRepositoryWrapper staffRepository, final SavingsAccountApplicationTransitionApiJsonValidator savingsAccountApplicationTransitionApiJsonValidator, - final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper, - final JournalEntryWritePlatformService journalEntryWritePlatformService, final SavingsAccountChargeAssembler savingsAccountChargeAssembler) { this.context = context; this.savingAccountRepository = savingAccountRepository; @@ -101,8 +93,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl this.noteRepository = noteRepository; this.staffRepository = staffRepository; this.savingsAccountApplicationTransitionApiJsonValidator = savingsAccountApplicationTransitionApiJsonValidator; - this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper; - this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.savingsAccountChargeAssembler = savingsAccountChargeAssembler; } @@ -140,8 +130,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl try { this.savingsAccountDataValidator.validateForSubmit(command.json()); - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(command); this.savingAccountRepository.save(account); @@ -153,8 +141,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl this.savingAccountRepository.save(account); } - postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); - final Long savingsId = account.getId(); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // @@ -233,7 +219,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl } if (changes.containsKey("charges")) { - final Set charges = this.savingsAccountChargeAssembler.fromParsedJson(command.parsedJson(), account.getCurrency().getCode()); + final Set charges = this.savingsAccountChargeAssembler.fromParsedJson(command.parsedJson(), + account.getCurrency().getCode()); final boolean updated = account.update(charges); if (!updated) { changes.remove("charges"); @@ -426,17 +413,6 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl .build(); } - private void postJournalEntries(final SavingsAccount savingsAccount, final List existingTransactionIds, - final List existingReversedTransactionIds) { - - final MonetaryCurrency currency = savingsAccount.getCurrency(); - final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency); - - final Map accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(), - existingTransactionIds, existingReversedTransactionIds); - this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData); - } - private void checkClientOrGroupActive(final SavingsAccount account) { final Client client = account.getClient(); if (client != null) {