From aabe9549e0e76c76cbe7dd8bfd8780cce56c4dca Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Wed, 25 Sep 2013 16:34:22 +0530 Subject: [PATCH] added support for undo savings fees --- .../core/service/DateUtils.java | 4 + .../SavingsAccountTransactionType.java | 2 +- .../savings/SavingsApiConstants.java | 1 + .../savings/domain/SavingsAccount.java | 149 +++++++++--------- .../savings/domain/SavingsAccountCharge.java | 34 ++-- .../SavingsAccountChargeRepository.java | 2 +- ...SavingsAccountChargeRepositoryWrapper.java | 1 - .../domain/SavingsAccountTransaction.java | 4 + .../SavingsAccountWritePlatformService.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 128 ++++++++------- 10 files changed, 176 insertions(+), 151 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java index 9a56e0b41..6643de289 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/service/DateUtils.java @@ -63,4 +63,8 @@ public class DateUtils { final String formattedSqlDate = df.format(date); return formattedSqlDate; } + + public static boolean isInFuture(final LocalDate date){ + return date.isAfter(getLocalDateOfTenant()); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java index 2b57156ef..74dfd4acb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsAccountTransactionType.java @@ -19,7 +19,7 @@ public enum SavingsAccountTransactionType { INTEREST_POSTING(3, "savingsAccountTransactionType.interestPosting"), // WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee"), // ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee"), // - WAIVE_CHARGES(6, "savingsAccountTransactionType.waiveCharges"), // + WAIVE_CHARGES(6, "savingsAccountTransactionType.waiveCharge"), // PAY_CHARGE(7, "savingsAccountTransactionType.payCharge"), // INITIATE_TRANSFER(12, "savingsAccountTransactionType.initiateTransfer"), // APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), // diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java index b669f9672..fb1fd6d77 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/SavingsApiConstants.java @@ -34,6 +34,7 @@ public class SavingsApiConstants { public static String adjustTransactionAction = ".adjusttransaction"; public static String closeAction = ".close"; public static String payChargeTransactionAction = ".paycharge"; + public static String waiveChargeTransactionAction = ".waivecharge"; // command public static String COMMAND_UNDO_TRANSACTION = "undo"; 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 41987277b..347068b9d 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 @@ -8,13 +8,13 @@ package org.mifosplatform.portfolio.savings.domain; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeOnMonthDayParamName; +import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.localeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.lockinPeriodFrequencyTypeParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeAmountParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName; import static org.mifosplatform.portfolio.savings.SavingsApiConstants.withdrawalFeeTypeParamName; -import static org.mifosplatform.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName; import java.math.BigDecimal; import java.math.MathContext; @@ -1497,15 +1497,18 @@ public class SavingsAccount extends AbstractPersistable { .withMonthOfYear(this.annualFeeOnMonth).withDayOfMonth(this.annualFeeOnDay).plusYears(1); this.annualFeeNextDueDate = newAnnualFeeNextDueDate.toDate(); } - } else if (transactionToUndo.isCharge()) { + } else if (transactionToUndo.isCharge() || transactionToUndo.isWaiveCharge()) { // undo charge final Set chargesPaidBy = transactionToUndo.getSavingsAccountChargesPaid(); for (final SavingsAccountChargePaidBy savingsAccountChargePaidBy : chargesPaidBy) { final SavingsAccountCharge chargeToUndo = savingsAccountChargePaidBy.getSavingsAccountCharge(); - chargeToUndo.resetPaidAmount(this.currency); + if(transactionToUndo.isCharge()){ + chargeToUndo.undoPayment(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); + }else if (transactionToUndo.isWaiveCharge()){ + chargeToUndo.undoWaiver(this.getCurrency(), transactionToUndo.getAmount(this.getCurrency())); + } } } - } } @@ -1899,6 +1902,20 @@ public class SavingsAccount extends AbstractPersistable { } public void removeCharge(final SavingsAccountCharge charge) { + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(SAVINGS_ACCOUNT_RESOURCE_NAME); + + if (isClosed()) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("delete.transaction.invalid.account.is.closed"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + if (isActive() || isApproved()) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("delete.transaction.invalid.account.is.active"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + this.charges.remove(charge); } @@ -1936,15 +1953,7 @@ public class SavingsAccount extends AbstractPersistable { // waive charge final Money amountWaived = savingsAccountCharge.waive(getCurrency()); - // persist an entry in transaction details - final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), - savingsAccountCharge.getDueLocalDate(), amountWaived); - - this.transactions.add(chargeTransaction); - - this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); - final MathContext mc = MathContext.DECIMAL64; - calculateInterestUsing(mc, savingsAccountCharge.getDueLocalDate()); + handleWaiverChargeTransactions(savingsAccountCharge, amountWaived); } public void addCharge(final DateTimeFormatter formatter, final SavingsAccountCharge savingsAccountCharge, final Charge chargeDefinition) { @@ -1966,10 +1975,16 @@ public class SavingsAccount extends AbstractPersistable { final LocalDate chargeDueDate = savingsAccountCharge.getDueLocalDate(); - if (chargeDueDate != null && chargeDueDate.isBefore(getActivationLocalDate())) { - baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); - throw new PlatformApiDataValidationException(dataValidationErrors); + if (savingsAccountCharge.isOnSpecifiedDueDate()){ + if(getActivationLocalDate() != null && chargeDueDate.isBefore(getActivationLocalDate())){ + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); + }else if(getSubmittedOnLocalDate() != null && chargeDueDate.isBefore(getSubmittedOnLocalDate())){ + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("before.submittedOnDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } } validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_APPLY_CHARGE, chargeDueDate); @@ -1979,7 +1994,7 @@ public class SavingsAccount extends AbstractPersistable { } - public void payCharge(final Long savingsAccountChargeId, final BigDecimal amountPaid, final LocalDate transactionDate, + public void payCharge(final SavingsAccountCharge savingsAccountCharge, final BigDecimal amountPaid, final LocalDate transactionDate, final DateTimeFormatter formatter, final List existingTransactionIds, final List existingReversedTransactionIds) { final List dataValidationErrors = new ArrayList(); @@ -1996,13 +2011,23 @@ public class SavingsAccount extends AbstractPersistable { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - if (transactionDate != null && transactionDate.isBefore(getActivationLocalDate())) { - baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) - .failWithCodeNoParameterAddedToErrorCode("before.activationDate"); - throw new PlatformApiDataValidationException(dataValidationErrors); + if (savingsAccountCharge.isOnSpecifiedDueDate()){ + if(getActivationLocalDate() != null && transactionDate.isBefore(getActivationLocalDate())){ + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getActivationLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); + }else if(getSubmittedOnLocalDate() != null && transactionDate.isBefore(getSubmittedOnLocalDate())){ + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("transaction.before.submittedOnDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } } - final SavingsAccountCharge savingsAccountCharge = getCharge(savingsAccountChargeId); + if (DateUtils.isInFuture(transactionDate)){ + baseDataValidator.reset().parameter(dueAsOfDateParamName).value(getSubmittedOnLocalDate().toString(formatter)) + .failWithCodeNoParameterAddedToErrorCode("transaction.is.futureDate"); + throw new PlatformApiDataValidationException(dataValidationErrors); + } // validate charge is not already paid or waived if (savingsAccountCharge.isWaived()) { @@ -2012,9 +2037,9 @@ public class SavingsAccount extends AbstractPersistable { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.account.charge.is.paid"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - + final Money amountPaidMoney = Money.of(currency, amountPaid); - if (!savingsAccountCharge.getAmountOutstanding(getCurrency()).isGreaterThanOrEqualTo(amountPaidMoney)){ + if (!savingsAccountCharge.getAmountOutstanding(getCurrency()).isGreaterThanOrEqualTo(amountPaidMoney)) { baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("transaction.invalid.charge.amount.paid.in.access"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } @@ -2022,61 +2047,33 @@ public class SavingsAccount extends AbstractPersistable { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - final Money chargePaid = savingsAccountCharge.pay(getCurrency(), amountPaidMoney); - - // persist transaction details - final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, chargePaid); - - final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(chargeTransaction, savingsAccountCharge, - amountPaid); - chargeTransaction.getSavingsAccountChargesPaid().add(chargePaidBy); - this.transactions.add(chargeTransaction); - - final MathContext mc = MathContext.DECIMAL64; - if (isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - calculateInterestUsing(mc, today); - } - - this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); - - validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.payChargeTransactionAction); + savingsAccountCharge.pay(getCurrency(), amountPaidMoney); + handlePayChargeTransactions(savingsAccountCharge, amountPaidMoney, transactionDate); } - public void payDueCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final List existingTransactionIds, - final List existingReversedTransactionIds) { - - existingTransactionIds.addAll(findExistingTransactionIds()); - existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - final Money amountPaid = savingsAccountCharge.payDueCharge(getCurrency()); - - // persist transaction details - final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, amountPaid); - - final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(chargeTransaction, savingsAccountCharge, - amountPaid.getAmount()); - - chargeTransaction.getSavingsAccountChargesPaid().add(chargePaidBy); - - this.transactions.add(chargeTransaction); - - final MathContext mc = MathContext.DECIMAL64; - if (isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); - } else { - final LocalDate today = DateUtils.getLocalDateOfTenant(); - calculateInterestUsing(mc, today); - } - - this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions); - validateAccountBalanceDoesNotBecomeNegative(amountPaid.getAmount()); + private void handlePayChargeTransactions(SavingsAccountCharge savingsAccountCharge, Money transactionAmount, + final LocalDate transactionDate) { + final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.charge(this, office(), transactionDate, + transactionAmount); + handleChargeTransactions(savingsAccountCharge, chargeTransaction); } - + + private void handleWaiverChargeTransactions(SavingsAccountCharge savingsAccountCharge, Money transactionAmount) { + final SavingsAccountTransaction chargeTransaction = SavingsAccountTransaction.waiver(this, office(), + DateUtils.getLocalDateOfTenant(), transactionAmount); + handleChargeTransactions(savingsAccountCharge, chargeTransaction); + } + + private void handleChargeTransactions(final SavingsAccountCharge savingsAccountCharge, final SavingsAccountTransaction transaction) { + // Provide a link between transaction and savings charge for which + // amount is waived. + final SavingsAccountChargePaidBy chargePaidBy = SavingsAccountChargePaidBy.instance(transaction, savingsAccountCharge, transaction + .getAmount(this.getCurrency()).getAmount()); + transaction.getSavingsAccountChargesPaid().add(chargePaidBy); + this.getTransactions().add(transaction); + } + private SavingsAccountCharge getCharge(final Long savingsAccountChargeId) { SavingsAccountCharge charge = null; for (final SavingsAccountCharge existingCharge : this.charges) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java index 4b296b9f6..8a45e448b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java @@ -214,8 +214,10 @@ public class SavingsAccountCharge extends AbstractPersistable { this.waived = false; } - public void resetPaidAmount(final MonetaryCurrency currency) { - this.amountPaid = BigDecimal.ZERO; + public void undoPayment(final MonetaryCurrency currency, final Money transactionAmount) { + Money amountPaid = getAmountPaid(currency); + amountPaid = amountPaid.minus(transactionAmount); + this.amountPaid = amountPaid.getAmount(); this.amountOutstanding = calculateAmountOutstanding(currency); this.paid = false; } @@ -223,10 +225,17 @@ public class SavingsAccountCharge extends AbstractPersistable { public Money waive(final MonetaryCurrency currency) { this.amountWaived = this.amountOutstanding; this.amountOutstanding = BigDecimal.ZERO; - this.paid = false; this.waived = true; return getAmountWaived(currency); } + + public void undoWaiver(final MonetaryCurrency currency, final Money transactionAmount) { + Money amountWaived = getAmountWaived(currency); + amountWaived = amountWaived.minus(transactionAmount); + this.amountWaived = amountWaived.getAmount(); + this.amountOutstanding = calculateAmountOutstanding(currency); + this.waived = false; + } public Money pay(final MonetaryCurrency currency, final Money amountPaid) { @@ -237,20 +246,7 @@ public class SavingsAccountCharge extends AbstractPersistable { this.amountPaid = amountPaidToDate.getAmount(); this.amountOutstanding = amountOutstanding.getAmount(); this.paid = determineIfFullyPaid(); - return amountPaid; - } - - public Money payDueCharge(final MonetaryCurrency currency) { - - Money amountPaidToDate = Money.of(currency, this.amountPaid); - Money amountOutstanding = Money.of(currency, this.amountOutstanding); - final Money amountPaid = Money.of(currency, this.amountOutstanding);; - amountPaidToDate = amountPaidToDate.plus(amountPaid); - amountOutstanding = amountOutstanding.minus(amountPaid); - this.amountPaid = amountPaidToDate.getAmount(); - this.amountOutstanding = amountOutstanding.getAmount(); - this.paid = determineIfFullyPaid(); - return amountPaid; + return amountOutstanding; } private BigDecimal calculateAmountOutstanding(final MonetaryCurrency currency) { @@ -409,6 +405,10 @@ public class SavingsAccountCharge extends AbstractPersistable { public BigDecimal amount() { return this.amount; } + + public BigDecimal amoutOutstanding(){ + return this.amountOutstanding; + } public boolean isFeeCharge() { return !this.penaltyCharge; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepository.java index 959e22f8f..714988d9d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepository.java @@ -18,6 +18,6 @@ public interface SavingsAccountChargeRepository extends JpaRepository findPendingCharges(@Param("transactionDate") Date transactionDate); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepositoryWrapper.java index 68187bbd6..8aba4acb6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountChargeRepositoryWrapper.java @@ -5,7 +5,6 @@ */ package org.mifosplatform.portfolio.savings.domain; -import java.util.Collection; import java.util.Date; import java.util.List; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java index a9df3aa95..a4865e2b2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountTransaction.java @@ -210,6 +210,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable { return SavingsAccountTransactionType.fromInt(this.typeOf).isWithdrawal(); } + public boolean isPostInterestCalculationRequired(){ + return this.isDeposit() || this.isWithdrawal() || this.isCharge(); + } + public boolean isInterestPostingAndNotReversed() { return SavingsAccountTransactionType.fromInt(this.typeOf).isInterestPosting() && isNotReversed(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java index 77130b111..7d02115aa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformService.java @@ -52,5 +52,5 @@ public interface SavingsAccountWritePlatformService { CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command); - void payDueCharges(); + void processDueCharges(); } \ No newline at end of file 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 ff5489ac0..8915e1c51 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 @@ -353,7 +353,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi } account.undoTransaction(transactionId, reversedTransactionIds); checkClientOrGroupActive(account); - if ((savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) + if (savingsAccountTransaction.isPostInterestCalculationRequired() && account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate())) { account.postInterest(mc, today, newTransactionIds, reversedTransactionIds); } else { @@ -606,7 +606,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId); checkClientOrGroupActive(savingsAccount); - final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy"); + final Locale locale = command.extractLocale(); + final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final Long chargeDefinitionId = command.longValueOfParameterNamed(chargeIdParamName); final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); @@ -635,12 +636,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.savingAccountRepository.saveAndFlush(savingsAccount); return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // .withEntityId(savingsAccountCharge.getId()) // .withOfficeId(savingsAccount.officeId()) // .withClientId(savingsAccount.clientId()) // .withGroupId(savingsAccount.groupId()) // - .withLoanId(savingsAccountId) // + .withSavingsId(savingsAccountId) // .build(); } @@ -688,11 +688,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi this.savingsAccountChargeRepository.saveAndFlush(savingsAccountCharge); return new CommandProcessingResultBuilder() // - .withEntityId(savingsChargeId) // - // .withOfficeId(savings) // - // .withClientId(loan.getClientId()) // - // .withGroupId(loan.getGroupId()) // - // .withLoanId(savingsAccountId) // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) // + .withClientId(savingsAccountCharge.savingsAccount().clientId()) // + .withGroupId(savingsAccountCharge.savingsAccount().groupId()) // + .withSavingsId(savingsAccountCharge.savingsAccount().getId()) // .with(changes) // .build(); } @@ -702,22 +702,37 @@ 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(); - final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId); - checkClientOrGroupActive(savingsAccount); + account.waiveCharge(savingsAccountChargeId, existingTransactionIds, existingReversedTransactionIds); - savingsAccount.waiveCharge(savingsAccountChargeId, existingTransactionIds, existingReversedTransactionIds); + final MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } - this.savingAccountRepository.saveAndFlush(savingsAccount); + account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction); + + this.savingAccountRepository.saveAndFlush(account); return new CommandProcessingResultBuilder() // .withEntityId(savingsAccountChargeId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // + .withOfficeId(account.officeId()) // + .withClientId(account.clientId()) // + .withGroupId(account.groupId()) // .withSavingsId(savingsAccountId) // .build(); } @@ -733,8 +748,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection( savingsAccountChargeId, savingsAccountId); - // TODO AA: validate before deleting a charge - savingsAccount.removeCharge(savingsAccountCharge); this.savingAccountRepository.saveAndFlush(savingsAccount); @@ -747,31 +760,25 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .build(); } - @Transactional @Override public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) { this.context.authenticatedUser(); this.savingsAccountChargeDataValidator.validatePayCharge(command.json()); - - final List existingTransactionIds = new ArrayList(); - final List existingReversedTransactionIds = new ArrayList(); - final Locale locale = command.extractLocale(); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); - - final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId); - checkClientOrGroupActive(savingsAccount); - - final BigDecimal chargeAmount = command.bigDecimalValueOfParameterNamed(amountParamName); + 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); //transaction date should not be on a holiday or non working day if (!this.holidayWritePlatformService.isTransactionAllowedOnHoliday() - && this.holidayWritePlatformService.isHoliday(savingsAccount.officeId(), transactionDate)) { + && this.holidayWritePlatformService.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } @@ -783,50 +790,63 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } - - savingsAccount.payCharge(savingsAccountChargeId, chargeAmount, transactionDate, fmt, existingTransactionIds, existingReversedTransactionIds); - - this.savingAccountRepository.saveAndFlush(savingsAccount); - postJournalEntries(savingsAccount, existingTransactionIds, existingReversedTransactionIds); - + this.payCharge(savingsAccountCharge, transactionDate, amountPaid, fmt); return new CommandProcessingResultBuilder() // - .withEntityId(savingsAccountChargeId) // - .withOfficeId(savingsAccount.officeId()) // - .withClientId(savingsAccount.clientId()) // - .withGroupId(savingsAccount.groupId()) // - .withSavingsId(savingsAccountId) // + .withEntityId(savingsAccountCharge.getId()) // + .withOfficeId(savingsAccountCharge.savingsAccount().officeId()) // + .withClientId(savingsAccountCharge.savingsAccount().clientId()) // + .withGroupId(savingsAccountCharge.savingsAccount().groupId()) // + .withSavingsId(savingsAccountCharge.savingsAccount().getId()) // .build(); } - @Transactional @Override @CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES) - public void payDueCharges() { + public void processDueCharges() { //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) { - // 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(); - try { - account.payDueCharge(savingsAccountCharge, transactionDate, existingTransactionIds, existingReversedTransactionIds); - paidCharges.add(savingsAccountCharge); + 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 MathContext mc = MathContext.DECIMAL64; + if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate())) { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.postInterest(mc, today, existingTransactionIds, existingReversedTransactionIds); + } else { + final LocalDate today = DateUtils.getLocalDateOfTenant(); + account.calculateInterestUsing(mc, today); + } + + account.validateAccountBalanceDoesNotBecomeNegative(amountPaid); + + this.savingAccountRepository.save(account); + + postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds); + } } \ No newline at end of file