Skip to content

Commit

Permalink
FINERACT-1960: GL account mappings to use Savings product with Accrua…
Browse files Browse the repository at this point in the history
…l accounting
  • Loading branch information
Jose Alberto Hernandez committed Aug 21, 2023
1 parent 91751eb commit 30423b6
Show file tree
Hide file tree
Showing 25 changed files with 1,384 additions and 934 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.common;

public class AccountingValidations {

protected AccountingValidations() {}

public static boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}

public static boolean isAccrualPeriodicBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingRuleType);
}

public static boolean isUpfrontAccrualAccounting(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingRuleType);
}

public static boolean isAccrualBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingRuleType)
|| AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingRuleType);
}

public static boolean isCashOrAccrualBasedAccounting(final Integer accountingRuleType) {
return isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,14 @@ private DepositsApiConstants() {
interestPostingPeriodTypeParamName, interestCalculationTypeParamName, interestCalculationDaysInYearTypeParamName,
lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName, accountingRuleParamName, chargesParamName,
SavingProductAccountingParams.INCOME_FROM_FEES.getValue(), SavingProductAccountingParams.INCOME_FROM_PENALTIES.getValue(),
SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(),
SavingProductAccountingParams.INTEREST_ON_SAVINGS.getValue(), SavingProductAccountingParams.FEES_RECEIVABLE.getValue(),
SavingProductAccountingParams.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(),
SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(), SavingProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), chartsParamName,
SavingsApiConstants.withHoldTaxParamName, SavingsApiConstants.taxGroupIdParamName));
SavingProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), SavingProductAccountingParams.SAVINGS_CONTROL.getValue(),
SavingProductAccountingParams.TRANSFERS_SUSPENSE.getValue(), SavingProductAccountingParams.SAVINGS_REFERENCE.getValue(),
SavingProductAccountingParams.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),
SavingProductAccountingParams.INTEREST_PAYABLE.getValue(), chartsParamName, SavingsApiConstants.withHoldTaxParamName,
SavingsApiConstants.taxGroupIdParamName));

private static final Set<String> PRECLOSURE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(preClosurePenalApplicableParamName, preClosurePenalInterestParamName, preClosurePenalInterestOnTypeIdParamName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ public enum SavingsAccountTransactionType {
WITHDRAWAL_FEE(4, "savingsAccountTransactionType.withdrawalFee", TransactionEntryType.DEBIT), //
ANNUAL_FEE(5, "savingsAccountTransactionType.annualFee", TransactionEntryType.DEBIT), //
WAIVE_CHARGES(6, "savingsAccountTransactionType.waiveCharge"), //
PAY_CHARGE(7, "savingsAccountTransactionType.payCharge", TransactionEntryType.DEBIT), //
DIVIDEND_PAYOUT(8, "savingsAccountTransactionType.dividendPayout", TransactionEntryType.CREDIT), //
PAY_CHARGE(7, "savingsAccountTransactionType.payCharge"), //
DIVIDEND_PAYOUT(8, "savingsAccountTransactionType.dividendPayout"), //
ACCRUAL(10, "savingsAccountTransactionType.accrual"), //
INITIATE_TRANSFER(12, "savingsAccountTransactionType.initiateTransfer"), //
APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), //
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
Expand Down Expand Up @@ -94,9 +95,33 @@ public boolean isDebitEntryType() {
return entryType != null && entryType.isDebit();
}

public static SavingsAccountTransactionType fromInt(final Integer value) {
SavingsAccountTransactionType transactionType = BY_ID.get(value);
return transactionType == null ? INVALID : transactionType;
public static SavingsAccountTransactionType fromInt(final Integer transactionType) {

if (transactionType == null) {
return SavingsAccountTransactionType.INVALID;
}
return switch (transactionType) {
case 1 -> SavingsAccountTransactionType.DEPOSIT;
case 2 -> SavingsAccountTransactionType.WITHDRAWAL;
case 3 -> SavingsAccountTransactionType.INTEREST_POSTING;
case 4 -> SavingsAccountTransactionType.WITHDRAWAL_FEE;
case 5 -> SavingsAccountTransactionType.ANNUAL_FEE;
case 6 -> SavingsAccountTransactionType.WAIVE_CHARGES;
case 7 -> SavingsAccountTransactionType.PAY_CHARGE;
case 8 -> SavingsAccountTransactionType.DIVIDEND_PAYOUT;
case 10 -> SavingsAccountTransactionType.ACCRUAL;
case 12 -> SavingsAccountTransactionType.INITIATE_TRANSFER;
case 13 -> SavingsAccountTransactionType.APPROVE_TRANSFER;
case 14 -> SavingsAccountTransactionType.WITHDRAW_TRANSFER;
case 15 -> SavingsAccountTransactionType.REJECT_TRANSFER;
case 16 -> SavingsAccountTransactionType.WRITTEN_OFF;
case 17 -> SavingsAccountTransactionType.OVERDRAFT_INTEREST;
case 18 -> SavingsAccountTransactionType.WITHHOLD_TAX;
case 19 -> SavingsAccountTransactionType.ESCHEAT;
case 20 -> SavingsAccountTransactionType.AMOUNT_HOLD;
case 21 -> SavingsAccountTransactionType.AMOUNT_RELEASE;
default -> SavingsAccountTransactionType.INVALID;
};
}

public boolean isDeposit() {
Expand All @@ -111,6 +136,10 @@ public boolean isInterestPosting() {
return this == INTEREST_POSTING;
}

public boolean isAccrual() {
return this.equals(SavingsAccountTransactionType.ACCRUAL);
}

public boolean isOverDraftInterestPosting() {
return this == OVERDRAFT_INTEREST;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ public class SavingsAccountTransactionEnumData implements Serializable {
private final boolean escheat;
private final boolean amountHold;
private final boolean amountRelease;
private final boolean accrual;

public SavingsAccountTransactionEnumData(final Long id, final String code, final String value) {
this.id = id;
this.code = code;
this.value = value;
this.deposit = Long.valueOf(SavingsAccountTransactionType.DEPOSIT.getValue()).equals(this.id);
this.dividendPayout = Long.valueOf(SavingsAccountTransactionType.DIVIDEND_PAYOUT.getValue()).equals(this.id);
this.accrual = Long.valueOf(SavingsAccountTransactionType.ACCRUAL.getValue()).equals(this.id);
this.withdrawal = Long.valueOf(SavingsAccountTransactionType.WITHDRAWAL.getValue()).equals(this.id);
this.interestPosting = Long.valueOf(SavingsAccountTransactionType.INTEREST_POSTING.getValue()).equals(this.id);
this.feeDeduction = Long.valueOf(SavingsAccountTransactionType.ANNUAL_FEE.getValue()).equals(this.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import org.apache.fineract.accounting.common.AccountingConstants.LoanProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingConstants.SavingProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingConstants.SharesProductAccountingParams;
import org.apache.fineract.accounting.common.AccountingRuleType;
import org.apache.fineract.accounting.common.AccountingValidations;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
Expand Down Expand Up @@ -84,7 +84,7 @@ public void validateForLoanProductCreate(final String json) {
final Integer accountingRuleType = this.fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 4);

if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
if (AccountingValidations.isCashBasedAccounting(accountingRuleType)) {

final Long fundAccountId = this.fromApiJsonHelper.extractLongNamed(LoanProductAccountingParams.FUND_SOURCE.getValue(), element);
baseDataValidator.reset().parameter(LoanProductAccountingParams.FUND_SOURCE.getValue()).value(fundAccountId).notNull()
Expand Down Expand Up @@ -132,7 +132,7 @@ public void validateForLoanProductCreate(final String json) {

}

if (isAccrualBasedAccounting(accountingRuleType)) {
if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {

final Long receivableInterestAccountId = this.fromApiJsonHelper
.extractLongNamed(LoanProductAccountingParams.INTEREST_RECEIVABLE.getValue(), element);
Expand Down Expand Up @@ -169,7 +169,7 @@ public void validateForSavingsProductCreate(final String json, DepositAccountTyp
Locale.getDefault());
baseDataValidator.reset().parameter(accountingRuleParamName).value(accountingRuleType).notNull().inMinMaxRange(1, 3);

if (isCashBasedAccounting(accountingRuleType)) {
if (AccountingValidations.isCashOrAccrualBasedAccounting(accountingRuleType)) {

final Long savingsControlAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.SAVINGS_CONTROL.getValue(), element);
Expand Down Expand Up @@ -225,7 +225,24 @@ public void validateForSavingsProductCreate(final String json, DepositAccountTyp
baseDataValidator.reset().parameter(SavingProductAccountingParams.LOSSES_WRITTEN_OFF.getValue()).value(writtenOff).notNull()
.integerGreaterThanZero();
}
}

// Periodic Accrual Accounting aditional GL Accounts
if (AccountingValidations.isAccrualBasedAccounting(accountingRuleType)) {
final Long feeReceivableAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.FEES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.FEES_RECEIVABLE.getValue()).value(feeReceivableAccountId)
.notNull().integerGreaterThanZero();

final Long penaltyReceivableAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.PENALTIES_RECEIVABLE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.PENALTIES_RECEIVABLE.getValue())
.value(penaltyReceivableAccountId).notNull().integerGreaterThanZero();

final Long interestPayableAccountId = this.fromApiJsonHelper
.extractLongNamed(SavingProductAccountingParams.INTEREST_PAYABLE.getValue(), element);
baseDataValidator.reset().parameter(SavingProductAccountingParams.INTEREST_PAYABLE.getValue()).value(interestPayableAccountId)
.notNull().integerGreaterThanZero();
}

throwExceptionIfValidationWarningsExist(dataValidationErrors);
Expand All @@ -247,7 +264,7 @@ public void validateForShareProductCreate(final String json) {
Locale.getDefault());
baseDataValidator.reset().parameter(accountingRuleParamName).value(accountingRuleType).notNull().inMinMaxRange(1, 3);

if (isCashBasedAccounting(accountingRuleType)) {
if (AccountingValidations.isCashBasedAccounting(accountingRuleType)) {

final Long shareReferenceId = this.fromApiJsonHelper.extractLongNamed(SharesProductAccountingParams.SHARES_REFERENCE.getValue(),
element);
Expand All @@ -274,15 +291,6 @@ public void validateForShareProductCreate(final String json) {
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}

private boolean isCashBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.CASH_BASED.getValue().equals(accountingRuleType);
}

private boolean isAccrualBasedAccounting(final Integer accountingRuleType) {
return AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingRuleType)
|| AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingRuleType);
}

private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
Expand Down
Loading

0 comments on commit 30423b6

Please sign in to comment.