added support for undo savings fees

This commit is contained in:
ashok-conflux
2013-09-25 19:35:42 +05:30
parent 617662f0df
commit aabe9549e0
10 changed files with 176 additions and 151 deletions
@@ -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());
}
}
@@ -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"), //
@@ -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";
@@ -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<Long> {
.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<SavingsAccountChargePaidBy> 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<Long> {
}
public void removeCharge(final SavingsAccountCharge charge) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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<Long> {
// 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<Long> {
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<Long> {
}
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<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
@@ -1996,13 +2011,23 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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<Long> {
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<Long> {
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<Long> existingTransactionIds,
final List<Long> 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) {
@@ -214,8 +214,10 @@ public class SavingsAccountCharge extends AbstractPersistable<Long> {
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<Long> {
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<Long> {
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<Long> {
public BigDecimal amount() {
return this.amount;
}
public BigDecimal amoutOutstanding(){
return this.amountOutstanding;
}
public boolean isFeeCharge() {
return !this.penaltyCharge;
@@ -18,6 +18,6 @@ public interface SavingsAccountChargeRepository extends JpaRepository<SavingsAcc
SavingsAccountCharge findByIdAndSavingsAccountId(Long id, Long savingsAccountId);
@Query("from SavingsAccountCharge sac where sac.dueDate <=:transactionDate and sac.waived = 0 and sac.paid=0")
@Query("from SavingsAccountCharge sac where sac.dueDate <=:transactionDate and sac.waived = 0 and sac.paid=0 order by sac.dueDate")
List<SavingsAccountCharge> findPendingCharges(@Param("transactionDate") Date transactionDate);
}
@@ -5,7 +5,6 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -210,6 +210,10 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
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();
}
@@ -52,5 +52,5 @@ public interface SavingsAccountWritePlatformService {
CommandProcessingResult payCharge(Long savingsAccountId, Long savingsAccountChargeId, JsonCommand command);
void payDueCharges();
void processDueCharges();
}
@@ -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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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<SavingsAccountCharge> pendingCharges = this.savingsAccountChargeRepository.findPendingCharges(transactionDate.toDate());
final List<SavingsAccountCharge> paidCharges = new ArrayList<SavingsAccountCharge>(pendingCharges.size());
//final List<SavingsAccountCharge> paidCharges = new ArrayList<SavingsAccountCharge>(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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
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);
}
}