FINERACT-33 : savings withhold tax changes
This commit is contained in:
+118
-1
@@ -26,6 +26,7 @@ import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -33,6 +34,8 @@ import java.util.Locale;
|
||||
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.TaxComponentHelper;
|
||||
import org.apache.fineract.integrationtests.common.TaxGroupHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
||||
@@ -789,7 +792,106 @@ public class ClientSavingsIntegrationTest {
|
||||
assertEquals("validation.msg.savingsaccount.close.results.in.balance.not.zero",
|
||||
savingsAccountErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavingsAccount_WITH_WITHHOLD_TAX() {
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientID);
|
||||
final String minBalanceForInterestCalculation = null;
|
||||
final String minRequiredBalance = null;
|
||||
final String enforceMinRequiredBalance = "false";
|
||||
final boolean allowOverdraft = false;
|
||||
final String percentage = "10";
|
||||
final Integer taxGroupId = createTaxGroup(percentage);
|
||||
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
|
||||
minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft, String.valueOf(taxGroupId));
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
HashMap modifications = this.savingsAccountHelper.updateSavingsAccount(clientID, savingsProductID, savingsId,
|
||||
ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertTrue(modifications.containsKey("submittedOnDate"));
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
|
||||
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
|
||||
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
|
||||
final HashMap summaryBefore = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
this.savingsAccountHelper.calculateInterestForSavings(savingsId);
|
||||
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
assertEquals(summaryBefore, summary);
|
||||
|
||||
this.savingsAccountHelper.postInterestForSavings(savingsId);
|
||||
summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
Assert.assertFalse(summaryBefore.equals(summary));
|
||||
Assert.assertNotNull(summary.get("totalWithholdTax"));
|
||||
Float expected = (Float) summary.get("totalDeposits") + (Float) summary.get("totalInterestPosted")
|
||||
- (Float) summary.get("totalWithholdTax");
|
||||
Float actual = (Float) summary.get("accountBalance");
|
||||
Assert.assertEquals(expected, actual, 1);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSavingsAccount_WITH_WITHHOLD_TAX_DISABLE_AT_ACCOUNT_LEVEL() {
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientID);
|
||||
final String minBalanceForInterestCalculation = null;
|
||||
final String minRequiredBalance = null;
|
||||
final String enforceMinRequiredBalance = "false";
|
||||
final boolean allowOverdraft = false;
|
||||
final String percentage = "10";
|
||||
final Integer taxGroupId = createTaxGroup(percentage);
|
||||
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE,
|
||||
minBalanceForInterestCalculation, minRequiredBalance, enforceMinRequiredBalance, allowOverdraft, String.valueOf(taxGroupId));
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientID, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
HashMap modifications = this.savingsAccountHelper.updateSavingsAccount(clientID, savingsProductID, savingsId,
|
||||
ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertTrue(modifications.containsKey("submittedOnDate"));
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
|
||||
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
|
||||
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
|
||||
final HashMap summaryBefore = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
this.savingsAccountHelper.calculateInterestForSavings(savingsId);
|
||||
HashMap summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
assertEquals(summaryBefore, summary);
|
||||
|
||||
final HashMap changes = this.savingsAccountHelper.updateSavingsAccountWithHoldTaxStatus(savingsId, false);
|
||||
Assert.assertTrue(changes.containsKey("withHoldTax"));
|
||||
|
||||
this.savingsAccountHelper.postInterestForSavings(savingsId);
|
||||
summary = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
Assert.assertFalse(summaryBefore.equals(summary));
|
||||
Assert.assertNull(summary.get("totalWithholdTax"));
|
||||
Float expected = (Float) summary.get("totalDeposits") + (Float) summary.get("totalInterestPosted");
|
||||
Float actual = (Float) summary.get("accountBalance");
|
||||
Assert.assertEquals(expected, actual, 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private HashMap activateSavingsAccount(final Integer savingsId, final String activationDate) {
|
||||
final HashMap status = this.savingsAccountHelper.activateSavingsAccount(savingsId, activationDate);
|
||||
return status;
|
||||
@@ -798,6 +900,14 @@ public class ClientSavingsIntegrationTest {
|
||||
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String minOpenningBalance, String minBalanceForInterestCalculation, String minRequiredBalance,
|
||||
String enforceMinRequiredBalance, final boolean allowOverdraft) {
|
||||
final String taxGroupId = null;
|
||||
return createSavingsProduct(requestSpec, responseSpec, minOpenningBalance, minBalanceForInterestCalculation, minRequiredBalance,
|
||||
enforceMinRequiredBalance, allowOverdraft, taxGroupId);
|
||||
}
|
||||
|
||||
private Integer createSavingsProduct(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String minOpenningBalance, String minBalanceForInterestCalculation, String minRequiredBalance,
|
||||
String enforceMinRequiredBalance, final boolean allowOverdraft, final String taxGroupId) {
|
||||
System.out.println("------------------------------CREATING NEW SAVINGS PRODUCT ---------------------------------------");
|
||||
SavingsProductHelper savingsProductHelper = new SavingsProductHelper();
|
||||
if (allowOverdraft) {
|
||||
@@ -816,9 +926,16 @@ public class ClientSavingsIntegrationTest {
|
||||
.withMinBalanceForInterestCalculation(minBalanceForInterestCalculation)
|
||||
//
|
||||
.withMinRequiredBalance(minRequiredBalance).withEnforceMinRequiredBalance(enforceMinRequiredBalance)
|
||||
.withMinimumOpenningBalance(minOpenningBalance).build();
|
||||
.withMinimumOpenningBalance(minOpenningBalance).withWithHoldTax(taxGroupId).build();
|
||||
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
private Integer createTaxGroup(final String percentage){
|
||||
final Integer liabilityAccountId = null;
|
||||
final Integer taxComponentId = TaxComponentHelper.createTaxComponent(this.requestSpec, this.responseSpec, percentage, liabilityAccountId);
|
||||
return TaxGroupHelper.createTaxGroup(this.requestSpec, this.responseSpec, Arrays.asList(taxComponentId));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* private void verifySavingsInterest(final Object savingsInterest) {
|
||||
|
||||
+305
@@ -27,6 +27,7 @@ import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@@ -36,6 +37,9 @@ import java.util.Locale;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||
import org.apache.fineract.integrationtests.common.TaxComponentHelper;
|
||||
import org.apache.fineract.integrationtests.common.TaxGroupHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
@@ -240,6 +244,7 @@ public class FixedDepositTest {
|
||||
this.responseSpec, fixedDepositAccountId.toString());
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsPrematureClosed(fixedDepositAccountStatusHashMap);
|
||||
|
||||
|
||||
/***
|
||||
* Verify journal entry transactions for preclosure transaction
|
||||
*/
|
||||
@@ -253,6 +258,284 @@ public class FixedDepositTest {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFixedDepositAccountWithPrematureClosureTypeWithdrawal_WITH_HOLD_TAX() {
|
||||
this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
/***
|
||||
* Create GL Accounts for product account mapping
|
||||
*/
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
|
||||
final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
|
||||
DateFormat currentDateFormat = new SimpleDateFormat("dd");
|
||||
|
||||
Calendar todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -3);
|
||||
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
|
||||
todaysDate.add(Calendar.YEAR, 10);
|
||||
final String VALID_TO = dateFormat.format(todaysDate.getTime());
|
||||
|
||||
todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -1);
|
||||
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
|
||||
|
||||
Integer currentDate = new Integer(currentDateFormat.format(todaysDate.getTime()));
|
||||
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
|
||||
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
|
||||
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
|
||||
final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime());
|
||||
|
||||
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientId);
|
||||
|
||||
/***
|
||||
* Create FD product with CashBased accounting enabled
|
||||
*/
|
||||
final String accountingRule = CASH_BASED;
|
||||
final Integer taxGroupId = createTaxGroup("10", liabilityAccountForTax);
|
||||
Integer fixedDepositProductId = createFixedDepositProductWithWithHoldTax(VALID_FROM, VALID_TO, String.valueOf(taxGroupId),
|
||||
accountingRule, assetAccount, liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(fixedDepositProductId);
|
||||
|
||||
/***
|
||||
* Apply for FD account with created product and verify status
|
||||
*/
|
||||
Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(),
|
||||
SUBMITTED_ON_DATE, WHOLE_TERM);
|
||||
Assert.assertNotNull(fixedDepositAccountId);
|
||||
|
||||
HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
|
||||
this.responseSpec, fixedDepositAccountId.toString());
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the FD account and verify whether account is approved
|
||||
*/
|
||||
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE);
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the FD Account and verify whether account is activated
|
||||
*/
|
||||
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE);
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
|
||||
|
||||
HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId);
|
||||
|
||||
Float depositAmount = (Float) accountSummary.get("totalDeposits");
|
||||
|
||||
/***
|
||||
* Verify journal entries posted for initial deposit transaction which
|
||||
* happened at activation time
|
||||
*/
|
||||
final JournalEntry[] assetAccountInitialEntry = { new JournalEntry(depositAmount, JournalEntry.TransactionType.DEBIT) };
|
||||
final JournalEntry[] liablilityAccountInitialEntry = { new JournalEntry(depositAmount, JournalEntry.TransactionType.CREDIT) };
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, ACTIVATION_DATE, assetAccountInitialEntry);
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, ACTIVATION_DATE, liablilityAccountInitialEntry);
|
||||
|
||||
/***
|
||||
* Update interest earned of FD account
|
||||
*/
|
||||
fixedDepositAccountId = this.fixedDepositAccountHelper.calculateInterestForFixedDeposit(fixedDepositAccountId);
|
||||
Assert.assertNotNull(fixedDepositAccountId);
|
||||
|
||||
/***
|
||||
* Post interest and verify the account summary
|
||||
*/
|
||||
Integer transactionIdForPostInterest = this.fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId);
|
||||
Assert.assertNotNull(transactionIdForPostInterest);
|
||||
|
||||
accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId);
|
||||
Float totalInterestPosted = (Float) accountSummary.get("totalInterestPosted");
|
||||
Assert.assertNull(accountSummary.get("totalWithholdTax"));
|
||||
|
||||
/***
|
||||
* Verify journal entries transactions for interest posting transaction
|
||||
*/
|
||||
final JournalEntry[] expenseAccountEntry = { new JournalEntry(totalInterestPosted, JournalEntry.TransactionType.DEBIT) };
|
||||
final JournalEntry[] liablilityAccountEntry = { new JournalEntry(totalInterestPosted, JournalEntry.TransactionType.CREDIT) };
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(expenseAccount, INTEREST_POSTED_DATE, expenseAccountEntry);
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, INTEREST_POSTED_DATE, liablilityAccountEntry);
|
||||
|
||||
/***
|
||||
* Preclose the FD account verify whether account is preClosed
|
||||
*/
|
||||
HashMap fixedDepositPrematureData = this.fixedDepositAccountHelper.calculatePrematureAmountForFixedDeposit(fixedDepositAccountId,
|
||||
CLOSED_ON_DATE);
|
||||
|
||||
Integer prematureClosureTransactionId = (Integer) this.fixedDepositAccountHelper.prematureCloseForFixedDeposit(
|
||||
fixedDepositAccountId, CLOSED_ON_DATE, CLOSURE_TYPE_WITHDRAW_DEPOSIT, null, CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
Assert.assertNotNull(prematureClosureTransactionId);
|
||||
|
||||
fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
|
||||
this.responseSpec, fixedDepositAccountId.toString());
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsPrematureClosed(fixedDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Verify journal entry transactions for preclosure transaction
|
||||
*/
|
||||
HashMap accountDetails = this.fixedDepositAccountHelper.getFixedDepositAccountById(this.requestSpec, this.responseSpec,
|
||||
fixedDepositAccountId);
|
||||
Float maturityAmount = Float.valueOf(accountDetails.get("maturityAmount").toString());
|
||||
|
||||
HashMap summary = (HashMap) accountDetails.get("summary");
|
||||
Assert.assertNotNull(summary.get("totalWithholdTax"));
|
||||
Float withHoldTax = (Float) summary.get("totalWithholdTax");
|
||||
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, CLOSED_ON_DATE, new JournalEntry(maturityAmount,
|
||||
JournalEntry.TransactionType.CREDIT));
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, CLOSED_ON_DATE, new JournalEntry(maturityAmount,
|
||||
JournalEntry.TransactionType.DEBIT));
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccountForTax, CLOSED_ON_DATE, new JournalEntry(withHoldTax,
|
||||
JournalEntry.TransactionType.CREDIT));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFixedDepositAccountClosureTypeWithdrawal_WITH_HOLD_TAX() throws InterruptedException {
|
||||
this.fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.fixedDepositAccountHelper = new FixedDepositAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
/***
|
||||
* Create GL Accounts for product account mapping
|
||||
*/
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
|
||||
final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
|
||||
DateFormat currentDateFormat = new SimpleDateFormat("dd");
|
||||
|
||||
Calendar todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -20);
|
||||
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
|
||||
todaysDate.add(Calendar.YEAR, 10);
|
||||
final String VALID_TO = dateFormat.format(todaysDate.getTime());
|
||||
|
||||
todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -20);
|
||||
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
|
||||
|
||||
Integer currentDate = new Integer(currentDateFormat.format(todaysDate.getTime()));
|
||||
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
|
||||
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
|
||||
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
|
||||
Calendar closedOn = Calendar.getInstance();
|
||||
closedOn.add(Calendar.MONTH, -6);
|
||||
final String CLOSED_ON_DATE = dateFormat.format(closedOn.getTime());
|
||||
|
||||
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientId);
|
||||
|
||||
/***
|
||||
* Create FD product with CashBased accounting enabled
|
||||
*/
|
||||
final String accountingRule = CASH_BASED;
|
||||
final Integer taxGroupId = createTaxGroup("10", liabilityAccountForTax);
|
||||
Integer fixedDepositProductId = createFixedDepositProductWithWithHoldTax(VALID_FROM, VALID_TO, String.valueOf(taxGroupId),
|
||||
accountingRule, assetAccount, liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(fixedDepositProductId);
|
||||
|
||||
/***
|
||||
* Apply for FD account with created product and verify status
|
||||
*/
|
||||
Integer fixedDepositAccountId = applyForFixedDepositApplication(clientId.toString(), fixedDepositProductId.toString(),
|
||||
SUBMITTED_ON_DATE, WHOLE_TERM);
|
||||
Assert.assertNotNull(fixedDepositAccountId);
|
||||
|
||||
HashMap fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
|
||||
this.responseSpec, fixedDepositAccountId.toString());
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsPending(fixedDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the FD account and verify whether account is approved
|
||||
*/
|
||||
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.approveFixedDeposit(fixedDepositAccountId, APPROVED_ON_DATE);
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsApproved(fixedDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the FD Account and verify whether account is activated
|
||||
*/
|
||||
fixedDepositAccountStatusHashMap = this.fixedDepositAccountHelper.activateFixedDeposit(fixedDepositAccountId, ACTIVATION_DATE);
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositIsActive(fixedDepositAccountStatusHashMap);
|
||||
|
||||
HashMap accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId);
|
||||
|
||||
Float depositAmount = (Float) accountSummary.get("totalDeposits");
|
||||
|
||||
/***
|
||||
* Verify journal entries posted for initial deposit transaction which
|
||||
* happened at activation time
|
||||
*/
|
||||
final JournalEntry[] assetAccountInitialEntry = { new JournalEntry(depositAmount, JournalEntry.TransactionType.DEBIT) };
|
||||
final JournalEntry[] liablilityAccountInitialEntry = { new JournalEntry(depositAmount, JournalEntry.TransactionType.CREDIT) };
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, ACTIVATION_DATE, assetAccountInitialEntry);
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, ACTIVATION_DATE, liablilityAccountInitialEntry);
|
||||
|
||||
/***
|
||||
* Update interest earned of FD account
|
||||
*/
|
||||
fixedDepositAccountId = this.fixedDepositAccountHelper.calculateInterestForFixedDeposit(fixedDepositAccountId);
|
||||
Assert.assertNotNull(fixedDepositAccountId);
|
||||
|
||||
/***
|
||||
* Post interest and verify the account summary
|
||||
*/
|
||||
Integer transactionIdForPostInterest = this.fixedDepositAccountHelper.postInterestForFixedDeposit(fixedDepositAccountId);
|
||||
Assert.assertNotNull(transactionIdForPostInterest);
|
||||
|
||||
accountSummary = this.fixedDepositAccountHelper.getFixedDepositSummary(fixedDepositAccountId);
|
||||
Float totalInterestPosted = (Float) accountSummary.get("totalInterestPosted");
|
||||
Assert.assertNull(accountSummary.get("totalWithholdTax"));
|
||||
|
||||
/***
|
||||
* FD account verify whether account is matured
|
||||
*/
|
||||
|
||||
SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec, responseSpec);
|
||||
String JobName = "Update Deposit Accounts Maturity details";
|
||||
schedulerJobHelper.executeJob(JobName);
|
||||
|
||||
HashMap accountDetails = this.fixedDepositAccountHelper.getFixedDepositAccountById(this.requestSpec, this.responseSpec,
|
||||
fixedDepositAccountId);
|
||||
|
||||
HashMap summary = (HashMap) accountDetails.get("summary");
|
||||
Assert.assertNotNull(summary.get("totalWithholdTax"));
|
||||
Float withHoldTax = (Float) summary.get("totalWithholdTax");
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccountForTax, CLOSED_ON_DATE, new JournalEntry(withHoldTax,
|
||||
JournalEntry.TransactionType.CREDIT));
|
||||
|
||||
|
||||
fixedDepositAccountStatusHashMap = FixedDepositAccountStatusChecker.getStatusOfFixedDepositAccount(this.requestSpec,
|
||||
this.responseSpec, fixedDepositAccountId.toString());
|
||||
FixedDepositAccountStatusChecker.verifyFixedDepositAccountIsMatured(fixedDepositAccountStatusHashMap);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFixedDepositAccountWithPeriodInterestRateChart() {
|
||||
final String chartToUse = "period";
|
||||
@@ -2139,6 +2422,22 @@ public class FixedDepositTest {
|
||||
return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
|
||||
private Integer createFixedDepositProductWithWithHoldTax(final String validFrom, final String validTo, final String taxGroupId,
|
||||
final String accountingRule, Account... accounts) {
|
||||
System.out.println("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------");
|
||||
FixedDepositProductHelper fixedDepositProductHelper = new FixedDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
if (accountingRule.equals(CASH_BASED)) {
|
||||
fixedDepositProductHelper = fixedDepositProductHelper.withAccountingRuleAsCashBased(accounts);
|
||||
} else if (accountingRule.equals(NONE)) {
|
||||
fixedDepositProductHelper = fixedDepositProductHelper.withAccountingRuleAsNone();
|
||||
}
|
||||
final String fixedDepositProductJSON = fixedDepositProductHelper.withPeriodRangeChart() //
|
||||
.withWithHoldTax(taxGroupId)//
|
||||
.build(validFrom, validTo);
|
||||
return FixedDepositProductHelper.createFixedDepositProduct(fixedDepositProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
private Integer createFixedDepositProduct(final String validFrom, final String validTo, final String accountingRule,
|
||||
final String chartToBePicked, Account... accounts) {
|
||||
System.out.println("------------------------------CREATING NEW FIXED DEPOSIT PRODUCT ---------------------------------------");
|
||||
@@ -2262,6 +2561,12 @@ public class FixedDepositTest {
|
||||
Assert.assertEquals(financialActivityId, ((HashMap) mappingDetails.get("financialActivityData")).get("id"));
|
||||
Assert.assertEquals(glAccount.getAccountID(), ((HashMap) mappingDetails.get("glAccountData")).get("id"));
|
||||
}
|
||||
|
||||
private Integer createTaxGroup(final String percentage, final Account liabilityAccountForTax){
|
||||
final Integer liabilityAccountId = liabilityAccountForTax.getAccountID();
|
||||
final Integer taxComponentId = TaxComponentHelper.createTaxComponent(this.requestSpec, this.responseSpec, percentage, liabilityAccountId);
|
||||
return TaxGroupHelper.createTaxGroup(this.requestSpec, this.responseSpec, Arrays.asList(taxComponentId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the Liability transfer account
|
||||
|
||||
+404
@@ -25,6 +25,7 @@ import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -35,6 +36,9 @@ import net.sf.ehcache.transaction.xa.EhcacheXAException;
|
||||
import org.apache.fineract.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||
import org.apache.fineract.integrationtests.common.TaxComponentHelper;
|
||||
import org.apache.fineract.integrationtests.common.TaxGroupHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||
@@ -472,6 +476,384 @@ public class RecurringDepositTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecurringDepositAccountWithPrematureClosureTypeTransferToSavings_WITH_HOLD_TAX() {
|
||||
this.recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.recurringDepositAccountHelper = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
|
||||
/***
|
||||
* Create GL Accounts for product account mapping
|
||||
*/
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
|
||||
final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
|
||||
DateFormat currentDateFormat = new SimpleDateFormat("dd");
|
||||
|
||||
Calendar todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -3);
|
||||
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
|
||||
todaysDate.add(Calendar.YEAR, 10);
|
||||
final String VALID_TO = dateFormat.format(todaysDate.getTime());
|
||||
|
||||
todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -1);
|
||||
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String EXPECTED_FIRST_DEPOSIT_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
|
||||
|
||||
Integer currentDate = new Integer(currentDateFormat.format(todaysDate.getTime()));
|
||||
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
|
||||
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
|
||||
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
|
||||
final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String CLOSED_ON_DATE = dateFormat.format(Calendar.getInstance().getTime());
|
||||
|
||||
/***
|
||||
* Create client for applying Deposit and Savings accounts
|
||||
*/
|
||||
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientId);
|
||||
|
||||
/***
|
||||
* Create Savings product with CashBased accounting enabled
|
||||
*/
|
||||
final String accountingRule = CASH_BASED;
|
||||
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, accountingRule,
|
||||
assetAccount, liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
/***
|
||||
* Create Savings account and verify status is pending
|
||||
*/
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientId, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the savings account and verify account is approved
|
||||
*/
|
||||
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the savings account and verify account is activated
|
||||
*/
|
||||
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Create RD product with CashBased accounting enabled
|
||||
*/
|
||||
final Integer taxGroupId = createTaxGroup("10", liabilityAccountForTax);
|
||||
Integer recurringDepositProductId = createRecurringDepositProductWithWithHoldTax(VALID_FROM, VALID_TO,String.valueOf(taxGroupId), accountingRule, assetAccount,
|
||||
liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(recurringDepositProductId);
|
||||
|
||||
/***
|
||||
* Apply for RD account with created product and verify status
|
||||
*/
|
||||
Integer recurringDepositAccountId = applyForRecurringDepositApplication(clientId.toString(), recurringDepositProductId.toString(),
|
||||
VALID_FROM, VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM, EXPECTED_FIRST_DEPOSIT_ON_DATE);
|
||||
Assert.assertNotNull(recurringDepositAccountId);
|
||||
|
||||
HashMap recurringDepositAccountStatusHashMap = RecurringDepositAccountStatusChecker.getStatusOfRecurringDepositAccount(
|
||||
this.requestSpec, this.responseSpec, recurringDepositAccountId.toString());
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsPending(recurringDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the RD account and verify whether account is approved
|
||||
*/
|
||||
recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.approveRecurringDeposit(recurringDepositAccountId,
|
||||
APPROVED_ON_DATE);
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsApproved(recurringDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the RD Account and verify whether account is activated
|
||||
*/
|
||||
recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.activateRecurringDeposit(recurringDepositAccountId,
|
||||
ACTIVATION_DATE);
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsActive(recurringDepositAccountStatusHashMap);
|
||||
|
||||
HashMap recurringDepositAccountData = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId);
|
||||
Float depositAmount = (Float) recurringDepositAccountData.get("mandatoryRecommendedDepositAmount");
|
||||
|
||||
/***
|
||||
* Perform Deposit transaction and verify journal entries are posted for
|
||||
* the transaction
|
||||
*/
|
||||
Integer depositTransactionId = this.recurringDepositAccountHelper.depositToRecurringDepositAccount(recurringDepositAccountId,
|
||||
depositAmount, EXPECTED_FIRST_DEPOSIT_ON_DATE);
|
||||
Assert.assertNotNull(depositTransactionId);
|
||||
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, EXPECTED_FIRST_DEPOSIT_ON_DATE, new JournalEntry(
|
||||
depositAmount, JournalEntry.TransactionType.DEBIT));
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, EXPECTED_FIRST_DEPOSIT_ON_DATE, new JournalEntry(
|
||||
depositAmount, JournalEntry.TransactionType.CREDIT));
|
||||
|
||||
/***
|
||||
* Update interest earned field for RD account
|
||||
*/
|
||||
recurringDepositAccountId = this.recurringDepositAccountHelper.calculateInterestForRecurringDeposit(recurringDepositAccountId);
|
||||
Assert.assertNotNull(recurringDepositAccountId);
|
||||
|
||||
/***
|
||||
* Post interest and verify journal entries
|
||||
*/
|
||||
Integer transactionIdForPostInterest = this.recurringDepositAccountHelper
|
||||
.postInterestForRecurringDeposit(recurringDepositAccountId);
|
||||
Assert.assertNotNull(transactionIdForPostInterest);
|
||||
|
||||
|
||||
HashMap accountSummary = this.recurringDepositAccountHelper.getRecurringDepositSummary(recurringDepositAccountId);
|
||||
Float totalInterestPosted = (Float) accountSummary.get("totalInterestPosted");
|
||||
Assert.assertNull(accountSummary.get("totalWithholdTax"));
|
||||
|
||||
|
||||
final JournalEntry[] expenseAccountEntry = { new JournalEntry(totalInterestPosted, JournalEntry.TransactionType.DEBIT) };
|
||||
final JournalEntry[] liablilityAccountEntry = { new JournalEntry(totalInterestPosted, JournalEntry.TransactionType.CREDIT) };
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(expenseAccount, INTEREST_POSTED_DATE, expenseAccountEntry);
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, INTEREST_POSTED_DATE, liablilityAccountEntry);
|
||||
|
||||
/***
|
||||
* Get saving account balance before preClosing RD account
|
||||
*/
|
||||
HashMap savingsSummaryBefore = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
Float balanceBefore = (Float) savingsSummaryBefore.get("accountBalance");
|
||||
|
||||
HashMap recurringDepositPrematureData = this.recurringDepositAccountHelper.calculatePrematureAmountForRecurringDeposit(
|
||||
recurringDepositAccountId, CLOSED_ON_DATE);
|
||||
|
||||
/***
|
||||
* Retrieve mapped financial account for liability transfer
|
||||
*/
|
||||
Account financialAccount = getMappedLiabilityFinancialAccount();
|
||||
|
||||
/***
|
||||
* Preclose the RD account verify whether account is preClosed
|
||||
*/
|
||||
Integer prematureClosureTransactionId = (Integer) this.recurringDepositAccountHelper.prematureCloseForRecurringDeposit(
|
||||
recurringDepositAccountId, CLOSED_ON_DATE, CLOSURE_TYPE_TRANSFER_TO_SAVINGS, savingsId,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
Assert.assertNotNull(prematureClosureTransactionId);
|
||||
|
||||
recurringDepositAccountStatusHashMap = RecurringDepositAccountStatusChecker.getStatusOfRecurringDepositAccount(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId.toString());
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositAccountIsPrematureClosed(recurringDepositAccountStatusHashMap);
|
||||
|
||||
recurringDepositAccountData = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId);
|
||||
Float maturityAmount = Float.valueOf(recurringDepositAccountData.get("maturityAmount").toString());
|
||||
/***
|
||||
* Verify journal entry transactions for preclosure transaction As this
|
||||
* transaction is an account transfer you should get financial account
|
||||
* mapping details and verify amounts
|
||||
*/
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, CLOSED_ON_DATE, new JournalEntry(maturityAmount,
|
||||
JournalEntry.TransactionType.CREDIT), new JournalEntry(maturityAmount, JournalEntry.TransactionType.DEBIT));
|
||||
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(financialAccount, CLOSED_ON_DATE, new JournalEntry(maturityAmount,
|
||||
JournalEntry.TransactionType.DEBIT), new JournalEntry(maturityAmount, JournalEntry.TransactionType.CREDIT));
|
||||
/***
|
||||
* Verify rd account maturity amount and savings account balance
|
||||
*/
|
||||
HashMap recurringDepositData = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId);
|
||||
Float prematurityAmount = (Float) recurringDepositData.get("maturityAmount");
|
||||
HashMap summary = (HashMap) recurringDepositData.get("summary");
|
||||
Assert.assertNotNull(summary.get("totalWithholdTax"));
|
||||
Float withHoldTax = (Float) summary.get("totalWithholdTax");
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccountForTax, CLOSED_ON_DATE, new JournalEntry(withHoldTax,
|
||||
JournalEntry.TransactionType.CREDIT));
|
||||
|
||||
HashMap savingsSummaryAfter = this.savingsAccountHelper.getSavingsSummary(savingsId);
|
||||
Float balanceAfter = (Float) savingsSummaryAfter.get("accountBalance");
|
||||
Float expectedSavingsBalance = balanceBefore + prematurityAmount;
|
||||
|
||||
DecimalFormat decimalFormat = new DecimalFormat("", new DecimalFormatSymbols(Locale.US));
|
||||
decimalFormat.applyPattern(".");
|
||||
|
||||
Assert.assertEquals("Verifying Savings Account Balance after Premature Closure", decimalFormat.format(expectedSavingsBalance),
|
||||
decimalFormat.format(balanceAfter));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testRecurringDepositAccountWithClosureTypeTransferToSavings_WITH_HOLD_TAX() throws InterruptedException {
|
||||
this.recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
this.accountHelper = new AccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
this.recurringDepositAccountHelper = new RecurringDepositAccountHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
|
||||
/***
|
||||
* Create GL Accounts for product account mapping
|
||||
*/
|
||||
final Account assetAccount = this.accountHelper.createAssetAccount();
|
||||
final Account incomeAccount = this.accountHelper.createIncomeAccount();
|
||||
final Account expenseAccount = this.accountHelper.createExpenseAccount();
|
||||
final Account liabilityAccount = this.accountHelper.createLiabilityAccount();
|
||||
final Account liabilityAccountForTax = this.accountHelper.createLiabilityAccount();
|
||||
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
DateFormat monthDayFormat = new SimpleDateFormat("dd MMM", Locale.US);
|
||||
DateFormat currentDateFormat = new SimpleDateFormat("dd");
|
||||
|
||||
Calendar todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -20);
|
||||
final String VALID_FROM = dateFormat.format(todaysDate.getTime());
|
||||
todaysDate.add(Calendar.YEAR, 10);
|
||||
final String VALID_TO = dateFormat.format(todaysDate.getTime());
|
||||
|
||||
todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -20);
|
||||
final String SUBMITTED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String APPROVED_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String ACTIVATION_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String EXPECTED_FIRST_DEPOSIT_ON_DATE = dateFormat.format(todaysDate.getTime());
|
||||
final String MONTH_DAY = monthDayFormat.format(todaysDate.getTime());
|
||||
|
||||
Integer currentDate = new Integer(currentDateFormat.format(todaysDate.getTime()));
|
||||
Integer daysInMonth = todaysDate.getActualMaximum(Calendar.DATE);
|
||||
Integer numberOfDaysLeft = (daysInMonth - currentDate) + 1;
|
||||
todaysDate.add(Calendar.DATE, numberOfDaysLeft);
|
||||
final String INTEREST_POSTED_DATE = dateFormat.format(todaysDate.getTime());
|
||||
Calendar closedOn = Calendar.getInstance();
|
||||
closedOn.add(Calendar.MONTH, -6);
|
||||
final String CLOSED_ON_DATE = dateFormat.format(closedOn.getTime());
|
||||
|
||||
/***
|
||||
* Create client for applying Deposit and Savings accounts
|
||||
*/
|
||||
Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
Assert.assertNotNull(clientId);
|
||||
|
||||
/***
|
||||
* Create Savings product with CashBased accounting enabled
|
||||
*/
|
||||
final String accountingRule = CASH_BASED;
|
||||
final Integer savingsProductID = createSavingsProduct(this.requestSpec, this.responseSpec, MINIMUM_OPENING_BALANCE, accountingRule,
|
||||
assetAccount, liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
/***
|
||||
* Create Savings account and verify status is pending
|
||||
*/
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplication(clientId, savingsProductID, ACCOUNT_TYPE_INDIVIDUAL);
|
||||
Assert.assertNotNull(savingsProductID);
|
||||
|
||||
HashMap savingsStatusHashMap = SavingsStatusChecker.getStatusOfSavings(this.requestSpec, this.responseSpec, savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsPending(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the savings account and verify account is approved
|
||||
*/
|
||||
savingsStatusHashMap = this.savingsAccountHelper.approveSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the savings account and verify account is activated
|
||||
*/
|
||||
savingsStatusHashMap = this.savingsAccountHelper.activateSavings(savingsId);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
|
||||
/***
|
||||
* Create RD product with CashBased accounting enabled
|
||||
*/
|
||||
final Integer taxGroupId = createTaxGroup("10", liabilityAccountForTax);
|
||||
Integer recurringDepositProductId = createRecurringDepositProductWithWithHoldTax(VALID_FROM, VALID_TO,String.valueOf(taxGroupId), accountingRule, assetAccount,
|
||||
liabilityAccount, incomeAccount, expenseAccount);
|
||||
Assert.assertNotNull(recurringDepositProductId);
|
||||
|
||||
/***
|
||||
* Apply for RD account with created product and verify status
|
||||
*/
|
||||
Integer recurringDepositAccountId = applyForRecurringDepositApplication(clientId.toString(), recurringDepositProductId.toString(),
|
||||
VALID_FROM, VALID_TO, SUBMITTED_ON_DATE, WHOLE_TERM, EXPECTED_FIRST_DEPOSIT_ON_DATE);
|
||||
Assert.assertNotNull(recurringDepositAccountId);
|
||||
|
||||
HashMap recurringDepositAccountStatusHashMap = RecurringDepositAccountStatusChecker.getStatusOfRecurringDepositAccount(
|
||||
this.requestSpec, this.responseSpec, recurringDepositAccountId.toString());
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsPending(recurringDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Approve the RD account and verify whether account is approved
|
||||
*/
|
||||
recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.approveRecurringDeposit(recurringDepositAccountId,
|
||||
APPROVED_ON_DATE);
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsApproved(recurringDepositAccountStatusHashMap);
|
||||
|
||||
/***
|
||||
* Activate the RD Account and verify whether account is activated
|
||||
*/
|
||||
recurringDepositAccountStatusHashMap = this.recurringDepositAccountHelper.activateRecurringDeposit(recurringDepositAccountId,
|
||||
ACTIVATION_DATE);
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositIsActive(recurringDepositAccountStatusHashMap);
|
||||
|
||||
HashMap recurringDepositAccountData = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId);
|
||||
Float depositAmount = (Float) recurringDepositAccountData.get("mandatoryRecommendedDepositAmount");
|
||||
|
||||
/***
|
||||
* Perform Deposit transaction and verify journal entries are posted for
|
||||
* the transaction
|
||||
*/
|
||||
todaysDate = Calendar.getInstance();
|
||||
todaysDate.add(Calendar.MONTH, -20);
|
||||
|
||||
for (int i = 0; i < 14; i++) {
|
||||
Integer depositTransactionId = this.recurringDepositAccountHelper.depositToRecurringDepositAccount(recurringDepositAccountId,
|
||||
depositAmount, dateFormat.format(todaysDate.getTime()));
|
||||
Assert.assertNotNull(depositTransactionId);
|
||||
|
||||
this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, dateFormat.format(todaysDate.getTime()), new JournalEntry(
|
||||
depositAmount, JournalEntry.TransactionType.DEBIT));
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccount, dateFormat.format(todaysDate.getTime()),
|
||||
new JournalEntry(depositAmount, JournalEntry.TransactionType.CREDIT));
|
||||
todaysDate.add(Calendar.MONTH, 1);
|
||||
}
|
||||
|
||||
/***
|
||||
* FD account verify whether account is matured
|
||||
*/
|
||||
|
||||
SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec, responseSpec);
|
||||
String JobName = "Update Deposit Accounts Maturity details";
|
||||
schedulerJobHelper.executeJob(JobName);
|
||||
|
||||
HashMap accountDetails = this.recurringDepositAccountHelper.getRecurringDepositAccountById(this.requestSpec, this.responseSpec,
|
||||
recurringDepositAccountId);
|
||||
|
||||
HashMap summary = (HashMap) accountDetails.get("summary");
|
||||
Assert.assertNotNull(summary.get("totalWithholdTax"));
|
||||
Float withHoldTax = (Float) summary.get("totalWithholdTax");
|
||||
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(liabilityAccountForTax, CLOSED_ON_DATE, new JournalEntry(withHoldTax,
|
||||
JournalEntry.TransactionType.CREDIT));
|
||||
|
||||
|
||||
recurringDepositAccountStatusHashMap = RecurringDepositAccountStatusChecker.getStatusOfRecurringDepositAccount(this.requestSpec,
|
||||
this.responseSpec, recurringDepositAccountId.toString());
|
||||
RecurringDepositAccountStatusChecker.verifyRecurringDepositAccountIsMatured(recurringDepositAccountStatusHashMap);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***
|
||||
* Test case for Recurring Deposit Account premature closure with
|
||||
* transaction type ReInvest and Cash Based accounting enabled
|
||||
@@ -2671,6 +3053,21 @@ public class RecurringDepositTest {
|
||||
final String recurringDepositProductJSON = recurringDepositProductHelper.withPeriodRangeChart().build(validFrom, validTo);
|
||||
return RecurringDepositProductHelper.createRecurringDepositProduct(recurringDepositProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
private Integer createRecurringDepositProductWithWithHoldTax(final String validFrom, final String validTo,final String taxGroupId, final String accountingRule,
|
||||
Account... accounts) {
|
||||
System.out.println("------------------------------CREATING NEW RECURRING DEPOSIT PRODUCT ---------------------------------------");
|
||||
RecurringDepositProductHelper recurringDepositProductHelper = new RecurringDepositProductHelper(this.requestSpec, this.responseSpec);
|
||||
if (accountingRule.equals(CASH_BASED)) {
|
||||
recurringDepositProductHelper = recurringDepositProductHelper.withAccountingRuleAsCashBased(accounts);
|
||||
} else if (accountingRule.equals(NONE)) {
|
||||
recurringDepositProductHelper = recurringDepositProductHelper.withAccountingRuleAsNone();
|
||||
}
|
||||
final String recurringDepositProductJSON = recurringDepositProductHelper.withPeriodRangeChart()
|
||||
.withWithHoldTax(taxGroupId)//
|
||||
.build(validFrom, validTo);
|
||||
return RecurringDepositProductHelper.createRecurringDepositProduct(recurringDepositProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
private Integer createRecurringDepositProduct(final String validFrom, final String validTo, final String accountingRule,
|
||||
final String chartToBePicked, Account... accounts) {
|
||||
@@ -2793,6 +3190,13 @@ public class RecurringDepositTest {
|
||||
Assert.assertEquals(financialActivityId, ((HashMap) mappingDetails.get("financialActivityData")).get("id"));
|
||||
Assert.assertEquals(glAccount.getAccountID(), ((HashMap) mappingDetails.get("glAccountData")).get("id"));
|
||||
}
|
||||
|
||||
private Integer createTaxGroup(final String percentage, final Account liabilityAccountForTax){
|
||||
final Integer liabilityAccountId = liabilityAccountForTax.getAccountID();
|
||||
final Integer taxComponentId = TaxComponentHelper.createTaxComponent(this.requestSpec, this.responseSpec, percentage, liabilityAccountId);
|
||||
return TaxGroupHelper.createTaxGroup(this.requestSpec, this.responseSpec, Arrays.asList(taxComponentId));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete the Liability transfer account
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.integrationtests.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
public class TaxComponentHelper {
|
||||
|
||||
private static final String CREATE_TAX_COMPONENT_URL = "/fineract-provider/api/v1/taxes/component?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public static Integer createTaxComponent(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final String percentage, final Integer liabilityAccountId) {
|
||||
System.out.println("---------------------------------CREATING A TAX COMPONENT---------------------------------------------");
|
||||
return Utils.performServerPost(requestSpec, responseSpec, CREATE_TAX_COMPONENT_URL,
|
||||
getTaxComponentAsJSON(percentage, liabilityAccountId), "resourceId");
|
||||
}
|
||||
|
||||
public static String getTaxComponentAsJSON(final String percentage, final Integer creditAccountId) {
|
||||
final HashMap<String, String> map = getBasicTaxComponentMap(percentage);
|
||||
if (creditAccountId != null) {
|
||||
map.put("creditAccountType", Account.AccountType.LIABILITY.toString());
|
||||
map.put("creditAcountId", String.valueOf(creditAccountId));
|
||||
}
|
||||
System.out.println("map : " + map);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getBasicTaxComponentMap(final String percentage) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("name", randomNameGenerator("Tax_component_Name_", 5));
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("locale", "en");
|
||||
map.put("percentage", percentage);
|
||||
map.put("startDate", "01 January 2013");
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) {
|
||||
return Utils.randomStringGenerator(prefix, lenOfRandomSuffix);
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.integrationtests.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.jayway.restassured.specification.ResponseSpecification;
|
||||
|
||||
public class TaxGroupHelper {
|
||||
|
||||
private static final String CREATE_TAX_COMPONENT_URL = "/fineract-provider/api/v1/taxes/group?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public static Integer createTaxGroup(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Collection<Integer> taxComponentIds) {
|
||||
System.out.println("---------------------------------CREATING A TAX GROUP---------------------------------------------");
|
||||
return Utils.performServerPost(requestSpec, responseSpec, CREATE_TAX_COMPONENT_URL, getTaxGroupAsJSON(taxComponentIds), "resourceId");
|
||||
}
|
||||
|
||||
public static String getTaxGroupAsJSON(final Collection<Integer> taxComponentIds) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("name", randomNameGenerator("Tax_component_Name_", 5));
|
||||
map.put("dateFormat", "dd MMMM yyyy");
|
||||
map.put("locale", "en");
|
||||
map.put("taxComponents", getTaxGroupComponents(taxComponentIds));
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
|
||||
public static List<HashMap<String, String>> getTaxGroupComponents(final Collection<Integer> taxComponentIds) {
|
||||
List<HashMap<String, String>> taxGroupComponents = new ArrayList<>();
|
||||
for (Integer taxComponentId : taxComponentIds) {
|
||||
taxGroupComponents.add(getTaxComponentMap(taxComponentId));
|
||||
}
|
||||
return taxGroupComponents;
|
||||
}
|
||||
|
||||
public static HashMap<String, String> getTaxComponentMap(final Integer taxComponentId) {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
map.put("taxComponentId", String.valueOf(taxComponentId));
|
||||
map.put("startDate", "01 January 2013");
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String randomNameGenerator(final String prefix, final int lenOfRandomSuffix) {
|
||||
return Utils.randomStringGenerator(prefix, lenOfRandomSuffix);
|
||||
}
|
||||
|
||||
}
|
||||
+12
-3
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.jayway.restassured.specification.RequestSpecification;
|
||||
import com.jayway.restassured.specification.ResponseSpecification;
|
||||
@@ -81,9 +82,17 @@ public class JournalEntryHelper {
|
||||
private void checkJournalEntry(final Integer officeId, final Account account, final String date, final JournalEntry... accountEntries) {
|
||||
final String url = createURLForGettingAccountEntries(account, date, officeId);
|
||||
final ArrayList<HashMap> response = Utils.performServerGet(this.requestSpec, this.responseSpec, url, "pageItems");
|
||||
for (int i = 0; i < accountEntries.length; i++) {
|
||||
assertThat(getEntryValueFromJournalEntry(response, i), equalTo(accountEntries[i].getTransactionType()));
|
||||
assertThat(getTransactionAmountFromJournalEntry(response, i), equalTo(accountEntries[i].getTransactionAmount()));
|
||||
|
||||
for (JournalEntry entry : accountEntries) {
|
||||
boolean matchFound = false;
|
||||
for (HashMap map : response) {
|
||||
final HashMap entryType = (HashMap) map.get("entryType");
|
||||
if (entry.getTransactionType().equals(entryType.get("value")) && entry.getTransactionAmount().equals(map.get("amount"))) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue("Journal Entry not found", matchFound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -87,6 +87,12 @@ public class FixedDepositAccountStatusChecker {
|
||||
assertTrue("ERROR IN PREMATURELY CLOSING THE FIXED DEPOSIT ACCOUNT", getStatus(fixedDepositStatusHashMap, "prematureClosed"));
|
||||
System.out.println(fixedDepositStatusHashMap);
|
||||
}
|
||||
|
||||
public static void verifyFixedDepositAccountIsMatured(HashMap fixedDepositStatusHashMap) {
|
||||
System.out.println("--------------------- VERIFYING FIXED DEPOSIT APPLICATION IS MATURED ---------------------");
|
||||
assertTrue("ERROR IN MATURITY JOB OF THE FIXED DEPOSIT ACCOUNT", getStatus(fixedDepositStatusHashMap, "matured"));
|
||||
System.out.println(fixedDepositStatusHashMap);
|
||||
}
|
||||
|
||||
private static boolean getStatus(final HashMap fixedDepositStatusMap, final String fixedDepositStatusString) {
|
||||
return (Boolean) fixedDepositStatusMap.get(fixedDepositStatusString);
|
||||
|
||||
+17
@@ -74,6 +74,7 @@ public class FixedDepositProductHelper {
|
||||
private static final String ACCRUAL_UPFRONT = "4";
|
||||
private static final String WHOLE_TERM = "1";
|
||||
private static final String TILL_PREMATURE_WITHDRAWAL = "2";
|
||||
|
||||
|
||||
private String name = Utils.randomNameGenerator("FIXED_DEPOSIT_PRODUCT_", 6);
|
||||
private String shortName = Utils.randomNameGenerator("", 4);
|
||||
@@ -99,6 +100,8 @@ public class FixedDepositProductHelper {
|
||||
private Account[] accountList = null;
|
||||
private List<HashMap<String, String>> chartSlabs = null;
|
||||
private boolean isPrimaryGroupingByAmount = false;
|
||||
private Boolean withHoldTax = false;
|
||||
private String taxGroupId = null;
|
||||
|
||||
public String build(final String validFrom, final String validTo) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
@@ -139,6 +142,11 @@ public class FixedDepositProductHelper {
|
||||
map.put("inMultiplesOfDepositTermTypeId", this.inMultiplesOfDepositTermTypeId);
|
||||
map.put("preClosurePenalInterest", this.preClosurePenalInterest);
|
||||
map.put("preClosurePenalInterestOnTypeId", this.preClosurePenalInterestOnTypeId);
|
||||
map.put("withHoldTax", this.withHoldTax.toString());
|
||||
if (withHoldTax) {
|
||||
map.put("taxGroupId", taxGroupId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.accountingRule.equals(CASH_BASED)) {
|
||||
@@ -381,6 +389,15 @@ public class FixedDepositProductHelper {
|
||||
this.chartSlabs = constructChartSlabWithAmountAndPeriodRange();
|
||||
return this;
|
||||
}
|
||||
|
||||
public FixedDepositProductHelper withWithHoldTax(final String taxGroupId) {
|
||||
if (taxGroupId != null) {
|
||||
this.withHoldTax = true;
|
||||
this.taxGroupId = taxGroupId;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getAccountMappingForCashBased() {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
|
||||
+6
@@ -89,6 +89,12 @@ public class RecurringDepositAccountStatusChecker {
|
||||
getStatus(recurringDepositStatusHashMap, "prematureClosed"));
|
||||
System.out.println(recurringDepositStatusHashMap);
|
||||
}
|
||||
|
||||
public static void verifyRecurringDepositAccountIsMatured(HashMap recurringDepositStatusHashMap) {
|
||||
System.out.println("--------------------- VERIFYING RECURRING DEPOSIT APPLICATION IS MATURED ---------------------");
|
||||
assertTrue("ERROR IN MATURITY JOB OF THE RECURRING DEPOSIT ACCOUNT", getStatus(recurringDepositStatusHashMap, "matured"));
|
||||
System.out.println(recurringDepositStatusHashMap);
|
||||
}
|
||||
|
||||
private static boolean getStatus(final HashMap recurringDepositStatusMap, final String recurringDepositStatusString) {
|
||||
return (Boolean) recurringDepositStatusMap.get(recurringDepositStatusString);
|
||||
|
||||
+15
@@ -98,6 +98,8 @@ public class RecurringDepositProductHelper {
|
||||
private Account[] accountList = null;
|
||||
private List<HashMap<String, String>> chartSlabs = null;
|
||||
private boolean isPrimaryGroupingByAmount = false;
|
||||
private Boolean withHoldTax = false;
|
||||
private String taxGroupId = null;
|
||||
|
||||
public String build(final String validFrom, final String validTo) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
@@ -144,6 +146,10 @@ public class RecurringDepositProductHelper {
|
||||
map.put("depositAmount", this.depositAmount);
|
||||
map.put("minDepositAmount", this.minDepositAmount);
|
||||
map.put("maxDepositAmount", this.maxDepositAmount);
|
||||
map.put("withHoldTax", this.withHoldTax.toString());
|
||||
if (withHoldTax) {
|
||||
map.put("taxGroupId", taxGroupId);
|
||||
}
|
||||
|
||||
if (this.accountingRule.equals(CASH_BASED)) {
|
||||
map.putAll(getAccountMappingForCashBased());
|
||||
@@ -184,6 +190,15 @@ public class RecurringDepositProductHelper {
|
||||
this.chartSlabs = constructChartSlabWithAmountAndPeriodRange();
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecurringDepositProductHelper withWithHoldTax(final String taxGroupId) {
|
||||
if (taxGroupId != null) {
|
||||
this.withHoldTax = true;
|
||||
this.taxGroupId = taxGroupId;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private Map<String, String> getAccountMappingForCashBased() {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
|
||||
+13
-3
@@ -48,6 +48,7 @@ public class SavingsAccountHelper {
|
||||
private static final String CALCULATE_INTEREST_SAVINGS_COMMAND = "calculateInterest";
|
||||
private static final String POST_INTEREST_SAVINGS_COMMAND = "postInterest";
|
||||
private static final String CLOSE_SAVINGS_COMMAND = "close";
|
||||
private static final String UPDATE_WITHHOLD_TAX_STATUS = "updateWithHoldTax";
|
||||
|
||||
private static final String DEPOSIT_SAVINGS_COMMAND = "deposit";
|
||||
private static final String WITHDRAW_SAVINGS_COMMAND = "withdrawal";
|
||||
@@ -96,6 +97,15 @@ public class SavingsAccountHelper {
|
||||
+ Utils.TENANT_IDENTIFIER, savingsApplicationJSON, CommonConstants.RESPONSE_CHANGES);
|
||||
}
|
||||
|
||||
public HashMap updateSavingsAccountWithHoldTaxStatus(final Integer savingsId, final boolean value) {
|
||||
final HashMap<String, Object> map = new HashMap<>();
|
||||
map.put("withHoldTax", value);
|
||||
String json = new Gson().toJson(map);
|
||||
|
||||
return Utils.performServerPut(this.requestSpec, this.responseSpec, SAVINGS_ACCOUNT_URL + "/" + savingsId + "?command="
|
||||
+ UPDATE_WITHHOLD_TAX_STATUS + "&" + Utils.TENANT_IDENTIFIER, json, CommonConstants.RESPONSE_CHANGES);
|
||||
}
|
||||
|
||||
public HashMap approveSavings(final Integer savingsID) {
|
||||
return approveSavingsOnDate(savingsID, null);
|
||||
}
|
||||
@@ -341,9 +351,9 @@ public class SavingsAccountHelper {
|
||||
final ArrayList<HashMap> response = Utils.performServerGet(requestSpec, responseSpec, URL, jSONAttribute);
|
||||
return response;
|
||||
}
|
||||
|
||||
public Object getSavingsAccountDetail(final Integer savingsID, final String jsonAttribute){
|
||||
final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
public Object getSavingsAccountDetail(final Integer savingsID, final String jsonAttribute) {
|
||||
final String URL = SAVINGS_ACCOUNT_URL + "/" + savingsID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerGet(requestSpec, responseSpec, URL, jsonAttribute);
|
||||
}
|
||||
|
||||
|
||||
+19
-6
@@ -83,9 +83,11 @@ public class SavingsProductHelper {
|
||||
private Account[] accountList = null;
|
||||
private String minBalanceForInterestCalculation = null;
|
||||
private String allowOverdraft = "false";
|
||||
private String overdraftLimit = null;
|
||||
private String overdraftLimit = null;
|
||||
private String minRequiredBalance = null;
|
||||
private String enforceMinRequiredBalance = "false";
|
||||
private Boolean withHoldTax = false;
|
||||
private String taxGroupId = null;
|
||||
|
||||
public String build() {
|
||||
final HashMap<String, String> map = new HashMap<>();
|
||||
@@ -121,7 +123,10 @@ public class SavingsProductHelper {
|
||||
map.put("overdraftLimit", this.overdraftLimit);
|
||||
map.put("minRequiredBalance", this.minRequiredBalance);
|
||||
map.put("enforceMinRequiredBalance", this.enforceMinRequiredBalance);
|
||||
|
||||
map.put("withHoldTax", this.withHoldTax.toString());
|
||||
if (withHoldTax) {
|
||||
map.put("taxGroupId", taxGroupId);
|
||||
}
|
||||
if (this.accountingRule.equals(CASH_BASED)) {
|
||||
map.putAll(getAccountMappingForCashBased());
|
||||
}
|
||||
@@ -144,7 +149,7 @@ public class SavingsProductHelper {
|
||||
this.minRequiredOpeningBalance = minBalance;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SavingsProductHelper withInterestCompoundingPeriodTypeAsMonthly() {
|
||||
this.interestCompoundingPeriodType = MONTHLY;
|
||||
return this;
|
||||
@@ -154,7 +159,7 @@ public class SavingsProductHelper {
|
||||
this.interestPostingPeriodType = MONTHLY;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SavingsProductHelper withMinBalanceForInterestCalculation(final String amount) {
|
||||
this.minBalanceForInterestCalculation = amount;
|
||||
return this;
|
||||
@@ -190,12 +195,12 @@ public class SavingsProductHelper {
|
||||
this.accountList = account_list;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SavingsProductHelper withMinRequiredBalance(String minBalance) {
|
||||
this.minRequiredBalance = minBalance;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public SavingsProductHelper withEnforceMinRequiredBalance(String enforceMinRequiredBalance) {
|
||||
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
|
||||
return this;
|
||||
@@ -207,6 +212,14 @@ public class SavingsProductHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SavingsProductHelper withWithHoldTax(final String taxGroupId) {
|
||||
if (taxGroupId != null) {
|
||||
this.withHoldTax = true;
|
||||
this.taxGroupId = taxGroupId;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private Map<String, String> getAccountMappingForCashBased() {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
if (accountList != null) {
|
||||
|
||||
+7
-1
@@ -41,13 +41,14 @@ public class SavingsTransactionDTO {
|
||||
/** Breakdowns of fees and penalties this Transaction pays **/
|
||||
private final List<ChargePaymentDTO> penaltyPayments;
|
||||
private final List<ChargePaymentDTO> feePayments;
|
||||
private final List<TaxPaymentDTO> taxPayments;
|
||||
|
||||
private final boolean isAccountTransfer;
|
||||
|
||||
public SavingsTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
|
||||
final SavingsAccountTransactionEnumData transactionType, final BigDecimal amount, final boolean reversed,
|
||||
final List<ChargePaymentDTO> feePayments, final List<ChargePaymentDTO> penaltyPayments, final BigDecimal overdraftAmount,
|
||||
boolean isAccountTransfer) {
|
||||
boolean isAccountTransfer, final List<TaxPaymentDTO> taxPayments) {
|
||||
this.paymentTypeId = paymentTypeId;
|
||||
this.transactionId = transactionId;
|
||||
this.transactionDate = transactionDate;
|
||||
@@ -59,6 +60,7 @@ public class SavingsTransactionDTO {
|
||||
this.officeId = officeId;
|
||||
this.overdraftAmount = overdraftAmount;
|
||||
this.isAccountTransfer = isAccountTransfer;
|
||||
this.taxPayments = taxPayments;
|
||||
}
|
||||
|
||||
public Long getOfficeId() {
|
||||
@@ -109,4 +111,8 @@ public class SavingsTransactionDTO {
|
||||
return this.isAccountTransfer;
|
||||
}
|
||||
|
||||
public List<TaxPaymentDTO> getTaxPayments() {
|
||||
return this.taxPayments;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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.journalentry.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class TaxPaymentDTO {
|
||||
|
||||
private final Long creditAccountId;
|
||||
private final Long debitAccountId;
|
||||
private final BigDecimal amount;
|
||||
|
||||
public TaxPaymentDTO(final Long debitAccountId, final Long creditAccountId, final BigDecimal amount) {
|
||||
this.debitAccountId = debitAccountId;
|
||||
this.creditAccountId = creditAccountId;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public Long getCreditAccountId() {
|
||||
return this.creditAccountId;
|
||||
}
|
||||
|
||||
public Long getDebitAccountId() {
|
||||
return this.debitAccountId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
}
|
||||
+171
-52
@@ -43,6 +43,7 @@ import org.apache.fineract.accounting.journalentry.data.LoanDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.SavingsTransactionDTO;
|
||||
import org.apache.fineract.accounting.journalentry.data.TaxPaymentDTO;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
||||
@@ -78,7 +79,7 @@ public class AccountingProcessorHelper {
|
||||
public static final String LOAN_TRANSACTION_IDENTIFIER = "L";
|
||||
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
|
||||
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
|
||||
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P" ;
|
||||
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P";
|
||||
private final JournalEntryRepository glJournalEntryRepository;
|
||||
private final ProductToGLAccountMappingRepository accountMappingRepository;
|
||||
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
|
||||
@@ -214,12 +215,26 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<TaxPaymentDTO> taxPayments = new ArrayList<>();
|
||||
if (map.containsKey("taxDetails")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<Map<String, Object>> taxDatas = (List<Map<String, Object>>) map.get("taxDetails");
|
||||
for (final Map<String, Object> taxData : taxDatas) {
|
||||
final BigDecimal taxAmount = (BigDecimal) taxData.get("amount");
|
||||
final Long creditAccountId = (Long) taxData.get("creditAccountId");
|
||||
final Long debitAccountId = (Long) taxData.get("debitAccountId");
|
||||
taxPayments.add(new TaxPaymentDTO(debitAccountId, creditAccountId, taxAmount));
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAccountTransfer) {
|
||||
isAccountTransfer = this.accountTransfersReadPlatformService.isAccountTransfer(Long.parseLong(transactionId),
|
||||
PortfolioAccountType.SAVINGS);
|
||||
}
|
||||
final SavingsTransactionDTO transaction = new SavingsTransactionDTO(transactionOfficeId, paymentTypeId, transactionId,
|
||||
transactionDate, transactionType, amount, reversed, feePayments, penaltyPayments, overdraftAmount, isAccountTransfer);
|
||||
transactionDate, transactionType, amount, reversed, feePayments, penaltyPayments, overdraftAmount, isAccountTransfer,
|
||||
taxPayments);
|
||||
|
||||
newSavingsTransactions.add(transaction);
|
||||
|
||||
@@ -254,15 +269,14 @@ public class AccountingProcessorHelper {
|
||||
final boolean isPenalty = (Boolean) clientChargePaid.get("isPenalty");
|
||||
final BigDecimal chargeAmountPaid = (BigDecimal) clientChargePaid.get("amount");
|
||||
final Long incomeAccountId = (Long) clientChargePaid.get("incomeAccountId");
|
||||
final ClientChargePaymentDTO clientChargePaymentDTO = new ClientChargePaymentDTO(chargeId, chargeAmountPaid, clientChargeId,
|
||||
isPenalty, incomeAccountId);
|
||||
final ClientChargePaymentDTO clientChargePaymentDTO = new ClientChargePaymentDTO(chargeId, chargeAmountPaid,
|
||||
clientChargeId, isPenalty, incomeAccountId);
|
||||
clientChargePaymentDTOs.add(clientChargePaymentDTO);
|
||||
}
|
||||
}
|
||||
|
||||
final ClientTransactionDTO clientTransactionDTO = new ClientTransactionDTO(clientId, transactionOfficeId, paymentTypeId,
|
||||
transactionId, transactionDate, transactionType, currencyCode, amount, reversed, accountingEnabled,
|
||||
clientChargePaymentDTOs);
|
||||
transactionId, transactionDate, transactionType, currencyCode, amount, reversed, accountingEnabled, clientChargePaymentDTOs);
|
||||
|
||||
return clientTransactionDTO;
|
||||
|
||||
@@ -289,8 +303,9 @@ public class AccountingProcessorHelper {
|
||||
* @param isReversal
|
||||
*/
|
||||
public void createAccrualBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
|
||||
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
|
||||
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId,
|
||||
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
||||
final Boolean isReversal) {
|
||||
int accountTypeToDebitId = accountTypeToBeDebited;
|
||||
int accountTypeToCreditId = accountTypeToBeCredited;
|
||||
// reverse debits and credits for reversals
|
||||
@@ -298,8 +313,8 @@ public class AccountingProcessorHelper {
|
||||
accountTypeToDebitId = accountTypeToBeCredited;
|
||||
accountTypeToCreditId = accountTypeToBeDebited;
|
||||
}
|
||||
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
|
||||
transactionId, transactionDate, amount);
|
||||
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId,
|
||||
loanId, transactionId, transactionDate, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,8 +434,9 @@ public class AccountingProcessorHelper {
|
||||
* @param isReversal
|
||||
*/
|
||||
public void createCashBasedJournalEntriesAndReversalsForLoan(final Office office, final String currencyCode,
|
||||
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId, final Long paymentTypeId,
|
||||
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
final Integer accountTypeToBeDebited, final Integer accountTypeToBeCredited, final Long loanProductId,
|
||||
final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount,
|
||||
final Boolean isReversal) {
|
||||
int accountTypeToDebitId = accountTypeToBeDebited;
|
||||
int accountTypeToCreditId = accountTypeToBeCredited;
|
||||
// reverse debits and credits for reversals
|
||||
@@ -428,8 +444,8 @@ public class AccountingProcessorHelper {
|
||||
accountTypeToDebitId = accountTypeToBeCredited;
|
||||
accountTypeToCreditId = accountTypeToBeDebited;
|
||||
}
|
||||
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId, loanId,
|
||||
transactionId, transactionDate, amount);
|
||||
createJournalEntriesForLoan(office, currencyCode, accountTypeToDebitId, accountTypeToCreditId, loanProductId, paymentTypeId,
|
||||
loanId, transactionId, transactionDate, amount);
|
||||
}
|
||||
|
||||
public void createCreditJournalEntryOrReversalForLoan(final Office office, final String currencyCode,
|
||||
@@ -458,9 +474,8 @@ public class AccountingProcessorHelper {
|
||||
* transaction Date
|
||||
**/
|
||||
if (latestGLClosure != null) {
|
||||
if (latestGLClosure.getClosingDate().after(transactionDate)
|
||||
|| latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
|
||||
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
|
||||
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
|
||||
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +505,111 @@ public class AccountingProcessorHelper {
|
||||
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that creates a pair of related Debits and Credits for
|
||||
* Cash Based accounting.
|
||||
*
|
||||
* The target accounts for debits and credits are switched in case of a
|
||||
* reversal
|
||||
*
|
||||
* @param office
|
||||
* @param accountTypeToBeDebited
|
||||
* Enum of the placeholder GLAccount to be debited
|
||||
* @param accountTypeToBeCredited
|
||||
* Enum of the placeholder of the GLAccount to be credited
|
||||
* @param savingsProductId
|
||||
* @param paymentTypeId
|
||||
* @param loanId
|
||||
* @param transactionId
|
||||
* @param transactionDate
|
||||
* @param amount
|
||||
* @param isReversal
|
||||
*/
|
||||
public void createCashBasedJournalEntriesAndReversalsForSavingsTax(final Office office, final String currencyCode,
|
||||
final CASH_ACCOUNTS_FOR_SAVINGS accountTypeToBeDebited, final CASH_ACCOUNTS_FOR_SAVINGS accountTypeToBeCredited,
|
||||
final Long savingsProductId, final Long paymentTypeId, final Long savingsId, final String transactionId,
|
||||
final Date transactionDate, final BigDecimal amount, final Boolean isReversal, final List<TaxPaymentDTO> taxDetails) {
|
||||
|
||||
for (TaxPaymentDTO taxPaymentDTO : taxDetails) {
|
||||
if (taxPaymentDTO.getAmount() != null) {
|
||||
if (taxPaymentDTO.getCreditAccountId() == null) {
|
||||
createCashBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode, accountTypeToBeCredited.getValue(),
|
||||
savingsProductId, paymentTypeId, savingsId, transactionId, transactionDate, taxPaymentDTO.getAmount(),
|
||||
isReversal);
|
||||
} else {
|
||||
createCashBasedCreditJournalEntriesAndReversalsForSavings(office, currencyCode, taxPaymentDTO.getCreditAccountId(),
|
||||
savingsId, transactionId, transactionDate, taxPaymentDTO.getAmount(), isReversal);
|
||||
}
|
||||
}
|
||||
}
|
||||
createCashBasedDebitJournalEntriesAndReversalsForSavings(office, currencyCode, accountTypeToBeDebited.getValue(), savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal);
|
||||
}
|
||||
|
||||
public void createCashBasedDebitJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode,
|
||||
final Integer accountTypeToBeDebited, final Long savingsProductId, final Long paymentTypeId, final Long savingsId,
|
||||
final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
// reverse debits and credits for reversals
|
||||
if (isReversal) {
|
||||
createCreditJournalEntriesForSavings(office, currencyCode, accountTypeToBeDebited, savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount);
|
||||
} else {
|
||||
createDebitJournalEntriesForSavings(office, currencyCode, accountTypeToBeDebited, savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public void createCashBasedCreditJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode,
|
||||
final Integer accountTypeToBeCredited, final Long savingsProductId, final Long paymentTypeId, final Long savingsId,
|
||||
final String transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
// reverse debits and credits for reversals
|
||||
if (isReversal) {
|
||||
createDebitJournalEntriesForSavings(office, currencyCode, accountTypeToBeCredited, savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount);
|
||||
} else {
|
||||
createCreditJournalEntriesForSavings(office, currencyCode, accountTypeToBeCredited, savingsProductId, paymentTypeId, savingsId,
|
||||
transactionId, transactionDate, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public void createCashBasedDebitJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode,
|
||||
final Long debitAccountId, final Long savingsId, final String transactionId, final Date transactionDate,
|
||||
final BigDecimal amount, final Boolean isReversal) {
|
||||
// reverse debits and credits for reversals
|
||||
final GLAccount debitAccount = getGLAccountById(debitAccountId);
|
||||
if (isReversal) {
|
||||
createCreditJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount);
|
||||
} else {
|
||||
createDebitJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public void createCashBasedCreditJournalEntriesAndReversalsForSavings(final Office office, final String currencyCode,
|
||||
final Long creditAccountId, final Long savingsId, final String transactionId, final Date transactionDate,
|
||||
final BigDecimal amount, final Boolean isReversal) {
|
||||
// reverse debits and credits for reversals
|
||||
final GLAccount creditAccount = getGLAccountById(creditAccountId);
|
||||
if (isReversal) {
|
||||
createDebitJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||
} else {
|
||||
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||
}
|
||||
}
|
||||
|
||||
private void createDebitJournalEntriesForSavings(final Office office, final String currencyCode, final int accountTypeToDebitId,
|
||||
final Long savingsProductId, final Long paymentTypeId, final Long savingsId, final String transactionId,
|
||||
final Date transactionDate, final BigDecimal amount) {
|
||||
final GLAccount debitAccount = getLinkedGLAccountForSavingsProduct(savingsProductId, accountTypeToDebitId, paymentTypeId);
|
||||
createDebitJournalEntryForSavings(office, currencyCode, debitAccount, savingsId, transactionId, transactionDate, amount);
|
||||
}
|
||||
|
||||
private void createCreditJournalEntriesForSavings(final Office office, final String currencyCode, final int accountTypeToCreditId,
|
||||
final Long savingsProductId, final Long paymentTypeId, final Long savingsId, final String transactionId,
|
||||
final Date transactionDate, final BigDecimal amount) {
|
||||
final GLAccount creditAccount = getLinkedGLAccountForSavingsProduct(savingsProductId, accountTypeToCreditId, paymentTypeId);
|
||||
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||
}
|
||||
|
||||
public void createDebitJournalEntryOrReversalForLoan(final Office office, final String currencyCode, final int accountMappingTypeId,
|
||||
final Long loanProductId, final Long paymentTypeId, final Long loanId, final String transactionId, final Date transactionDate,
|
||||
final BigDecimal amount, final Boolean isReversal) {
|
||||
@@ -631,8 +751,8 @@ public class AccountingProcessorHelper {
|
||||
String modifiedTransactionId = transactionId.toString();
|
||||
modifiedTransactionId = CLIENT_TRANSACTION_IDENTIFIER + transactionId;
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
|
||||
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId, null,
|
||||
loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.CLIENT.getValue(), clientId,
|
||||
null, loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
@@ -655,8 +775,8 @@ public class AccountingProcessorHelper {
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
private void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account, final Long loanId,
|
||||
final String transactionId, final Date transactionDate, final BigDecimal amount) {
|
||||
private void createCreditJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account,
|
||||
final Long loanId, final String transactionId, final Date transactionDate, final BigDecimal amount) {
|
||||
final boolean manualEntry = false;
|
||||
LoanTransaction loanTransaction = null;
|
||||
SavingsAccountTransaction savingsAccountTransaction = null;
|
||||
@@ -674,33 +794,35 @@ public class AccountingProcessorHelper {
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
public void createProvisioningDebitJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode, GLAccount account,BigDecimal amount) {
|
||||
public void createProvisioningDebitJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode,
|
||||
GLAccount account, BigDecimal amount) {
|
||||
LoanTransaction loanTransaction = null;
|
||||
SavingsAccountTransaction savingsAccountTransaction = null;
|
||||
ClientTransaction clientTransaction = null;
|
||||
PaymentDetail paymentDetail = null ;
|
||||
PaymentDetail paymentDetail = null;
|
||||
final boolean manualEntry = false;
|
||||
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
|
||||
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.PROVISIONING.getValue(), provisioningentryId, null,
|
||||
loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
|
||||
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.PROVISIONING.getValue(),
|
||||
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
public void createProvisioningCreditJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode, GLAccount account, BigDecimal amount) {
|
||||
|
||||
public void createProvisioningCreditJournalEntry(Date transactionDate, Long provisioningentryId, Office office, String currencyCode,
|
||||
GLAccount account, BigDecimal amount) {
|
||||
LoanTransaction loanTransaction = null;
|
||||
SavingsAccountTransaction savingsAccountTransaction = null;
|
||||
ClientTransaction clientTransaction = null;
|
||||
PaymentDetail paymentDetail = null ;
|
||||
PaymentDetail paymentDetail = null;
|
||||
final boolean manualEntry = false;
|
||||
String modifiedTransactionId = PROVISIONING_TRANSACTION_IDENTIFIER + provisioningentryId;
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
|
||||
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.PROVISIONING.getValue(), provisioningentryId, null,
|
||||
loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
manualEntry, transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.PROVISIONING.getValue(),
|
||||
provisioningentryId, null, loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
|
||||
private void createDebitJournalEntryForLoan(final Office office, final String currencyCode, final GLAccount account, final Long loanId,
|
||||
final String transactionId, final Date transactionDate, final BigDecimal amount) {
|
||||
final boolean manualEntry = false;
|
||||
@@ -734,8 +856,8 @@ public class AccountingProcessorHelper {
|
||||
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
|
||||
}
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, paymentDetail, account, currencyCode, modifiedTransactionId,
|
||||
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null,
|
||||
loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
manualEntry, transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId,
|
||||
null, loanTransaction, savingsAccountTransaction, clientTransaction);
|
||||
this.glJournalEntryRepository.saveAndFlush(journalEntry);
|
||||
}
|
||||
|
||||
@@ -791,13 +913,12 @@ public class AccountingProcessorHelper {
|
||||
private GLAccount getLinkedGLAccountForLoanCharges(final Long loanProductId, final int accountMappingTypeId, final Long chargeId) {
|
||||
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(loanProductId,
|
||||
PortfolioProductType.LOAN.getValue(), accountMappingTypeId);
|
||||
/*****
|
||||
* Get more specific mappings for Charges and penalties (based
|
||||
* on the actual charge /penalty coupled with the loan product).
|
||||
* Note the income from fees and income from penalties
|
||||
* placeholder ID would be the same for both cash and accrual
|
||||
* based accounts
|
||||
*****/
|
||||
/*****
|
||||
* Get more specific mappings for Charges and penalties (based on the
|
||||
* actual charge /penalty coupled with the loan product). Note the
|
||||
* income from fees and income from penalties placeholder ID would be
|
||||
* the same for both cash and accrual based accounts
|
||||
*****/
|
||||
|
||||
// Vishwas TODO: remove this condition as it should always be true
|
||||
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_LOAN.INCOME_FROM_FEES.getValue()
|
||||
@@ -812,17 +933,15 @@ public class AccountingProcessorHelper {
|
||||
return accountMapping.getGlAccount();
|
||||
}
|
||||
|
||||
private GLAccount getLinkedGLAccountForSavingsCharges(final Long savingsProductId, final int accountMappingTypeId,
|
||||
final Long chargeId) {
|
||||
private GLAccount getLinkedGLAccountForSavingsCharges(final Long savingsProductId, final int accountMappingTypeId, final Long chargeId) {
|
||||
ProductToGLAccountMapping accountMapping = this.accountMappingRepository.findCoreProductToFinAccountMapping(savingsProductId,
|
||||
PortfolioProductType.SAVING.getValue(), accountMappingTypeId);
|
||||
/*****
|
||||
* Get more specific mappings for Charges and penalties (based
|
||||
* on the actual charge /penalty coupled with the loan product).
|
||||
* Note the income from fees and income from penalties
|
||||
* placeholder ID would be the same for both cash and accrual
|
||||
* based accounts
|
||||
*****/
|
||||
/*****
|
||||
* Get more specific mappings for Charges and penalties (based on the
|
||||
* actual charge /penalty coupled with the loan product). Note the
|
||||
* income from fees and income from penalties placeholder ID would be
|
||||
* the same for both cash and accrual based accounts
|
||||
*****/
|
||||
|
||||
// Vishwas TODO: remove this condition as it should always be true
|
||||
if (accountMappingTypeId == CASH_ACCOUNTS_FOR_SAVINGS.INCOME_FROM_FEES.getValue()
|
||||
@@ -912,8 +1031,8 @@ public class AccountingProcessorHelper {
|
||||
|
||||
public void createDebitJournalEntryOrReversalForClientChargePayments(final Office office, final String currencyCode,
|
||||
final Long clientId, final Long transactionId, final Date transactionDate, final BigDecimal amount, final Boolean isReversal) {
|
||||
final GLAccount account = financialActivityAccountRepository
|
||||
.findByFinancialActivityTypeWithNotFoundDetection(FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue()).getGlAccount();
|
||||
final GLAccount account = financialActivityAccountRepository.findByFinancialActivityTypeWithNotFoundDetection(
|
||||
FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue()).getGlAccount();
|
||||
if (isReversal) {
|
||||
createCreditJournalEntryForClientPayments(office, currencyCode, account, clientId, transactionId, transactionDate, amount);
|
||||
} else {
|
||||
|
||||
+7
@@ -168,6 +168,13 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
||||
}
|
||||
}
|
||||
|
||||
else if (savingsTransactionDTO.getTransactionType().isWithholdTax()) {
|
||||
this.helper.createCashBasedJournalEntriesAndReversalsForSavingsTax(office, currencyCode,
|
||||
CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_CONTROL, CASH_ACCOUNTS_FOR_SAVINGS.SAVINGS_REFERENCE, savingsProductId,
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal,
|
||||
savingsTransactionDTO.getTaxPayments());
|
||||
}
|
||||
|
||||
/** Handle Fees Deductions and reversals of Fees Deductions **/
|
||||
else if (savingsTransactionDTO.getTransactionType().isFeeDeduction() && savingsTransactionDTO.isOverdraftTransaction()) {
|
||||
// Is the Charge a penalty?
|
||||
|
||||
+40
@@ -2616,4 +2616,44 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/accounts/" + accountType+"/"+accountId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createTaxComponent() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "TAXCOMPONENT";
|
||||
this.entityId = null;
|
||||
this.href = "/taxes/component";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateTaxComponent(final Long taxComponentId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "TAXCOMPONENT";
|
||||
this.entityId = taxComponentId;
|
||||
this.href = "/taxes/component/" + taxComponentId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createTaxGroup() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "TAXGROUP";
|
||||
this.entityId = null;
|
||||
this.href = "/taxes/group";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateTaxGroup(final Long taxGroupId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "TAXGROUP";
|
||||
this.entityId = taxGroupId;
|
||||
this.href = "/taxes/group/" + taxGroupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateWithHoldTax(final Long accountId) {
|
||||
this.actionName = "UPDATEWITHHOLDTAX";
|
||||
this.entityName = "SAVINGSACCOUNT";
|
||||
this.entityId = accountId;
|
||||
this.href = "/savingsaccounts/" + accountId + "?commad=updateTaxWithHoldTax";
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+1
@@ -21,5 +21,6 @@ package org.apache.fineract.portfolio.charge.api;
|
||||
public class ChargesApiConstants {
|
||||
|
||||
public static final String glAccountIdParamName = "incomeAccountId";
|
||||
public static final String taxGroupIdParamName = "taxGroupId";
|
||||
|
||||
}
|
||||
|
||||
+35
-25
@@ -30,6 +30,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.MonthDay;
|
||||
|
||||
@@ -54,6 +55,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final BigDecimal maxCap;
|
||||
private final EnumOptionData feeFrequency;
|
||||
private final GLAccountData incomeOrLiabilityAccount;
|
||||
private final TaxGroupData taxGroup;
|
||||
|
||||
private final Collection<CurrencyData> currencyOptions;
|
||||
private final List<EnumOptionData> chargeCalculationTypeOptions;//
|
||||
@@ -70,6 +72,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
private final List<EnumOptionData> feeFrequencyOptions;
|
||||
|
||||
private final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions;
|
||||
private final Collection<TaxGroupData> taxGroupOptions;
|
||||
|
||||
public static ChargeData template(final Collection<CurrencyData> currencyOptions,
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions, final List<EnumOptionData> chargeAppliesToOptions,
|
||||
@@ -77,31 +80,33 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> loansChargeCalculationTypeOptions, final List<EnumOptionData> loansChargeTimeTypeOptions,
|
||||
final List<EnumOptionData> savingsChargeCalculationTypeOptions, final List<EnumOptionData> savingsChargeTimeTypeOptions,
|
||||
final List<EnumOptionData> clientChargeCalculationTypeOptions, final List<EnumOptionData> clientChargeTimeTypeOptions,
|
||||
final List<EnumOptionData> feeFrequencyOptions, final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions) {
|
||||
final List<EnumOptionData> feeFrequencyOptions, final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions,
|
||||
final Collection<TaxGroupData> taxGroupOptions) {
|
||||
final GLAccountData account = null;
|
||||
return new ChargeData(null, null, null, null, null, null, null, null, false, false, currencyOptions, chargeCalculationTypeOptions,
|
||||
chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions,
|
||||
loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions,
|
||||
clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null, null, feeFrequencyOptions, account,
|
||||
incomeOrLiabilityAccountOptions);
|
||||
final TaxGroupData taxGroupData = null;
|
||||
return new ChargeData(null, null, null, null, null, null, null, null, false, false, taxGroupData, currencyOptions,
|
||||
chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions, chargePaymentModeOptions,
|
||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, null, null, null, null,
|
||||
null, feeFrequencyOptions, account, incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static ChargeData withTemplate(final ChargeData charge, final ChargeData template) {
|
||||
return new ChargeData(charge.id, charge.name, charge.amount, charge.currency, charge.chargeTimeType, charge.chargeAppliesTo,
|
||||
charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, template.currencyOptions,
|
||||
template.chargeCalculationTypeOptions, template.chargeAppliesToOptions, template.chargeTimeTypeOptions,
|
||||
template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions, template.loanChargeTimeTypeOptions,
|
||||
template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions,
|
||||
template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay, charge.feeInterval,
|
||||
charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions, charge.incomeOrLiabilityAccount,
|
||||
template.incomeOrLiabilityAccountOptions);
|
||||
charge.chargeCalculationType, charge.chargePaymentMode, charge.penalty, charge.active, charge.taxGroup,
|
||||
template.currencyOptions, template.chargeCalculationTypeOptions, template.chargeAppliesToOptions,
|
||||
template.chargeTimeTypeOptions, template.chargePaymetModeOptions, template.loanChargeCalculationTypeOptions,
|
||||
template.loanChargeTimeTypeOptions, template.savingsChargeCalculationTypeOptions, template.savingsChargeTimeTypeOptions,
|
||||
template.clientChargeCalculationTypeOptions, template.clientChargeTimeTypeOptions, charge.feeOnMonthDay,
|
||||
charge.feeInterval, charge.minCap, charge.maxCap, charge.feeFrequency, template.feeFrequencyOptions,
|
||||
charge.incomeOrLiabilityAccount, template.incomeOrLiabilityAccountOptions, template.taxGroupOptions);
|
||||
}
|
||||
|
||||
public static ChargeData instance(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
|
||||
final EnumOptionData chargePaymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final boolean penalty,
|
||||
final boolean active, final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
|
||||
final GLAccountData accountData) {
|
||||
final GLAccountData accountData, TaxGroupData taxGroupData) {
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final List<EnumOptionData> chargeCalculationTypeOptions = null;
|
||||
@@ -116,13 +121,14 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> clientChargeCalculationTypeOptions = null;
|
||||
final List<EnumOptionData> clientChargeTimeTypeOptions = null;
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
penalty, active, taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions,
|
||||
chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
|
||||
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, accountData,
|
||||
incomeOrLiabilityAccountOptions);
|
||||
incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) {
|
||||
@@ -153,17 +159,19 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> feeFrequencyOptions = null;
|
||||
final GLAccountData account = null;
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
|
||||
final TaxGroupData taxGroupData = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, chargePaymentMode,
|
||||
penalty, active, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions,
|
||||
chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
penalty, active, taxGroupData, currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions,
|
||||
chargeTimeTypeOptions, chargePaymentModeOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
|
||||
clientChargeTimeTypeOptions, feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, feeFrequencyOptions, account,
|
||||
incomeOrLiabilityAccountOptions);
|
||||
incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
|
||||
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
|
||||
final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active,
|
||||
final EnumOptionData chargePaymentMode, final boolean penalty, final boolean active, final TaxGroupData taxGroupData,
|
||||
final Collection<CurrencyData> currencyOptions, final List<EnumOptionData> chargeCalculationTypeOptions,
|
||||
final List<EnumOptionData> chargeAppliesToOptions, final List<EnumOptionData> chargeTimeTypeOptions,
|
||||
final List<EnumOptionData> chargePaymentModeOptions, final List<EnumOptionData> loansChargeCalculationTypeOptions,
|
||||
@@ -172,7 +180,7 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final List<EnumOptionData> clientChargeTimeTypeOptions, final MonthDay feeOnMonthDay, final Integer feeInterval,
|
||||
final BigDecimal minCap, final BigDecimal maxCap, final EnumOptionData feeFrequency,
|
||||
final List<EnumOptionData> feeFrequencyOptions, final GLAccountData account,
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions) {
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
@@ -202,6 +210,8 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
this.feeFrequencyOptions = feeFrequencyOptions;
|
||||
this.incomeOrLiabilityAccount = account;
|
||||
this.incomeOrLiabilityAccountOptions = incomeOrLiabilityAccountOptions;
|
||||
this.taxGroup = taxGroupData;
|
||||
this.taxGroupOptions = taxGroupOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -249,9 +259,9 @@ public class ChargeData implements Comparable<ChargeData>, Serializable {
|
||||
final LocalDate inactivationDate = null;
|
||||
|
||||
return SavingsAccountChargeData.instance(savingsChargeId, this.id, savingsAccountId, this.name, this.currency, this.amount,
|
||||
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate, this.chargeCalculationType,
|
||||
percentage, amountPercentageAppliedTo, chargeOptions, this.penalty, this.feeOnMonthDay, this.feeInterval, isActive,
|
||||
inactivationDate);
|
||||
amountPaid, amountWaived, amountWrittenOff, amountOutstanding, this.chargeTimeType, dueAsOfDate,
|
||||
this.chargeCalculationType, percentage, amountPercentageAppliedTo, chargeOptions, this.penalty, this.feeOnMonthDay,
|
||||
this.feeInterval, isActive, inactivationDate);
|
||||
}
|
||||
|
||||
public boolean isPenalty() {
|
||||
|
||||
+49
-14
@@ -49,6 +49,8 @@ import org.apache.fineract.portfolio.charge.exception.ChargeMustBePenaltyExcepti
|
||||
import org.apache.fineract.portfolio.charge.exception.ChargeParameterUpdateNotSupportedException;
|
||||
import org.apache.fineract.portfolio.charge.service.ChargeEnumerations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@@ -108,7 +110,11 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "income_or_liability_account_id")
|
||||
private GLAccount account;
|
||||
|
||||
public static Charge fromJson(final JsonCommand command, final GLAccount account) {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tax_group_id")
|
||||
private TaxGroup taxGroup;
|
||||
|
||||
public static Charge fromJson(final JsonCommand command, final GLAccount account, final TaxGroup taxGroup) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("amount");
|
||||
@@ -116,8 +122,8 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
final ChargeAppliesTo chargeAppliesTo = ChargeAppliesTo.fromInt(command.integerValueOfParameterNamed("chargeAppliesTo"));
|
||||
final ChargeTimeType chargeTimeType = ChargeTimeType.fromInt(command.integerValueOfParameterNamed("chargeTimeType"));
|
||||
final ChargeCalculationType chargeCalculationType = ChargeCalculationType
|
||||
.fromInt(command.integerValueOfParameterNamed("chargeCalculationType"));
|
||||
final ChargeCalculationType chargeCalculationType = ChargeCalculationType.fromInt(command
|
||||
.integerValueOfParameterNamed("chargeCalculationType"));
|
||||
final Integer chargePaymentMode = command.integerValueOfParameterNamed("chargePaymentMode");
|
||||
|
||||
final ChargePaymentMode paymentMode = chargePaymentMode == null ? null : ChargePaymentMode.fromInt(chargePaymentMode);
|
||||
@@ -131,7 +137,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
final Integer feeFrequency = command.integerValueOfParameterNamed("feeFrequency");
|
||||
|
||||
return new Charge(name, amount, currencyCode, chargeAppliesTo, chargeTimeType, chargeCalculationType, penalty, active, paymentMode,
|
||||
feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, account);
|
||||
feeOnMonthDay, feeInterval, minCap, maxCap, feeFrequency, account, taxGroup);
|
||||
}
|
||||
|
||||
protected Charge() {
|
||||
@@ -139,9 +145,9 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
private Charge(final String name, final BigDecimal amount, final String currencyCode, final ChargeAppliesTo chargeAppliesTo,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty, final boolean active,
|
||||
final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval, final BigDecimal minCap,
|
||||
final BigDecimal maxCap, final Integer feeFrequency, final GLAccount account) {
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculationType, final boolean penalty,
|
||||
final boolean active, final ChargePaymentMode paymentMode, final MonthDay feeOnMonthDay, final Integer feeInterval,
|
||||
final BigDecimal minCap, final BigDecimal maxCap, final Integer feeFrequency, final GLAccount account, final TaxGroup taxGroup) {
|
||||
this.name = name;
|
||||
this.amount = amount;
|
||||
this.currencyCode = currencyCode;
|
||||
@@ -151,6 +157,7 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
this.penalty = penalty;
|
||||
this.active = active;
|
||||
this.account = account;
|
||||
this.taxGroup = taxGroup;
|
||||
this.chargePaymentMode = paymentMode == null ? null : paymentMode.getValue();
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
@@ -185,8 +192,8 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
} else if (isLoanCharge()) {
|
||||
|
||||
if (penalty && (chargeTime.isTimeOfDisbursement()
|
||||
|| chargeTime.isTrancheDisbursement())) { throw new ChargeDueAtDisbursementCannotBePenaltyException(name); }
|
||||
if (penalty && (chargeTime.isTimeOfDisbursement() || chargeTime.isTrancheDisbursement())) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
|
||||
name); }
|
||||
if (!penalty && chargeTime.isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
// TODO vishwas, this validation seems unnecessary as identical
|
||||
// validation is performed in the write service
|
||||
@@ -487,16 +494,23 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
|
||||
}
|
||||
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTimeType)
|
||||
.isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(this.name); }
|
||||
if (!penalty
|
||||
&& ChargeTimeType.fromInt(this.chargeTimeType).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
if (this.penalty && ChargeTimeType.fromInt(this.chargeTimeType).isTimeOfDisbursement()) { throw new ChargeDueAtDisbursementCannotBePenaltyException(
|
||||
this.name); }
|
||||
if (!penalty && ChargeTimeType.fromInt(this.chargeTimeType).isOverdueInstallment()) { throw new ChargeMustBePenaltyException(name); }
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ChargesApiConstants.glAccountIdParamName, getIncomeAccountId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.glAccountIdParamName);
|
||||
actualChanges.put(ChargesApiConstants.glAccountIdParamName, newValue);
|
||||
}
|
||||
|
||||
if (command.isChangeInLongParameterNamed(ChargesApiConstants.taxGroupIdParamName, getTaxGroupId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.taxGroupIdParamName);
|
||||
actualChanges.put(ChargesApiConstants.taxGroupIdParamName, newValue);
|
||||
if(taxGroup != null){
|
||||
baseDataValidator.reset().parameter(ChargesApiConstants.taxGroupIdParamName).failWithCode("modification.not.supported");
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
return actualChanges;
|
||||
@@ -524,10 +538,15 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
if (account != null) {
|
||||
accountData = new GLAccountData(account.getId(), account.getName(), account.getGlCode());
|
||||
}
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (this.taxGroup != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroup.getId(), taxGroup.getName());
|
||||
}
|
||||
|
||||
final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
|
||||
return ChargeData.instance(getId(), this.name, this.amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType,
|
||||
chargePaymentmode, getFeeOnMonthDay(), this.feeInterval, this.penalty, this.active, this.minCap, this.maxCap,
|
||||
feeFrequencyType, accountData);
|
||||
feeFrequencyType, accountData, taxGroupData);
|
||||
}
|
||||
|
||||
public Integer getChargePaymentMode() {
|
||||
@@ -582,8 +601,24 @@ public class Charge extends AbstractPersistable<Long> {
|
||||
return incomeAccountId;
|
||||
}
|
||||
|
||||
private Long getTaxGroupId() {
|
||||
Long taxGroupId = null;
|
||||
if (this.taxGroup != null) {
|
||||
taxGroupId = this.taxGroup.getId();
|
||||
}
|
||||
return taxGroupId;
|
||||
}
|
||||
|
||||
public boolean isDisbursementCharge() {
|
||||
return ChargeTimeType.fromInt(this.chargeTimeType).equals(ChargeTimeType.DISBURSEMENT)
|
||||
|| ChargeTimeType.fromInt(this.chargeTimeType).equals(ChargeTimeType.TRANCHE_DISBURSEMENT);
|
||||
}
|
||||
|
||||
public TaxGroup getTaxGroup() {
|
||||
return this.taxGroup;
|
||||
}
|
||||
|
||||
public void setTaxGroup(TaxGroup taxGroup) {
|
||||
this.taxGroup = taxGroup;
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -56,7 +56,7 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("name", "amount", "locale", "currencyCode",
|
||||
"currencyOptions", "chargeAppliesTo", "chargeTimeType", "chargeCalculationType", "chargeCalculationTypeOptions", "penalty",
|
||||
"active", "chargePaymentMode", "feeOnMonthDay", "feeInterval", "monthDayFormat", "minCap", "maxCap", "feeFrequency",
|
||||
ChargesApiConstants.glAccountIdParamName));
|
||||
ChargesApiConstants.glAccountIdParamName, ChargesApiConstants.taxGroupIdParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -206,6 +206,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
baseDataValidator.reset().parameter("maxCap").value(maxCap).notNull().positiveAmount();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ChargesApiConstants.taxGroupIdParamName, element)) {
|
||||
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(ChargesApiConstants.taxGroupIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ChargesApiConstants.taxGroupIdParamName).value(taxGroupId).notNull().longGreaterThanZero();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -316,6 +321,11 @@ public final class ChargeDefinitionCommandFromApiJsonDeserializer {
|
||||
.longGreaterThanZero();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(ChargesApiConstants.taxGroupIdParamName, element)) {
|
||||
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(ChargesApiConstants.taxGroupIdParamName, element);
|
||||
baseDataValidator.reset().parameter(ChargesApiConstants.taxGroupIdParamName).value(taxGroupId).notNull().longGreaterThanZero();
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
|
||||
+24
-8
@@ -41,6 +41,8 @@ import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException;
|
||||
import org.apache.fineract.portfolio.common.service.CommonEnumerations;
|
||||
import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
@@ -64,12 +66,14 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public ChargeReadPlatformServiceImpl(final CurrencyReadPlatformService currencyReadPlatformService,
|
||||
final ChargeDropdownReadPlatformService chargeDropdownReadPlatformService, final RoutingDataSource dataSource,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService, final FineractEntityAccessUtil fineractEntityAccessUtil,
|
||||
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) {
|
||||
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService,
|
||||
final TaxReadPlatformService taxReadPlatformService) {
|
||||
this.chargeDropdownReadPlatformService = chargeDropdownReadPlatformService;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
@@ -77,6 +81,7 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
|
||||
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
|
||||
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||
this.taxReadPlatformService = taxReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,11 +147,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = this.accountingDropdownReadPlatformService
|
||||
.retrieveAccountMappingOptionsForCharges();
|
||||
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
return ChargeData.template(currencyOptions, allowedChargeCalculationTypeOptions, allowedChargeAppliesToOptions,
|
||||
allowedChargeTimeOptions, chargePaymentOptions, loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions,
|
||||
savingsChargeCalculationTypeOptions, savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions,
|
||||
clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions);
|
||||
clientChargeTimeTypeOptions, feeFrequencyOptions, incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,7 +257,8 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only
|
||||
// charges mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.CHARGE);
|
||||
String inClause = fineractEntityAccessUtil
|
||||
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.CHARGE);
|
||||
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||
sql += " and c.id in ( " + inClause + " ) ";
|
||||
}
|
||||
@@ -271,9 +277,11 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
+ "oc.currency_multiplesof as inMultiplesOf, oc.display_symbol as currencyDisplaySymbol, "
|
||||
+ "oc.internationalized_name_code as currencyNameCode, c.fee_on_day as feeOnDay, c.fee_on_month as feeOnMonth, "
|
||||
+ "c.fee_interval as feeInterval, c.fee_frequency as feeFrequency,c.min_cap as minCap,c.max_cap as maxCap, "
|
||||
+ "c.income_or_liability_account_id as glAccountId , acc.name as glAccountName, acc.gl_code as glCode "
|
||||
+ "from m_charge c " + "join m_organisation_currency oc on c.currency_code = oc.code "
|
||||
+ " LEFT JOIN acc_gl_account acc on acc.id = c.income_or_liability_account_id ";
|
||||
+ "c.income_or_liability_account_id as glAccountId , acc.name as glAccountName, acc.gl_code as glCode, "
|
||||
+ "tg.id as taxGroupId, tg.name as taxGroupName " + "from m_charge c "
|
||||
+ "join m_organisation_currency oc on c.currency_code = oc.code "
|
||||
+ " LEFT JOIN acc_gl_account acc on acc.id = c.income_or_liability_account_id "
|
||||
+ " LEFT JOIN m_tax_group tg on tg.id = c.tax_group_id ";
|
||||
}
|
||||
|
||||
public String loanProductChargeSchema() {
|
||||
@@ -339,8 +347,16 @@ public class ChargeReadPlatformServiceImpl implements ChargeReadPlatformService
|
||||
glAccountData = new GLAccountData(glAccountId, glAccountName, glCode);
|
||||
}
|
||||
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return ChargeData.instance(id, name, amount, currency, chargeTimeType, chargeAppliesToType, chargeCalculationType,
|
||||
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType, glAccountData);
|
||||
chargePaymentMode, feeOnMonthDay, feeInterval, penalty, active, minCap, maxCap, feeFrequencyType, glAccountData,
|
||||
taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-10
@@ -43,6 +43,8 @@ import org.apache.fineract.portfolio.charge.exception.ChargeNotFoundException;
|
||||
import org.apache.fineract.portfolio.charge.serialization.ChargeDefinitionCommandFromApiJsonDeserializer;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -64,12 +66,14 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
private final LoanProductRepository loanProductRepository;
|
||||
private final FineractEntityAccessUtil fineractEntityAccessUtil;
|
||||
private final GLAccountRepositoryWrapper gLAccountRepository;
|
||||
private final TaxGroupRepositoryWrapper taxGroupRepository;
|
||||
|
||||
@Autowired
|
||||
public ChargeWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
final ChargeDefinitionCommandFromApiJsonDeserializer fromApiJsonDeserializer, final ChargeRepository chargeRepository,
|
||||
final LoanProductRepository loanProductRepository, final RoutingDataSource dataSource,
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil, final GLAccountRepositoryWrapper glAccountRepository) {
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil, final GLAccountRepositoryWrapper glAccountRepository,
|
||||
final TaxGroupRepositoryWrapper taxGroupRepository) {
|
||||
this.context = context;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.dataSource = dataSource;
|
||||
@@ -78,6 +82,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
|
||||
this.gLAccountRepository = glAccountRepository;
|
||||
this.taxGroupRepository = taxGroupRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -96,14 +101,20 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
glAccount = this.gLAccountRepository.findOneWithNotFoundDetection(glAccountId);
|
||||
}
|
||||
|
||||
final Charge charge = Charge.fromJson(command, glAccount);
|
||||
final Long taxGroupId = command.longValueOfParameterNamed(ChargesApiConstants.taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
|
||||
}
|
||||
|
||||
final Charge charge = Charge.fromJson(command, glAccount, taxGroup);
|
||||
this.chargeRepository.save(charge);
|
||||
|
||||
// check if the office specific products are enabled. If yes, then
|
||||
// save this savings product against a specific office
|
||||
// i.e. this savings product is specific for this office.
|
||||
fineractEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(FineractEntityAccessType.OFFICE_ACCESS_TO_CHARGES,
|
||||
FineractEntityType.CHARGE, charge.getId());
|
||||
fineractEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(
|
||||
FineractEntityAccessType.OFFICE_ACCESS_TO_CHARGES, FineractEntityType.CHARGE, charge.getId());
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(charge.getId()).build();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
@@ -126,7 +137,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
final Map<String, Object> changes = chargeForUpdate.update(command);
|
||||
|
||||
this.fromApiJsonDeserializer.validateChargeTimeNCalculationType(chargeForUpdate.getChargeTimeType(),
|
||||
chargeForUpdate.getChargeCalculation());
|
||||
chargeForUpdate.getChargeCalculation());
|
||||
|
||||
// MIFOSX-900: Check if the Charge has been active before and now is
|
||||
// deactivated:
|
||||
@@ -146,7 +157,7 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
if (isChargeExistWithLoans) { throw new ChargeCannotBeUpdatedException(
|
||||
"error.msg.charge.frequency.cannot.be.updated.it.is.used.in.loan",
|
||||
"This charge frequency cannot be updated, it is used in loan"); }
|
||||
}
|
||||
}
|
||||
|
||||
// Has account Id been changed ?
|
||||
if (changes.containsKey(ChargesApiConstants.glAccountIdParamName)) {
|
||||
@@ -158,6 +169,15 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
chargeForUpdate.setAccount(newIncomeAccount);
|
||||
}
|
||||
|
||||
if (changes.containsKey(ChargesApiConstants.taxGroupIdParamName)) {
|
||||
final Long newValue = command.longValueOfParameterNamed(ChargesApiConstants.taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
if (newValue != null) {
|
||||
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(newValue);
|
||||
}
|
||||
chargeForUpdate.setTaxGroup(taxGroup);
|
||||
}
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.chargeRepository.save(chargeForUpdate);
|
||||
}
|
||||
@@ -182,10 +202,9 @@ public class ChargeWritePlatformServiceJpaRepositoryImpl implements ChargeWriteP
|
||||
final Boolean isChargeExistWithSavings = isAnySavingsAssociateWithThisCharge(chargeId);
|
||||
|
||||
// TODO: Change error messages around:
|
||||
if (!loanProducts.isEmpty() || isChargeExistWithLoans
|
||||
|| isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException(
|
||||
"error.msg.charge.cannot.be.deleted.it.is.already.used.in.loan",
|
||||
"This charge cannot be deleted, it is already used in loan"); }
|
||||
if (!loanProducts.isEmpty() || isChargeExistWithLoans || isChargeExistWithSavings) { throw new ChargeCannotBeDeletedException(
|
||||
"error.msg.charge.cannot.be.deleted.it.is.already.used.in.loan",
|
||||
"This charge cannot be deleted, it is already used in loan"); }
|
||||
|
||||
chargeForDelete.delete();
|
||||
|
||||
|
||||
+3
-1
@@ -41,6 +41,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -156,11 +157,12 @@ public class LoanChargeReadPlatformServiceImpl implements LoanChargeReadPlatform
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
// this field is applicable only for client charges
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
|
||||
incomeOrLiabilityAccountOptions);
|
||||
incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -217,7 +217,8 @@ public class DepositsApiConstants {
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_CONTROL.getValue(), SAVINGS_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.SAVINGS_REFERENCE.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), chartsParamName));
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), chartsParamName,
|
||||
SavingsApiConstants.withHoldTaxParamName, SavingsApiConstants.taxGroupIdParamName));
|
||||
|
||||
private static final Set<String> PRECLOSURE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(preClosurePenalApplicableParamName,
|
||||
preClosurePenalInterestParamName, preClosurePenalInterestOnTypeIdParamName));
|
||||
@@ -298,7 +299,7 @@ public class DepositsApiConstants {
|
||||
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, lockinPeriodFrequencyParamName, lockinPeriodFrequencyTypeParamName,
|
||||
chargesParamName, chartsParamName, depositAmountParamName, depositPeriodParamName, depositPeriodFrequencyIdParamName,
|
||||
savingsAccounts, expectedFirstDepositOnDateParamName));
|
||||
savingsAccounts, expectedFirstDepositOnDateParamName, SavingsApiConstants.withHoldTaxParamName));
|
||||
|
||||
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_REQUEST_DATA_PARAMETERS = fixedDepositAccountRequestData();
|
||||
public static final Set<String> FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS = fixedDepositAccountResponseData();
|
||||
|
||||
+10
-2
@@ -38,7 +38,8 @@ public enum SavingsAccountTransactionType {
|
||||
APPROVE_TRANSFER(13, "savingsAccountTransactionType.approveTransfer"), //
|
||||
WITHDRAW_TRANSFER(14, "savingsAccountTransactionType.withdrawTransfer"), //
|
||||
REJECT_TRANSFER(15, "savingsAccountTransactionType.rejectTransfer"), WRITTEN_OFF(16, "savingsAccountTransactionType.writtenoff"), //
|
||||
OVERDRAFT_INTEREST(17, "savingsAccountTransactionType.overdraftInterest"); //
|
||||
OVERDRAFT_INTEREST(17, "savingsAccountTransactionType.overdraftInterest"), //
|
||||
WITHHOLD_TAX(18, "savingsAccountTransactionType.withholdTax");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -101,6 +102,9 @@ public enum SavingsAccountTransactionType {
|
||||
case 17:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST;
|
||||
break;
|
||||
case 18:
|
||||
savingsAccountTransactionType = SavingsAccountTransactionType.WITHHOLD_TAX;
|
||||
break;
|
||||
}
|
||||
return savingsAccountTransactionType;
|
||||
}
|
||||
@@ -117,6 +121,10 @@ public enum SavingsAccountTransactionType {
|
||||
return this.value.equals(SavingsAccountTransactionType.INTEREST_POSTING.getValue());
|
||||
}
|
||||
|
||||
public boolean isWithHoldTax() {
|
||||
return this.value.equals(SavingsAccountTransactionType.WITHHOLD_TAX.getValue());
|
||||
}
|
||||
|
||||
public boolean isWithdrawalFee() {
|
||||
return this.value.equals(SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue());
|
||||
}
|
||||
@@ -162,7 +170,7 @@ public enum SavingsAccountTransactionType {
|
||||
}
|
||||
|
||||
public boolean isDebit() {
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest();
|
||||
return isWithdrawal() || isWithdrawalFee() || isAnnualFee() || isPayCharge() || isIncomeFromInterest() || isWithHoldTax();
|
||||
}
|
||||
|
||||
public boolean isCredit() {
|
||||
|
||||
+9
-5
@@ -117,6 +117,8 @@ public class SavingsApiConstants {
|
||||
public static final String minBalanceForInterestCalculationParamName = "minBalanceForInterestCalculation";
|
||||
public static final String withdrawBalanceParamName = "withdrawBalance";
|
||||
public static final String onHoldFundsParamName = "onHoldFunds";
|
||||
public static final String withHoldTaxParamName = "withHoldTax";
|
||||
public static final String taxGroupIdParamName = "taxGroupId";
|
||||
|
||||
// transaction parameters
|
||||
public static final String transactionDateParamName = "transactionDate";
|
||||
@@ -177,8 +179,8 @@ public class SavingsApiConstants {
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.OVERDRAFT_PORTFOLIO_CONTROL.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
||||
SAVINGS_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_INTEREST.getValue(), allowOverdraftParamName, overdraftLimitParamName,
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName,
|
||||
minRequiredBalanceParamName, enforceMinRequiredBalanceParamName, minBalanceForInterestCalculationParamName));
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, minRequiredBalanceParamName,
|
||||
enforceMinRequiredBalanceParamName, minBalanceForInterestCalculationParamName, withHoldTaxParamName, taxGroupIdParamName));
|
||||
|
||||
/**
|
||||
* These parameters will match the class level parameters of
|
||||
@@ -193,18 +195,20 @@ public class SavingsApiConstants {
|
||||
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, "currencyOptions",
|
||||
"interestCompoundingPeriodTypeOptions", "interestPostingPeriodTypeOptions", "interestCalculationTypeOptions",
|
||||
"interestCalculationDaysInYearTypeOptions", "lockinPeriodFrequencyTypeOptions", "withdrawalFeeTypeOptions",
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName));
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName,
|
||||
taxGroupIdParamName));
|
||||
|
||||
public static final Set<String> SAVINGS_ACCOUNT_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, monthDayFormatParamName, staffIdParamName, accountNoParamName, externalIdParamName, clientIdParamName,
|
||||
groupIdParamName, productIdParamName, fieldOfficerIdParamName, submittedOnDateParamName, nominalAnnualInterestRateParamName,
|
||||
interestCompoundingPeriodTypeParamName, interestPostingPeriodTypeParamName, interestCalculationTypeParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName, lockinPeriodFrequencyParamName,
|
||||
interestCalculationDaysInYearTypeParamName, minRequiredOpeningBalanceParamName,
|
||||
lockinPeriodFrequencyParamName,
|
||||
lockinPeriodFrequencyTypeParamName,
|
||||
// withdrawalFeeAmountParamName, withdrawalFeeTypeParamName,
|
||||
withdrawalFeeForTransfersParamName, feeAmountParamName, feeOnMonthDayParamName, chargesParamName, allowOverdraftParamName,
|
||||
overdraftLimitParamName, minRequiredBalanceParamName, enforceMinRequiredBalanceParamName,
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName));
|
||||
nominalAnnualInterestRateOverdraftParamName, minOverdraftForInterestCalculationParamName, withHoldTaxParamName));
|
||||
|
||||
/**
|
||||
* These parameters will match the class level parameters of
|
||||
|
||||
+9
-3
@@ -71,6 +71,8 @@ import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformS
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -96,6 +98,7 @@ public class FixedDepositProductsApiResource {
|
||||
private final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService;
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public FixedDepositProductsApiResource(final DepositProductReadPlatformService depositProductReadPlatformService,
|
||||
@@ -110,7 +113,7 @@ public class FixedDepositProductsApiResource {
|
||||
final InterestRateChartReadPlatformService interestRateChartReadPlatformService,
|
||||
final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService,
|
||||
final PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
|
||||
final PaymentTypeReadPlatformService paymentTypeReadPlatformService, final TaxReadPlatformService taxReadPlatformService) {
|
||||
this.depositProductReadPlatformService = depositProductReadPlatformService;
|
||||
this.savingsDropdownReadPlatformService = savingsDropdownReadPlatformService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
@@ -126,6 +129,7 @@ public class FixedDepositProductsApiResource {
|
||||
this.depositsDropdownReadPlatformService = depositsDropdownReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.taxReadPlatformService = taxReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -287,6 +291,8 @@ public class FixedDepositProductsApiResource {
|
||||
Collection<ChargeData> penaltyOptions = this.chargeReadPlatformService.retrieveSavingsApplicablePenalties();
|
||||
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
|
||||
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
|
||||
// interest rate chart template
|
||||
final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template();
|
||||
|
||||
@@ -296,14 +302,14 @@ public class FixedDepositProductsApiResource {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions);
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
|
||||
} else {
|
||||
fixedDepositProductToReturn = FixedDepositProductData.template(currency, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions);
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
return fixedDepositProductToReturn;
|
||||
|
||||
+8
-3
@@ -71,6 +71,8 @@ import org.apache.fineract.portfolio.savings.service.DepositProductReadPlatformS
|
||||
import org.apache.fineract.portfolio.savings.service.DepositsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -96,6 +98,7 @@ public class RecurringDepositProductsApiResource {
|
||||
private final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService;
|
||||
private final DropdownReadPlatformService dropdownReadPlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public RecurringDepositProductsApiResource(final DepositProductReadPlatformService depositProductReadPlatformService,
|
||||
@@ -110,7 +113,7 @@ public class RecurringDepositProductsApiResource {
|
||||
final InterestRateChartReadPlatformService interestRateChartReadPlatformService,
|
||||
final DepositsDropdownReadPlatformService depositsDropdownReadPlatformService,
|
||||
final DropdownReadPlatformService dropdownReadPlatformService,
|
||||
final PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
|
||||
final PaymentTypeReadPlatformService paymentTypeReadPlatformService, final TaxReadPlatformService taxReadPlatformService) {
|
||||
this.depositProductReadPlatformService = depositProductReadPlatformService;
|
||||
this.savingsDropdownReadPlatformService = savingsDropdownReadPlatformService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
@@ -126,6 +129,7 @@ public class RecurringDepositProductsApiResource {
|
||||
this.depositsDropdownReadPlatformService = depositsDropdownReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.taxReadPlatformService = taxReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -287,6 +291,7 @@ public class RecurringDepositProductsApiResource {
|
||||
|
||||
// interest rate chart template
|
||||
final InterestRateChartData chartTemplate = this.interestRateChartReadPlatformService.template();
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
|
||||
RecurringDepositProductData recurringDepositProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
@@ -294,14 +299,14 @@ public class RecurringDepositProductsApiResource {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions);
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
|
||||
} else {
|
||||
recurringDepositProductToReturn = RecurringDepositProductData.template(currency, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, chartTemplate,
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions);
|
||||
preClosurePenalInterestOnTypeOptions, periodFrequencyTypeOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
return recurringDepositProductToReturn;
|
||||
|
||||
+12
-4
@@ -209,7 +209,15 @@ public class SavingsAccountsApiResource {
|
||||
@Path("{accountId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String update(@PathParam("accountId") final Long accountId, final String apiRequestBodyAsJson) {
|
||||
public String update(@PathParam("accountId") final Long accountId, final String apiRequestBodyAsJson,
|
||||
@QueryParam("command") final String commandParam) {
|
||||
|
||||
if (is(commandParam, "updateWithHoldTax")) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson).updateWithHoldTax(accountId)
|
||||
.build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSavingsAccount(accountId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
@@ -273,9 +281,9 @@ public class SavingsAccountsApiResource {
|
||||
|
||||
if (result == null) {
|
||||
//
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "reject", "withdrawnByApplicant", "approve", "undoapproval", "activate", "calculateInterest",
|
||||
"postInterest", "close", "assignSavingsOfficer", "unassignSavingsOfficer" });
|
||||
throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "reject", "withdrawnByApplicant", "approve",
|
||||
"undoapproval", "activate", "calculateInterest", "postInterest", "close", "assignSavingsOfficer",
|
||||
"unassignSavingsOfficer" });
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
|
||||
+9
-4
@@ -66,6 +66,8 @@ import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsDropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -87,6 +89,7 @@ public class SavingsProductsApiResource {
|
||||
private final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService;
|
||||
private final ChargeReadPlatformService chargeReadPlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final TaxReadPlatformService taxReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public SavingsProductsApiResource(final SavingsProductReadPlatformService savingProductReadPlatformService,
|
||||
@@ -97,7 +100,8 @@ public class SavingsProductsApiResource {
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService,
|
||||
final ProductToGLAccountMappingReadPlatformService accountMappingReadPlatformService,
|
||||
final ChargeReadPlatformService chargeReadPlatformService, PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
|
||||
final ChargeReadPlatformService chargeReadPlatformService, PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
final TaxReadPlatformService taxReadPlatformService) {
|
||||
this.savingProductReadPlatformService = savingProductReadPlatformService;
|
||||
this.dropdownReadPlatformService = dropdownReadPlatformService;
|
||||
this.currencyReadPlatformService = currencyReadPlatformService;
|
||||
@@ -109,6 +113,7 @@ public class SavingsProductsApiResource {
|
||||
this.accountMappingReadPlatformService = accountMappingReadPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.taxReadPlatformService = taxReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -255,19 +260,19 @@ public class SavingsProductsApiResource {
|
||||
|
||||
Collection<ChargeData> penaltyOptions = this.chargeReadPlatformService.retrieveSavingsApplicablePenalties();
|
||||
penaltyOptions = CollectionUtils.isEmpty(penaltyOptions) ? null : penaltyOptions;
|
||||
|
||||
final Collection<TaxGroupData> taxGroupOptions = this.taxReadPlatformService.retrieveTaxGroupsForLookUp();
|
||||
SavingsProductData savingsProductToReturn = null;
|
||||
if (savingsProduct != null) {
|
||||
savingsProductToReturn = SavingsProductData.withTemplate(savingsProduct, currencyOptions, interestCompoundingPeriodTypeOptions,
|
||||
interestPostingPeriodTypeOptions, interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions,
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, chargeOptions, penaltyOptions);
|
||||
accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions);
|
||||
} else {
|
||||
savingsProductToReturn = SavingsProductData.template(currency, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, accountingRule, currencyOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions,
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions);
|
||||
paymentTypeOptions, accountingRuleOptions, accountingMappingOptions, chargeOptions, penaltyOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
return savingsProductToReturn;
|
||||
|
||||
+13
-4
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing abstract for Fixed and Recurring Deposit
|
||||
@@ -60,6 +61,8 @@ public class DepositAccountData {
|
||||
protected final boolean withdrawalFeeForTransfers;
|
||||
protected final EnumOptionData depositType;
|
||||
protected final BigDecimal minBalanceForInterestCalculation;
|
||||
protected final boolean withHoldTax;
|
||||
protected final TaxGroupData taxGroup;
|
||||
|
||||
// associations
|
||||
protected final SavingsAccountSummaryData summary;
|
||||
@@ -92,7 +95,8 @@ public class DepositAccountData {
|
||||
final EnumOptionData interestCalculationType, final EnumOptionData interestCalculationDaysInYearType,
|
||||
final BigDecimal minRequiredOpeningBalance, final Integer lockinPeriodFrequency,
|
||||
final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
|
||||
final SavingsAccountSummaryData summary, final EnumOptionData depositType, final BigDecimal minBalanceForInterestCalculation) {
|
||||
final SavingsAccountSummaryData summary, final EnumOptionData depositType, final BigDecimal minBalanceForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
|
||||
final Collection<DepositProductData> productOptions = null;
|
||||
final Collection<StaffData> fieldOfficerOptions = null;
|
||||
@@ -116,7 +120,7 @@ public class DepositAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, depositType,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static DepositAccountData lookup(final Long id, final String accountNo, final EnumOptionData depositType) {
|
||||
@@ -158,6 +162,8 @@ public class DepositAccountData {
|
||||
final DepositAccountInterestRateChartData accountChart = null;
|
||||
final DepositAccountInterestRateChartData chartTemplate = null;
|
||||
final BigDecimal minBalanceForInterestCalculation = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new DepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, interestRate, interestCompoundingPeriodType,
|
||||
@@ -166,7 +172,7 @@ public class DepositAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, accountChart, chartTemplate, depositType,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
protected DepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
|
||||
@@ -186,7 +192,8 @@ public class DepositAccountData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions,
|
||||
final DepositAccountInterestRateChartData accountChart, final DepositAccountInterestRateChartData chartTemplate,
|
||||
final EnumOptionData depositType, final BigDecimal minBalanceForInterestCalculation) {
|
||||
final EnumOptionData depositType, final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax,
|
||||
final TaxGroupData taxGroup) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.externalId = externalId;
|
||||
@@ -233,6 +240,8 @@ public class DepositAccountData {
|
||||
this.chartTemplate = chartTemplate;
|
||||
this.depositType = depositType;
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.taxGroup = taxGroup;
|
||||
this.withHoldTax = withHoldTax;
|
||||
}
|
||||
|
||||
private SavingsAccountChargeData getWithdrawalFee() {
|
||||
|
||||
+13
-1
@@ -59,6 +59,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequi
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
@@ -120,6 +121,7 @@ public class DepositAccountDataValidator {
|
||||
validatePreClosureDetailForSubmit(element, baseDataValidator);
|
||||
validateDepositTermDeatilForSubmit(element, baseDataValidator, DepositAccountType.FIXED_DEPOSIT);
|
||||
validateSavingsCharges(element, baseDataValidator);
|
||||
validateWithHoldTax(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -140,6 +142,7 @@ public class DepositAccountDataValidator {
|
||||
validatePreClosureDetailForUpdate(element, baseDataValidator);
|
||||
validateDepositTermDeatilForUpdate(element, baseDataValidator, DepositAccountType.FIXED_DEPOSIT);
|
||||
// validateSavingsCharges(element, baseDataValidator);
|
||||
validateWithHoldTax(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
@@ -161,7 +164,8 @@ public class DepositAccountDataValidator {
|
||||
validateDepositTermDeatilForSubmit(element, baseDataValidator, DepositAccountType.RECURRING_DEPOSIT);
|
||||
validateRecurringDetailForSubmit(element, baseDataValidator);
|
||||
validateSavingsCharges(element, baseDataValidator);
|
||||
|
||||
validateWithHoldTax(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -182,6 +186,7 @@ public class DepositAccountDataValidator {
|
||||
validateDepositTermDeatilForUpdate(element, baseDataValidator, DepositAccountType.RECURRING_DEPOSIT);
|
||||
validateRecurringDetailForUpdate(element, baseDataValidator);
|
||||
// validateSavingsCharges(element, baseDataValidator);
|
||||
validateWithHoldTax(element, baseDataValidator);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
|
||||
@@ -714,6 +719,13 @@ public class DepositAccountDataValidator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateWithHoldTax(final JsonElement element, final DataValidatorBuilder baseDataValidator){
|
||||
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
|
||||
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
|
||||
+36
-11
@@ -32,6 +32,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object represents a partial data of Deposit Proucts.
|
||||
@@ -53,6 +54,8 @@ public class DepositProductData {
|
||||
protected final EnumOptionData lockinPeriodFrequencyType;
|
||||
// protected final boolean withdrawalFeeForTransfers;
|
||||
protected final BigDecimal minBalanceForInterestCalculation;
|
||||
protected final boolean withHoldTax;
|
||||
protected final TaxGroupData taxGroup;
|
||||
|
||||
// accounting
|
||||
protected final EnumOptionData accountingRule;
|
||||
@@ -82,6 +85,7 @@ public class DepositProductData {
|
||||
protected final Collection<ChargeData> chargeOptions;
|
||||
protected final Collection<ChargeData> penaltyOptions;
|
||||
protected final InterestRateChartData chartTemplate;
|
||||
protected final Collection<TaxGroupData> taxGroupOptions;
|
||||
|
||||
public static DepositProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
@@ -93,7 +97,8 @@ public class DepositProductData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate) {
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
|
||||
final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
@@ -109,6 +114,8 @@ public class DepositProductData {
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<InterestRateChartData> interestRateCharts = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
|
||||
@@ -116,7 +123,8 @@ public class DepositProductData {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation);
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
|
||||
taxGroupOptions);
|
||||
}
|
||||
|
||||
public static DepositProductData withCharges(final DepositProductData existingProduct, final Collection<ChargeData> charges) {
|
||||
@@ -132,12 +140,16 @@ public class DepositProductData {
|
||||
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
|
||||
charges, existingProduct.chargeOptions, existingProduct.penaltyOptions, existingProduct.feeToIncomeAccountMappings,
|
||||
existingProduct.penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
|
||||
existingProduct.minBalanceForInterestCalculation);
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link DepositProductData} that contains and exist
|
||||
* {@link DepositProductData} data with further template data for dropdowns.
|
||||
*
|
||||
* @param taxGroupOptions
|
||||
* TODO
|
||||
*/
|
||||
public static DepositProductData withTemplate(final DepositProductData existingProduct, final Collection<CurrencyData> currencyOptions,
|
||||
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
|
||||
@@ -147,7 +159,7 @@ public class DepositProductData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate) {
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate, Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
|
||||
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
|
||||
@@ -159,7 +171,8 @@ public class DepositProductData {
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions,
|
||||
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings,
|
||||
existingProduct.interestRateCharts, chartTemplate, existingProduct.minBalanceForInterestCalculation);
|
||||
existingProduct.interestRateCharts, chartTemplate, existingProduct.minBalanceForInterestCalculation,
|
||||
existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static DepositProductData withAccountingDetails(final DepositProductData existingProduct,
|
||||
@@ -180,6 +193,7 @@ public class DepositProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new DepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
|
||||
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
|
||||
@@ -191,7 +205,7 @@ public class DepositProductData {
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, existingProduct.charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, existingProduct.interestRateCharts, existingProduct.chartTemplate,
|
||||
existingProduct.minBalanceForInterestCalculation);
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static DepositProductData instance(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -199,7 +213,7 @@ public class DepositProductData {
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
final EnumOptionData interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
|
||||
final EnumOptionData lockinPeriodFrequencyType, final EnumOptionData accountingType,
|
||||
final BigDecimal minBalanceForInterestCalculation) {
|
||||
final BigDecimal minBalanceForInterestCalculation, boolean withHoldTax, TaxGroupData taxGroup) {
|
||||
|
||||
final Map<String, Object> accountingMappings = null;
|
||||
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
|
||||
@@ -221,6 +235,7 @@ public class DepositProductData {
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<InterestRateChartData> interestRateCharts = null;
|
||||
final InterestRateChartData chartTemplate = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
|
||||
@@ -228,7 +243,8 @@ public class DepositProductData {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation);
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
|
||||
taxGroupOptions);
|
||||
}
|
||||
|
||||
public static DepositProductData lookup(final Long id, final String name) {
|
||||
@@ -266,6 +282,9 @@ public class DepositProductData {
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<InterestRateChartData> interestRateCharts = null;
|
||||
final InterestRateChartData chartTemplate = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new DepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
|
||||
@@ -273,7 +292,8 @@ public class DepositProductData {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation);
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
|
||||
taxGroupOptions);
|
||||
}
|
||||
|
||||
public static DepositProductData withInterestChart(final DepositProductData existingProduct,
|
||||
@@ -290,7 +310,8 @@ public class DepositProductData {
|
||||
existingProduct.paymentTypeOptions, existingProduct.accountingRuleOptions, existingProduct.accountingMappingOptions,
|
||||
existingProduct.charges, existingProduct.chargeOptions, existingProduct.penaltyOptions,
|
||||
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, interestRateCharts,
|
||||
existingProduct.chartTemplate, existingProduct.minBalanceForInterestCalculation);
|
||||
existingProduct.chartTemplate, existingProduct.minBalanceForInterestCalculation, existingProduct.withHoldTax,
|
||||
existingProduct.taxGroup, existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
protected DepositProductData(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -311,7 +332,8 @@ public class DepositProductData {
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings,
|
||||
final Collection<InterestRateChartData> interestRateCharts, final InterestRateChartData chartTemplate,
|
||||
final BigDecimal minBalanceForInterestCalculation) {
|
||||
final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -357,6 +379,9 @@ public class DepositProductData {
|
||||
this.activeChart = activeChart(this.interestRateCharts);
|
||||
this.chartTemplate = chartTemplate;
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.taxGroup = taxGroup;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroupOptions = taxGroupOptions;
|
||||
}
|
||||
|
||||
public static InterestRateChartData activeChart(Collection<InterestRateChartData> interestRateCharts) {
|
||||
|
||||
+30
@@ -39,6 +39,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepo
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.digitsAfterDecimalParamName;
|
||||
@@ -56,6 +57,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequi
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@@ -333,6 +335,8 @@ public class DepositProductDataValidator {
|
||||
validatePaymentChannelFundSourceMappings(fromApiJsonHelper, baseDataValidator, element);
|
||||
validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element);
|
||||
}
|
||||
|
||||
validateTaxWithHoldingParams(baseDataValidator, element, true);
|
||||
}
|
||||
|
||||
public void validatePreClosureDetailForCreate(JsonElement element, DataValidatorBuilder baseDataValidator) {
|
||||
@@ -539,6 +543,7 @@ public class DepositProductDataValidator {
|
||||
|
||||
validatePaymentChannelFundSourceMappings(fromApiJsonHelper, baseDataValidator, element);
|
||||
validateChargeToIncomeAccountMappings(fromApiJsonHelper, baseDataValidator, element);
|
||||
validateTaxWithHoldingParams(baseDataValidator, element, false);
|
||||
}
|
||||
|
||||
public void validatePreClosureDetailForUpdate(JsonElement element, DataValidatorBuilder baseDataValidator) {
|
||||
@@ -778,4 +783,29 @@ public class DepositProductDataValidator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element,
|
||||
final boolean isCreate) {
|
||||
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
|
||||
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
Boolean withHoldTax = this.fromApiJsonHelper.extractBooleanNamed(withHoldTaxParamName, element);
|
||||
if (withHoldTax == null) {
|
||||
withHoldTax = false;
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(taxGroupIdParamName, element)) {
|
||||
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(taxGroupIdParamName, element);
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).ignoreIfNull().longGreaterThanZero();
|
||||
if (withHoldTax) {
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
}
|
||||
|
||||
} else if (withHoldTax && isCreate) {
|
||||
final Long taxGroupId = null;
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+16
-9
@@ -31,6 +31,7 @@ import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
@@ -101,7 +102,8 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, depositAmount, maturityAmount, maturityDate,
|
||||
depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions,
|
||||
paymentTypeOptions, savingsAccountDatas, linkedAccount, transferInterestToSavings);
|
||||
paymentTypeOptions, savingsAccountDatas, linkedAccount, transferInterestToSavings, depositAccountData.withHoldTax,
|
||||
depositAccountData.taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData withInterestChart(final FixedDepositAccountData account,
|
||||
@@ -122,7 +124,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
|
||||
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
|
||||
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, account.savingsAccounts,
|
||||
account.linkedAccount, account.transferInterestToSavings);
|
||||
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData associationsAndTemplate(final FixedDepositAccountData account, FixedDepositAccountData template,
|
||||
@@ -149,7 +151,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
|
||||
account.depositPeriod, account.depositPeriodFrequency, template.periodFrequencyTypeOptions, account.depositType,
|
||||
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, template.savingsAccounts,
|
||||
linkedAccount, account.transferInterestToSavings);
|
||||
linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData withTemplateOptions(final FixedDepositAccountData account,
|
||||
@@ -178,7 +180,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
|
||||
account.depositPeriod, account.depositPeriodFrequency, periodFrequencyTypeOptions, account.depositType,
|
||||
account.onAccountClosure, account.onAccountClosureOptions, account.paymentTypeOptions, savingsAccounts,
|
||||
account.linkedAccount, account.transferInterestToSavings);
|
||||
account.linkedAccount, account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
|
||||
@@ -206,6 +208,8 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
final BigDecimal minBalanceForInterestCalculation = null;
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
final Collection<DepositProductData> productOptions = null;
|
||||
final Collection<StaffData> fieldOfficerOptions = null;
|
||||
@@ -257,7 +261,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
|
||||
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions,
|
||||
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
|
||||
transferInterestToSavings);
|
||||
transferInterestToSavings, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData preClosureDetails(final Long accountId, BigDecimal maturityAmount,
|
||||
@@ -324,6 +328,8 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
|
||||
final EnumOptionData depositType = SavingsEnumerations.depositType(DepositAccountType.FIXED_DEPOSIT.getValue());
|
||||
final PortfolioAccountData linkedAccount = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new FixedDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
@@ -336,7 +342,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
maxDepositTerm, minDepositTermType, maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType,
|
||||
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, periodFrequencyTypeOptions,
|
||||
depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, linkedAccount,
|
||||
transferInterestToSavings);
|
||||
transferInterestToSavings, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static FixedDepositAccountData withClosureTemplateDetails(final FixedDepositAccountData account,
|
||||
@@ -359,7 +365,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
account.inMultiplesOfDepositTermType, account.depositAmount, account.maturityAmount, account.maturityDate,
|
||||
account.depositPeriod, account.depositPeriodFrequency, account.periodFrequencyTypeOptions, account.depositType,
|
||||
account.onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, account.linkedAccount,
|
||||
account.transferInterestToSavings);
|
||||
account.transferInterestToSavings, account.withHoldTax, account.taxGroup);
|
||||
|
||||
}
|
||||
|
||||
@@ -389,7 +395,8 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final EnumOptionData depositType,
|
||||
final EnumOptionData onAccountClosure, final Collection<EnumOptionData> onAccountClosureOptions,
|
||||
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<SavingsAccountData> savingsAccountDatas,
|
||||
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings) {
|
||||
final PortfolioAccountData linkedAccount, final Boolean transferInterestToSavings, final boolean withHoldTax,
|
||||
final TaxGroupData taxGroup) {
|
||||
|
||||
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId,
|
||||
fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
|
||||
@@ -397,7 +404,7 @@ public class FixedDepositAccountData extends DepositAccountData {
|
||||
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
|
||||
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation);
|
||||
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
|
||||
this.preClosurePenalApplicable = preClosurePenalApplicable;
|
||||
this.preClosurePenalInterest = preClosurePenalInterest;
|
||||
|
||||
+27
-11
@@ -31,6 +31,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a Fixed Deposit product.
|
||||
@@ -66,7 +67,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions) {
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
@@ -95,6 +96,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final BigDecimal minDepositAmount = null;
|
||||
final BigDecimal depositAmount = null;
|
||||
final BigDecimal maxDepositAmount = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
@@ -106,7 +109,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, minDepositAmount, depositAmount,
|
||||
maxDepositAmount, periodFrequencyTypeOptions);
|
||||
maxDepositAmount, periodFrequencyTypeOptions, withHoldTax, taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static FixedDepositProductData withCharges(final FixedDepositProductData existingProduct, final Collection<ChargeData> charges) {
|
||||
@@ -128,13 +131,17 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
|
||||
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
|
||||
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
|
||||
existingProduct.periodFrequencyTypeOptions);
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link FixedDepositProductData} that contains and exist
|
||||
* {@link FixedDepositProductData} data with further template data for
|
||||
* dropdowns.
|
||||
*
|
||||
* @param taxGroupOptions
|
||||
* TODO
|
||||
*/
|
||||
public static FixedDepositProductData withTemplate(final FixedDepositProductData existingProduct,
|
||||
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
|
||||
@@ -146,7 +153,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions) {
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
return new FixedDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
|
||||
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
|
||||
@@ -164,7 +171,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
preClosurePenalInterestOnTypeOptions, existingProduct.minDepositTerm, existingProduct.maxDepositTerm,
|
||||
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
|
||||
existingProduct.inMultiplesOfDepositTermType, existingProduct.minDepositAmount, existingProduct.depositAmount,
|
||||
existingProduct.maxDepositAmount, periodFrequencyTypeOptions);
|
||||
existingProduct.maxDepositAmount, periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
taxGroupOptions);
|
||||
}
|
||||
|
||||
public static FixedDepositProductData withAccountingDetails(final FixedDepositProductData existingProduct,
|
||||
@@ -202,7 +210,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
|
||||
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
|
||||
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
|
||||
existingProduct.periodFrequencyTypeOptions);
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
public static FixedDepositProductData instance(final DepositProductData depositProductData, final boolean preClosurePenalApplicable,
|
||||
@@ -233,6 +242,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final InterestRateChartData chartTemplate = null;
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new FixedDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName,
|
||||
depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate,
|
||||
@@ -247,7 +257,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, minDepositAmount, depositAmount,
|
||||
maxDepositAmount, periodFrequencyTypeOptions);
|
||||
maxDepositAmount, periodFrequencyTypeOptions, depositProductData.withHoldTax, depositProductData.taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static FixedDepositProductData lookup(final Long id, final String name) {
|
||||
@@ -298,6 +308,9 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final BigDecimal depositAmount = null;
|
||||
final BigDecimal maxDepositAmount = null;
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new FixedDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
@@ -309,7 +322,7 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, minDepositAmount, depositAmount,
|
||||
maxDepositAmount, periodFrequencyTypeOptions);
|
||||
maxDepositAmount, periodFrequencyTypeOptions, withHoldTax, taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static FixedDepositProductData withInterestChart(final FixedDepositProductData existingProduct,
|
||||
@@ -332,7 +345,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
existingProduct.minDepositTerm, existingProduct.maxDepositTerm, existingProduct.minDepositTermType,
|
||||
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
|
||||
existingProduct.minDepositAmount, existingProduct.depositAmount, existingProduct.maxDepositAmount,
|
||||
existingProduct.periodFrequencyTypeOptions);
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
|
||||
}
|
||||
|
||||
@@ -359,7 +373,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
final Integer minDepositTerm, final Integer maxDepositTerm, final EnumOptionData minDepositTermType,
|
||||
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
|
||||
final EnumOptionData inMultiplesOfDepositTermType, final BigDecimal minDepositAmount, final BigDecimal depositAmount,
|
||||
final BigDecimal maxDepositAmount, final Collection<EnumOptionData> periodFrequencyTypeOptions) {
|
||||
final BigDecimal maxDepositAmount, final Collection<EnumOptionData> periodFrequencyTypeOptions, final boolean withHoldTax,
|
||||
final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
|
||||
@@ -367,7 +382,8 @@ public class FixedDepositProductData extends DepositProductData {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation);
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
|
||||
taxGroupOptions);
|
||||
|
||||
// fixed deposit additional fields
|
||||
this.preClosurePenalApplicable = preClosurePenalApplicable;
|
||||
|
||||
+19
-9
@@ -30,6 +30,7 @@ import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/**
|
||||
@@ -115,7 +116,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount, periodFrequencyTypeOptions, depositType,
|
||||
onAccountClosure, onAccountClosureOptions, paymentTypeOptions, savingsAccountDatas, expectedFirstDepositOnDate,
|
||||
totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit, allowWithdrawal, adjustAdvanceTowardsFuturePayments,
|
||||
isCalendarInherited, recurringFrequency, recurringFrequencyType);
|
||||
isCalendarInherited, recurringFrequency, recurringFrequencyType, depositAccountData.withHoldTax,
|
||||
depositAccountData.taxGroup);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccountData withInterestChartAndRecurringDetails(final RecurringDepositAccountData account,
|
||||
@@ -139,7 +141,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
account.periodFrequencyTypeOptions, account.depositType, account.onAccountClosure, account.onAccountClosureOptions,
|
||||
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
|
||||
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
|
||||
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, recurringFrequency, recurringFrequencyType);
|
||||
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, recurringFrequency, recurringFrequencyType,
|
||||
account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccountData withTemplateOptions(final RecurringDepositAccountData account,
|
||||
@@ -185,7 +188,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
|
||||
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
|
||||
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
|
||||
account.recurringFrequencyType);
|
||||
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
|
||||
|
||||
}
|
||||
|
||||
@@ -218,7 +221,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
account.paymentTypeOptions, account.savingsAccounts, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
|
||||
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
|
||||
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
|
||||
account.recurringFrequencyType);
|
||||
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
|
||||
@@ -296,6 +299,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
final boolean isCalendarInherited = false;
|
||||
final Integer recurringFrequency = null;
|
||||
final EnumOptionData recurringFrequencyType = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new RecurringDepositAccountData(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
@@ -309,7 +314,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount,
|
||||
periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions,
|
||||
savingsAccountDatas, expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit,
|
||||
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType);
|
||||
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType,
|
||||
withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccountData preClosureDetails(final Long accountId, final BigDecimal maturityAmount,
|
||||
@@ -385,6 +391,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
final boolean isCalendarInherited = false;
|
||||
final Integer recurringFrequency = null;
|
||||
final EnumOptionData recurringFrequencyType = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new RecurringDepositAccountData(accountId, accountNo, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
@@ -398,7 +406,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
depositAmount, maturityAmount, maturityDate, depositPeriod, depositPeriodFrequency, mandatoryRecommendedDepositAmount,
|
||||
periodFrequencyTypeOptions, depositType, onAccountClosure, onAccountClosureOptions, paymentTypeOptions,
|
||||
savingsAccountDatas, expectedFirstDepositOnDate, totalOverdueAmount, noOfOverdueInstallments, isMandatoryDeposit,
|
||||
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType);
|
||||
allowWithdrawal, adjustAdvanceTowardsFuturePayments, isCalendarInherited, recurringFrequency, recurringFrequencyType,
|
||||
withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccountData withClosureTemplateDetails(final RecurringDepositAccountData account,
|
||||
@@ -424,7 +433,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
paymentTypeOptions, savingsAccountDatas, account.expectedFirstDepositOnDate, account.totalOverdueAmount,
|
||||
account.noOfOverdueInstallments, account.isMandatoryDeposit, account.allowWithdrawal,
|
||||
account.adjustAdvanceTowardsFuturePayments, account.isCalendarInherited, account.recurringFrequency,
|
||||
account.recurringFrequencyType);
|
||||
account.recurringFrequencyType, account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
private RecurringDepositAccountData(final Long id, final String accountNo, final String externalId, final Long groupId,
|
||||
@@ -456,7 +465,8 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
final Collection<SavingsAccountData> savingsAccountDatas, final LocalDate expectedFirstDepositOnDate,
|
||||
final BigDecimal totalOverdueAmount, final Integer noOfOverdueInstallments, final boolean isMandatoryDeposit,
|
||||
final boolean allowWithdrawal, final boolean adjustAdvanceTowardsFuturePayments, final boolean isCalendarInherited,
|
||||
final Integer recurringFrequency, final EnumOptionData recurringFrequencyType) {
|
||||
final Integer recurringFrequency, final EnumOptionData recurringFrequencyType, final boolean withHoldTax,
|
||||
final TaxGroupData taxGroup) {
|
||||
|
||||
super(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName, fieldofficerId,
|
||||
fieldofficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType, interestPostingPeriodType,
|
||||
@@ -464,7 +474,7 @@ public class RecurringDepositAccountData extends DepositAccountData {
|
||||
lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, transactions, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, charges,
|
||||
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation);
|
||||
chargeOptions, accountChart, chartTemplate, depositType, minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
|
||||
this.preClosurePenalApplicable = preClosurePenalApplicable;
|
||||
this.preClosurePenalInterest = preClosurePenalInterest;
|
||||
|
||||
+27
-11
@@ -31,6 +31,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object represent a Recurring Deposit product.
|
||||
@@ -68,7 +69,7 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions) {
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
@@ -101,6 +102,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final BigDecimal minDepositAmount = null;
|
||||
final BigDecimal depositAmount = null;
|
||||
final BigDecimal maxDepositAmount = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
@@ -112,7 +115,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal,
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount);
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount,
|
||||
withHoldTax, taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData withCharges(final RecurringDepositProductData existingProduct,
|
||||
@@ -136,13 +140,16 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
|
||||
existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal, existingProduct.adjustAdvanceTowardsFuturePayments,
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.minDepositAmount, existingProduct.depositAmount,
|
||||
existingProduct.maxDepositAmount);
|
||||
existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RecurringDepositProductData} that contains and exist
|
||||
* {@link RecurringDepositProductData} data with further template data for
|
||||
* dropdowns.
|
||||
*
|
||||
* @param taxGroupOptions
|
||||
* TODO
|
||||
*/
|
||||
public static RecurringDepositProductData withTemplate(final RecurringDepositProductData existingProduct,
|
||||
final Collection<CurrencyData> currencyOptions, final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
|
||||
@@ -154,7 +161,7 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions, final InterestRateChartData chartTemplate,
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions,
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions) {
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
return new RecurringDepositProductData(existingProduct.id, existingProduct.name, existingProduct.shortName,
|
||||
existingProduct.description, existingProduct.currency, existingProduct.nominalAnnualInterestRate,
|
||||
@@ -173,7 +180,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.minDepositTermType, existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm,
|
||||
existingProduct.inMultiplesOfDepositTermType, existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal,
|
||||
existingProduct.adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, existingProduct.minDepositAmount,
|
||||
existingProduct.depositAmount, existingProduct.maxDepositAmount);
|
||||
existingProduct.depositAmount, existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
taxGroupOptions);
|
||||
|
||||
}
|
||||
|
||||
@@ -213,7 +221,7 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
existingProduct.maxDepositTermType, existingProduct.inMultiplesOfDepositTerm, existingProduct.inMultiplesOfDepositTermType,
|
||||
existingProduct.isMandatoryDeposit, existingProduct.allowWithdrawal, existingProduct.adjustAdvanceTowardsFuturePayments,
|
||||
existingProduct.periodFrequencyTypeOptions, existingProduct.minDepositAmount, existingProduct.depositAmount,
|
||||
existingProduct.maxDepositAmount);
|
||||
existingProduct.maxDepositAmount, existingProduct.withHoldTax, existingProduct.taxGroup, existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData instance(final DepositProductData depositProductData,
|
||||
@@ -246,6 +254,7 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final InterestRateChartData chartTemplate = null;
|
||||
final Collection<EnumOptionData> preClosurePenalInterestOnTypeOptions = null;
|
||||
final Collection<EnumOptionData> periodFrequencyTypeOptions = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new RecurringDepositProductData(depositProductData.id, depositProductData.name, depositProductData.shortName,
|
||||
depositProductData.description, depositProductData.currency, depositProductData.nominalAnnualInterestRate,
|
||||
@@ -260,7 +269,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal,
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount);
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount,
|
||||
depositProductData.withHoldTax, depositProductData.taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData lookup(final Long id, final String name) {
|
||||
@@ -316,6 +326,9 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final BigDecimal minDepositAmount = null;
|
||||
final BigDecimal depositAmount = null;
|
||||
final BigDecimal maxDepositAmount = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new RecurringDepositProductData(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
@@ -327,7 +340,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, preClosurePenalApplicable, preClosurePenalInterest,
|
||||
preClosurePenalInterestOnType, preClosurePenalInterestOnTypeOptions, minDepositTerm, maxDepositTerm, minDepositTermType,
|
||||
maxDepositTermType, inMultiplesOfDepositTerm, inMultiplesOfDepositTermType, isMandatoryDeposit, allowWithdrawal,
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount);
|
||||
adjustAdvanceTowardsFuturePayments, periodFrequencyTypeOptions, minDepositAmount, depositAmount, maxDepositAmount,
|
||||
withHoldTax, taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static RecurringDepositProductData withInterestChart(final RecurringDepositProductData product,
|
||||
@@ -347,7 +361,7 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
product.maxDepositTerm, product.minDepositTermType, product.maxDepositTermType, product.inMultiplesOfDepositTerm,
|
||||
product.inMultiplesOfDepositTermType, product.isMandatoryDeposit, product.allowWithdrawal,
|
||||
product.adjustAdvanceTowardsFuturePayments, product.periodFrequencyTypeOptions, product.minDepositAmount,
|
||||
product.depositAmount, product.maxDepositAmount);
|
||||
product.depositAmount, product.maxDepositAmount, product.withHoldTax, product.taxGroup, product.taxGroupOptions);
|
||||
|
||||
}
|
||||
|
||||
@@ -375,7 +389,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
final EnumOptionData maxDepositTermType, final Integer inMultiplesOfDepositTerm,
|
||||
final EnumOptionData inMultiplesOfDepositTermType, final boolean isMandatoryDeposit, boolean allowWithdrawal,
|
||||
boolean adjustAdvanceTowardsFuturePayments, final Collection<EnumOptionData> periodFrequencyTypeOptions,
|
||||
final BigDecimal minDepositAmount, final BigDecimal depositAmount, final BigDecimal maxDepositAmount) {
|
||||
final BigDecimal minDepositAmount, final BigDecimal depositAmount, final BigDecimal maxDepositAmount,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
super(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, lockinPeriodFrequency,
|
||||
@@ -383,7 +398,8 @@ public class RecurringDepositProductData extends DepositProductData {
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions,
|
||||
accountingRuleOptions, accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation);
|
||||
penaltyToIncomeAccountMappings, interestRateCharts, chartTemplate, minBalanceForInterestCalculation, withHoldTax, taxGroup,
|
||||
taxGroupOptions);
|
||||
|
||||
this.preClosurePenalApplicable = preClosurePenalApplicable;
|
||||
this.preClosurePenalInterest = preClosurePenalInterest;
|
||||
|
||||
+29
-14
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.staff.data.StaffData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a savings account.
|
||||
@@ -64,6 +65,8 @@ public class SavingsAccountData {
|
||||
private final boolean enforceMinRequiredBalance;
|
||||
private final BigDecimal minBalanceForInterestCalculation;
|
||||
private final BigDecimal onHoldFunds;
|
||||
private final boolean withHoldTax;
|
||||
private final TaxGroupData taxGroup;
|
||||
|
||||
// associations
|
||||
private final SavingsAccountSummaryData summary;
|
||||
@@ -87,8 +90,8 @@ public class SavingsAccountData {
|
||||
private final SavingsAccountChargeData withdrawalFee;
|
||||
@SuppressWarnings("unused")
|
||||
private final SavingsAccountChargeData annualFee;
|
||||
private final BigDecimal nominalAnnualInterestRateOverdraft;
|
||||
private final BigDecimal minOverdraftForInterestCalculation;
|
||||
private final BigDecimal nominalAnnualInterestRateOverdraft;
|
||||
private final BigDecimal minOverdraftForInterestCalculation;
|
||||
|
||||
public static SavingsAccountData instance(final Long id, final String accountNo, final EnumOptionData depositType,
|
||||
final String externalId, final Long groupId, final String groupName, final Long clientId, final String clientName,
|
||||
@@ -101,7 +104,8 @@ public class SavingsAccountData {
|
||||
final SavingsAccountSummaryData summary, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
|
||||
final Collection<SavingsProductData> productOptions = null;
|
||||
final Collection<StaffData> fieldOfficerOptions = null;
|
||||
@@ -123,8 +127,8 @@ public class SavingsAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static SavingsAccountData lookup(final Long accountId, final String accountNo, final EnumOptionData depositType) {
|
||||
@@ -178,6 +182,8 @@ public class SavingsAccountData {
|
||||
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
@@ -186,8 +192,8 @@ public class SavingsAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static SavingsAccountData lookupWithProductDetails(final Long accountId, final String accountNo,
|
||||
@@ -239,6 +245,8 @@ public class SavingsAccountData {
|
||||
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
final Collection<ChargeData> chargeOptions = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new SavingsAccountData(accountId, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestPeriodType,
|
||||
@@ -247,8 +255,8 @@ public class SavingsAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account, final SavingsAccountData template,
|
||||
@@ -281,7 +289,8 @@ public class SavingsAccountData {
|
||||
template.interestCalculationDaysInYearTypeOptions, template.lockinPeriodFrequencyTypeOptions,
|
||||
template.withdrawalFeeTypeOptions, charges, template.chargeOptions, account.allowOverdraft, account.overdraftLimit,
|
||||
account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation,
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation);
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation,
|
||||
account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withTemplateOptions(final SavingsAccountData account,
|
||||
@@ -304,7 +313,8 @@ public class SavingsAccountData {
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, account.allowOverdraft, account.overdraftLimit,
|
||||
account.minRequiredBalance, account.enforceMinRequiredBalance, account.minBalanceForInterestCalculation,
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation);
|
||||
account.onHoldFunds, account.nominalAnnualInterestRateOverdraft, account.minOverdraftForInterestCalculation,
|
||||
account.withHoldTax, account.taxGroup);
|
||||
}
|
||||
|
||||
public static SavingsAccountData withClientTemplate(final Long clientId, final String clientName, final Long groupId,
|
||||
@@ -343,6 +353,8 @@ public class SavingsAccountData {
|
||||
final boolean enforceMinRequiredBalance = false;
|
||||
final BigDecimal minBalanceForInterestCalculation = null;
|
||||
final BigDecimal onHoldFunds = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
final Collection<SavingsAccountTransactionData> transactions = null;
|
||||
@@ -366,8 +378,8 @@ public class SavingsAccountData {
|
||||
fieldOfficerOptions, interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions,
|
||||
interestCalculationTypeOptions, interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions,
|
||||
withdrawalFeeTypeOptions, charges, chargeOptions, allowOverdraft, overdraftLimit, minRequiredBalance,
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
enforceMinRequiredBalance, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
private SavingsAccountData(final Long id, final String accountNo, final EnumOptionData depositType, final String externalId,
|
||||
@@ -388,7 +400,8 @@ public class SavingsAccountData {
|
||||
final Collection<SavingsAccountChargeData> charges, final Collection<ChargeData> chargeOptions, final boolean allowOverdraft,
|
||||
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal onHoldFunds,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
this.depositType = depositType;
|
||||
@@ -446,6 +459,8 @@ public class SavingsAccountData {
|
||||
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.onHoldFunds = onHoldFunds;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroup = taxGroup;
|
||||
}
|
||||
|
||||
private SavingsAccountChargeData getWithdrawalFee() {
|
||||
|
||||
+11
@@ -42,6 +42,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@@ -205,6 +206,11 @@ public class SavingsAccountDataValidator {
|
||||
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
|
||||
.ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
|
||||
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
|
||||
validateSavingsCharges(element, baseDataValidator);
|
||||
|
||||
@@ -384,6 +390,11 @@ public class SavingsAccountDataValidator {
|
||||
baseDataValidator.reset().parameter(withdrawalFeeForTransfersParamName).value(isWithdrawalFeeApplicableForTransfers)
|
||||
.ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
|
||||
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
|
||||
validateOverdraftParams(baseDataValidator, element);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
|
||||
+4
-1
@@ -46,10 +46,12 @@ public class SavingsAccountStatusEnumData {
|
||||
private final boolean transferInProgress;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean transferOnHold;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean matured;
|
||||
|
||||
public SavingsAccountStatusEnumData(final Long id, final String code, final String value, final boolean submittedAndPendingApproval,
|
||||
final boolean approved, final boolean rejected, final boolean withdrawnByApplicant, final boolean active, final boolean closed,
|
||||
final boolean prematureClosed, final boolean transferInProgress, final boolean transferOnHold) {
|
||||
final boolean prematureClosed, final boolean transferInProgress, final boolean transferOnHold, final boolean matured) {
|
||||
this.id = id;
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
@@ -62,6 +64,7 @@ public class SavingsAccountStatusEnumData {
|
||||
this.prematureClosed = prematureClosed;
|
||||
this.transferInProgress = transferInProgress;
|
||||
this.transferOnHold = transferOnHold;
|
||||
this.matured = matured;
|
||||
}
|
||||
|
||||
public Long id() {
|
||||
|
||||
+3
-1
@@ -39,11 +39,12 @@ public class SavingsAccountSummaryData {
|
||||
private final BigDecimal totalFeeCharge;
|
||||
private final BigDecimal totalPenaltyCharge;
|
||||
private final BigDecimal totalOverdraftInterestDerived;
|
||||
private final BigDecimal totalWithholdTax;
|
||||
|
||||
public SavingsAccountSummaryData(final CurrencyData currency, final BigDecimal totalDeposits, final BigDecimal totalWithdrawals,
|
||||
final BigDecimal totalWithdrawalFees, final BigDecimal totalAnnualFees, final BigDecimal totalInterestEarned,
|
||||
final BigDecimal totalInterestPosted, final BigDecimal accountBalance, final BigDecimal totalFeeCharge,
|
||||
final BigDecimal totalPenaltyCharge, final BigDecimal totalOverdraftInterestDerived) {
|
||||
final BigDecimal totalPenaltyCharge, final BigDecimal totalOverdraftInterestDerived,final BigDecimal totalWithholdTax) {
|
||||
this.currency = currency;
|
||||
this.totalDeposits = totalDeposits;
|
||||
this.totalWithdrawals = totalWithdrawals;
|
||||
@@ -55,5 +56,6 @@ public class SavingsAccountSummaryData {
|
||||
this.totalFeeCharge = totalFeeCharge;
|
||||
this.totalPenaltyCharge = totalPenaltyCharge;
|
||||
this.totalOverdraftInterestDerived = totalOverdraftInterestDerived;
|
||||
this.totalWithholdTax = totalWithholdTax;
|
||||
}
|
||||
}
|
||||
+6
@@ -41,6 +41,7 @@ public class SavingsAccountTransactionEnumData {
|
||||
private final boolean overdraftInterest;
|
||||
private final boolean writtenoff;
|
||||
private final boolean overdraftFee = true;
|
||||
private final boolean withholdTax;
|
||||
|
||||
public SavingsAccountTransactionEnumData(final Long id, final String code, final String value) {
|
||||
this.id = id;
|
||||
@@ -58,6 +59,7 @@ public class SavingsAccountTransactionEnumData {
|
||||
this.rejectTransfer = Long.valueOf(SavingsAccountTransactionType.REJECT_TRANSFER.getValue()).equals(this.id);
|
||||
this.writtenoff = Long.valueOf(SavingsAccountTransactionType.WRITTEN_OFF.getValue()).equals(this.id);
|
||||
this.overdraftInterest = Long.valueOf(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue()).equals(this.id);
|
||||
this.withholdTax = Long.valueOf(SavingsAccountTransactionType.WITHHOLD_TAX.getValue()).equals(this.id);
|
||||
// this.overdraftFee =
|
||||
// Long.valueOf(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue()).equals(this.id);
|
||||
}
|
||||
@@ -122,4 +124,8 @@ public class SavingsAccountTransactionEnumData {
|
||||
return this.overdraftFee;
|
||||
}
|
||||
|
||||
public boolean isWithholdTax() {
|
||||
return this.withholdTax;
|
||||
}
|
||||
|
||||
}
|
||||
+37
-15
@@ -31,6 +31,7 @@ import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.charge.data.ChargeData;
|
||||
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
/**
|
||||
* Immutable data object represent a savings product.
|
||||
@@ -56,6 +57,10 @@ public class SavingsProductData {
|
||||
private final BigDecimal minRequiredBalance;
|
||||
private final boolean enforceMinRequiredBalance;
|
||||
private final BigDecimal minBalanceForInterestCalculation;
|
||||
private final BigDecimal nominalAnnualInterestRateOverdraft;
|
||||
private final BigDecimal minOverdraftForInterestCalculation;
|
||||
private final boolean withHoldTax;
|
||||
private final TaxGroupData taxGroup;
|
||||
|
||||
// accounting
|
||||
private final EnumOptionData accountingRule;
|
||||
@@ -80,8 +85,7 @@ public class SavingsProductData {
|
||||
private final Map<String, List<GLAccountData>> accountingMappingOptions;
|
||||
private final Collection<ChargeData> chargeOptions;
|
||||
private final Collection<ChargeData> penaltyOptions;
|
||||
private final BigDecimal nominalAnnualInterestRateOverdraft;
|
||||
private final BigDecimal minOverdraftForInterestCalculation;
|
||||
private final Collection<TaxGroupData> taxGroupOptions;
|
||||
|
||||
public static SavingsProductData template(final CurrencyData currency, final EnumOptionData interestCompoundingPeriodType,
|
||||
final EnumOptionData interestPostingPeriodType, final EnumOptionData interestCalculationType,
|
||||
@@ -93,7 +97,7 @@ public class SavingsProductData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions) {
|
||||
final Collection<ChargeData> penaltyOptions, final Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
@@ -116,7 +120,8 @@ public class SavingsProductData {
|
||||
final BigDecimal minBalanceForInterestCalculation = null;
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = null;
|
||||
final BigDecimal minOverdraftForInterestCalculation = null;
|
||||
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -126,7 +131,8 @@ public class SavingsProductData {
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData withCharges(final SavingsProductData product, final Collection<ChargeData> charges) {
|
||||
@@ -141,12 +147,16 @@ public class SavingsProductData {
|
||||
product.accountingRuleOptions, product.accountingMappingOptions, charges, product.chargeOptions, product.penaltyOptions,
|
||||
product.feeToIncomeAccountMappings, product.penaltyToIncomeAccountMappings, product.allowOverdraft, product.overdraftLimit,
|
||||
product.minRequiredBalance, product.enforceMinRequiredBalance, product.minBalanceForInterestCalculation,
|
||||
product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation);
|
||||
product.nominalAnnualInterestRateOverdraft, product.minOverdraftForInterestCalculation, product.withHoldTax,
|
||||
product.taxGroup, product.taxGroupOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SavingsProductData} that contains and exist
|
||||
* {@link SavingsProductData} data with further template data for dropdowns.
|
||||
*
|
||||
* @param taxGroupOptions
|
||||
* TODO
|
||||
*/
|
||||
public static SavingsProductData withTemplate(final SavingsProductData existingProduct, final Collection<CurrencyData> currencyOptions,
|
||||
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions,
|
||||
@@ -156,7 +166,7 @@ public class SavingsProductData {
|
||||
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions, final Collection<EnumOptionData> withdrawalFeeTypeOptions,
|
||||
final Collection<PaymentTypeData> paymentTypeOptions, final Collection<EnumOptionData> accountingRuleOptions,
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final Collection<ChargeData> chargeOptions,
|
||||
final Collection<ChargeData> penaltyOptions) {
|
||||
final Collection<ChargeData> penaltyOptions, Collection<TaxGroupData> taxGroupOptions) {
|
||||
|
||||
return new SavingsProductData(existingProduct.id, existingProduct.name, existingProduct.shortName, existingProduct.description,
|
||||
existingProduct.currency, existingProduct.nominalAnnualInterestRate, existingProduct.interestCompoundingPeriodType,
|
||||
@@ -171,7 +181,7 @@ public class SavingsProductData {
|
||||
existingProduct.feeToIncomeAccountMappings, existingProduct.penaltyToIncomeAccountMappings, existingProduct.allowOverdraft,
|
||||
existingProduct.overdraftLimit, existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
|
||||
existingProduct.minOverdraftForInterestCalculation);
|
||||
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData withAccountingDetails(final SavingsProductData existingProduct,
|
||||
@@ -206,7 +216,8 @@ public class SavingsProductData {
|
||||
penaltyToIncomeAccountMappings, existingProduct.allowOverdraft, existingProduct.overdraftLimit,
|
||||
existingProduct.minRequiredBalance, existingProduct.enforceMinRequiredBalance,
|
||||
existingProduct.minBalanceForInterestCalculation, existingProduct.nominalAnnualInterestRateOverdraft,
|
||||
existingProduct.minOverdraftForInterestCalculation);
|
||||
existingProduct.minOverdraftForInterestCalculation, existingProduct.withHoldTax, existingProduct.taxGroup,
|
||||
existingProduct.taxGroupOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData instance(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -215,8 +226,9 @@ public class SavingsProductData {
|
||||
final EnumOptionData interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final EnumOptionData lockinPeriodFrequencyType, final boolean withdrawalFeeForTransfers,
|
||||
final EnumOptionData accountingType, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup) {
|
||||
|
||||
final Map<String, Object> accountingMappings = null;
|
||||
final Collection<PaymentTypeToGLAccountMapper> paymentChannelToFundSourceMappings = null;
|
||||
@@ -236,6 +248,7 @@ public class SavingsProductData {
|
||||
final Collection<ChargeData> charges = null;
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -245,7 +258,8 @@ public class SavingsProductData {
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
public static SavingsProductData lookup(final Long id, final String name) {
|
||||
@@ -272,6 +286,8 @@ public class SavingsProductData {
|
||||
final BigDecimal minRequiredBalance = null;
|
||||
final boolean enforceMinRequiredBalance = false;
|
||||
final BigDecimal minBalanceForInterestCalculation = null;
|
||||
final boolean withHoldTax = false;
|
||||
final TaxGroupData taxGroup = null;
|
||||
|
||||
final Collection<CurrencyData> currencyOptions = null;
|
||||
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
|
||||
@@ -288,6 +304,7 @@ public class SavingsProductData {
|
||||
final Collection<ChargeData> penaltyOptions = null;
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings = null;
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = null;
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
return new SavingsProductData(id, name, shortName, description, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
@@ -297,7 +314,8 @@ public class SavingsProductData {
|
||||
lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, paymentTypeOptions, accountingRuleOptions,
|
||||
accountingMappingOptions, charges, chargeOptions, penaltyOptions, feeToIncomeAccountMappings,
|
||||
penaltyToIncomeAccountMappings, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroup, taxGroupOptions);
|
||||
}
|
||||
|
||||
private SavingsProductData(final Long id, final String name, final String shortName, final String description,
|
||||
@@ -318,8 +336,9 @@ public class SavingsProductData {
|
||||
final Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings,
|
||||
final Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings, final boolean allowOverdraft,
|
||||
final BigDecimal overdraftLimit, final BigDecimal minRequiredBalance, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal minBalanceForInterestCalculation, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax, final TaxGroupData taxGroup,
|
||||
final Collection<TaxGroupData> taxGroupOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -369,6 +388,9 @@ public class SavingsProductData {
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.nominalAnnualInterestRateOverdraft = nominalAnnualInterestRateOverdraft;
|
||||
this.minOverdraftForInterestCalculation = minOverdraftForInterestCalculation;
|
||||
this.taxGroup = taxGroup;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroupOptions = taxGroupOptions;
|
||||
}
|
||||
|
||||
public boolean hasAccountingEnabled() {
|
||||
|
||||
+39
-4
@@ -42,6 +42,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
@@ -295,6 +297,8 @@ public class SavingsProductDataValidator {
|
||||
.ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
validateTaxWithHoldingParams(baseDataValidator, element, true);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -460,6 +464,8 @@ public class SavingsProductDataValidator {
|
||||
.ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
validateTaxWithHoldingParams(baseDataValidator, element, false);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
@@ -553,13 +559,42 @@ public class SavingsProductDataValidator {
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(nominalAnnualInterestRateOverdraftParamName, element)) {
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(nominalAnnualInterestRateOverdraftParamName, element);
|
||||
baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft).ignoreIfNull().zeroOrPositiveAmount();
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
nominalAnnualInterestRateOverdraftParamName, element);
|
||||
baseDataValidator.reset().parameter(nominalAnnualInterestRateOverdraftParamName).value(nominalAnnualInterestRateOverdraft)
|
||||
.ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(minOverdraftForInterestCalculationParamName, element)) {
|
||||
final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minOverdraftForInterestCalculationParamName, element);
|
||||
baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation).ignoreIfNull().zeroOrPositiveAmount();
|
||||
final BigDecimal minOverdraftForInterestCalculation = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
minOverdraftForInterestCalculationParamName, element);
|
||||
baseDataValidator.reset().parameter(minOverdraftForInterestCalculationParamName).value(minOverdraftForInterestCalculation)
|
||||
.ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void validateTaxWithHoldingParams(final DataValidatorBuilder baseDataValidator, final JsonElement element,
|
||||
final boolean isCreate) {
|
||||
if (this.fromApiJsonHelper.parameterExists(withHoldTaxParamName, element)) {
|
||||
final String withHoldTax = this.fromApiJsonHelper.extractStringNamed(withHoldTaxParamName, element);
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).value(withHoldTax).ignoreIfNull().validateForBooleanValue();
|
||||
}
|
||||
Boolean withHoldTax = this.fromApiJsonHelper.extractBooleanNamed(withHoldTaxParamName, element);
|
||||
if (withHoldTax == null) {
|
||||
withHoldTax = false;
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(taxGroupIdParamName, element)) {
|
||||
final Long taxGroupId = this.fromApiJsonHelper.extractLongNamed(taxGroupIdParamName, element);
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).ignoreIfNull().longGreaterThanZero();
|
||||
if (withHoldTax) {
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
}
|
||||
|
||||
} else if (withHoldTax && isCreate) {
|
||||
final Long taxGroupId = null;
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-2
@@ -48,10 +48,12 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequi
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
@@ -60,6 +62,7 @@ import java.util.Set;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
@@ -166,6 +169,7 @@ public class DepositAccountAssembler {
|
||||
}
|
||||
|
||||
if (product == null) { throw new SavingsProductNotFoundException(productId); }
|
||||
|
||||
|
||||
Client client = null;
|
||||
Group group = null;
|
||||
@@ -286,6 +290,14 @@ public class DepositAccountAssembler {
|
||||
if (productChart != null) {
|
||||
accountChart = DepositAccountInterestRateChart.from(productChart);
|
||||
}
|
||||
|
||||
boolean withHoldTax = product.withHoldTax();
|
||||
if (command.parameterExists(withHoldTaxParamName)) {
|
||||
withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
if(withHoldTax && product.getTaxGroup() == null){
|
||||
throw new UnsupportedParameterException(Arrays.asList(withHoldTaxParamName));
|
||||
}
|
||||
}
|
||||
|
||||
SavingsAccount account = null;
|
||||
if (depositAccountType.isFixedDeposit()) {
|
||||
@@ -297,7 +309,7 @@ public class DepositAccountAssembler {
|
||||
accountNo, externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges,
|
||||
accountTermAndPreClosure, accountChart);
|
||||
accountTermAndPreClosure, accountChart, withHoldTax);
|
||||
accountTermAndPreClosure.updateAccountReference(fdAccount);
|
||||
fdAccount.validateDomainRules();
|
||||
account = fdAccount;
|
||||
@@ -315,7 +327,7 @@ public class DepositAccountAssembler {
|
||||
fieldOfficer, accountNo, externalId, accountType, submittedOnDate, submittedBy, interestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer,
|
||||
charges, accountTermAndPreClosure, accountRecurringDetail, accountChart);
|
||||
charges, accountTermAndPreClosure, accountRecurringDetail, accountChart, withHoldTax);
|
||||
|
||||
accountTermAndPreClosure.updateAccountReference(rdAccount);
|
||||
accountRecurringDetail.updateAccountReference(rdAccount);
|
||||
|
||||
+33
-16
@@ -25,6 +25,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.transfe
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -164,6 +165,9 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
final boolean isPreMatureClosure = false;
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
/*
|
||||
* <<<<<<< HEAD final SavingsAccountTransactionDTO transactionDTO = new
|
||||
@@ -207,16 +211,13 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account,
|
||||
isAccountTransfer, isExceptionForBalanceCheck);
|
||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
updateAlreadyPostedTransactions(existingTransactionIds, account);
|
||||
} else {
|
||||
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
|
||||
paymentDetail, false, isRegularTransaction);
|
||||
savingsTransactionId = withdrawal.getId();
|
||||
}
|
||||
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
account.close(user, command, tenantsTodayDate, changes);
|
||||
this.savingsAccountRepository.save(account);
|
||||
|
||||
@@ -239,6 +240,9 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
final boolean isPreMatureClosure = false;
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
final MathContext mc = MathContext.DECIMAL64;
|
||||
@@ -282,16 +286,13 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account,
|
||||
isRegularTransaction, isExceptionForBalanceCheck);
|
||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
updateAlreadyPostedTransactions(existingTransactionIds, account);
|
||||
} else {
|
||||
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
|
||||
paymentDetail, false, isRegularTransaction);
|
||||
savingsTransactionId = withdrawal.getId();
|
||||
}
|
||||
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
account.close(user, command, tenantsTodayDate, changes);
|
||||
|
||||
this.savingsAccountRepository.save(account);
|
||||
@@ -348,12 +349,17 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
final boolean isPreMatureClosure = true;
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
|
||||
final Locale locale = command.extractLocale();
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
|
||||
Long savingsTransactionId = null;
|
||||
|
||||
|
||||
// post interest
|
||||
account.postPreMaturityInterest(closedDate, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||
financialYearBeginningMonth);
|
||||
@@ -372,16 +378,14 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account,
|
||||
isRegularTransaction, isExceptionForBalanceCheck);
|
||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
updateAlreadyPostedTransactions(existingTransactionIds, account);
|
||||
} else {
|
||||
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
|
||||
paymentDetail, false, isRegularTransaction);
|
||||
savingsTransactionId = withdrawal.getId();
|
||||
}
|
||||
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
account.prematureClosure(user, command, tenantsTodayDate, changes);
|
||||
|
||||
this.savingsAccountRepository.save(account);
|
||||
@@ -404,6 +408,9 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
boolean isRegularTransaction = false;
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
|
||||
@@ -428,16 +435,13 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
null, null, null, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, null, null, toSavingsAccount, account,
|
||||
isRegularTransaction, isExceptionForBalanceCheck);
|
||||
this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
|
||||
updateAlreadyPostedTransactions(existingTransactionIds, account);
|
||||
} else {
|
||||
final SavingsAccountTransaction withdrawal = this.handleWithdrawal(account, fmt, closedDate, account.getAccountBalance(),
|
||||
paymentDetail, false, isRegularTransaction);
|
||||
savingsTransactionId = withdrawal.getId();
|
||||
}
|
||||
|
||||
/***
|
||||
* Update account transactionIds for post journal entries.
|
||||
*/
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
account.prematureClosure(user, command, tenantsTodayDate, changes);
|
||||
this.savingsAccountRepository.save(account);
|
||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
@@ -460,4 +464,17 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
|
||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
|
||||
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
|
||||
}
|
||||
|
||||
private void updateAlreadyPostedTransactions(final Set<Long> existingTransactionIds, final SavingsAccount savingsAccount) {
|
||||
List<SavingsAccountTransaction> transactions = savingsAccount.getTransactions();
|
||||
int size = transactions.size();
|
||||
for (int i = size - 1;; i--) {
|
||||
SavingsAccountTransaction transaction = transactions.get(i);
|
||||
if (transaction.isWithdrawal() || transaction.isWithdrawalFee()) {
|
||||
existingTransactionIds.add(transaction.getId());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
-3
@@ -36,6 +36,7 @@ import static org.apache.fineract.portfolio.savings.DepositsApiConstants.minDepo
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalApplicableParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestOnTypeIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.preClosurePenalInterestParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
|
||||
@@ -52,6 +53,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minBalan
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
@@ -77,6 +79,8 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -88,11 +92,14 @@ public class DepositProductAssembler {
|
||||
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final InterestRateChartAssembler chartAssembler;
|
||||
private final TaxGroupRepositoryWrapper taxGroupRepository;
|
||||
|
||||
@Autowired
|
||||
public DepositProductAssembler(final ChargeRepositoryWrapper chargeRepository, final InterestRateChartAssembler chartAssembler) {
|
||||
public DepositProductAssembler(final ChargeRepositoryWrapper chargeRepository, final InterestRateChartAssembler chartAssembler,
|
||||
final TaxGroupRepositoryWrapper taxGroupRepository) {
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.chartAssembler = chartAssembler;
|
||||
this.taxGroupRepository = taxGroupRepository;
|
||||
}
|
||||
|
||||
public FixedDepositProduct assembleFixedDepositProduct(final JsonCommand command) {
|
||||
@@ -162,10 +169,18 @@ public class DepositProductAssembler {
|
||||
if (interestRate == null) {
|
||||
interestRate = BigDecimal.ZERO;
|
||||
}
|
||||
boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
|
||||
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
|
||||
}
|
||||
|
||||
FixedDepositProduct fixedDepositProduct = FixedDepositProduct.createNew(name, shortName, description, currency, interestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges, productTermAndPreClosure, charts,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
|
||||
// update product reference
|
||||
productTermAndPreClosure.updateProductReference(fixedDepositProduct);
|
||||
@@ -250,10 +265,13 @@ public class DepositProductAssembler {
|
||||
interestRate = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
final boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
final TaxGroup taxGroup = assembleTaxGroup(command);
|
||||
|
||||
RecurringDepositProduct recurringDepositProduct = RecurringDepositProduct.createNew(name, shortName, description, currency,
|
||||
interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, charges,
|
||||
productTermAndPreClosure, productRecurringDetail, charts, minBalanceForInterestCalculation);
|
||||
productTermAndPreClosure, productRecurringDetail, charts, minBalanceForInterestCalculation, taxGroup, withHoldTax);
|
||||
|
||||
// update product reference
|
||||
productTermAndPreClosure.updateProductReference(recurringDepositProduct);
|
||||
@@ -491,4 +509,13 @@ public class DepositProductAssembler {
|
||||
|
||||
return depositRecurringDetail;
|
||||
}
|
||||
|
||||
public TaxGroup assembleTaxGroup(final JsonCommand command) {
|
||||
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
|
||||
}
|
||||
return taxGroup;
|
||||
}
|
||||
}
|
||||
+13
-10
@@ -94,7 +94,7 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountInterestRateChart chart) {
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountInterestRateChart chart, boolean withHoldTax) {
|
||||
|
||||
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
|
||||
final boolean allowOverdraft = false;
|
||||
@@ -103,7 +103,7 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure, chart,
|
||||
allowOverdraft, overdraftLimit);
|
||||
allowOverdraft, overdraftLimit, withHoldTax);
|
||||
|
||||
return account;
|
||||
}
|
||||
@@ -117,12 +117,12 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, DepositAccountInterestRateChart chart,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit) {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, boolean withHoldTax) {
|
||||
|
||||
super(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy,
|
||||
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit);
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, withHoldTax);
|
||||
|
||||
this.accountTermAndPreClosure = accountTermAndPreClosure;
|
||||
this.chart = chart;
|
||||
@@ -525,7 +525,7 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recalucateDailyBalanceDetails = applyWithholdTaxForDepositAccounts(interestPostingUpToDate, recalucateDailyBalanceDetails);
|
||||
if (recalucateDailyBalanceDetails) {
|
||||
// update existing transactions so derived balance fields are
|
||||
// correct.
|
||||
@@ -555,6 +555,8 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
recalucateDailyBalance = true;
|
||||
}
|
||||
|
||||
recalucateDailyBalance = applyWithholdTaxForDepositAccounts(accountCloseDate, recalucateDailyBalance);
|
||||
|
||||
if (recalucateDailyBalance) {
|
||||
// update existing transactions so derived balance fields are
|
||||
// correct.
|
||||
@@ -768,11 +770,12 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
final Integer lockinPeriodFrequency = this.lockinPeriodFrequency;
|
||||
final boolean withdrawalFeeApplicableForTransfer = false;
|
||||
final String accountNumber = null;
|
||||
final FixedDepositAccount reInvestedAccount = FixedDepositAccount
|
||||
.createNewApplicationForSubmittal(client, group, product, savingsOfficer, accountNumber, externalId, accountType,
|
||||
getClosedOnDate(), closedBy, interestRate, compoundingPeriodType, postingPeriodType, interestCalculationType,
|
||||
daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure, newChart);
|
||||
final boolean withHoldTax = this.withHoldTax;
|
||||
final FixedDepositAccount reInvestedAccount = FixedDepositAccount.createNewApplicationForSubmittal(client, group, product,
|
||||
savingsOfficer, accountNumber, externalId, accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType,
|
||||
postingPeriodType, interestCalculationType, daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure,
|
||||
newChart, withHoldTax);
|
||||
|
||||
newAccountTermAndPreClosure.updateAccountReference(reInvestedAccount);
|
||||
newChart.updateDepositAccountReference(reInvestedAccount);
|
||||
|
||||
+9
-5
@@ -59,6 +59,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
@@ -89,7 +90,7 @@ public class FixedDepositProduct extends SavingsProduct {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final Integer lockinPeriodFrequency,
|
||||
final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final AccountingRuleType accountingRuleType,
|
||||
final Set<Charge> charges, final DepositProductTermAndPreClosure productTermAndPreClosure, final Set<InterestRateChart> charts,
|
||||
BigDecimal minBalanceForInterestCalculation) {
|
||||
BigDecimal minBalanceForInterestCalculation, boolean withHoldTax, TaxGroup taxGroup) {
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = null;
|
||||
final boolean withdrawalFeeApplicableForTransfer = false;
|
||||
@@ -99,7 +100,8 @@ public class FixedDepositProduct extends SavingsProduct {
|
||||
return new FixedDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
productTermAndPreClosure, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation);
|
||||
productTermAndPreClosure, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation, withHoldTax,
|
||||
taxGroup);
|
||||
}
|
||||
|
||||
protected FixedDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
|
||||
@@ -109,12 +111,13 @@ public class FixedDepositProduct extends SavingsProduct {
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
|
||||
final DepositProductTermAndPreClosure productTermAndPreClosure, final Set<InterestRateChart> charts,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minBalanceForInterestCalculation) {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final BigDecimal minBalanceForInterestCalculation,
|
||||
boolean withHoldTax, TaxGroup taxGroup) {
|
||||
|
||||
super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
|
||||
if (charts != null) {
|
||||
this.charts = charts;
|
||||
@@ -198,7 +201,8 @@ public class FixedDepositProduct extends SavingsProduct {
|
||||
}
|
||||
} else {
|
||||
// assemble chart
|
||||
final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode(), baseDataValidator);
|
||||
final InterestRateChart newChart = this.chartAssembler.assembleFrom(chartElement, this.currency().getCode(),
|
||||
baseDataValidator);
|
||||
this.addChart(newChart);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-8
@@ -106,7 +106,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail,
|
||||
final DepositAccountInterestRateChart chart) {
|
||||
final DepositAccountInterestRateChart chart, final boolean withHoldTax) {
|
||||
|
||||
final boolean allowOverdraft = false;
|
||||
final BigDecimal overdraftLimit = new BigDecimal(0);
|
||||
@@ -116,7 +116,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure,
|
||||
recurringDetail, chart, allowOverdraft, overdraftLimit);
|
||||
recurringDetail, chart, allowOverdraft, overdraftLimit, withHoldTax);
|
||||
}
|
||||
|
||||
public static RecurringDepositAccount createNewActivatedAccount(final Client client, final Group group, final SavingsProduct product,
|
||||
@@ -128,7 +128,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail,
|
||||
final DepositAccountInterestRateChart chart) {
|
||||
final DepositAccountInterestRateChart chart, final boolean withHoldTax) {
|
||||
|
||||
final boolean allowOverdraft = false;
|
||||
final BigDecimal overdraftLimit = new BigDecimal(0);
|
||||
@@ -138,7 +138,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, accountTermAndPreClosure,
|
||||
recurringDetail, chart, allowOverdraft, overdraftLimit);
|
||||
recurringDetail, chart, allowOverdraft, overdraftLimit, withHoldTax);
|
||||
}
|
||||
|
||||
private RecurringDepositAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
|
||||
@@ -150,12 +150,13 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final DepositAccountTermAndPreClosure accountTermAndPreClosure, final DepositAccountRecurringDetail recurringDetail,
|
||||
final DepositAccountInterestRateChart chart, final boolean allowOverdraft, final BigDecimal overdraftLimit) {
|
||||
final DepositAccountInterestRateChart chart, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final boolean withHoldTax) {
|
||||
|
||||
super(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy,
|
||||
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit);
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, withHoldTax);
|
||||
|
||||
this.accountTermAndPreClosure = accountTermAndPreClosure;
|
||||
this.recurringDetail = recurringDetail;
|
||||
@@ -632,7 +633,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyWithholdTaxForDepositAccounts(interestPostingUpToDate, recalucateDailyBalanceDetails);
|
||||
if (recalucateDailyBalanceDetails) {
|
||||
// update existing transactions so derived balance fields are
|
||||
// correct.
|
||||
@@ -663,6 +664,8 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
recalucateDailyBalance = true;
|
||||
}
|
||||
|
||||
applyWithholdTaxForDepositAccounts(accountCloseDate, recalucateDailyBalance);
|
||||
|
||||
if (recalucateDailyBalance) {
|
||||
// update existing transactions so derived balance fields are
|
||||
// correct.
|
||||
@@ -1083,7 +1086,7 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
accountNumber, externalId, accountType, getClosedOnDate(), closedBy, interestRate, compoundingPeriodType,
|
||||
postingPeriodType, interestCalculationType, daysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, savingsAccountCharges, newAccountTermAndPreClosure,
|
||||
recurringDetail, newChart);
|
||||
recurringDetail, newChart, withHoldTax);
|
||||
|
||||
rdAccount.setDatesFrom(now);
|
||||
newAccountTermAndPreClosure.updateAccountReference(rdAccount);
|
||||
|
||||
+6
-4
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
|
||||
@Entity
|
||||
@DiscriminatorValue("300")
|
||||
@@ -67,7 +68,7 @@ public class RecurringDepositProduct extends FixedDepositProduct {
|
||||
final SavingsPeriodFrequencyType lockinPeriodFrequencyType, final AccountingRuleType accountingRuleType,
|
||||
final Set<Charge> charges, final DepositProductTermAndPreClosure productTermAndPreClosure,
|
||||
final DepositProductRecurringDetail recurringDetail, final Set<InterestRateChart> charts,
|
||||
BigDecimal minBalanceForInterestCalculation) {
|
||||
BigDecimal minBalanceForInterestCalculation, final TaxGroup taxGroup, final boolean withHoldTax) {
|
||||
|
||||
final BigDecimal minRequiredOpeningBalance = null;
|
||||
final boolean withdrawalFeeApplicableForTransfer = false;
|
||||
@@ -77,7 +78,8 @@ public class RecurringDepositProduct extends FixedDepositProduct {
|
||||
return new RecurringDepositProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
productTermAndPreClosure, recurringDetail, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation);
|
||||
productTermAndPreClosure, recurringDetail, charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation,
|
||||
withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
protected RecurringDepositProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
|
||||
@@ -88,12 +90,12 @@ public class RecurringDepositProduct extends FixedDepositProduct {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
|
||||
final DepositProductTermAndPreClosure productTermAndPreClosure, final DepositProductRecurringDetail recurringDetail,
|
||||
final Set<InterestRateChart> charts, final boolean allowOverdraft, final BigDecimal overdraftLimit,
|
||||
final BigDecimal minBalanceForInterestCalculation) {
|
||||
final BigDecimal minBalanceForInterestCalculation, final boolean withHoldTax, final TaxGroup taxGroup) {
|
||||
|
||||
super(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, productTermAndPreClosure,
|
||||
charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation);
|
||||
charts, allowOverdraft, overdraftLimit, minBalanceForInterestCalculation, withHoldTax, taxGroup);
|
||||
|
||||
this.recurringDetail = recurringDetail;
|
||||
}
|
||||
|
||||
+136
-8
@@ -29,6 +29,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minOverd
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.minRequiredBalanceParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateOverdraftParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -102,6 +103,9 @@ import org.apache.fineract.portfolio.savings.exception.SavingsOfficerAssignmentD
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsOfficerUnassignmentDateException;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsTransferTransactionsCannotBeUndoneException;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxUtils;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
@@ -310,6 +314,13 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
@Column(name = "min_balance_for_interest_calculation", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal minBalanceForInterestCalculation;
|
||||
|
||||
@Column(name = "withhold_tax", nullable = false)
|
||||
protected boolean withHoldTax;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tax_group_id")
|
||||
private TaxGroup taxGroup;
|
||||
|
||||
protected SavingsAccount() {
|
||||
//
|
||||
}
|
||||
@@ -324,14 +335,14 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal minOverdraftForInterestCalculation, final boolean withHoldTax) {
|
||||
|
||||
final SavingsAccountStatusType status = SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL;
|
||||
return new SavingsAccount(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate,
|
||||
submittedBy, interestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, enforceMinRequiredBalance,
|
||||
minRequiredBalance, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
minRequiredBalance, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
|
||||
}
|
||||
|
||||
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff fieldOfficer,
|
||||
@@ -342,11 +353,12 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit) {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, boolean withHoldTax) {
|
||||
this(client, group, product, fieldOfficer, accountNo, externalId, status, accountType, submittedOnDate, submittedBy,
|
||||
nominalAnnualInterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
|
||||
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, null);
|
||||
withdrawalFeeApplicableForTransfer, savingsAccountCharges, allowOverdraft, overdraftLimit, false, null, null, null,
|
||||
withHoldTax);
|
||||
}
|
||||
|
||||
protected SavingsAccount(final Client client, final Group group, final SavingsProduct product, final Staff savingsOfficer,
|
||||
@@ -359,7 +371,7 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final Set<SavingsAccountCharge> savingsAccountCharges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, final BigDecimal nominalAnnualInterestRateOverdraft,
|
||||
final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal minOverdraftForInterestCalculation, boolean withHoldTax) {
|
||||
this.client = client;
|
||||
this.group = group;
|
||||
this.product = product;
|
||||
@@ -404,6 +416,8 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.minRequiredBalance = minRequiredBalance;
|
||||
this.minBalanceForInterestCalculation = product.minBalanceForInterestCalculation();
|
||||
this.savingsOfficerHistory = null;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroup = product.getTaxGroup();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,6 +472,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
Money interestPostedToDate = Money.zero(this.currency);
|
||||
|
||||
boolean recalucateDailyBalanceDetails = false;
|
||||
boolean applyWithHoldTax = isWithHoldTaxApplicableForInterestPosting();
|
||||
final List<SavingsAccountTransaction> withholdTransactions = new ArrayList<>();
|
||||
withholdTransactions.addAll(findWithHoldTransactions());
|
||||
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
|
||||
@@ -479,6 +496,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
interestEarnedToBePostedForPeriod.negated());
|
||||
}
|
||||
this.transactions.add(newPostingTransaction);
|
||||
if (applyWithHoldTax) {
|
||||
createWithHoldTransaction(interestEarnedToBePostedForPeriod.getAmount(), interestPostingTransactionDate);
|
||||
}
|
||||
recalucateDailyBalanceDetails = true;
|
||||
} else {
|
||||
boolean correctionRequired = false;
|
||||
@@ -488,7 +508,14 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
correctionRequired = postingTransaction.hasNotAmount(interestEarnedToBePostedForPeriod.negated());
|
||||
}
|
||||
if (correctionRequired) {
|
||||
boolean applyWithHoldTaxForOldTransaction = false;
|
||||
postingTransaction.reverse();
|
||||
final SavingsAccountTransaction withholdTransaction = findTransactionFor(interestPostingTransactionDate,
|
||||
withholdTransactions);
|
||||
if (withholdTransaction != null) {
|
||||
withholdTransaction.reverse();
|
||||
applyWithHoldTaxForOldTransaction = true;
|
||||
}
|
||||
SavingsAccountTransaction newPostingTransaction;
|
||||
if (interestEarnedToBePostedForPeriod.isGreaterThanOrEqualTo(Money.zero(currency))) {
|
||||
newPostingTransaction = SavingsAccountTransaction.interestPosting(this, office(),
|
||||
@@ -498,6 +525,9 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated());
|
||||
}
|
||||
this.transactions.add(newPostingTransaction);
|
||||
if (applyWithHoldTaxForOldTransaction) {
|
||||
createWithHoldTransaction(interestEarnedToBePostedForPeriod.getAmount(), interestPostingTransactionDate);
|
||||
}
|
||||
recalucateDailyBalanceDetails = true;
|
||||
}
|
||||
}
|
||||
@@ -518,10 +548,22 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.summary.updateSummary(this.currency, this.savingsAccountTransactionSummaryWrapper, this.transactions);
|
||||
}
|
||||
|
||||
protected List<SavingsAccountTransaction> findWithHoldTransactions() {
|
||||
final List<SavingsAccountTransaction> withholdTransactions = new ArrayList<>();
|
||||
for (final SavingsAccountTransaction transaction : this.transactions) {
|
||||
if ((transaction.isWithHoldTaxAndNotReversed())) {
|
||||
withholdTransactions.add(transaction);
|
||||
}
|
||||
}
|
||||
return withholdTransactions;
|
||||
}
|
||||
|
||||
private boolean isWithHoldTaxApplicableForInterestPosting() {
|
||||
return this.withHoldTax() && this.depositAccountType().isSavingsDeposit();
|
||||
}
|
||||
|
||||
protected SavingsAccountTransaction findInterestPostingTransactionFor(final LocalDate postingDate) {
|
||||
|
||||
SavingsAccountTransaction postingTransation = null;
|
||||
|
||||
for (final SavingsAccountTransaction transaction : this.transactions) {
|
||||
if ((transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
|
||||
&& transaction.occursOn(postingDate)) {
|
||||
@@ -529,10 +571,59 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return postingTransation;
|
||||
}
|
||||
|
||||
protected SavingsAccountTransaction findTransactionFor(final LocalDate postingDate, final List<SavingsAccountTransaction> transactions) {
|
||||
SavingsAccountTransaction transaction = null;
|
||||
for (final SavingsAccountTransaction savingsAccountTransaction : transactions) {
|
||||
if (savingsAccountTransaction.occursOn(postingDate)) {
|
||||
transaction = savingsAccountTransaction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
protected boolean createWithHoldTransaction(final BigDecimal amount, final LocalDate date) {
|
||||
boolean isTaxAdded = false;
|
||||
if (this.taxGroup != null && amount.compareTo(BigDecimal.ZERO) == 1) {
|
||||
Map<TaxComponent, BigDecimal> taxSplit = TaxUtils.splitTax(amount, date, this.taxGroup.getTaxGroupMappings(), amount.scale());
|
||||
BigDecimal totalTax = TaxUtils.totalTaxAmount(taxSplit);
|
||||
if (totalTax.compareTo(BigDecimal.ZERO) == 1) {
|
||||
SavingsAccountTransaction withholdTransaction = SavingsAccountTransaction.withHoldTax(this, office(), date,
|
||||
Money.of(currency, totalTax), taxSplit);
|
||||
this.transactions.add(withholdTransaction);
|
||||
isTaxAdded = true;
|
||||
}
|
||||
}
|
||||
return isTaxAdded;
|
||||
}
|
||||
|
||||
protected boolean updateWithHoldTransaction(final BigDecimal amount, final SavingsAccountTransaction withholdTransaction) {
|
||||
boolean isTaxAdded = false;
|
||||
if (this.taxGroup != null && amount.compareTo(BigDecimal.ZERO) == 1) {
|
||||
Map<TaxComponent, BigDecimal> taxSplit = TaxUtils.splitTax(amount, withholdTransaction.transactionLocalDate(),
|
||||
this.taxGroup.getTaxGroupMappings(), amount.scale());
|
||||
BigDecimal totalTax = TaxUtils.totalTaxAmount(taxSplit);
|
||||
if (totalTax.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (withholdTransaction.getId() == null) {
|
||||
withholdTransaction.updateAmount(Money.of(currency, totalTax));
|
||||
withholdTransaction.getTaxDetails().clear();
|
||||
SavingsAccountTransaction.updateTaxDetails(taxSplit, withholdTransaction);
|
||||
isTaxAdded = true;
|
||||
} else if (totalTax.compareTo(withholdTransaction.getAmount()) != 0) {
|
||||
withholdTransaction.reverse();
|
||||
SavingsAccountTransaction newWithholdTransaction = SavingsAccountTransaction.withHoldTax(this, office(),
|
||||
withholdTransaction.transactionLocalDate(), Money.of(currency, totalTax), taxSplit);
|
||||
this.transactions.add(newWithholdTransaction);
|
||||
isTaxAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isTaxAdded;
|
||||
}
|
||||
|
||||
// Determine the last transaction for given day
|
||||
protected SavingsAccountTransaction findLastTransaction(final LocalDate date) {
|
||||
|
||||
@@ -1231,6 +1322,15 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
this.minRequiredBalance = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, this.withHoldTax)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
actualChanges.put(withHoldTaxParamName, newValue);
|
||||
this.withHoldTax = newValue;
|
||||
if (this.withHoldTax && this.taxGroup == null) {
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported.for.this.account");
|
||||
}
|
||||
}
|
||||
|
||||
validateLockinDetails(baseDataValidator);
|
||||
esnureOverdraftLimitsSetForOverdraftAccounts();
|
||||
}
|
||||
@@ -2605,4 +2705,32 @@ public class SavingsAccount extends AbstractPersistable<Long> {
|
||||
public BigDecimal getWithdrawableBalance() {
|
||||
return getAccountBalance().subtract(minRequiredBalanceDerived(getCurrency()).getAmount()).subtract(this.getOnHoldFunds());
|
||||
}
|
||||
|
||||
public TaxGroup getTaxGroup() {
|
||||
return this.taxGroup;
|
||||
}
|
||||
|
||||
public boolean withHoldTax() {
|
||||
return this.withHoldTax;
|
||||
}
|
||||
|
||||
public void setWithHoldTax(boolean withHoldTax) {
|
||||
this.withHoldTax = withHoldTax;
|
||||
}
|
||||
|
||||
protected boolean applyWithholdTaxForDepositAccounts(final LocalDate interestPostingUpToDate, boolean recalucateDailyBalance) {
|
||||
final List<SavingsAccountTransaction> withholdTransactions = findWithHoldTransactions();
|
||||
SavingsAccountTransaction withholdTransaction = findTransactionFor(interestPostingUpToDate, withholdTransactions);
|
||||
final BigDecimal totalInterestPosted = this.savingsAccountTransactionSummaryWrapper.calculateTotalInterestPosted(this.currency,
|
||||
this.transactions);
|
||||
if (withholdTransaction == null && this.withHoldTax()) {
|
||||
boolean isWithholdTaxAdded = createWithHoldTransaction(totalInterestPosted, interestPostingUpToDate);
|
||||
recalucateDailyBalance = recalucateDailyBalance || isWithholdTaxAdded;
|
||||
} else {
|
||||
boolean isWithholdTaxAdded = updateWithHoldTransaction(totalInterestPosted, withholdTransaction);
|
||||
recalucateDailyBalance = recalucateDailyBalance || isWithholdTaxAdded;
|
||||
}
|
||||
|
||||
return recalucateDailyBalance;
|
||||
}
|
||||
}
|
||||
+20
-8
@@ -40,12 +40,15 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.productIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.submittedOnDateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.staff.domain.Staff;
|
||||
@@ -240,16 +243,18 @@ public class SavingsAccountAssembler {
|
||||
|
||||
BigDecimal nominalAnnualInterestRateOverdraft = BigDecimal.ZERO;
|
||||
if (command.parameterExists(nominalAnnualInterestRateOverdraftParamName)) {
|
||||
nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
|
||||
nominalAnnualInterestRateOverdraft = command
|
||||
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
|
||||
} else {
|
||||
nominalAnnualInterestRateOverdraft = product.nominalAnnualInterestRateOverdraft();
|
||||
nominalAnnualInterestRateOverdraft = product.nominalAnnualInterestRateOverdraft();
|
||||
}
|
||||
|
||||
BigDecimal minOverdraftForInterestCalculation = BigDecimal.ZERO;
|
||||
if (command.parameterExists(minOverdraftForInterestCalculationParamName)) {
|
||||
minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
|
||||
minOverdraftForInterestCalculation = command
|
||||
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
|
||||
} else {
|
||||
minOverdraftForInterestCalculation = product.minOverdraftForInterestCalculation();
|
||||
minOverdraftForInterestCalculation = product.minOverdraftForInterestCalculation();
|
||||
}
|
||||
|
||||
boolean enforceMinRequiredBalance = false;
|
||||
@@ -266,12 +271,20 @@ public class SavingsAccountAssembler {
|
||||
minRequiredBalance = product.minRequiredBalance();
|
||||
}
|
||||
|
||||
boolean withHoldTax = product.withHoldTax();
|
||||
if (command.parameterExists(withHoldTaxParamName)) {
|
||||
withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
if(withHoldTax && product.getTaxGroup() == null){
|
||||
throw new UnsupportedParameterException(Arrays.asList(withHoldTaxParamName));
|
||||
}
|
||||
}
|
||||
|
||||
final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, fieldOfficer, accountNo,
|
||||
externalId, accountType, submittedOnDate, submittedBy, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, charges, allowOverdraft,
|
||||
overdraftLimit, enforceMinRequiredBalance, minRequiredBalance,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, nominalAnnualInterestRateOverdraft,
|
||||
minOverdraftForInterestCalculation, withHoldTax);
|
||||
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
|
||||
|
||||
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||
@@ -319,14 +332,13 @@ public class SavingsAccountAssembler {
|
||||
}
|
||||
|
||||
final Set<SavingsAccountCharge> charges = this.savingsAccountChargeAssembler.fromSavingsProduct(product);
|
||||
|
||||
final SavingsAccount account = SavingsAccount.createNewApplicationForSubmittal(client, group, product, null, null, null,
|
||||
accountType, appliedonDate, appliedBy, product.nominalAnnualInterestRate(), product.interestCompoundingPeriodType(),
|
||||
product.interestPostingPeriodType(), product.interestCalculationType(), product.interestCalculationDaysInYearType(),
|
||||
product.minRequiredOpeningBalance(), product.lockinPeriodFrequency(), product.lockinPeriodFrequencyType(),
|
||||
product.isWithdrawalFeeApplicableForTransfer(), charges, product.isAllowOverdraft(), product.overdraftLimit(),
|
||||
product.isMinRequiredBalanceEnforced(), product.minRequiredBalance(), product.nominalAnnualInterestRateOverdraft(),
|
||||
product.minOverdraftForInterestCalculation());
|
||||
product.minOverdraftForInterestCalculation(), product.withHoldTax());
|
||||
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
|
||||
|
||||
account.validateNewApplicationState(DateUtils.getLocalDateOfTenant(), SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||
|
||||
+11
-2
@@ -71,7 +71,10 @@ public final class SavingsAccountSummary {
|
||||
private BigDecimal totalPenaltyChargesWaived = BigDecimal.ZERO;
|
||||
|
||||
@Column(name = "total_overdraft_interest_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalOverdraftInterestDerived;
|
||||
private BigDecimal totalOverdraftInterestDerived;
|
||||
|
||||
@Column(name = "total_withhold_tax_derived", scale = 6, precision = 19)
|
||||
private BigDecimal totalWithholdTax;
|
||||
|
||||
protected SavingsAccountSummary() {
|
||||
//
|
||||
@@ -90,10 +93,12 @@ public final class SavingsAccountSummary {
|
||||
this.totalFeeChargesWaived = wrapper.calculateTotalFeesChargeWaived(currency, transactions);
|
||||
this.totalPenaltyChargesWaived = wrapper.calculateTotalPenaltyChargeWaived(currency, transactions);
|
||||
this.totalOverdraftInterestDerived = wrapper.calculateTotalOverdraftInterest(currency, transactions);
|
||||
this.totalWithholdTax = wrapper.calculateTotalWithholdTaxWithdrawal(currency, transactions);
|
||||
|
||||
|
||||
this.accountBalance = Money.of(currency, this.totalDeposits).plus(this.totalInterestPosted).minus(this.totalWithdrawals)
|
||||
.minus(this.totalWithdrawalFees).minus(this.totalAnnualFees).minus(this.totalFeeCharge).minus(this.totalPenaltyCharge)
|
||||
.minus(totalOverdraftInterestDerived).getAmount();
|
||||
.minus(totalOverdraftInterestDerived).minus(totalWithholdTax).getAmount();
|
||||
}
|
||||
|
||||
public void updateFromInterestPeriodSummaries(final MonetaryCurrency currency, final List<PostingPeriod> allPostingPeriods) {
|
||||
@@ -122,4 +127,8 @@ public final class SavingsAccountSummary {
|
||||
return this.accountBalance;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalInterestPosted() {
|
||||
return this.totalInterestPosted;
|
||||
}
|
||||
|
||||
}
|
||||
+57
-11
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.domain;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -47,6 +48,7 @@ import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.domain.interest.EndOfDayBalance;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
@@ -115,6 +117,11 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "appuser_id", nullable = true)
|
||||
private AppUser appUser;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "savings_transaction_id", referencedColumnName = "id", nullable = false)
|
||||
private final List<SavingsAccountTransactionTaxDetails> taxDetails = new ArrayList<>();
|
||||
|
||||
protected SavingsAccountTransaction() {
|
||||
this.dateOf = null;
|
||||
this.typeOf = null;
|
||||
@@ -142,14 +149,14 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
amount, isReversed, null);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
final Money amount) {
|
||||
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
|
||||
final LocalDate date, final Money amount) {
|
||||
final boolean isReversed = false;
|
||||
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
|
||||
amount, isReversed, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
final Money amount, final AppUser appUser) {
|
||||
final boolean isReversed = false;
|
||||
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
|
||||
@@ -204,6 +211,23 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
.getAccountBalance(), isReversed, appUser);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
|
||||
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
|
||||
final boolean isReversed = false;
|
||||
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
|
||||
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null);
|
||||
updateTaxDetails(taxDetails, accountTransaction);
|
||||
return accountTransaction;
|
||||
}
|
||||
|
||||
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails, final SavingsAccountTransaction accountTransaction) {
|
||||
if (taxDetails != null) {
|
||||
for (Map.Entry<TaxComponent, BigDecimal> mapEntry : taxDetails.entrySet()) {
|
||||
accountTransaction.getTaxDetails().add(new SavingsAccountTransactionTaxDetails(mapEntry.getKey(), mapEntry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
|
||||
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office,
|
||||
accountTransaction.paymentDetail, accountTransaction.typeOf, accountTransaction.transactionLocalDate(),
|
||||
@@ -405,6 +429,19 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
thisTransactionData.put("savingsChargesPaid", savingsChargesPaidData);
|
||||
}
|
||||
|
||||
if (!this.taxDetails.isEmpty()) {
|
||||
final List<Map<String, Object>> taxData = new ArrayList<>();
|
||||
for (final SavingsAccountTransactionTaxDetails taxDetails : this.taxDetails) {
|
||||
final Map<String, Object> taxDetailsData = new HashMap<>();
|
||||
taxDetailsData.put("amount", taxDetails.getAmount());
|
||||
if (taxDetails.getTaxComponent().getCreditAcount() != null) {
|
||||
taxDetailsData.put("creditAccountId", taxDetails.getTaxComponent().getCreditAcount().getId());
|
||||
}
|
||||
taxData.add(taxDetailsData);
|
||||
}
|
||||
thisTransactionData.put("taxDetails", taxData);
|
||||
}
|
||||
|
||||
return thisTransactionData;
|
||||
}
|
||||
|
||||
@@ -525,18 +562,19 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public boolean isDebit() {
|
||||
return isWithdrawal()
|
||||
|| isWithdrawalFeeAndNotReversed()
|
||||
|| isAnnualFeeAndNotReversed()
|
||||
|| isPayCharge()
|
||||
|| isOverdraftInterestAndNotReversed();
|
||||
return isWithdrawal() || isWithdrawalFeeAndNotReversed() || isAnnualFeeAndNotReversed() || isPayCharge()
|
||||
|| isOverdraftInterestAndNotReversed() || isWithHoldTaxAndNotReversed();
|
||||
}
|
||||
|
||||
public boolean isWithHoldTaxAndNotReversed() {
|
||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isWithHoldTax() && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isOverdraftInterestAndNotReversed() {
|
||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isIncomeFromInterest() && isNotReversed();
|
||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isIncomeFromInterest() && isNotReversed();
|
||||
}
|
||||
|
||||
public boolean isPayCharge() {
|
||||
public boolean isPayCharge() {
|
||||
return SavingsAccountTransactionType.fromInt(this.typeOf).isPayCharge();
|
||||
}
|
||||
|
||||
@@ -637,4 +675,12 @@ public final class SavingsAccountTransaction extends AbstractPersistable<Long> {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public List<SavingsAccountTransactionTaxDetails> getTaxDetails() {
|
||||
return this.taxDetails;
|
||||
}
|
||||
|
||||
public void updateAmount(final Money amount) {
|
||||
this.amount = amount.getAmount();
|
||||
}
|
||||
|
||||
}
|
||||
+12
-3
@@ -122,8 +122,7 @@ public final class SavingsAccountTransactionSummaryWrapper {
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
public BigDecimal calculateTotalOverdraftInterest(MonetaryCurrency currency,
|
||||
List<SavingsAccountTransaction> transactions) {
|
||||
public BigDecimal calculateTotalOverdraftInterest(MonetaryCurrency currency, List<SavingsAccountTransaction> transactions) {
|
||||
Money total = Money.zero(currency);
|
||||
for (final SavingsAccountTransaction transaction : transactions) {
|
||||
if (transaction.isOverdraftInterestAndNotReversed()) {
|
||||
@@ -131,6 +130,16 @@ public final class SavingsAccountTransactionSummaryWrapper {
|
||||
}
|
||||
}
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal calculateTotalWithholdTaxWithdrawal(MonetaryCurrency currency, List<SavingsAccountTransaction> transactions) {
|
||||
Money total = Money.zero(currency);
|
||||
for (final SavingsAccountTransaction transaction : transactions) {
|
||||
if (transaction.isWithHoldTaxAndNotReversed()) {
|
||||
total = total.plus(transaction.getAmount(currency));
|
||||
}
|
||||
}
|
||||
return total.getAmountDefaultedToNullIfZero();
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.portfolio.savings.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_savings_account_transaction_tax_details")
|
||||
public class SavingsAccountTransactionTaxDetails extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tax_component_id", nullable = false)
|
||||
private TaxComponent taxComponent;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
protected SavingsAccountTransactionTaxDetails() {}
|
||||
|
||||
public SavingsAccountTransactionTaxDetails(final TaxComponent taxComponent, final BigDecimal amount) {
|
||||
this.taxComponent = taxComponent;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public TaxComponent getTaxComponent() {
|
||||
return this.taxComponent;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public void updateAmount(Money amount) {
|
||||
this.amount = amount.getAmount();
|
||||
}
|
||||
|
||||
}
|
||||
+75
-23
@@ -44,6 +44,8 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
@@ -63,6 +65,7 @@ import javax.persistence.Inheritance;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
@@ -80,6 +83,7 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@@ -156,7 +160,7 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
protected boolean withdrawalFeeApplicableForTransfer;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id") , inverseJoinColumns = @JoinColumn(name = "charge_id") )
|
||||
@JoinTable(name = "m_savings_product_charge", joinColumns = @JoinColumn(name = "savings_product_id"), inverseJoinColumns = @JoinColumn(name = "charge_id"))
|
||||
protected Set<Charge> charges;
|
||||
|
||||
@Column(name = "allow_overdraft")
|
||||
@@ -165,12 +169,12 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "overdraft_limit", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal overdraftLimit;
|
||||
|
||||
@Column(name = "nominal_annual_interest_rate_overdraft", scale = 6, precision = 19, nullable = true)
|
||||
@Column(name = "nominal_annual_interest_rate_overdraft", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal nominalAnnualInterestRateOverdraft;
|
||||
|
||||
@Column(name = "min_overdraft_for_interest_calculation", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal minOverdraftForInterestCalculation;
|
||||
|
||||
|
||||
@Column(name = "enforce_min_required_balance")
|
||||
private boolean enforceMinRequiredBalance;
|
||||
|
||||
@@ -180,6 +184,13 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "min_balance_for_interest_calculation", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal minBalanceForInterestCalculation;
|
||||
|
||||
@Column(name = "withhold_tax", nullable = false)
|
||||
private boolean withHoldTax;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tax_group_id")
|
||||
private TaxGroup taxGroup;
|
||||
|
||||
public static SavingsProduct createNew(final String name, final String shortName, final String description,
|
||||
final MonetaryCurrency currency, final BigDecimal interestRate,
|
||||
final SavingsCompoundingInterestPeriodType interestCompoundingPeriodType,
|
||||
@@ -189,13 +200,14 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, final BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
boolean withHoldTax, TaxGroup taxGroup) {
|
||||
|
||||
return new SavingsProduct(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
protected SavingsProduct() {
|
||||
@@ -209,11 +221,12 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final SavingsInterestCalculationDaysInYearType interestCalculationDaysInYearType, final BigDecimal minRequiredOpeningBalance,
|
||||
final Integer lockinPeriodFrequency, final SavingsPeriodFrequencyType lockinPeriodFrequencyType,
|
||||
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, BigDecimal minBalanceForInterestCalculation) {
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, BigDecimal minBalanceForInterestCalculation,
|
||||
boolean withHoldTax, TaxGroup taxGroup) {
|
||||
this(name, shortName, description, currency, interestRate, interestCompoundingPeriodType, interestPostingPeriodType,
|
||||
interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType, withdrawalFeeApplicableForTransfer, accountingRuleType, charges, allowOverdraft, overdraftLimit,
|
||||
false, null, minBalanceForInterestCalculation, null, null);
|
||||
false, null, minBalanceForInterestCalculation, null, null, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
protected SavingsProduct(final String name, final String shortName, final String description, final MonetaryCurrency currency,
|
||||
@@ -224,7 +237,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
final boolean withdrawalFeeApplicableForTransfer, final AccountingRuleType accountingRuleType, final Set<Charge> charges,
|
||||
final boolean allowOverdraft, final BigDecimal overdraftLimit, final boolean enforceMinRequiredBalance,
|
||||
final BigDecimal minRequiredBalance, BigDecimal minBalanceForInterestCalculation,
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation) {
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft, final BigDecimal minOverdraftForInterestCalculation,
|
||||
final boolean withHoldTax, final TaxGroup taxGroup) {
|
||||
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -267,6 +281,8 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.enforceMinRequiredBalance = enforceMinRequiredBalance;
|
||||
this.minRequiredBalance = minRequiredBalance;
|
||||
this.minBalanceForInterestCalculation = minBalanceForInterestCalculation;
|
||||
this.withHoldTax = withHoldTax;
|
||||
this.taxGroup = taxGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,9 +292,11 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
private void esnureOverdraftLimitsSetForOverdraftAccounts() {
|
||||
|
||||
if (this.allowOverdraft) {
|
||||
this.overdraftLimit = this.overdraftLimit == null? BigDecimal.ZERO : this.overdraftLimit;
|
||||
this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null? BigDecimal.ZERO : this.nominalAnnualInterestRateOverdraft;
|
||||
this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null? BigDecimal.ZERO : this.minOverdraftForInterestCalculation;
|
||||
this.overdraftLimit = this.overdraftLimit == null ? BigDecimal.ZERO : this.overdraftLimit;
|
||||
this.nominalAnnualInterestRateOverdraft = this.nominalAnnualInterestRateOverdraft == null ? BigDecimal.ZERO
|
||||
: this.nominalAnnualInterestRateOverdraft;
|
||||
this.minOverdraftForInterestCalculation = this.minOverdraftForInterestCalculation == null ? BigDecimal.ZERO
|
||||
: this.minOverdraftForInterestCalculation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,20 +481,24 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.overdraftLimit = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName, this.nominalAnnualInterestRateOverdraft)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
|
||||
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(nominalAnnualInterestRateOverdraftParamName,
|
||||
this.nominalAnnualInterestRateOverdraft)) {
|
||||
final BigDecimal newValue = command
|
||||
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(nominalAnnualInterestRateOverdraftParamName);
|
||||
actualChanges.put(nominalAnnualInterestRateOverdraftParamName, newValue);
|
||||
actualChanges.put(localeParamName, localeAsInput);
|
||||
this.nominalAnnualInterestRateOverdraft = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName, this.minOverdraftForInterestCalculation)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamedDefaultingZeroToNull(minOverdraftForInterestCalculationParamName,
|
||||
this.minOverdraftForInterestCalculation)) {
|
||||
final BigDecimal newValue = command
|
||||
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minOverdraftForInterestCalculationParamName);
|
||||
actualChanges.put(minOverdraftForInterestCalculationParamName, newValue);
|
||||
actualChanges.put(localeParamName, localeAsInput);
|
||||
this.minOverdraftForInterestCalculation = newValue;
|
||||
}
|
||||
|
||||
|
||||
if (!this.allowOverdraft) {
|
||||
this.overdraftLimit = null;
|
||||
this.nominalAnnualInterestRateOverdraft = null;
|
||||
@@ -505,6 +527,21 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
this.minBalanceForInterestCalculation = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, this.withHoldTax)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
actualChanges.put(withHoldTaxParamName, newValue);
|
||||
this.withHoldTax = newValue;
|
||||
}
|
||||
|
||||
if (this.withHoldTax) {
|
||||
if (this.taxGroup == null || command.isChangeInLongParameterNamed(taxGroupIdParamName, this.taxGroup.getId())) {
|
||||
final Long newValue = command.longValueOfParameterNamed(taxGroupIdParamName);
|
||||
actualChanges.put(taxGroupIdParamName, newValue);
|
||||
}
|
||||
} else {
|
||||
this.taxGroup = null;
|
||||
}
|
||||
|
||||
validateLockinDetails();
|
||||
esnureOverdraftLimitsSetForOverdraftAccounts();
|
||||
|
||||
@@ -617,11 +654,26 @@ public class SavingsProduct extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public BigDecimal nominalAnnualInterestRateOverdraft() {
|
||||
return this.nominalAnnualInterestRateOverdraft;
|
||||
}
|
||||
return this.nominalAnnualInterestRateOverdraft;
|
||||
}
|
||||
|
||||
public BigDecimal minOverdraftForInterestCalculation() {
|
||||
return this.minOverdraftForInterestCalculation;
|
||||
}
|
||||
public BigDecimal minOverdraftForInterestCalculation() {
|
||||
return this.minOverdraftForInterestCalculation;
|
||||
}
|
||||
|
||||
|
||||
public TaxGroup getTaxGroup() {
|
||||
return this.taxGroup;
|
||||
}
|
||||
|
||||
|
||||
public void setTaxGroup(TaxGroup taxGroup) {
|
||||
this.taxGroup = taxGroup;
|
||||
}
|
||||
|
||||
|
||||
public boolean withHoldTax() {
|
||||
return this.withHoldTax;
|
||||
}
|
||||
|
||||
}
|
||||
+27
-9
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.portfolio.savings.domain;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.allowOverdraftParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.currencyCodeParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.descriptionParamName;
|
||||
@@ -41,6 +42,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalA
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.nominalAnnualInterestRateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.overdraftLimitParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.shortNameParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawalFeeForTransfersParamName;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -59,6 +61,8 @@ import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYea
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupRepositoryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -69,10 +73,12 @@ import com.google.gson.JsonObject;
|
||||
public class SavingsProductAssembler {
|
||||
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final TaxGroupRepositoryWrapper taxGroupRepository;
|
||||
|
||||
@Autowired
|
||||
public SavingsProductAssembler(final ChargeRepositoryWrapper chargeRepository) {
|
||||
public SavingsProductAssembler(final ChargeRepositoryWrapper chargeRepository, final TaxGroupRepositoryWrapper taxGroupRepository) {
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.taxGroupRepository = taxGroupRepository;
|
||||
}
|
||||
|
||||
public SavingsProduct assemble(final JsonCommand command) {
|
||||
@@ -139,18 +145,18 @@ public class SavingsProductAssembler {
|
||||
}
|
||||
|
||||
BigDecimal overdraftLimit = BigDecimal.ZERO;
|
||||
if(command.parameterExists(overdraftLimitParamName)){
|
||||
if (command.parameterExists(overdraftLimitParamName)) {
|
||||
overdraftLimit = command.bigDecimalValueOfParameterNamed(overdraftLimitParamName);
|
||||
}
|
||||
|
||||
BigDecimal nominalAnnualInterestRateOverdraft = BigDecimal.ZERO;
|
||||
if(command.parameterExists(nominalAnnualInterestRateOverdraftParamName)){
|
||||
nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateOverdraftParamName);
|
||||
if (command.parameterExists(nominalAnnualInterestRateOverdraftParamName)) {
|
||||
nominalAnnualInterestRateOverdraft = command.bigDecimalValueOfParameterNamed(nominalAnnualInterestRateOverdraftParamName);
|
||||
}
|
||||
|
||||
|
||||
BigDecimal minOverdraftForInterestCalculation = BigDecimal.ZERO;
|
||||
if(command.parameterExists(minOverdraftForInterestCalculationParamName)){
|
||||
minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamed(minOverdraftForInterestCalculationParamName);
|
||||
if (command.parameterExists(minOverdraftForInterestCalculationParamName)) {
|
||||
minOverdraftForInterestCalculation = command.bigDecimalValueOfParameterNamed(minOverdraftForInterestCalculationParamName);
|
||||
}
|
||||
|
||||
boolean enforceMinRequiredBalance = false;
|
||||
@@ -159,17 +165,20 @@ public class SavingsProductAssembler {
|
||||
}
|
||||
|
||||
BigDecimal minRequiredBalance = BigDecimal.ZERO;
|
||||
if(command.parameterExists(minRequiredBalanceParamName)){
|
||||
if (command.parameterExists(minRequiredBalanceParamName)) {
|
||||
minRequiredBalance = command.bigDecimalValueOfParameterNamed(minRequiredBalanceParamName);
|
||||
}
|
||||
final BigDecimal minBalanceForInterestCalculation = command
|
||||
.bigDecimalValueOfParameterNamedDefaultToNullIfZero(minBalanceForInterestCalculationParamName);
|
||||
|
||||
boolean withHoldTax = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
final TaxGroup taxGroup = assembleTaxGroup(command);
|
||||
|
||||
return SavingsProduct.createNew(name, shortName, description, currency, interestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, iswithdrawalFeeApplicableForTransfer, accountingRuleType, charges,
|
||||
allowOverdraft, overdraftLimit, enforceMinRequiredBalance, minRequiredBalance, minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroup);
|
||||
}
|
||||
|
||||
public Set<Charge> assembleListOfSavingsProductCharges(final JsonCommand command, final String savingsProductCurrencyCode) {
|
||||
@@ -205,4 +214,13 @@ public class SavingsProductAssembler {
|
||||
|
||||
return charges;
|
||||
}
|
||||
|
||||
public TaxGroup assembleTaxGroup(final JsonCommand command) {
|
||||
final Long taxGroupId = command.longValueOfParameterNamed(taxGroupIdParamName);
|
||||
TaxGroup taxGroup = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroup = this.taxGroupRepository.findOneWithNotFoundDetection(taxGroupId);
|
||||
}
|
||||
return taxGroup;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.portfolio.savings.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "SAVINGSACCOUNT", action = "UPDATEWITHHOLDTAX")
|
||||
public class UpdateWithHoldTaxSavingsAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateWithHoldTaxSavingsAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.modifyWithHoldTax(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+32
-5
@@ -83,6 +83,7 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.apache.fineract.portfolio.savings.exception.DepositAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -580,8 +581,12 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
selectFieldsSqlBuilder.append("sa.account_balance_derived as accountBalance, ");
|
||||
selectFieldsSqlBuilder.append("sa.total_fees_charge_derived as totalFeeCharge, ");
|
||||
selectFieldsSqlBuilder.append("sa.total_penalty_charge_derived as totalPenaltyCharge, ");
|
||||
selectFieldsSqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax,");
|
||||
selectFieldsSqlBuilder.append("sa.deposit_type_enum as depositTypeId, ");
|
||||
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation ");
|
||||
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
||||
selectFieldsSqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
||||
selectFieldsSqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
|
||||
|
||||
this.selectFieldsSql = selectFieldsSqlBuilder.toString();
|
||||
|
||||
@@ -599,6 +604,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
selectTablesSqlBuilder.append("left join m_appuser abu on abu.id = sa.approvedon_userid ");
|
||||
selectTablesSqlBuilder.append("left join m_appuser avbu on rbu.id = sa.activatedon_userid ");
|
||||
selectTablesSqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
|
||||
selectTablesSqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
|
||||
|
||||
this.selectTablesSql = selectTablesSqlBuilder.toString();
|
||||
}
|
||||
@@ -722,17 +728,26 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
|
||||
final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge");
|
||||
final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge");
|
||||
final BigDecimal totalWithholdTax = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithholdTax");
|
||||
final BigDecimal totalOverdraftInterestDerived = null;
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
|
||||
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge,
|
||||
totalPenaltyCharge, totalOverdraftInterestDerived);
|
||||
totalPenaltyCharge, totalOverdraftInterestDerived, totalWithholdTax);
|
||||
|
||||
return DepositAccountData.instance(id, accountNo, externalId, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, depositType,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1068,7 +1083,9 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
selectFieldsSqlBuilder.append("sa.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
|
||||
selectFieldsSqlBuilder.append("sa.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
|
||||
selectFieldsSqlBuilder.append("sa.deposit_type_enum as depositTypeId, ");
|
||||
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation ");
|
||||
selectFieldsSqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
||||
selectFieldsSqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
||||
selectFieldsSqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
|
||||
this.selectFieldsSql = selectFieldsSqlBuilder.toString();
|
||||
|
||||
@@ -1076,6 +1093,8 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
selectTablesSqlBuilder.append("from m_savings_product sa ");
|
||||
selectTablesSqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sa.id = dptp.savings_product_id ");
|
||||
selectTablesSqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
selectTablesSqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
|
||||
|
||||
|
||||
this.selectTablesSql = selectTablesSqlBuilder.toString();
|
||||
}
|
||||
@@ -1158,12 +1177,20 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
final SavingsAccountStatusEnumData status = null;
|
||||
final SavingsAccountSummaryData summary = null;
|
||||
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return DepositAccountData.instance(null, null, null, groupId, groupName, clientId, clientName, productId, productName,
|
||||
fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualIterestRate, interestCompoundingPeriodType,
|
||||
interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType, minRequiredOpeningBalance,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary, depositType,
|
||||
minBalanceForInterestCalculation);
|
||||
minBalanceForInterestCalculation, withHoldTax, taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-2
@@ -36,6 +36,7 @@ import org.apache.fineract.portfolio.savings.data.DepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.FixedDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.data.RecurringDepositProductData;
|
||||
import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -144,7 +145,9 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
|
||||
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
|
||||
sqlBuilder.append("sp.accounting_type as accountingType, ");
|
||||
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation ");
|
||||
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
|
||||
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
@@ -197,9 +200,18 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
}
|
||||
final BigDecimal minBalanceForInterestCalculation = rs.getBigDecimal("minBalanceForInterestCalculation");
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return DepositProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, minBalanceForInterestCalculation);
|
||||
lockinPeriodFrequency, lockinPeriodFrequencyType, accountingRuleType, minBalanceForInterestCalculation, withHoldTax,
|
||||
taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +236,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append(" left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -292,6 +305,7 @@ public class DepositProductReadPlatformServiceImpl implements DepositProductRead
|
||||
sqlBuilder.append("left join m_deposit_product_term_and_preclosure dptp on sp.id=dptp.savings_product_id ");
|
||||
sqlBuilder.append("left join m_deposit_product_recurring_detail dprd on sp.id=dprd.savings_product_id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append(" left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
|
||||
+21
@@ -18,16 +18,23 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
@@ -38,6 +45,7 @@ import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.FixedDepositProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.FixedDepositProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.FixedDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -118,6 +126,19 @@ public class FixedDepositProductWritePlatformServiceJpaRepositoryImpl implements
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.containsKey(taxGroupIdParamName)) {
|
||||
final TaxGroup taxGroup = this.depositProductAssembler.assembleTaxGroup(command);
|
||||
product.setTaxGroup(taxGroup);
|
||||
if (product.withHoldTax() && product.getTaxGroup() == null) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(FIXED_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Long taxGroupId = null;
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
// accounting related changes
|
||||
final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
|
||||
final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
|
||||
|
||||
+22
@@ -18,16 +18,24 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.DepositsApiConstants.RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
@@ -38,6 +46,7 @@ import org.apache.fineract.portfolio.savings.domain.DepositProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.RecurringDepositProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.RecurringDepositProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.RecurringDepositProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -118,6 +127,19 @@ public class RecurringDepositProductWritePlatformServiceJpaRepositoryImpl implem
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.containsKey(taxGroupIdParamName)) {
|
||||
final TaxGroup taxGroup = this.depositProductAssembler.assembleTaxGroup(command);
|
||||
product.setTaxGroup(taxGroup);
|
||||
if (product.withHoldTax() && product.getTaxGroup() == null) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(RECURRING_DEPOSIT_PRODUCT_RESOURCE_NAME);
|
||||
final Long taxGroupId = null;
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
// accounting related changes
|
||||
final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
|
||||
final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
|
||||
|
||||
+4
-1
@@ -40,6 +40,7 @@ import org.apache.fineract.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountAnnualFeeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.MonthDay;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -159,12 +160,14 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
|
||||
final List<EnumOptionData> feeFrequencyOptions = this.dropdownReadPlatformService.retrievePeriodFrequencyTypeOptions();
|
||||
// this field is applicable only for client charges
|
||||
final Map<String, List<GLAccountData>> incomeOrLiabilityAccountOptions = null;
|
||||
|
||||
final Collection<TaxGroupData> taxGroupOptions = null;
|
||||
|
||||
// TODO AA : revisit for merge conflict - Not sure method signature
|
||||
return ChargeData.template(null, allowedChargeCalculationTypeOptions, null, allowedChargeTimeOptions, null,
|
||||
loansChargeCalculationTypeOptions, loansChargeTimeTypeOptions, savingsChargeCalculationTypeOptions,
|
||||
savingsChargeTimeTypeOptions, clientChargeCalculationTypeOptions, clientChargeTimeTypeOptions, feeFrequencyOptions,
|
||||
incomeOrLiabilityAccountOptions);
|
||||
incomeOrLiabilityAccountOptions, taxGroupOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+46
-12
@@ -61,6 +61,7 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -269,7 +270,11 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sa.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
|
||||
sqlBuilder.append("sa.min_required_balance as minRequiredBalance, ");
|
||||
sqlBuilder.append("sa.enforce_min_required_balance as enforceMinRequiredBalance, ");
|
||||
sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds ");
|
||||
sqlBuilder.append("sa.on_hold_funds_derived as onHoldFunds, ");
|
||||
sqlBuilder.append("sa.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("sa.total_withhold_tax_derived as totalWithholdTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
sqlBuilder.append("join m_savings_product sp ON sa.product_id = sp.id ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sa.currency_code ");
|
||||
@@ -282,6 +287,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("left join m_appuser abu on abu.id = sa.approvedon_userid ");
|
||||
sqlBuilder.append("left join m_appuser avbu on avbu.id = sa.activatedon_userid ");
|
||||
sqlBuilder.append("left join m_appuser cbu on cbu.id = sa.closedon_userid ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sa.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -400,8 +406,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
|
||||
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
|
||||
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "nominalAnnualInterestRateOverdraft");
|
||||
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minOverdraftForInterestCalculation");
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"nominalAnnualInterestRateOverdraft");
|
||||
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"minOverdraftForInterestCalculation");
|
||||
|
||||
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
|
||||
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
|
||||
@@ -431,22 +439,32 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final BigDecimal accountBalance = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "accountBalance");
|
||||
final BigDecimal totalFeeCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalFeeCharge");
|
||||
final BigDecimal totalPenaltyCharge = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalPenaltyCharge");
|
||||
final BigDecimal totalOverdraftInterestDerived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalOverdraftInterestDerived");
|
||||
|
||||
final BigDecimal totalOverdraftInterestDerived = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"totalOverdraftInterestDerived");
|
||||
final BigDecimal totalWithholdTax = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "totalWithholdTax");
|
||||
|
||||
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"minBalanceForInterestCalculation");
|
||||
final BigDecimal onHoldFunds = rs.getBigDecimal("onHoldFunds");
|
||||
|
||||
final SavingsAccountSummaryData summary = new SavingsAccountSummaryData(currency, totalDeposits, totalWithdrawals,
|
||||
totalWithdrawalFees, totalAnnualFees, totalInterestEarned, totalInterestPosted, accountBalance, totalFeeCharge,
|
||||
totalPenaltyCharge, totalOverdraftInterestDerived);
|
||||
totalPenaltyCharge, totalOverdraftInterestDerived, totalWithholdTax);
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return SavingsAccountData.instance(id, accountNo, depositType, externalId, groupId, groupName, clientId, clientName, productId,
|
||||
productName, fieldOfficerId, fieldOfficerName, status, timeline, currency, nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
|
||||
allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation,
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,7 +558,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
|
||||
|
||||
final boolean feeChargesOnly = false;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
|
||||
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
|
||||
Collection<StaffData> fieldOfficerOptions = null;
|
||||
|
||||
@@ -598,7 +617,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Collection<SavingsAccountChargeData> charges = null;
|
||||
|
||||
final boolean feeChargesOnly = true;
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
|
||||
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
|
||||
|
||||
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
|
||||
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
|
||||
@@ -842,6 +862,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.overdraft_limit as overdraftLimit, ");
|
||||
sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
|
||||
sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
|
||||
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
|
||||
|
||||
// sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,");
|
||||
// sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth, ");
|
||||
// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay ");
|
||||
@@ -849,6 +872,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -912,8 +936,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
|
||||
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
|
||||
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "nominalAnnualInterestRateOverdraft");
|
||||
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minOverdraftForInterestCalculation");
|
||||
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"nominalAnnualInterestRateOverdraft");
|
||||
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
|
||||
"minOverdraftForInterestCalculation");
|
||||
|
||||
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
|
||||
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
|
||||
@@ -933,6 +959,14 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
* MonthDay(annualFeeOnMonth, annualFeeOnDay); }
|
||||
*/
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
Long clientId = null;
|
||||
String clientName = null;
|
||||
if (this.client != null) {
|
||||
@@ -961,7 +995,7 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers, summary,
|
||||
allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance, minBalanceForInterestCalculation,
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
onHoldFunds, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax, taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -79,4 +79,6 @@ public interface SavingsAccountWritePlatformService {
|
||||
Set<Long> existingReversedTransactionIds);
|
||||
|
||||
void postInterest(SavingsAccount account);
|
||||
|
||||
CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command);
|
||||
}
|
||||
+85
-58
@@ -89,6 +89,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -102,6 +103,7 @@ import static org.apache.fineract.portfolio.savings.SavingsApiConstants.amountPa
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargeIdParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.dueAsOfDateParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withdrawBalanceParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.withHoldTaxParamName;
|
||||
|
||||
@Service
|
||||
public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements SavingsAccountWritePlatformService {
|
||||
@@ -129,7 +131,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
|
||||
|
||||
|
||||
@Autowired
|
||||
public SavingsAccountWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
final SavingsAccountRepository savingAccountRepository,
|
||||
@@ -141,8 +142,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
||||
final SavingsAccountDomainService savingsAccountDomainService, final NoteRepository noteRepository,
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService, final HolidayRepositoryWrapper holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final AccountTransfersReadPlatformService accountTransfersReadPlatformService,
|
||||
final HolidayRepositoryWrapper holidayRepository, final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final AccountAssociationsReadPlatformService accountAssociationsReadPlatformService,
|
||||
final ChargeRepositoryWrapper chargeRepository, final SavingsAccountChargeRepositoryWrapper savingsAccountChargeRepository,
|
||||
final SavingsAccountDataValidator fromApiJsonDeserializer, final SavingsAccountRepositoryWrapper savingsRepository,
|
||||
@@ -169,7 +170,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
this.savingsRepository = savingsRepository;
|
||||
this.staffRepository = staffRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.depositAccountOnHoldTransactionRepository= depositAccountOnHoldTransactionRepository;
|
||||
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -227,10 +228,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, user);
|
||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
}
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),depositAccountOnHoldTransactions);
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsAccountTransactionType.PAY_CHARGE.name(),
|
||||
depositAccountOnHoldTransactions);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -316,8 +319,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, accountId);
|
||||
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
|
||||
|
||||
@@ -360,7 +363,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult postInterest(final Long savingsId) {
|
||||
|
||||
@@ -385,7 +387,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||
|
||||
if (account.getNominalAnnualInterestRate().compareTo(BigDecimal.ZERO) > 0
|
||||
|| (account.allowOverdraft() && account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 0)) {
|
||||
|| (account.allowOverdraft() && account.getNominalAnnualInterestRateOverdraft().compareTo(BigDecimal.ZERO) > 0)) {
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
@@ -426,16 +428,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
|
||||
|
||||
if (!allowAccountTransferModification
|
||||
&& this.accountTransfersReadPlatformService
|
||||
.isAccountTransfer(transactionId,
|
||||
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:"
|
||||
+ transactionId + " update not allowed as it involves in account transfer",
|
||||
transactionId); }
|
||||
&& this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:" + transactionId
|
||||
+ " update not allowed as it involves in account transfer", transactionId); }
|
||||
|
||||
if (!account
|
||||
.allowModify()) { throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
|
||||
"Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); }
|
||||
if (!account.allowModify()) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transaction.update.not.allowed", "Savings account transaction:" + transactionId
|
||||
+ " update not allowed for this savings type", transactionId); }
|
||||
|
||||
final LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
|
||||
@@ -464,10 +463,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
financialYearBeginningMonth);
|
||||
}
|
||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
}
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction,depositAccountOnHoldTransactions);
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.undoTransactionAction, depositAccountOnHoldTransactions);
|
||||
account.activateAccountBasedOnBalance();
|
||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
|
||||
@@ -493,14 +493,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.findOneByIdAndSavingsAccountId(transactionId, savingsId);
|
||||
if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); }
|
||||
|
||||
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal())
|
||||
|| savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(savingsId, transactionId); }
|
||||
if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException(
|
||||
savingsId, transactionId); }
|
||||
|
||||
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId,
|
||||
PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transfer.transaction.update.not.allowed",
|
||||
"Savings account transaction:" + transactionId + " update not allowed as it involves in account transfer",
|
||||
transactionId); }
|
||||
if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transfer.transaction.update.not.allowed", "Savings account transaction:" + transactionId
|
||||
+ " update not allowed as it involves in account transfer", transactionId); }
|
||||
|
||||
this.savingsAccountTransactionDataValidator.validate(command);
|
||||
|
||||
@@ -510,9 +508,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
if (account.isNotActive()) {
|
||||
throwValidationForActiveStatus(SavingsApiConstants.adjustTransactionAction);
|
||||
}
|
||||
if (!account
|
||||
.allowModify()) { throw new PlatformServiceUnavailableException("error.msg.saving.account.transaction.update.not.allowed",
|
||||
"Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); }
|
||||
if (!account.allowModify()) { throw new PlatformServiceUnavailableException(
|
||||
"error.msg.saving.account.transaction.update.not.allowed", "Savings account transaction:" + transactionId
|
||||
+ " update not allowed for this savings type", transactionId); }
|
||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
@@ -553,10 +551,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
financialYearBeginningMonth);
|
||||
}
|
||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
}
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction,depositAccountOnHoldTransactions);
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.adjustTransactionAction, depositAccountOnHoldTransactions);
|
||||
account.activateAccountBasedOnBalance();
|
||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
@@ -878,8 +877,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
// Get Savings account from savings charge
|
||||
final SavingsAccount account = savingsAccountCharge.savingsAccount();
|
||||
@@ -901,11 +900,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
financialYearBeginningMonth);
|
||||
}
|
||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
}
|
||||
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,depositAccountOnHoldTransactions);
|
||||
account.validateAccountBalanceDoesNotBecomeNegative(SavingsApiConstants.waiveChargeTransactionAction,
|
||||
depositAccountOnHoldTransactions);
|
||||
|
||||
this.savingAccountRepository.saveAndFlush(account);
|
||||
|
||||
@@ -928,8 +929,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
final SavingsAccount savingsAccount = this.savingAccountAssembler.assembleFrom(savingsAccountId);
|
||||
checkClientOrGroupActive(savingsAccount);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
savingsAccount.removeCharge(savingsAccountCharge);
|
||||
this.savingAccountRepository.saveAndFlush(savingsAccount);
|
||||
@@ -954,8 +955,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(amountParamName);
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed(dueAsOfDateParamName);
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
@@ -994,8 +995,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
AppUser user = null;
|
||||
|
||||
final LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, accountId);
|
||||
|
||||
final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd MM yyyy");
|
||||
fmt.withZone(DateUtils.getDateTimeZoneOfTenant());
|
||||
@@ -1031,11 +1032,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
financialYearBeginningMonth);
|
||||
}
|
||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||
if(account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1){
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) == 1) {
|
||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||
}
|
||||
|
||||
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),depositAccountOnHoldTransactions);
|
||||
account.validateAccountBalanceDoesNotBecomeNegative("." + SavingsAccountTransactionType.PAY_CHARGE.getCode(),
|
||||
depositAccountOnHoldTransactions);
|
||||
|
||||
this.savingAccountRepository.save(account);
|
||||
|
||||
@@ -1064,8 +1067,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
.findOneWithNotFoundDetection(savingsAccountChargeId, savingsAccountId);
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository.findOneWithNotFoundDetection(
|
||||
savingsAccountChargeId, savingsAccountId);
|
||||
|
||||
final SavingsAccount account = savingsAccountCharge.savingsAccount();
|
||||
this.savingAccountAssembler.assignSavingAccountHelpers(account);
|
||||
@@ -1143,16 +1146,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final LocalDate dateOfSavingsOfficerAssignment = command.localDateValueOfParameterNamed("assignmentDate");
|
||||
|
||||
if (fromSavingsOfficerId != null) {
|
||||
fromSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId,
|
||||
savingsForUpdate.office().getHierarchy());
|
||||
fromSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(fromSavingsOfficerId, savingsForUpdate
|
||||
.office().getHierarchy());
|
||||
}
|
||||
if (toSavingsOfficerId != null) {
|
||||
toSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId,
|
||||
savingsForUpdate.office().getHierarchy());
|
||||
toSavingsOfficer = this.staffRepository.findByOfficeHierarchyWithNotFoundDetection(toSavingsOfficerId, savingsForUpdate
|
||||
.office().getHierarchy());
|
||||
actualChanges.put("toSavingsOfficerId", toSavingsOfficer.getId());
|
||||
}
|
||||
if (!savingsForUpdate.hasSavingsOfficer(
|
||||
fromSavingsOfficer)) { throw new SavingsOfficerAssignmentException(savingsAccountId, fromSavingsOfficerId); }
|
||||
if (!savingsForUpdate.hasSavingsOfficer(fromSavingsOfficer)) { throw new SavingsOfficerAssignmentException(savingsAccountId,
|
||||
fromSavingsOfficerId); }
|
||||
|
||||
savingsForUpdate.reassignSavingsOfficer(toSavingsOfficer, dateOfSavingsOfficerAssignment);
|
||||
|
||||
@@ -1196,6 +1199,30 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command) {
|
||||
final Map<String, Object> actualChanges = new HashMap<>(1);
|
||||
final SavingsAccount savingsForUpdate = this.savingsRepository.findOneWithNotFoundDetection(savingsAccountId);
|
||||
if (command.isChangeInBooleanParameterNamed(withHoldTaxParamName, savingsForUpdate.withHoldTax())) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(withHoldTaxParamName);
|
||||
actualChanges.put(withHoldTaxParamName, newValue);
|
||||
savingsForUpdate.setWithHoldTax(newValue);
|
||||
if (savingsForUpdate.getTaxGroup() == null) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("account");
|
||||
baseDataValidator.reset().parameter(withHoldTaxParamName).failWithCode("not.supported");
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(savingsAccountId) //
|
||||
.withSavingsId(savingsAccountId) //
|
||||
.with(actualChanges) //
|
||||
.build();
|
||||
}
|
||||
|
||||
private AppUser getAppUserIfPresent() {
|
||||
AppUser user = null;
|
||||
if (this.context != null) {
|
||||
|
||||
+17
-12
@@ -179,6 +179,10 @@ public class SavingsEnumerations {
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue().longValue(),
|
||||
SavingsAccountTransactionType.OVERDRAFT_INTEREST.getCode(), "Overdraft Interest");
|
||||
break;
|
||||
case WITHHOLD_TAX:
|
||||
optionData = new SavingsAccountTransactionEnumData(SavingsAccountTransactionType.WITHHOLD_TAX.getValue().longValue(),
|
||||
SavingsAccountTransactionType.WITHHOLD_TAX.getCode(), "Withhold Tax");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
@@ -198,70 +202,71 @@ public class SavingsEnumerations {
|
||||
final boolean isPrematureClosed = type.isPreMatureClosure();
|
||||
final boolean isTransferInProgress = type.isTransferInProgress();
|
||||
final boolean isTransferOnHold = type.isTransferOnHold();
|
||||
final boolean matured = type.isMatured();
|
||||
|
||||
SavingsAccountStatusEnumData optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
|
||||
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
|
||||
switch (type) {
|
||||
case INVALID:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.INVALID.getValue().longValue(),
|
||||
SavingsAccountStatusType.INVALID.getCode(), "Invalid", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case SUBMITTED_AND_PENDING_APPROVAL:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getValue()
|
||||
.longValue(), SavingsAccountStatusType.SUBMITTED_AND_PENDING_APPROVAL.getCode(), "Submitted and pending approval",
|
||||
submittedAndPendingApproval, isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed,
|
||||
isTransferInProgress, isTransferOnHold);
|
||||
isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case REJECTED:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.REJECTED.getValue().longValue(),
|
||||
SavingsAccountStatusType.REJECTED.getCode(), "Rejected", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case WITHDRAWN_BY_APPLICANT:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getValue().longValue(),
|
||||
SavingsAccountStatusType.WITHDRAWN_BY_APPLICANT.getCode(), "Withdrawn by applicant", submittedAndPendingApproval,
|
||||
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress,
|
||||
isTransferOnHold);
|
||||
isTransferOnHold, matured);
|
||||
break;
|
||||
case APPROVED:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.APPROVED.getValue().longValue(),
|
||||
SavingsAccountStatusType.APPROVED.getCode(), "Approved", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case ACTIVE:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.ACTIVE.getValue().longValue(),
|
||||
SavingsAccountStatusType.ACTIVE.getCode(), "Active", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case CLOSED:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.CLOSED.getValue().longValue(),
|
||||
SavingsAccountStatusType.CLOSED.getCode(), "Closed", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case TRANSFER_IN_PROGRESS:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue().longValue(),
|
||||
SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getCode(), "Transfer in progress", submittedAndPendingApproval,
|
||||
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress,
|
||||
isTransferOnHold);
|
||||
isTransferOnHold, matured);
|
||||
break;
|
||||
case TRANSFER_ON_HOLD:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.TRANSFER_ON_HOLD.getValue().longValue(),
|
||||
SavingsAccountStatusType.TRANSFER_ON_HOLD.getCode(), "Transfer on hold", submittedAndPendingApproval,
|
||||
isApproved, isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress,
|
||||
isTransferOnHold);
|
||||
isTransferOnHold, matured);
|
||||
break;
|
||||
case PRE_MATURE_CLOSURE:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.PRE_MATURE_CLOSURE.getValue().longValue(),
|
||||
SavingsAccountStatusType.PRE_MATURE_CLOSURE.getCode(), "Premature Closed", submittedAndPendingApproval, isApproved,
|
||||
isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isRejected, isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
case MATURED:
|
||||
optionData = new SavingsAccountStatusEnumData(SavingsAccountStatusType.MATURED.getValue().longValue(),
|
||||
SavingsAccountStatusType.MATURED.getCode(), "Matured", submittedAndPendingApproval, isApproved, isRejected,
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold);
|
||||
isWithdrawnByApplicant, isActive, isClosed, isPrematureClosed, isTransferInProgress, isTransferOnHold, matured);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
+55
-43
@@ -34,6 +34,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -51,7 +52,7 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
|
||||
@Autowired
|
||||
public SavingsProductReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource,
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
this.context = context;
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.fineractEntityAccessUtil = fineractEntityAccessUtil;
|
||||
@@ -63,14 +64,14 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
this.context.authenticatedUser();
|
||||
|
||||
String sql = "select " + this.savingsProductRowMapper.schema() + "where sp.deposit_type_enum = ?";
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil.
|
||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
||||
FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
||||
sql += " and sp.id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only
|
||||
// products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil
|
||||
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||
sql += " and sp.id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.savingsProductRowMapper, new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() });
|
||||
}
|
||||
@@ -79,15 +80,14 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
public Collection<SavingsProductData> retrieveAllForLookup() {
|
||||
|
||||
String sql = "select " + this.savingsProductLookupsRowMapper.schema() + " where sp.deposit_type_enum = ? ";
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil.
|
||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
||||
FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
||||
sql += " and id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only
|
||||
// products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil
|
||||
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||
sql += " and id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper,
|
||||
new Object[] { DepositAccountType.SAVINGS_DEPOSIT.getValue() });
|
||||
@@ -132,9 +132,12 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, ");
|
||||
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, ");
|
||||
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation,");
|
||||
sqlBuilder.append("sp.accounting_type as accountingType ");
|
||||
sqlBuilder.append("sp.accounting_type as accountingType, ");
|
||||
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
|
||||
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName ");
|
||||
sqlBuilder.append("from m_savings_product sp ");
|
||||
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
|
||||
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -200,11 +203,20 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
|
||||
final BigDecimal minBalanceForInterestCalculation = rs.getBigDecimal("minBalanceForInterestCalculation");
|
||||
|
||||
final boolean withHoldTax = rs.getBoolean("withHoldTax");
|
||||
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
|
||||
final String taxGroupName = rs.getString("taxGroupName");
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (taxGroupId != null) {
|
||||
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
|
||||
}
|
||||
|
||||
return SavingsProductData.instance(id, name, shortName, description, currency, nominalAnnualInterestRate,
|
||||
compoundingInterestPeriodType, interestPostingPeriodType, interestCalculationType, interestCalculationDaysInYearType,
|
||||
minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType, withdrawalFeeForTransfers,
|
||||
accountingRuleType, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation);
|
||||
minBalanceForInterestCalculation, nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax,
|
||||
taxGroupData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,22 +241,22 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
String sql = "select " + this.savingsProductLookupsRowMapper.schema();
|
||||
|
||||
boolean inClauseAdded = false;
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil.
|
||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
||||
FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
||||
sql += " where id in ( " + inClause + " ) ";
|
||||
inClauseAdded = true;
|
||||
}
|
||||
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only
|
||||
// products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil
|
||||
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||
sql += " where id in ( " + inClause + " ) ";
|
||||
inClauseAdded = true;
|
||||
}
|
||||
|
||||
if (isOverdraftType != null) {
|
||||
if (inClauseAdded) {
|
||||
sql += " and sp.allow_overdraft=?";
|
||||
} else {
|
||||
sql += " where sp.allow_overdraft=?";
|
||||
}
|
||||
if (inClauseAdded) {
|
||||
sql += " and sp.allow_overdraft=?";
|
||||
} else {
|
||||
sql += " where sp.allow_overdraft=?";
|
||||
}
|
||||
return this.jdbcTemplate.query(sql, this.savingsProductLookupsRowMapper, isOverdraftType);
|
||||
}
|
||||
|
||||
@@ -258,14 +270,14 @@ public class SavingsProductReadPlatformServiceImpl implements SavingsProductRead
|
||||
this.context.authenticatedUser();
|
||||
|
||||
String sql = "select " + this.savingsProductRowMapper.schema() + " where sp.currency_code='" + currencyCode + "'";
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil.
|
||||
getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(
|
||||
FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ( (inClause != null) && (!(inClause.trim().isEmpty())) ) {
|
||||
sql += " and id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
// Check if branch specific products are enabled. If yes, fetch only
|
||||
// products mapped to current user's office
|
||||
String inClause = fineractEntityAccessUtil
|
||||
.getSQLWhereClauseForProductIDsForUserOffice_ifGlobalConfigEnabled(FineractEntityType.SAVINGS_PRODUCT);
|
||||
if ((inClause != null) && (!(inClause.trim().isEmpty()))) {
|
||||
sql += " and id in ( " + inClause + " ) ";
|
||||
}
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.savingsProductRowMapper);
|
||||
}
|
||||
|
||||
+26
-7
@@ -18,16 +18,23 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.savings.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.chargesParamName;
|
||||
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.taxGroupIdParamName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.fineract.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccessType;
|
||||
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityType;
|
||||
@@ -40,6 +47,7 @@ import org.apache.fineract.portfolio.savings.domain.SavingsProduct;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductAssembler;
|
||||
import org.apache.fineract.portfolio.savings.domain.SavingsProductRepository;
|
||||
import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -63,8 +71,7 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
final SavingsProductRepository savingProductRepository, final SavingsProductDataValidator fromApiJsonDataValidator,
|
||||
final SavingsProductAssembler savingsProductAssembler,
|
||||
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil
|
||||
) {
|
||||
final FineractEntityAccessUtil fineractEntityAccessUtil) {
|
||||
this.context = context;
|
||||
this.savingProductRepository = savingProductRepository;
|
||||
this.fromApiJsonDataValidator = fromApiJsonDataValidator;
|
||||
@@ -116,13 +123,12 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
// save accounting mappings
|
||||
this.accountMappingWritePlatformService.createSavingProductToGLAccountMapping(product.getId(), command,
|
||||
DepositAccountType.SAVINGS_DEPOSIT);
|
||||
|
||||
// check if the office specific products are enabled. If yes, then save this savings product against a specific office
|
||||
|
||||
// check if the office specific products are enabled. If yes, then
|
||||
// save this savings product against a specific office
|
||||
// i.e. this savings product is specific for this office.
|
||||
fineractEntityAccessUtil.checkConfigurationAndAddProductResrictionsForUserOffice(
|
||||
FineractEntityAccessType.OFFICE_ACCESS_TO_SAVINGS_PRODUCTS,
|
||||
FineractEntityType.SAVINGS_PRODUCT,
|
||||
product.getId());
|
||||
FineractEntityAccessType.OFFICE_ACCESS_TO_SAVINGS_PRODUCTS, FineractEntityType.SAVINGS_PRODUCT, product.getId());
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withEntityId(product.getId()) //
|
||||
@@ -155,6 +161,19 @@ public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.containsKey(taxGroupIdParamName)) {
|
||||
final TaxGroup taxGroup = this.savingsProductAssembler.assembleTaxGroup(command);
|
||||
product.setTaxGroup(taxGroup);
|
||||
if (product.withHoldTax() && product.getTaxGroup() == null) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SAVINGS_PRODUCT_RESOURCE_NAME);
|
||||
final Long taxGroupId = null;
|
||||
baseDataValidator.reset().parameter(taxGroupIdParamName).value(taxGroupId).notBlank();
|
||||
throw new PlatformApiDataValidationException(dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
// accounting related changes
|
||||
final boolean accountingTypeChanged = changes.containsKey(accountingRuleParamName);
|
||||
final Map<String, Object> accountingMappingChanges = this.accountMappingWritePlatformService
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.portfolio.tax.api;
|
||||
|
||||
public interface TaxApiConstants {
|
||||
|
||||
public static final String nameParamName = "name";
|
||||
public static final String percentageParamName = "percentage";
|
||||
public static final String debitAccountTypeParamName = "debitAccountType";
|
||||
public static final String debitAcountIdParamName = "debitAcountId";
|
||||
public static final String creditAccountTypeParamName = "creditAccountType";
|
||||
public static final String creditAcountIdParamName = "creditAcountId";
|
||||
|
||||
public static final String startDateParamName = "startDate";
|
||||
public static final String endDateParamName = "endDate";
|
||||
|
||||
public static final String chargeIncludesTaxParamName = "chargeIncludesTax";
|
||||
public static final String taxComponentsParamName = "taxComponents";
|
||||
public static final String idParamName = "id";
|
||||
public static final String taxComponentIdParamName = "taxComponentId";
|
||||
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 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.portfolio.tax.api;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/taxes/component")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class TaxComponentApiResource {
|
||||
|
||||
private final String resourceNameForPermissions = "TAXCOMPONENT";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final TaxReadPlatformService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<TaxComponentData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public TaxComponentApiResource(final PlatformSecurityContext context, final TaxReadPlatformService readPlatformService,
|
||||
final DefaultToApiJsonSerializer<TaxComponentData> toApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.readPlatformService = readPlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveAllTaxComponents(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final Collection<TaxComponentData> TaxComponents = this.readPlatformService.retrieveAllTaxComponents();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, TaxComponents);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{taxComponentId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTaxComponent(@PathParam("taxComponentId") final Long taxComponentId, @Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
|
||||
TaxComponentData taxComponentData = this.readPlatformService.retrieveTaxComponentData(taxComponentId);
|
||||
return this.toApiJsonSerializer.serialize(settings, taxComponentData);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final TaxComponentData taxComponentData = this.readPlatformService.retrieveTaxComponentTemplate();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, taxComponentData);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String createTaxCompoent(final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createTaxComponent().withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{taxComponentId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateTaxCompoent(@PathParam("taxComponentId") final Long taxComponentId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxComponent(taxComponentId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* 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.portfolio.tax.api;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/taxes/group")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class TaxGroupApiResource {
|
||||
|
||||
private final String resourceNameForPermissions = "TAXGROUP";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final TaxReadPlatformService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<TaxGroupData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public TaxGroupApiResource(final PlatformSecurityContext context, final TaxReadPlatformService readPlatformService,
|
||||
final DefaultToApiJsonSerializer<TaxGroupData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.readPlatformService = readPlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveAllTaxGroups(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final Collection<TaxGroupData> taxGroupDatas = this.readPlatformService.retrieveAllTaxGroups();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, taxGroupDatas);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{taxGroupId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTaxGroup(@PathParam("taxGroupId") final Long taxGroupId, @Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
TaxGroupData taxGroupData = null;
|
||||
if (settings.isTemplate()) {
|
||||
taxGroupData = this.readPlatformService.retrieveTaxGroupWithTemplate(taxGroupId);
|
||||
} else {
|
||||
taxGroupData = this.readPlatformService.retrieveTaxGroupData(taxGroupId);
|
||||
}
|
||||
return this.toApiJsonSerializer.serialize(settings, taxGroupData);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("template")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveTemplate(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final TaxGroupData taxGroupData = this.readPlatformService.retrieveTaxGroupTemplate();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, taxGroupData);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String createTaxGroup(final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createTaxGroup().withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{taxGroupId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateTaxGroup(@PathParam("taxGroupId") final Long taxGroupId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxGroup(taxGroupId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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.portfolio.tax.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class TaxComponentData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
@SuppressWarnings("unused")
|
||||
private final String name;
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal percentage;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData debitAccountType;
|
||||
@SuppressWarnings("unused")
|
||||
private final GLAccountData debitAccount;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData creditAccountType;
|
||||
@SuppressWarnings("unused")
|
||||
private final GLAccountData creditAccount;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate startDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<TaxComponentHistoryData> taxComponentHistories;
|
||||
|
||||
// template options
|
||||
@SuppressWarnings("unused")
|
||||
private final Map<String, List<GLAccountData>> glAccountOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<EnumOptionData> glAccountTypeOptions;
|
||||
|
||||
public static TaxComponentData instance(final Long id, final String name, final BigDecimal percentage,
|
||||
final EnumOptionData debitAccountType, final GLAccountData debitAcount, final EnumOptionData creditAccountType,
|
||||
final GLAccountData creditAcount, final LocalDate startDate, final Collection<TaxComponentHistoryData> taxComponentHistories) {
|
||||
final Map<String, List<GLAccountData>> glAccountOptions = null;
|
||||
final Collection<EnumOptionData> glAccountTypeOptions = null;
|
||||
return new TaxComponentData(id, name, percentage, debitAccountType, debitAcount, creditAccountType, creditAcount, startDate,
|
||||
taxComponentHistories, glAccountOptions, glAccountTypeOptions);
|
||||
}
|
||||
|
||||
public static TaxComponentData lookup(final Long id, final String name) {
|
||||
final BigDecimal percentage = null;
|
||||
final EnumOptionData debitAccountType = null;
|
||||
final GLAccountData debitAcount = null;
|
||||
final EnumOptionData creditAccountType = null;
|
||||
final GLAccountData creditAcount = null;
|
||||
final LocalDate startDate = null;
|
||||
final Collection<TaxComponentHistoryData> taxComponentHistories = null;
|
||||
final Map<String, List<GLAccountData>> glAccountOptions = null;
|
||||
final Collection<EnumOptionData> glAccountTypeOptions = null;
|
||||
return new TaxComponentData(id, name, percentage, debitAccountType, debitAcount, creditAccountType, creditAcount, startDate,
|
||||
taxComponentHistories, glAccountOptions, glAccountTypeOptions);
|
||||
}
|
||||
|
||||
public static TaxComponentData template(final Map<String, List<GLAccountData>> glAccountOptions,
|
||||
final Collection<EnumOptionData> glAccountTypeOptions) {
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
final BigDecimal percentage = null;
|
||||
final EnumOptionData debitAccountType = null;
|
||||
final GLAccountData debitAcount = null;
|
||||
final EnumOptionData creditAccountType = null;
|
||||
final GLAccountData creditAcount = null;
|
||||
final LocalDate startDate = null;
|
||||
final Collection<TaxComponentHistoryData> taxComponentHistories = null;
|
||||
return new TaxComponentData(id, name, percentage, debitAccountType, debitAcount, creditAccountType, creditAcount, startDate,
|
||||
taxComponentHistories, glAccountOptions, glAccountTypeOptions);
|
||||
}
|
||||
|
||||
private TaxComponentData(final Long id, final String name, final BigDecimal percentage, final EnumOptionData debitAccountType,
|
||||
final GLAccountData debitAcount, final EnumOptionData creditAccountType, final GLAccountData creditAcount,
|
||||
final LocalDate startDate, final Collection<TaxComponentHistoryData> taxComponentHistories,
|
||||
final Map<String, List<GLAccountData>> glAccountOptions, final Collection<EnumOptionData> glAccountTypeOptions) {
|
||||
this.id = id;
|
||||
this.percentage = percentage;
|
||||
this.name = name;
|
||||
this.debitAccountType = debitAccountType;
|
||||
this.debitAccount = debitAcount;
|
||||
this.creditAccountType = creditAccountType;
|
||||
this.creditAccount = creditAcount;
|
||||
this.startDate = startDate;
|
||||
this.taxComponentHistories = taxComponentHistories;
|
||||
this.glAccountOptions = glAccountOptions;
|
||||
this.glAccountTypeOptions = glAccountTypeOptions;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.portfolio.tax.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class TaxComponentHistoryData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final BigDecimal percentage;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate startDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate endDate;
|
||||
|
||||
public TaxComponentHistoryData(final BigDecimal percentage, final LocalDate startDate, final LocalDate endDate) {
|
||||
this.percentage = percentage;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.portfolio.tax.data;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class TaxGroupData {
|
||||
|
||||
private final Long id;
|
||||
private final String name;
|
||||
private final Collection<TaxGroupMappingsData> taxAssociations;
|
||||
|
||||
// Template options
|
||||
@SuppressWarnings("unused")
|
||||
private final Collection<TaxComponentData> taxComponents;
|
||||
|
||||
public static TaxGroupData instance(final Long id, final String name, final Collection<TaxGroupMappingsData> taxAssociations) {
|
||||
final Collection<TaxComponentData> taxComponents = null;
|
||||
return new TaxGroupData(id, name, taxAssociations, taxComponents);
|
||||
}
|
||||
|
||||
public static TaxGroupData lookup(final Long id, final String name) {
|
||||
final Collection<TaxComponentData> taxComponents = null;
|
||||
final Collection<TaxGroupMappingsData> taxAssociations = null;
|
||||
return new TaxGroupData(id, name, taxAssociations, taxComponents);
|
||||
}
|
||||
|
||||
public static TaxGroupData template(final Collection<TaxComponentData> taxComponents) {
|
||||
final Long id = null;
|
||||
final String name = null;
|
||||
final Collection<TaxGroupMappingsData> taxAssociations = null;
|
||||
return new TaxGroupData(id, name, taxAssociations, taxComponents);
|
||||
}
|
||||
|
||||
public static TaxGroupData template(final TaxGroupData taxGroupData, final Collection<TaxComponentData> taxComponents) {
|
||||
return new TaxGroupData(taxGroupData.id, taxGroupData.name, taxGroupData.taxAssociations, taxComponents);
|
||||
}
|
||||
|
||||
private TaxGroupData(final Long id, final String name, final Collection<TaxGroupMappingsData> taxAssociations,
|
||||
final Collection<TaxComponentData> taxComponents) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.taxAssociations = taxAssociations;
|
||||
this.taxComponents = taxComponents;
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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.portfolio.tax.data;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class TaxGroupMappingsData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
@SuppressWarnings("unused")
|
||||
private final TaxComponentData taxComponent;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate startDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final LocalDate endDate;
|
||||
|
||||
public TaxGroupMappingsData(final Long id, final TaxComponentData taxComponent, final LocalDate startDate, final LocalDate endDate) {
|
||||
this.id = id;
|
||||
this.taxComponent = taxComponent;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
}
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_tax_component")
|
||||
public class TaxComponent extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@Column(name = "name", length = 100)
|
||||
private String name;
|
||||
|
||||
@Column(name = "percentage", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal percentage;
|
||||
|
||||
@Column(name = "debit_account_type_enum")
|
||||
private Integer debitAccountType;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "debit_account_id")
|
||||
private GLAccount debitAcount;
|
||||
|
||||
@Column(name = "credit_account_type_enum")
|
||||
private Integer creditAccountType;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "credit_account_id")
|
||||
private GLAccount creditAcount;
|
||||
|
||||
@Column(name = "start_date", nullable = false)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date startDate;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "tax_component_id", referencedColumnName = "id", nullable = false)
|
||||
private List<TaxComponentHistory> taxComponentHistories = new ArrayList<>();
|
||||
|
||||
@LazyCollection(LazyCollectionOption.TRUE)
|
||||
@OneToMany(cascade = CascadeType.DETACH, mappedBy = "taxComponent", orphanRemoval = false)
|
||||
private List<TaxGroupMappings> taxGroupMappings = new ArrayList<>();
|
||||
|
||||
protected TaxComponent() {
|
||||
|
||||
}
|
||||
|
||||
private TaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType, final GLAccount debitAcount,
|
||||
final GLAccountType creditAccountType, final GLAccount creditAcount, final LocalDate startDate) {
|
||||
this.name = name;
|
||||
this.percentage = percentage;
|
||||
if (debitAccountType != null) {
|
||||
this.debitAccountType = debitAccountType.getValue();
|
||||
}
|
||||
this.debitAcount = debitAcount;
|
||||
if (creditAccountType != null) {
|
||||
this.creditAccountType = creditAccountType.getValue();
|
||||
}
|
||||
this.creditAcount = creditAcount;
|
||||
this.startDate = startDate.toDate();
|
||||
}
|
||||
|
||||
public static TaxComponent createTaxComponent(final String name, final BigDecimal percentage, final GLAccountType debitAccountType,
|
||||
final GLAccount debitAcount, final GLAccountType creditAccountType, final GLAccount creditAcount, final LocalDate startDate) {
|
||||
return new TaxComponent(name, percentage, debitAccountType, debitAcount, creditAccountType, creditAcount, startDate);
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
if (command.isChangeInStringParameterNamed(TaxApiConstants.nameParamName, this.name)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(TaxApiConstants.nameParamName);
|
||||
changes.put(TaxApiConstants.nameParamName, newValue);
|
||||
this.name = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
if (command.isChangeInBigDecimalParameterNamed(TaxApiConstants.percentageParamName, this.percentage)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(TaxApiConstants.percentageParamName);
|
||||
changes.put(TaxApiConstants.percentageParamName, newValue);
|
||||
|
||||
LocalDate oldStartDate = new LocalDate(this.startDate);
|
||||
updateStartDate(command, changes, true);
|
||||
LocalDate newStartDate = new LocalDate(this.startDate);
|
||||
|
||||
TaxComponentHistory history = TaxComponentHistory.createTaxComponentHistory(this.percentage, oldStartDate, newStartDate);
|
||||
this.taxComponentHistories.add(history);
|
||||
this.percentage = newValue;
|
||||
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private void updateStartDate(final JsonCommand command, final Map<String, Object> changes, boolean setAsCurrentDate) {
|
||||
LocalDate startDate = DateUtils.getLocalDateOfTenant();
|
||||
if (command.parameterExists(TaxApiConstants.startDateParamName)) {
|
||||
LocalDate startDateFromUI = command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName);
|
||||
if (startDateFromUI != null) {
|
||||
startDate = startDateFromUI;
|
||||
}
|
||||
this.startDate = startDate.toDate();
|
||||
changes.put(TaxApiConstants.startDateParamName, startDate);
|
||||
} else if (setAsCurrentDate) {
|
||||
changes.put(TaxApiConstants.startDateParamName, startDate);
|
||||
this.startDate = startDate.toDate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public BigDecimal getPercentage() {
|
||||
return this.percentage;
|
||||
}
|
||||
|
||||
public LocalDate startDate() {
|
||||
LocalDate startDate = null;
|
||||
if (this.startDate != null) {
|
||||
startDate = new LocalDate(this.startDate);
|
||||
}
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public BigDecimal getApplicablePercentage(final LocalDate date) {
|
||||
BigDecimal percentage = null;
|
||||
if (occursOnDayFrom(date)) {
|
||||
percentage = getPercentage();
|
||||
} else {
|
||||
for (TaxComponentHistory componentHistory : taxComponentHistories) {
|
||||
if (componentHistory.occursOnDayFromAndUpToAndIncluding(date)) {
|
||||
percentage = componentHistory.getPercentage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return percentage;
|
||||
}
|
||||
|
||||
private boolean occursOnDayFrom(final LocalDate target) {
|
||||
return target != null && target.isAfter(startDate());
|
||||
}
|
||||
|
||||
public List<TaxComponentHistory> getTaxComponentHistories() {
|
||||
return this.taxComponentHistories;
|
||||
}
|
||||
|
||||
public List<TaxGroupMappings> getTaxGroupMappings() {
|
||||
return this.taxGroupMappings;
|
||||
}
|
||||
|
||||
public Collection<LocalDate> allStartDates() {
|
||||
List<LocalDate> dates = new ArrayList<>();
|
||||
dates.add(startDate());
|
||||
for (TaxComponentHistory componentHistory : taxComponentHistories) {
|
||||
dates.add(componentHistory.startDate());
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
|
||||
public Integer getDebitAccountType() {
|
||||
return this.debitAccountType;
|
||||
}
|
||||
|
||||
|
||||
public GLAccount getDebitAcount() {
|
||||
return this.debitAcount;
|
||||
}
|
||||
|
||||
|
||||
public Integer getCreditAccountType() {
|
||||
return this.creditAccountType;
|
||||
}
|
||||
|
||||
|
||||
public GLAccount getCreditAcount() {
|
||||
return this.creditAcount;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_tax_component_history")
|
||||
public class TaxComponentHistory extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@Column(name = "percentage", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal percentage;
|
||||
|
||||
@Column(name = "start_date", nullable = false)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date startDate;
|
||||
|
||||
@Column(name = "end_date", nullable = false)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date endDate;
|
||||
|
||||
protected TaxComponentHistory() {
|
||||
|
||||
}
|
||||
|
||||
private TaxComponentHistory(final BigDecimal percentage, final LocalDate startDate, final LocalDate endDate) {
|
||||
this.percentage = percentage;
|
||||
this.startDate = startDate.toDate();
|
||||
this.endDate = endDate.toDate();
|
||||
}
|
||||
|
||||
public static TaxComponentHistory createTaxComponentHistory(final BigDecimal percentage, final LocalDate startDate,
|
||||
final LocalDate endDate) {
|
||||
return new TaxComponentHistory(percentage, startDate, endDate);
|
||||
}
|
||||
|
||||
public LocalDate startDate(){
|
||||
LocalDate startDate = null;
|
||||
if(this.startDate != null){
|
||||
startDate = new LocalDate(this.startDate);
|
||||
}
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public LocalDate endDate(){
|
||||
LocalDate endDate = null;
|
||||
if(this.endDate != null){
|
||||
endDate = new LocalDate(this.endDate);
|
||||
}
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
|
||||
if(this.endDate == null){
|
||||
return target != null && target.isAfter(startDate());
|
||||
}
|
||||
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getPercentage() {
|
||||
return this.percentage;
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface TaxComponentRepository extends JpaRepository<TaxComponent, Long>, JpaSpecificationExecutor<TaxComponent> {
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import org.apache.fineract.portfolio.tax.exception.TaxComponentNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TaxComponentRepositoryWrapper {
|
||||
|
||||
private final TaxComponentRepository repository;
|
||||
|
||||
@Autowired
|
||||
public TaxComponentRepositoryWrapper(final TaxComponentRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public TaxComponent findOneWithNotFoundDetection(final Long id) {
|
||||
|
||||
final TaxComponent taxComponent = this.repository.findOne(id);
|
||||
if (taxComponent == null) { throw new TaxComponentNotFoundException(id); }
|
||||
|
||||
return taxComponent;
|
||||
}
|
||||
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
|
||||
import org.apache.fineract.portfolio.tax.exception.TaxMappingNotFoundException;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_tax_group")
|
||||
public class TaxGroup extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@Column(name = "name", length = 100)
|
||||
private String name;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "tax_group_id", referencedColumnName = "id", nullable = false)
|
||||
private List<TaxGroupMappings> taxGroupMappings = new ArrayList<>();
|
||||
|
||||
protected TaxGroup() {
|
||||
|
||||
}
|
||||
|
||||
private TaxGroup(final String name, final List<TaxGroupMappings> taxGroupMappings) {
|
||||
this.name = name;
|
||||
this.taxGroupMappings = taxGroupMappings;
|
||||
}
|
||||
|
||||
public static TaxGroup createTaxGroup(final String name, final List<TaxGroupMappings> taxGroupMappings) {
|
||||
return new TaxGroup(name, taxGroupMappings);
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command, final List<TaxGroupMappings> taxGroupMappings) {
|
||||
final Map<String, Object> changes = new HashMap<>();
|
||||
|
||||
if (command.isChangeInStringParameterNamed(TaxApiConstants.nameParamName, this.name)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(TaxApiConstants.nameParamName);
|
||||
changes.put(TaxApiConstants.nameParamName, newValue);
|
||||
this.name = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
List<Long> taxComponentList = new ArrayList<>();
|
||||
final List<Map<String, Object>> modifications = new ArrayList<>();
|
||||
|
||||
for (TaxGroupMappings groupMappings : taxGroupMappings) {
|
||||
TaxGroupMappings mappings = findOneBy(groupMappings);
|
||||
if (mappings == null) {
|
||||
this.taxGroupMappings.add(groupMappings);
|
||||
taxComponentList.add(groupMappings.getTaxComponent().getId());
|
||||
} else {
|
||||
mappings.update(groupMappings.getEndDate(), modifications);
|
||||
}
|
||||
}
|
||||
|
||||
if (!taxComponentList.isEmpty()) {
|
||||
changes.put("addComponents", taxComponentList);
|
||||
}
|
||||
if (!modifications.isEmpty()) {
|
||||
changes.put("modifiedComponents", modifications);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public TaxGroupMappings findOneBy(final TaxGroupMappings groupMapping) {
|
||||
if (groupMapping.getId() != null) {
|
||||
for (TaxGroupMappings groupMappings : this.taxGroupMappings) {
|
||||
if (groupMappings.getId().equals(groupMapping.getId())) { return groupMappings; }
|
||||
}
|
||||
throw new TaxMappingNotFoundException(groupMapping.getId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<TaxGroupMappings> getTaxGroupMappings() {
|
||||
return this.taxGroupMappings;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_tax_group_mappings")
|
||||
public class TaxGroupMappings extends AbstractAuditableCustom<AppUser, Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "tax_component_id", nullable = false)
|
||||
private TaxComponent taxComponent;
|
||||
|
||||
@Column(name = "start_date", nullable = false)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date startDate;
|
||||
|
||||
@Column(name = "end_date", nullable = true)
|
||||
@Temporal(TemporalType.DATE)
|
||||
private Date endDate;
|
||||
|
||||
protected TaxGroupMappings() {}
|
||||
|
||||
private TaxGroupMappings(final TaxComponent taxComponent, final LocalDate startDate, final LocalDate endDate) {
|
||||
|
||||
this.taxComponent = taxComponent;
|
||||
if (startDate != null) {
|
||||
this.startDate = startDate.toDate();
|
||||
}
|
||||
if (endDate != null) {
|
||||
this.endDate = endDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public static TaxGroupMappings createTaxGroupMappings(final TaxComponent taxComponent, final LocalDate startDate) {
|
||||
final LocalDate endDate = null;
|
||||
return new TaxGroupMappings(taxComponent, startDate, endDate);
|
||||
|
||||
}
|
||||
|
||||
public static TaxGroupMappings createTaxGroupMappings(final Long id, final TaxComponent taxComponent, final LocalDate endDate) {
|
||||
final LocalDate startDate = null;
|
||||
TaxGroupMappings groupMappings = new TaxGroupMappings(taxComponent, startDate, endDate);
|
||||
groupMappings.setId(id);
|
||||
return groupMappings;
|
||||
|
||||
}
|
||||
|
||||
public void update(final Date endDate, final List<Map<String, Object>> changes) {
|
||||
if (endDate != null && this.endDate == null) {
|
||||
this.endDate = endDate;
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
map.put(TaxApiConstants.endDateParamName, endDate);
|
||||
map.put(TaxApiConstants.taxComponentIdParamName, this.getTaxComponent().getId());
|
||||
changes.add(map);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
|
||||
if (this.endDate == null) { return target != null && target.isAfter(startDate()); }
|
||||
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
|
||||
}
|
||||
|
||||
public TaxComponent getTaxComponent() {
|
||||
return this.taxComponent;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return this.endDate;
|
||||
}
|
||||
|
||||
public LocalDate startDate() {
|
||||
LocalDate startDate = null;
|
||||
if (this.startDate != null) {
|
||||
startDate = new LocalDate(this.startDate);
|
||||
}
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public LocalDate endDate() {
|
||||
LocalDate endDate = null;
|
||||
if (this.endDate != null) {
|
||||
endDate = new LocalDate(this.endDate);
|
||||
}
|
||||
return endDate;
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface TaxGroupRepository extends JpaRepository<TaxGroup, Long>, JpaSpecificationExecutor<TaxGroup> {
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.portfolio.tax.domain;
|
||||
|
||||
import org.apache.fineract.portfolio.tax.exception.TaxGroupNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TaxGroupRepositoryWrapper {
|
||||
|
||||
private final TaxGroupRepository repository;
|
||||
|
||||
@Autowired
|
||||
public TaxGroupRepositoryWrapper(final TaxGroupRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public TaxGroup findOneWithNotFoundDetection(final Long id) {
|
||||
|
||||
final TaxGroup taxGroup = this.repository.findOne(id);
|
||||
if (taxGroup == null) { throw new TaxGroupNotFoundException(id); }
|
||||
|
||||
return taxGroup;
|
||||
}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.portfolio.tax.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class TaxComponentNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public TaxComponentNotFoundException(final Long id) {
|
||||
super("error.msg.tax.component.id.invalid", "tax component with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.portfolio.tax.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class TaxGroupNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public TaxGroupNotFoundException(final Long id) {
|
||||
super("error.msg.tax.group.id.invalid", "tax group with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.portfolio.tax.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* {@link AbstractPlatformDomainRuleException} thrown when tax group mapping
|
||||
* does not exist.
|
||||
*/
|
||||
public class TaxMappingNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public TaxMappingNotFoundException(final Long id) {
|
||||
super("error.msg.tax.group.id.invalid", "Tax group mapping with identifier " + id + " does not exist", id);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -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.portfolio.tax.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "TAXCOMPONENT", action = "CREATE")
|
||||
public class CreateTaxComponentCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final TaxWritePlatformService taxWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateTaxComponentCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
|
||||
this.taxWritePlatformService = taxWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.taxWritePlatformService.createTaxComponent(jsonCommand);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -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.portfolio.tax.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "TAXGROUP", action = "CREATE")
|
||||
public class CreateTaxGroupCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final TaxWritePlatformService taxWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateTaxGroupCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
|
||||
this.taxWritePlatformService = taxWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.taxWritePlatformService.createTaxGroup(jsonCommand);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -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.portfolio.tax.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "TAXCOMPONENT", action = "UPDATE")
|
||||
public class UpdateTaxComponentCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final TaxWritePlatformService taxWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateTaxComponentCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
|
||||
this.taxWritePlatformService = taxWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.taxWritePlatformService.updateTaxComponent(jsonCommand.entityId(), jsonCommand);
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -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.portfolio.tax.handler;
|
||||
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.tax.service.TaxWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "TAXGROUP", action = "UPDATE")
|
||||
public class UpdateTaxGroupCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final TaxWritePlatformService taxWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateTaxGroupCommandHandler(final TaxWritePlatformService taxWritePlatformService) {
|
||||
this.taxWritePlatformService = taxWritePlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(JsonCommand jsonCommand) {
|
||||
return this.taxWritePlatformService.updateTaxGroup(jsonCommand.entityId(), jsonCommand);
|
||||
}
|
||||
|
||||
}
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
* 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.portfolio.tax.serialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public class TaxValidator {
|
||||
|
||||
final Set<String> supportedTaxComponentCreateParameters = new HashSet<>(Arrays.asList("dateFormat", "locale",
|
||||
TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName,
|
||||
TaxApiConstants.debitAccountTypeParamName, TaxApiConstants.debitAcountIdParamName, TaxApiConstants.creditAccountTypeParamName,
|
||||
TaxApiConstants.creditAcountIdParamName));
|
||||
|
||||
final Set<String> supportedTaxComponentUpdateParameters = new HashSet<>(Arrays.asList("dateFormat", "locale",
|
||||
TaxApiConstants.nameParamName, TaxApiConstants.percentageParamName, TaxApiConstants.startDateParamName));
|
||||
|
||||
final Set<String> supportedTaxGroupParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", TaxApiConstants.nameParamName,
|
||||
TaxApiConstants.taxComponentsParamName));
|
||||
|
||||
final Set<String> supportedTaxGroupTaxComponentsCreateParameters = new HashSet<>(Arrays.asList(TaxApiConstants.taxComponentIdParamName,
|
||||
TaxApiConstants.startDateParamName));
|
||||
|
||||
final Set<String> supportedTaxGroupTaxComponentsUpdateParameters = new HashSet<>(Arrays.asList(TaxApiConstants.idParamName,
|
||||
TaxApiConstants.taxComponentIdParamName, TaxApiConstants.startDateParamName, TaxApiConstants.endDateParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public TaxValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateForTaxComponentCreate(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxComponentCreateParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.component");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
|
||||
|
||||
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank().positiveAmount()
|
||||
.notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
|
||||
final Integer debitAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
|
||||
element);
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(TaxApiConstants.debitAccountTypeParamName)
|
||||
.value(debitAccountType)
|
||||
.ignoreIfNull()
|
||||
.isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
|
||||
GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue());
|
||||
|
||||
final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).longGreaterThanZero();
|
||||
if (debitAccountType != null || debitAccountId != null) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.debitAccountTypeParamName).value(debitAccountType).notBlank();
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId).notBlank();
|
||||
}
|
||||
|
||||
final Integer creditAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName,
|
||||
element);
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(TaxApiConstants.creditAccountTypeParamName)
|
||||
.value(creditAccountType)
|
||||
.ignoreIfNull()
|
||||
.isOneOfTheseValues(GLAccountType.ASSET.getValue(), GLAccountType.LIABILITY.getValue(), GLAccountType.EQUITY.getValue(),
|
||||
GLAccountType.INCOME.getValue(), GLAccountType.EXPENSE.getValue());
|
||||
|
||||
final Long creditAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).longGreaterThanZero();
|
||||
if (creditAccountType != null || creditAccountId != null) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId).notBlank();
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.creditAccountTypeParamName).value(creditAccountType).notBlank();
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForTaxComponentUpdate(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxComponentUpdateParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.component");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) {
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.percentageParamName, element)) {
|
||||
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.percentageParamName).value(percentage).notBlank().positiveAmount()
|
||||
.notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.startDateParamName, element)) {
|
||||
final LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate)
|
||||
.validateDateAfter(DateUtils.getLocalDateOfTenant());
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForTaxGroupCreate(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxGroupParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.group");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
|
||||
|
||||
final JsonArray taxComponents = this.fromApiJsonHelper.extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents).notBlank();
|
||||
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).getAsJsonArray();
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(array.size()).integerGreaterThanZero();
|
||||
for (int i = 1; i <= array.size(); i++) {
|
||||
final JsonObject taxComponent = array.get(i - 1).getAsJsonObject();
|
||||
final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent);
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson,
|
||||
supportedTaxGroupTaxComponentsCreateParameters);
|
||||
final Long taxComponentId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName)
|
||||
.parameterAtIndexArray(TaxApiConstants.taxComponentIdParamName, i).value(taxComponentId).notNull()
|
||||
.longGreaterThanZero();
|
||||
|
||||
}
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForTaxGroupUpdate(final String json) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedTaxGroupParameters);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.group");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.nameParamName, element)) {
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.nameParamName).value(name).notBlank();
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(TaxApiConstants.taxComponentsParamName, element)) {
|
||||
final JsonArray taxComponents = this.fromApiJsonHelper.extractJsonArrayNamed(TaxApiConstants.taxComponentsParamName, element);
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName).value(taxComponents).notBlank();
|
||||
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).getAsJsonArray();
|
||||
for (int i = 1; i <= array.size(); i++) {
|
||||
final JsonObject taxComponent = array.get(i - 1).getAsJsonObject();
|
||||
final String arrayObjectJson = this.fromApiJsonHelper.toJson(taxComponent);
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, arrayObjectJson,
|
||||
supportedTaxGroupTaxComponentsUpdateParameters);
|
||||
final Long taxComponentId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName,
|
||||
taxComponent);
|
||||
final Long taxMappingId = this.fromApiJsonHelper
|
||||
.extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent);
|
||||
if (taxMappingId == null) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(
|
||||
TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.taxComponentIdParamName
|
||||
+ ".at.index." + i).value(taxComponentId).notNull().longGreaterThanZero();
|
||||
} else {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(
|
||||
TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.taxComponentIdParamName
|
||||
+ ".at.index." + i).value(taxComponentId).longGreaterThanZero();
|
||||
baseDataValidator.reset()
|
||||
.parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.idParamName + ".at.index." + i)
|
||||
.value(taxMappingId).longGreaterThanZero();
|
||||
}
|
||||
|
||||
final LocalDate endDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.endDateParamName, taxComponent,
|
||||
dateFormat, locale);
|
||||
baseDataValidator.reset()
|
||||
.parameter(TaxApiConstants.taxComponentsParamName + "." + TaxApiConstants.endDateParamName + ".at.index." + i)
|
||||
.value(endDate).ignoreIfNull().validateDateAfter(DateUtils.getLocalDateOfTenant());
|
||||
final LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName,
|
||||
taxComponent, dateFormat, locale);
|
||||
if (endDate != null && startDate != null) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentsParamName + ".at.index." + i)
|
||||
.failWithCode("start.date.end.date.both.should.not.be.present", startDate, endDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateTaxGroupEndDateAndTaxComponent(final TaxGroup taxGroup, final List<TaxGroupMappings> groupMappings) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.group");
|
||||
|
||||
for (TaxGroupMappings mapping : groupMappings) {
|
||||
if (mapping.getId() != null) {
|
||||
TaxGroupMappings existing = taxGroup.findOneBy(mapping);
|
||||
if (existing.endDate() != null && mapping.endDate() != null && !existing.endDate().isEqual(mapping.endDate())) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName)
|
||||
.failWithCode("can.not.modify.end.date.once.updated");
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName).value(mapping.endDate()).ignoreIfNull()
|
||||
.validateDateAfter(existing.startDate());
|
||||
}
|
||||
if(mapping.getTaxComponent()!= null && !existing.getTaxComponent().getId().equals(mapping.getTaxComponent().getId())){
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.taxComponentIdParamName).failWithCode("update.not.supported");;
|
||||
}
|
||||
} else if (mapping.endDate() != null) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.endDateParamName).failWithCode("not.supported.for.new.association");
|
||||
}
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateTaxGroup(final TaxGroup taxGroup) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.group");
|
||||
List<TaxGroupMappings> groupMappings = taxGroup.getTaxGroupMappings();
|
||||
validateGroupTotal(groupMappings, baseDataValidator, "total.percentage");
|
||||
validateOverlappingComponents(groupMappings, baseDataValidator);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateTaxComponentForUpdate(final TaxComponent taxComponent) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.component");
|
||||
validateGroupTotal(taxComponent.getTaxGroupMappings(), baseDataValidator, "group.total." + TaxApiConstants.percentageParamName);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateStartDate(final LocalDate existingStartDate, final JsonCommand command) {
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.component");
|
||||
validateStartDate(existingStartDate, command.localDateValueOfParameterNamed(TaxApiConstants.startDateParamName), baseDataValidator);
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void validateStartDate(final LocalDate existingStartDate, final LocalDate startDate,
|
||||
final DataValidatorBuilder baseDataValidator) {
|
||||
baseDataValidator.reset().parameter(TaxApiConstants.startDateParamName).value(startDate).validateDateAfter(existingStartDate);
|
||||
}
|
||||
|
||||
private void validateOverlappingComponents(final List<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator) {
|
||||
for (TaxGroupMappings groupMappingsOne : taxMappings) {
|
||||
final List<TaxGroupMappings> mappings = new ArrayList<>(taxMappings);
|
||||
mappings.remove(groupMappingsOne);
|
||||
for (TaxGroupMappings groupMappings : mappings) {
|
||||
if (groupMappingsOne.getTaxComponent().equals(groupMappings.getTaxComponent())) {
|
||||
if (groupMappingsOne.endDate() == null && groupMappings.endDate() == null) {
|
||||
baseDataValidator.reset().parameter("component").failWithCode("dates.are.overlapping");
|
||||
} else if (groupMappingsOne.startDate().isAfter(groupMappings.startDate())) {
|
||||
baseDataValidator.reset().parameter("component.start.date").value(groupMappingsOne.startDate())
|
||||
.validateDateAfter(groupMappings.endDate());
|
||||
} else {
|
||||
baseDataValidator.reset().parameter("component.start.date").value(groupMappings.startDate())
|
||||
.validateDateAfter(groupMappingsOne.endDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGroupTotal(final List<TaxGroupMappings> taxMappings, final DataValidatorBuilder baseDataValidator,
|
||||
final String paramenter) {
|
||||
for (TaxGroupMappings groupMappingsOne : taxMappings) {
|
||||
Collection<LocalDate> dates = groupMappingsOne.getTaxComponent().allStartDates();
|
||||
for (LocalDate date : dates) {
|
||||
LocalDate applicableDate = date.plusDays(1);
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (TaxGroupMappings groupMappings : taxMappings) {
|
||||
if (groupMappings.occursOnDayFromAndUpToAndIncluding(applicableDate)) {
|
||||
BigDecimal applicablePercentage = groupMappings.getTaxComponent().getApplicablePercentage(applicableDate);
|
||||
if (applicablePercentage != null) {
|
||||
total = total.add(applicablePercentage);
|
||||
}
|
||||
}
|
||||
}
|
||||
baseDataValidator.reset().parameter(paramenter).value(total).notGreaterThanMax(BigDecimal.valueOf(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* 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.portfolio.tax.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponentRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroup;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Component
|
||||
public class TaxAssembler {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final GLAccountRepositoryWrapper glAccountRepositoryWrapper;
|
||||
private final TaxComponentRepositoryWrapper taxComponentRepositoryWrapper;
|
||||
|
||||
@Autowired
|
||||
public TaxAssembler(final FromJsonHelper fromApiJsonHelper, final GLAccountRepositoryWrapper glAccountRepositoryWrapper,
|
||||
final TaxComponentRepositoryWrapper taxComponentRepositoryWrapper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.glAccountRepositoryWrapper = glAccountRepositoryWrapper;
|
||||
this.taxComponentRepositoryWrapper = taxComponentRepositoryWrapper;
|
||||
}
|
||||
|
||||
public TaxComponent assembleTaxComponentFrom(final JsonCommand command) {
|
||||
final JsonElement element = command.parsedJson();
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("tax.component");
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
final BigDecimal percentage = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TaxApiConstants.percentageParamName, element);
|
||||
final Integer debitAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.debitAccountTypeParamName,
|
||||
element);
|
||||
final Long debitAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.debitAcountIdParamName, element);
|
||||
GLAccountType debitGlAccountType = null;
|
||||
if (debitAccountType != null) {
|
||||
debitGlAccountType = GLAccountType.fromInt(debitAccountType);
|
||||
}
|
||||
GLAccount debitGlAccount = null;
|
||||
if (debitAccountId != null) {
|
||||
debitGlAccount = this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(debitAccountId);
|
||||
if (!debitGlAccount.getType().equals(debitAccountType) || debitGlAccount.isHeaderAccount()) {
|
||||
baseDataValidator.parameter(TaxApiConstants.debitAcountIdParamName).value(debitAccountId)
|
||||
.failWithCode("not.a.valid.account");
|
||||
}
|
||||
}
|
||||
|
||||
final Integer creditAccountType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(TaxApiConstants.creditAccountTypeParamName,
|
||||
element);
|
||||
GLAccountType creditGlAccountType = null;
|
||||
if (creditAccountType != null) {
|
||||
creditGlAccountType = GLAccountType.fromInt(creditAccountType);
|
||||
}
|
||||
final Long creditAccountId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.creditAcountIdParamName, element);
|
||||
GLAccount creditGlAccount = null;
|
||||
if (creditAccountId != null) {
|
||||
creditGlAccount = this.glAccountRepositoryWrapper.findOneWithNotFoundDetection(creditAccountId);
|
||||
if (!creditGlAccount.getType().equals(creditAccountType) || creditGlAccount.isHeaderAccount()) {
|
||||
baseDataValidator.parameter(TaxApiConstants.creditAcountIdParamName).value(creditAccountId)
|
||||
.failWithCode("not.a.valid.account");
|
||||
}
|
||||
}
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName, element);
|
||||
if (startDate == null) {
|
||||
startDate = DateUtils.getLocalDateOfTenant();
|
||||
}
|
||||
|
||||
return TaxComponent.createTaxComponent(name, percentage, debitGlAccountType, debitGlAccount, creditGlAccountType, creditGlAccount,
|
||||
startDate);
|
||||
}
|
||||
|
||||
public TaxGroup assembleTaxGroupFrom(final JsonCommand command) {
|
||||
final JsonElement element = command.parsedJson();
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(TaxApiConstants.nameParamName, element);
|
||||
boolean isUpdate = false;
|
||||
final List<TaxGroupMappings> groupMappings = assembleTaxGroupMappingsFrom(command, isUpdate);
|
||||
return TaxGroup.createTaxGroup(name, groupMappings);
|
||||
}
|
||||
|
||||
public List<TaxGroupMappings> assembleTaxGroupMappingsFrom(final JsonCommand command, boolean isUpdate) {
|
||||
List<TaxGroupMappings> groupMappings = new ArrayList<>();
|
||||
final JsonElement element = command.parsedJson();
|
||||
|
||||
final JsonObject topLevelJsonElement = element.getAsJsonObject();
|
||||
final String dateFormat = this.fromApiJsonHelper.extractDateFormatParameter(topLevelJsonElement);
|
||||
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
|
||||
|
||||
if (topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).isJsonArray()) {
|
||||
final JsonArray array = topLevelJsonElement.get(TaxApiConstants.taxComponentsParamName).getAsJsonArray();
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
final JsonObject taxComponent = array.get(i).getAsJsonObject();
|
||||
final Long mappingId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.idParamName, taxComponent);
|
||||
final Long taxComponentId = this.fromApiJsonHelper.extractLongNamed(TaxApiConstants.taxComponentIdParamName, taxComponent);
|
||||
TaxComponent component = null;
|
||||
if(taxComponentId != null){
|
||||
component = this.taxComponentRepositoryWrapper.findOneWithNotFoundDetection(taxComponentId);
|
||||
}
|
||||
LocalDate startDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.startDateParamName, taxComponent,
|
||||
dateFormat, locale);
|
||||
final LocalDate endDate = this.fromApiJsonHelper.extractLocalDateNamed(TaxApiConstants.endDateParamName, taxComponent,
|
||||
dateFormat, locale);
|
||||
if (endDate == null && startDate == null) {
|
||||
startDate = DateUtils.getLocalDateOfTenant();
|
||||
}
|
||||
TaxGroupMappings mappings = null;
|
||||
if (isUpdate && mappingId != null) {
|
||||
mappings = TaxGroupMappings.createTaxGroupMappings(mappingId, component, endDate);
|
||||
} else {
|
||||
mappings = TaxGroupMappings.createTaxGroupMappings(component, startDate);
|
||||
}
|
||||
groupMappings.add(mappings);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return groupMappings;
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.portfolio.tax.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
|
||||
public interface TaxReadPlatformService {
|
||||
|
||||
public TaxComponentData retrieveTaxComponentData(final Long id);
|
||||
|
||||
public TaxComponentData retrieveTaxComponentTemplate();
|
||||
|
||||
public TaxGroupData retrieveTaxGroupData(final Long id);
|
||||
|
||||
public TaxGroupData retrieveTaxGroupWithTemplate(final Long id);
|
||||
|
||||
public TaxGroupData retrieveTaxGroupTemplate();
|
||||
|
||||
Collection<TaxComponentData> retrieveAllTaxComponents();
|
||||
|
||||
Collection<TaxGroupData> retrieveAllTaxGroups();
|
||||
|
||||
Collection<TaxGroupData> retrieveTaxGroupsForLookUp();
|
||||
|
||||
}
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
/**
|
||||
* 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.portfolio.tax.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.fineract.accounting.common.AccountingDropdownReadPlatformService;
|
||||
import org.apache.fineract.accounting.common.AccountingEnumerations;
|
||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
|
||||
import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxComponentData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxComponentHistoryData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
import org.apache.fineract.portfolio.tax.data.TaxGroupMappingsData;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TaxReadPlatformServiceImpl implements TaxReadPlatformService {
|
||||
|
||||
final TaxComponentMapper taxComponentMapper = new TaxComponentMapper();
|
||||
final TaxGroupMapper taxGroupMapper = new TaxGroupMapper();
|
||||
final TaxComponentLookUpMapper taxComponentLookUpMapper = new TaxComponentLookUpMapper();
|
||||
final TaxGroupLookUpMapper taxGroupLookUpMapper = new TaxGroupLookUpMapper();
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public TaxReadPlatformServiceImpl(final RoutingDataSource dataSource,
|
||||
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TaxComponentData> retrieveAllTaxComponents() {
|
||||
String sql = "select " + this.taxComponentMapper.getSchema();
|
||||
return this.jdbcTemplate.query(sql, this.taxComponentMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxComponentData retrieveTaxComponentData(final Long id) {
|
||||
String sql = "select " + this.taxComponentMapper.getSchema() + " where tc.id=?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.taxComponentMapper, new Object[] { id });
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxComponentData retrieveTaxComponentTemplate() {
|
||||
return TaxComponentData.template(this.accountingDropdownReadPlatformService.retrieveAccountMappingOptions(),
|
||||
this.accountingDropdownReadPlatformService.retrieveGLAccountTypeOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TaxGroupData> retrieveAllTaxGroups() {
|
||||
String sql = "select " + this.taxGroupMapper.getSchema();
|
||||
return this.jdbcTemplate.query(sql, this.taxGroupMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxGroupData retrieveTaxGroupData(final Long id) {
|
||||
String sql = "select " + this.taxGroupMapper.getSchema() + " where tg.id=?";
|
||||
return this.jdbcTemplate.queryForObject(sql, this.taxGroupMapper, new Object[] { id });
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxGroupData retrieveTaxGroupWithTemplate(final Long id) {
|
||||
TaxGroupData taxGroupData = retrieveTaxGroupData(id);
|
||||
taxGroupData = TaxGroupData.template(taxGroupData, retrieveTaxComponentsForLookUp());
|
||||
return taxGroupData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxGroupData retrieveTaxGroupTemplate() {
|
||||
return TaxGroupData.template(retrieveTaxComponentsForLookUp());
|
||||
}
|
||||
|
||||
private Collection<TaxComponentData> retrieveTaxComponentsForLookUp() {
|
||||
String sql = "select " + this.taxComponentLookUpMapper.getSchema();
|
||||
return this.jdbcTemplate.query(sql, this.taxComponentLookUpMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TaxGroupData> retrieveTaxGroupsForLookUp() {
|
||||
String sql = "select " + this.taxGroupLookUpMapper.getSchema();
|
||||
return this.jdbcTemplate.query(sql, this.taxGroupLookUpMapper);
|
||||
}
|
||||
|
||||
private static final class TaxComponentMapper implements RowMapper<TaxComponentData> {
|
||||
|
||||
private final String schema;
|
||||
private TaxComponentHistoryDataMapper componentHistoryDataMapper = new TaxComponentHistoryDataMapper();
|
||||
|
||||
public TaxComponentMapper() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("tc.id as id, tc.name as name,");
|
||||
sb.append("tc.percentage as percentage, tc.start_date as startDate,");
|
||||
sb.append("tc.debit_account_type_enum as debitAccountTypeEnum,");
|
||||
sb.append("dgl.id as debitAccountId, dgl.name as debitAccountName, dgl.gl_code as debitAccountGlCode,");
|
||||
sb.append("tc.credit_account_type_enum as creditAccountTypeEnum,");
|
||||
sb.append("cgl.id as creditAccountId, cgl.name as creditAccountName, cgl.gl_code as creditAccountGlCode,");
|
||||
sb.append("history.percentage as historyPercentage, history.start_date as historyStartDate,");
|
||||
sb.append("history.end_date as historyEndDate");
|
||||
sb.append(" from m_tax_component tc ");
|
||||
sb.append(" left join acc_gl_account dgl on dgl.id = tc.debit_account_id");
|
||||
sb.append(" left join acc_gl_account cgl on cgl.id = tc.credit_account_id");
|
||||
sb.append(" left join m_tax_component_history history on history.tax_component_id = tc.id");
|
||||
|
||||
this.schema = sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxComponentData mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
final BigDecimal percentage = rs.getBigDecimal("percentage");
|
||||
final Integer debitAccountTypeEnum = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "debitAccountTypeEnum");
|
||||
EnumOptionData debitAccountType = null;
|
||||
if (debitAccountTypeEnum != null) {
|
||||
debitAccountType = AccountingEnumerations.gLAccountType(debitAccountTypeEnum);
|
||||
}
|
||||
GLAccountData debitAccountData = null;
|
||||
if (debitAccountTypeEnum != null && debitAccountTypeEnum > 0) {
|
||||
final Long debitAccountId = rs.getLong("debitAccountId");
|
||||
final String debitAccountName = rs.getString("debitAccountName");
|
||||
final String debitAccountGlCode = rs.getString("debitAccountGlCode");
|
||||
debitAccountData = new GLAccountData(debitAccountId, debitAccountName, debitAccountGlCode);
|
||||
}
|
||||
|
||||
final Integer creditAccountTypeEnum = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "creditAccountTypeEnum");
|
||||
EnumOptionData creditAccountType = null;
|
||||
if (creditAccountTypeEnum != null) {
|
||||
creditAccountType = AccountingEnumerations.gLAccountType(creditAccountTypeEnum);
|
||||
}
|
||||
GLAccountData creditAccountData = null;
|
||||
if (creditAccountTypeEnum != null && creditAccountTypeEnum > 0) {
|
||||
final Long creditAccountId = rs.getLong("creditAccountId");
|
||||
final String creditAccountName = rs.getString("creditAccountName");
|
||||
final String creditAccountGlCode = rs.getString("creditAccountGlCode");
|
||||
creditAccountData = new GLAccountData(creditAccountId, creditAccountName, creditAccountGlCode);
|
||||
}
|
||||
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
|
||||
|
||||
Collection<TaxComponentHistoryData> historyDatas = new ArrayList<>();
|
||||
historyDatas.add(componentHistoryDataMapper.mapRow(rs, rowNum));
|
||||
while (rs.next()) {
|
||||
if (id.equals(rs.getLong("id"))) {
|
||||
historyDatas.add(componentHistoryDataMapper.mapRow(rs, rowNum));
|
||||
} else {
|
||||
rs.previous();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TaxComponentData.instance(id, name, percentage, debitAccountType, debitAccountData, creditAccountType,
|
||||
creditAccountData, startDate, historyDatas);
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TaxComponentHistoryDataMapper implements RowMapper<TaxComponentHistoryData> {
|
||||
|
||||
@Override
|
||||
public TaxComponentHistoryData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
final BigDecimal percentage = rs.getBigDecimal("historyPercentage");
|
||||
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "historyStartDate");
|
||||
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "historyEndDate");
|
||||
return new TaxComponentHistoryData(percentage, startDate, endDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TaxGroupMapper implements RowMapper<TaxGroupData> {
|
||||
|
||||
private final String schema;
|
||||
private final TaxGroupMappingsDataMapper taxGroupMappingsDataMapper = new TaxGroupMappingsDataMapper();
|
||||
|
||||
public TaxGroupMapper() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("tg.id as id, tg.name as name,");
|
||||
sb.append("tgm.id as mappingId,");
|
||||
sb.append("tc.id as taxComponentId, tc.name as taxComponentName,");
|
||||
sb.append("tgm.start_date as startDate, tgm.end_date as endDate ");
|
||||
sb.append(" from m_tax_group tg ");
|
||||
sb.append(" inner join m_tax_group_mappings tgm on tgm.tax_group_id = tg.id ");
|
||||
sb.append(" inner join m_tax_component tc on tc.id = tgm.tax_component_id ");
|
||||
this.schema = sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxGroupData mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
final Collection<TaxGroupMappingsData> taxAssociations = new ArrayList<>();
|
||||
taxAssociations.add(this.taxGroupMappingsDataMapper.mapRow(rs, rowNum));
|
||||
while (rs.next()) {
|
||||
if (id.equals(rs.getLong("id"))) {
|
||||
taxAssociations.add(this.taxGroupMappingsDataMapper.mapRow(rs, rowNum));
|
||||
} else {
|
||||
rs.previous();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TaxGroupData.instance(id, name, taxAssociations);
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TaxGroupMappingsDataMapper implements RowMapper<TaxGroupMappingsData> {
|
||||
|
||||
@Override
|
||||
public TaxGroupMappingsData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
final Long mappingId = rs.getLong("mappingId");
|
||||
final Long id = rs.getLong("taxComponentId");
|
||||
final String name = rs.getString("taxComponentName");
|
||||
TaxComponentData componentData = TaxComponentData.lookup(id, name);
|
||||
|
||||
final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate");
|
||||
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
|
||||
return new TaxGroupMappingsData(mappingId, componentData, startDate, endDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TaxComponentLookUpMapper implements RowMapper<TaxComponentData> {
|
||||
|
||||
private final String schema;
|
||||
|
||||
public TaxComponentLookUpMapper() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("tc.id as id, tc.name as name ");
|
||||
sb.append(" from m_tax_component tc ");
|
||||
this.schema = sb.toString();
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxComponentData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
return TaxComponentData.lookup(id, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class TaxGroupLookUpMapper implements RowMapper<TaxGroupData> {
|
||||
|
||||
private final String schema;
|
||||
|
||||
public TaxGroupLookUpMapper() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("tg.id as id, tg.name as name ");
|
||||
sb.append(" from m_tax_group tg ");
|
||||
this.schema = sb.toString();
|
||||
}
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaxGroupData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
return TaxGroupData.lookup(id, name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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.portfolio.tax.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
|
||||
import org.apache.fineract.portfolio.tax.domain.TaxGroupMappings;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class TaxUtils {
|
||||
|
||||
public static Map<TaxComponent, BigDecimal> splitTax(final BigDecimal amount, final LocalDate date,
|
||||
final List<TaxGroupMappings> taxGroupMappings, final int scale) {
|
||||
Map<TaxComponent, BigDecimal> map = new HashMap<>(3);
|
||||
if (amount != null) {
|
||||
final double amountVal = amount.doubleValue();
|
||||
double cent_percentage = Double.valueOf("100.0");
|
||||
for (TaxGroupMappings groupMappings : taxGroupMappings) {
|
||||
if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
|
||||
TaxComponent component = groupMappings.getTaxComponent();
|
||||
BigDecimal percentage = component.getApplicablePercentage(date);
|
||||
if (percentage != null) {
|
||||
double percentageVal = percentage.doubleValue();
|
||||
double tax = amountVal * percentageVal / cent_percentage;
|
||||
map.put(component, BigDecimal.valueOf(tax).setScale(scale, MoneyHelper.getRoundingMode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static BigDecimal incomeAmount(final BigDecimal amount, final LocalDate date, final List<TaxGroupMappings> taxGroupMappings,
|
||||
final int scale) {
|
||||
Map<TaxComponent, BigDecimal> map = splitTax(amount, date, taxGroupMappings, scale);
|
||||
return incomeAmount(amount, map);
|
||||
}
|
||||
|
||||
public static BigDecimal incomeAmount(final BigDecimal amount, final Map<TaxComponent, BigDecimal> map) {
|
||||
BigDecimal totalTax = totalTaxAmount(map);
|
||||
return amount.subtract(totalTax);
|
||||
}
|
||||
|
||||
public static BigDecimal totalTaxAmount(final Map<TaxComponent, BigDecimal> map) {
|
||||
BigDecimal totalTax = BigDecimal.ZERO;
|
||||
for (BigDecimal tax : map.values()) {
|
||||
totalTax = totalTax.add(tax);
|
||||
}
|
||||
return totalTax;
|
||||
}
|
||||
|
||||
public static BigDecimal addTax(final BigDecimal amount, final LocalDate date, final List<TaxGroupMappings> taxGroupMappings,
|
||||
final int scale) {
|
||||
BigDecimal totalAmount = null;
|
||||
if (amount != null && amount.compareTo(BigDecimal.ZERO) == 1) {
|
||||
double percentageVal = 0;
|
||||
double amountVal = amount.doubleValue();
|
||||
double cent_percentage = Double.valueOf("100.0");
|
||||
for (TaxGroupMappings groupMappings : taxGroupMappings) {
|
||||
if (groupMappings.occursOnDayFromAndUpToAndIncluding(date)) {
|
||||
TaxComponent component = groupMappings.getTaxComponent();
|
||||
BigDecimal percentage = component.getApplicablePercentage(date);
|
||||
if (percentage != null) {
|
||||
percentageVal = percentageVal + percentage.doubleValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
double total = amountVal * cent_percentage / (cent_percentage - percentageVal);
|
||||
totalAmount = BigDecimal.valueOf(total).setScale(scale, MoneyHelper.getRoundingMode());
|
||||
}
|
||||
return totalAmount;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user