Merge pull request #937 from pramodn02/MIFOSX-1270

MIFOSX-1270 : corrected accounting for  activation charge
This commit is contained in:
Vishwas Babu A J
2014-06-10 14:50:48 -07:00
2 changed files with 81 additions and 21 deletions
@@ -77,7 +77,7 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
ORGANIZATION_ACCOUNTS.LIABILITY_TRANSFER_SUSPENSE.getValue(), ORGANIZATION_ACCOUNTS.LIABILITY_TRANSFER_SUSPENSE.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
transactionId, transactionDate, amount, isReversal); transactionId, transactionDate, overdraftAmount, isReversal);
if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) { if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
ORGANIZATION_ACCOUNTS.LIABILITY_TRANSFER_SUSPENSE.getValue(), ORGANIZATION_ACCOUNTS.LIABILITY_TRANSFER_SUSPENSE.getValue(),
@@ -88,7 +88,7 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE.getValue(), CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId, CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
transactionId, transactionDate, amount, isReversal); transactionId, transactionDate, overdraftAmount, isReversal);
if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) { if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode, this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE.getValue(), CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE.getValue(),
@@ -130,6 +130,23 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
* Handle Interest Applications and reversals of Interest * Handle Interest Applications and reversals of Interest
* Applications * Applications
**/ **/
else if (savingsTransactionDTO.getTransactionType().isInterestPosting() && savingsTransactionDTO.isOverdraftTransaction()) {
// Post journal entry if earned interest amount is greater than
// zero
if (savingsTransactionDTO.getAmount().compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.INTEREST_ON_SAVINGS.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
transactionId, transactionDate, overdraftAmount, isReversal);
if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavings(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.INTEREST_ON_SAVINGS.getValue(),
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL.getValue(), savingsProductId, paymentTypeId, savingsId,
transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal);
}
}
}
else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) { else if (savingsTransactionDTO.getTransactionType().isInterestPosting()) {
// Post journal entry if earned interest amount is greater than // Post journal entry if earned interest amount is greater than
// zero // zero
@@ -141,6 +158,33 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
} }
/** Handle Fees Deductions and reversals of Fees Deductions **/ /** Handle Fees Deductions and reversals of Fees Deductions **/
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
// Is the Charge a penalty?
if (penaltyPayments.size() > 0) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL, CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_PENALTIES,
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
penaltyPayments);
if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL, CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_PENALTIES,
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate,
amount.subtract(overdraftAmount), isReversal, penaltyPayments);
}
} else {
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.OVERDRAFT_PORTFOLIO_CONTROL, CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES,
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, overdraftAmount, isReversal,
feePayments);
if (amount.subtract(overdraftAmount).compareTo(BigDecimal.ZERO) == 1) {
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsCharges(office, currencyCode,
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL, CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES, savingsProductId,
paymentTypeId, savingsId, transactionId, transactionDate, amount.subtract(overdraftAmount), isReversal,
feePayments);
}
}
}
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) { else if (savingsTransactionDTO.getTransactionType().isFeeDeduction()) {
// Is the Charge a penalty? // Is the Charge a penalty?
if (penaltyPayments.size() > 0) { if (penaltyPayments.size() > 0) {
@@ -449,9 +449,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
* calculate the amount of interest due at the end of each 'crediting' * calculate the amount of interest due at the end of each 'crediting'
* period check if an existing 'interest posting' transaction exists for * period check if an existing 'interest posting' transaction exists for
* date and matches the amount posted * date and matches the amount posted
* @param isInterestTransfer TODO *
* @param isInterestTransfer
* TODO
*/ */
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate, boolean isInterestTransfer) { public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate,
boolean isInterestTransfer) {
// no openingBalance concept supported yet but probably will to allow // no openingBalance concept supported yet but probably will to allow
// for migrations. // for migrations.
@@ -488,7 +491,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance, final PostingPeriod postingPeriod = PostingPeriod.createFrom(periodInterval, periodStartingBalance,
retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType, retreiveOrderedNonInterestPostingTransactions(), this.currency, compoundingPeriodType, interestCalculationType,
interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions, isInterestTransfer); interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate, interestPostTransactions,
isInterestTransfer);
periodStartingBalance = postingPeriod.closingBalance(); periodStartingBalance = postingPeriod.closingBalance();
@@ -587,9 +591,10 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate); resetAccountTransactionsEndOfDayBalances(accountTransactionsSorted, interestPostingUpToDate);
} }
protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransaction> accountTransactionsSorted, final LocalDate interestPostingUpToDate) { protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransaction> accountTransactionsSorted,
// loop over transactions in reverse final LocalDate interestPostingUpToDate) {
// loop over transactions in reverse
LocalDate endOfBalanceDate = interestPostingUpToDate; LocalDate endOfBalanceDate = interestPostingUpToDate;
for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) { for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i); final SavingsAccountTransaction transaction = accountTransactionsSorted.get(i);
@@ -618,8 +623,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
if (isDateInTheFuture(transactionDTO.getTransactionDate())) { if (isDateInTheFuture(transactionDTO.getTransactionDate())) {
final String defaultUserMessage = "Transaction date cannot be in the future."; final String defaultUserMessage = "Transaction date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg." + resourceTypeName + ".transaction.in.the.future", final ApiParameterError error = ApiParameterError.parameterError(
defaultUserMessage, "transactionDate", transactionDTO.getTransactionDate().toString(transactionDTO.getFormatter())); "error.msg." + resourceTypeName + ".transaction.in.the.future", defaultUserMessage, "transactionDate", transactionDTO
.getTransactionDate().toString(transactionDTO.getFormatter()));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error); dataValidationErrors.add(error);
@@ -790,8 +796,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
if (runningBalance.isLessThanZero()) { if (runningBalance.isLessThanZero()) {
// //
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(depositAccountType()
.resource(depositAccountType().resourceName() + transactionAction); .resourceName() + transactionAction);
if (this.allowOverdraft) { if (this.allowOverdraft) {
Money limit = runningBalance.zero(); Money limit = runningBalance.zero();
@@ -1316,7 +1322,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
if (transactionToUndo == null) { throw new SavingsAccountTransactionNotFoundException(this.getId(), transactionId); } if (transactionToUndo == null) { throw new SavingsAccountTransactionNotFoundException(this.getId(), transactionId); }
validateAttemptToUndoTransferRelatedTransactions(transactionToUndo); validateAttemptToUndoTransferRelatedTransactions(transactionToUndo);
validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, transactionToUndo.transactionLocalDate()); validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_UNDO_TRANSACTION, transactionToUndo.transactionLocalDate());
transactionToUndo.reverse(); transactionToUndo.reverse();
@@ -1480,8 +1486,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(); final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>();
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(depositAccountType()
.resource(depositAccountType().resourceName() + SavingsApiConstants.activateAction); .resourceName() + SavingsApiConstants.activateAction);
final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status); final SavingsAccountStatusType currentStatus = SavingsAccountStatusType.fromInt(this.status);
if (!SavingsAccountStatusType.APPROVED.hasStateOf(currentStatus)) { if (!SavingsAccountStatusType.APPROVED.hasStateOf(currentStatus)) {
@@ -1553,10 +1559,6 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_ACTIVATE, activationDate); validateActivityNotBeforeClientOrGroupTransferDate(SavingsEvent.SAVINGS_ACTIVATE, activationDate);
// auto enter deposit for minimum required opening balance when
// activating account.
processAccountUponActivation();
return actualChanges; return actualChanges;
} }
@@ -1592,11 +1594,25 @@ public class SavingsAccount extends AbstractPersistable<Long> {
} }
private void payActivationCharges() { private void payActivationCharges() {
boolean isSavingsChargeApplied = false;
for (SavingsAccountCharge savingsAccountCharge : this.charges()) { for (SavingsAccountCharge savingsAccountCharge : this.charges()) {
if (savingsAccountCharge.isSavingsActivation()) { if (savingsAccountCharge.isSavingsActivation()) {
isSavingsChargeApplied = true;
payCharge(savingsAccountCharge, savingsAccountCharge.getAmountOutstanding(getCurrency()), getActivationLocalDate()); payCharge(savingsAccountCharge, savingsAccountCharge.getAmountOutstanding(getCurrency()), getActivationLocalDate());
} }
} }
if (isSavingsChargeApplied) {
final MathContext mc = MathContext.DECIMAL64;
boolean isInterestTransfer = false;
if (this.isBeforeLastPostingPeriod(getActivationLocalDate())) {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.postInterest(mc, today, isInterestTransfer);
} else {
final LocalDate today = DateUtils.getLocalDateOfTenant();
this.calculateInterestUsing(mc, today, isInterestTransfer);
}
}
} }
public Map<String, Object> close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { public Map<String, Object> close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) {
@@ -2027,7 +2043,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
baseDataValidator.reset().parameter(SavingsApiConstants.overdraftLimitParamName).value(this.overdraftLimit) baseDataValidator.reset().parameter(SavingsApiConstants.overdraftLimitParamName).value(this.overdraftLimit)
.failWithCode("cannot.exceed.product.value"); .failWithCode("cannot.exceed.product.value");
} }
validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator); validateInterestPostingAndCompoundingPeriodTypes(baseDataValidator);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -2054,7 +2070,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
return getActivationLocalDate().isEqual(getSubmittedOnLocalDate()); return getActivationLocalDate().isEqual(getSubmittedOnLocalDate());
} }
public void validateInterestPostingAndCompoundingPeriodTypes(final DataValidatorBuilder baseDataValidator) { public void validateInterestPostingAndCompoundingPeriodTypes(final DataValidatorBuilder baseDataValidator) {
Map<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>> postingtoCompoundMap = new HashMap<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>>(); Map<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>> postingtoCompoundMap = new HashMap<SavingsPostingInterestPeriodType, List<SavingsCompoundingInterestPeriodType>>();
postingtoCompoundMap.put( postingtoCompoundMap.put(