Merge pull request #611 from vishwasbabu/mifosplatform-1.10.3

cleaning up accounting integration for savings
This commit is contained in:
Vishwas Babu A J
2013-09-26 06:56:56 -07:00
5 changed files with 146 additions and 180 deletions
@@ -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<Long> existingTransactionIds;
private final List<Long> existingReversedTransactionIds;
private final PaymentDetail paymentDetail;
public SavingsAccountTransactionDTO(final DateTimeFormatter formatter, final LocalDate transactionDate,
final BigDecimal transactionAmount, final List<Long> existingTransactionIds, final List<Long> 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<Long> getExistingTransactionIds() {
return this.existingTransactionIds;
}
public List<Long> getExistingReversedTransactionIds() {
return this.existingReversedTransactionIds;
}
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
@@ -376,16 +376,13 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return SavingsAccountStatusType.fromInt(this.status).isClosed();
}
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate) {
final List<PostingPeriod> 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<Long> {
}
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<Long> {
}
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<Long> {
return transactionBeforeLastInterestPosting;
}
public void validateAccountBalanceDoesNotBecomeNegative(final BigDecimal transactionAmount) {
final List<SavingsAccountTransaction> transactionsSortedByDate = retreiveListOfTransactions();
Money runningBalance = Money.zero(this.currency);
@@ -779,8 +770,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
public SavingsAccountTransaction addAnnualFee(final MathContext mc, final DateTimeFormatter formatter,
final LocalDate annualFeeTransactionDate, final LocalDate today, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final LocalDate annualFeeTransactionDate, final LocalDate today) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -828,9 +818,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
}
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<Long> {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final List<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
public Map<String, Object> deriveAccountingBridgeData(final CurrencyData currencyData, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) {
final Map<String, Object> accountingBridgeData = new LinkedHashMap<String, Object>();
accountingBridgeData.put("savingsId", getId());
@@ -1466,7 +1453,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return actualChanges;
}
public void undoTransaction(final Long transactionId, final List<Long> 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<Long> {
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<Long> {
final Set<SavingsAccountChargePaidBy> 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<Long> {
return actualChanges;
}
public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate,
final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
@@ -1734,7 +1718,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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<Long> {
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<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
public void waiveCharge(final Long savingsAccountChargeId) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -1944,9 +1927,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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<Long> {
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<Long> {
}
public void payCharge(final SavingsAccountCharge savingsAccountCharge, final BigDecimal amountPaid, final LocalDate transactionDate,
final DateTimeFormatter formatter, final List<Long> existingTransactionIds, final List<Long> existingReversedTransactionIds) {
final DateTimeFormatter formatter) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
@@ -2008,21 +1988,21 @@ public class SavingsAccount extends AbstractPersistable<Long> {
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<Long> {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
existingTransactionIds.addAll(findExistingTransactionIds());
existingReversedTransactionIds.addAll(findExistingReversedTransactionIds());
savingsAccountCharge.pay(getCurrency(), amountPaidMoney);
handlePayChargeTransactions(savingsAccountCharge, amountPaidMoney, transactionDate);
@@ -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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
private void updateExistingTransactionsDetails(SavingsAccount account, Set<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds) {
existingTransactionIds.addAll(account.findExistingTransactionIds());
existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds());
}
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
@@ -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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final Map<String, Object> changes = account.activate(user, command, DateUtils.getLocalDateOfTenant(),
existingReversedTransactionIds, existingReversedTransactionIds);
final Map<String, Object> 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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> newTransactionIds = new ArrayList<Long>();
final List<Long> reversedTransactionIds = new ArrayList<Long>();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId);
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
final Map<String, Object> changes = new LinkedHashMap<String, Object>();
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<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> 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<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds = new ArrayList<Long>(savingsAccount.findExistingTransactionIds());
final List<Long> existingReversedTransactionIds = new ArrayList<Long>(savingsAccount.findExistingReversedTransactionIds());
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
account.waiveCharge(savingsAccountChargeId, existingTransactionIds, existingReversedTransactionIds);
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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<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) {
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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, existingTransactionIds,
existingReversedTransactionIds);
final Set<Long> existingTransactionIds = new HashSet<Long>();
final Set<Long> existingReversedTransactionIds = new HashSet<Long>();
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<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds) {
existingTransactionIds.addAll(account.findExistingTransactionIds());
existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds());
}
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(applicationCurrency.toData(),
existingTransactionIds, existingReversedTransactionIds);
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
}
}
@@ -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<Long> existingTransactionIds = new ArrayList<Long>();
final List<Long> existingReversedTransactionIds = new ArrayList<Long>();
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<SavingsAccountCharge> charges = this.savingsAccountChargeAssembler.fromParsedJson(command.parsedJson(), account.getCurrency().getCode());
final Set<SavingsAccountCharge> 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<Long> existingTransactionIds,
final List<Long> existingReversedTransactionIds) {
final MonetaryCurrency currency = savingsAccount.getCurrency();
final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepositoryWrapper.findOneWithNotFoundDetection(currency);
final Map<String, Object> 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) {