feat: fix standing instructions GL accounting for current account transfers
- AccountTransfersWritePlatformServiceImpl:
* Direct GL posting for current↔savings/current transfers:
DR debit-side account GL (with correct account entity_id)
CR credit-side account GL (with correct account entity_id)
* Prefix current account txn_id with CA (not C) so
JournalEntryReadPlatformServiceImpl joins m_current_account
and shows correct current account number on GL entries
* Suppress redundant savings deposit/withdrawal GL entries
(skipAccountingPostings=true) when current account handles
the full double-entry directly
* Separate performCurrentWithdrawalForLoan for Current→Loan
transfers (loan domain service handles the other GL leg)
* Reuse existing AccountTransferDetails for SI recurring runs
so transactions appear in list-account-transactions history
- SavingsAccountDomainService / SavingsAccountDomainServiceJpa:
* New handleDeposit overload with skipAccountingPostings flag
* handleWithdrawal respects SavingsTransactionBooleanValues
.isSkipAccountingPostings() to suppress redundant GL entries
- SavingsTransactionBooleanValues:
* Added skipAccountingPostings field + overloaded constructor
- AccountingProcessorHelper:
* New createDirectTransferJournalEntriesForSavings() method
creates DR and CR entries with separate productId/entityId
so each GL line shows the correct account number
- JournalEntryReadPlatformServiceImpl:
* Join both m_savings_account and m_current_account always
* COALESCE(ca.account_no, sa.account_no) to resolve correct
account number regardless of transaction_id prefix
- ExecuteStandingInstructionsTasklet:
* Load existing AccountTransferDetails via repository so
subsequent SI runs link to the same transfer detail record
- ExecuteStandingInstructionsConfig / AccountConfiguration:
* Wire AccountTransferDetailRepository and
AccountingProcessorHelper into the relevant beans
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2251fd2259
commit
7d4f409202
+21
@@ -600,6 +600,27 @@ public class AccountingProcessorHelper {
|
|||||||
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, savingsId, transactionId, transactionDate, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a direct transfer journal entry between two savings-type accounts (savings or current),
|
||||||
|
* each with their own product GL mapping and entity ID so the correct account number appears per entry.
|
||||||
|
*
|
||||||
|
* @param debitProductId product ID whose SAVINGS_CONTROL GL is debited
|
||||||
|
* @param debitEntityId entity (account) ID shown on the debit entry
|
||||||
|
* @param creditProductId product ID whose SAVINGS_CONTROL GL is credited
|
||||||
|
* @param creditEntityId entity (account) ID shown on the credit entry
|
||||||
|
*/
|
||||||
|
public void createDirectTransferJournalEntriesForSavings(final Office office, final String currencyCode,
|
||||||
|
final Long debitProductId, final Long debitEntityId,
|
||||||
|
final Long creditProductId, final Long creditEntityId,
|
||||||
|
final String transactionId, final LocalDate transactionDate, final BigDecimal amount) {
|
||||||
|
final GLAccount debitAccount = getLinkedGLAccountForSavingsProduct(debitProductId,
|
||||||
|
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), null);
|
||||||
|
final GLAccount creditAccount = getLinkedGLAccountForSavingsProduct(creditProductId,
|
||||||
|
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), null);
|
||||||
|
createDebitJournalEntryForSavings(office, currencyCode, debitAccount, debitEntityId, transactionId, transactionDate, amount);
|
||||||
|
createCreditJournalEntryForSavings(office, currencyCode, creditAccount, creditEntityId, transactionId, transactionDate, amount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that creates a pair of related Debits and Credits for Cash Based accounting.
|
* Convenience method that creates a pair of related Debits and Credits for Cash Based accounting.
|
||||||
*
|
*
|
||||||
|
|||||||
+3
-4
@@ -106,8 +106,7 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ")
|
.append(" curr.display_symbol as currencyDisplaySymbol, curr.decimal_places as currencyDigits, curr.currency_multiplesof as inMultiplesOf, ")
|
||||||
.append(" eao.external_id as externalAssetOwner, ")
|
.append(" eao.external_id as externalAssetOwner, ")
|
||||||
.append(" CASE ")
|
.append(" CASE ")
|
||||||
.append(" WHEN journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' THEN ca.account_no ")
|
.append(" WHEN journalEntry.entity_type_enum = 2 THEN COALESCE(ca.account_no, sa.account_no) ")
|
||||||
.append(" WHEN journalEntry.entity_type_enum = 2 THEN sa.account_no ")
|
|
||||||
.append(" ELSE NULL ")
|
.append(" ELSE NULL ")
|
||||||
.append(" END as savingsAccountNo ");
|
.append(" END as savingsAccountNo ");
|
||||||
if (associationParametersData.isRunningBalanceRequired()) {
|
if (associationParametersData.isRunningBalanceRequired()) {
|
||||||
@@ -130,8 +129,8 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" join m_currency curr on curr.code = journalEntry.currency_code ")
|
.append(" join m_currency curr on curr.code = journalEntry.currency_code ")
|
||||||
.append(" left join m_external_asset_owner_journal_entry_mapping eajem on eajem.journal_entry_id = journalEntry.id ")
|
.append(" left join m_external_asset_owner_journal_entry_mapping eajem on eajem.journal_entry_id = journalEntry.id ")
|
||||||
.append(" left join m_external_asset_owner eao on eao.id = eajem.owner_id ")
|
.append(" left join m_external_asset_owner eao on eao.id = eajem.owner_id ")
|
||||||
.append(" left join m_savings_account sa on sa.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id NOT LIKE 'CA%' ")
|
.append(" left join m_savings_account sa on sa.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 ")
|
||||||
.append(" left join m_current_account ca on ca.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' ");
|
.append(" left join m_current_account ca on ca.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 ");
|
||||||
if (associationParametersData.isTransactionDetailsRequired()) {
|
if (associationParametersData.isTransactionDetailsRequired()) {
|
||||||
sb.append(" left join m_loan_transaction as lt on journalEntry.loan_transaction_id = lt.id ")
|
sb.append(" left join m_loan_transaction as lt on journalEntry.loan_transaction_id = lt.id ")
|
||||||
.append(" left join m_savings_account_transaction as st on journalEntry.savings_transaction_id = st.id ")
|
.append(" left join m_savings_account_transaction as st on journalEntry.savings_transaction_id = st.id ")
|
||||||
|
|||||||
+73
-70
@@ -1,70 +1,73 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
package org.apache.fineract.portfolio.account.jobs.executestandinginstructions;
|
||||||
|
|
||||||
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
|
||||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
import org.apache.fineract.portfolio.account.service.AccountTransfersWritePlatformService;
|
||||||
import org.springframework.batch.core.Job;
|
import org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformService;
|
||||||
import org.springframework.batch.core.Step;
|
import org.springframework.batch.core.Job;
|
||||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
import org.springframework.batch.core.Step;
|
||||||
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||||
import org.springframework.batch.core.repository.JobRepository;
|
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
import org.springframework.batch.core.repository.JobRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
@Configuration
|
|
||||||
public class ExecuteStandingInstructionsConfig {
|
@Configuration
|
||||||
|
public class ExecuteStandingInstructionsConfig {
|
||||||
@Autowired
|
|
||||||
private JobRepository jobRepository;
|
@Autowired
|
||||||
@Autowired
|
private JobRepository jobRepository;
|
||||||
private PlatformTransactionManager transactionManager;
|
@Autowired
|
||||||
@Autowired
|
private PlatformTransactionManager transactionManager;
|
||||||
private StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
@Autowired
|
||||||
@Autowired
|
private StandingInstructionReadPlatformService standingInstructionReadPlatformService;
|
||||||
private JdbcTemplate jdbcTemplate;
|
@Autowired
|
||||||
@Autowired
|
private JdbcTemplate jdbcTemplate;
|
||||||
private DatabaseSpecificSQLGenerator sqlGenerator;
|
@Autowired
|
||||||
@Autowired
|
private DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
private AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
@Autowired
|
||||||
|
private AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||||
@Bean
|
@Autowired
|
||||||
protected Step executeStandingInstructionsStep() {
|
private AccountTransferDetailRepository accountTransferDetailRepository;
|
||||||
return new StepBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository)
|
|
||||||
.tasklet(executeStandingInstructionsTasklet(), transactionManager).build();
|
@Bean
|
||||||
}
|
protected Step executeStandingInstructionsStep() {
|
||||||
|
return new StepBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository)
|
||||||
@Bean
|
.tasklet(executeStandingInstructionsTasklet(), transactionManager).build();
|
||||||
public Job executeStandingInstructionsJob() {
|
}
|
||||||
return new JobBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository).start(executeStandingInstructionsStep())
|
|
||||||
.incrementer(new RunIdIncrementer()).build();
|
@Bean
|
||||||
}
|
public Job executeStandingInstructionsJob() {
|
||||||
|
return new JobBuilder(JobName.EXECUTE_STANDING_INSTRUCTIONS.name(), jobRepository).start(executeStandingInstructionsStep())
|
||||||
@Bean
|
.incrementer(new RunIdIncrementer()).build();
|
||||||
public ExecuteStandingInstructionsTasklet executeStandingInstructionsTasklet() {
|
}
|
||||||
return new ExecuteStandingInstructionsTasklet(standingInstructionReadPlatformService, jdbcTemplate, sqlGenerator,
|
|
||||||
accountTransfersWritePlatformService);
|
@Bean
|
||||||
}
|
public ExecuteStandingInstructionsTasklet executeStandingInstructionsTasklet() {
|
||||||
}
|
return new ExecuteStandingInstructionsTasklet(standingInstructionReadPlatformService, jdbcTemplate, sqlGenerator,
|
||||||
|
accountTransfersWritePlatformService, accountTransferDetailRepository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+9
-1
@@ -34,6 +34,8 @@ import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
|||||||
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionData;
|
||||||
import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData;
|
import org.apache.fineract.portfolio.account.data.StandingInstructionDuesData;
|
||||||
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetailRepository;
|
||||||
|
import org.apache.fineract.portfolio.account.domain.AccountTransferDetails;
|
||||||
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
import org.apache.fineract.portfolio.account.domain.AccountTransferRecurrenceType;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionStatus;
|
||||||
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
import org.apache.fineract.portfolio.account.domain.StandingInstructionType;
|
||||||
@@ -58,6 +60,7 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
|||||||
private final JdbcTemplate jdbcTemplate;
|
private final JdbcTemplate jdbcTemplate;
|
||||||
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
private final DatabaseSpecificSQLGenerator sqlGenerator;
|
||||||
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
private final AccountTransfersWritePlatformService accountTransfersWritePlatformService;
|
||||||
|
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||||
@@ -105,10 +108,15 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
|
|||||||
final SavingsAccount fromSavingsAccount = null;
|
final SavingsAccount fromSavingsAccount = null;
|
||||||
final boolean isRegularTransaction = true;
|
final boolean isRegularTransaction = true;
|
||||||
final boolean isExceptionForBalanceCheck = false;
|
final boolean isExceptionForBalanceCheck = false;
|
||||||
|
// Load existing AccountTransferDetails so transactions are linked to
|
||||||
|
// this standing instruction and appear in its transaction history
|
||||||
|
final AccountTransferDetails existingDetails = data.getAccountDetailId() != null
|
||||||
|
? accountTransferDetailRepository.findById(data.getAccountDetailId()).orElse(null)
|
||||||
|
: null;
|
||||||
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount,
|
||||||
data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
data.fromAccountTypeEnum(), data.toAccountTypeEnum(), data.getFromAccount().getId(), data.getToAccount().getId(),
|
||||||
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
|
data.getName() + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null,
|
||||||
data.transferTypeEnum().getValue(), null, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
data.transferTypeEnum().getValue(), existingDetails, null, ExternalId.empty(), null, null, fromSavingsAccount,
|
||||||
isRegularTransaction, isExceptionForBalanceCheck);
|
isRegularTransaction, isExceptionForBalanceCheck);
|
||||||
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
final boolean transferCompleted = transferAmount(errors, accountTransferDTO, data.getId());
|
||||||
|
|
||||||
|
|||||||
+147
-35
@@ -34,6 +34,9 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
|
||||||
|
import org.apache.fineract.accounting.journalentry.service.AccountingProcessorHelper;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||||
@@ -96,6 +99,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
private final FineractProperties fineractProperties;
|
private final FineractProperties fineractProperties;
|
||||||
private final CurrentAccountRepository currentAccountRepository;
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
||||||
|
private final AccountingProcessorHelper accountingProcessorHelper;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
@@ -467,23 +471,29 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
.assembleFrom(accountTransferDTO.getToAccountId(), backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
final CurrentAccountTransaction withdrawal = performCurrentWithdrawal(fromCurrentAccount,
|
final CurrentAccountTransaction withdrawal = performCurrentWithdrawal(fromCurrentAccount,
|
||||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getTransactionDate());
|
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getTransactionDate(),
|
||||||
|
toSavingsAccount.productId(), toSavingsAccount.getId());
|
||||||
|
|
||||||
|
// skipAccountingPostings=true: current account withdrawal leg (C-txn) already posts
|
||||||
|
// DR CURRENT ACCOUNT / CR SAVING INDIVIDUAL directly — each with its own account number.
|
||||||
|
// Savings deposit GL entries would produce a self-cancelling DR+CR on SAVING INDIVIDUAL — suppress them.
|
||||||
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
final SavingsAccountTransaction deposit = this.savingsAccountDomainService.handleDeposit(toSavingsAccount,
|
||||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
||||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
||||||
isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill);
|
isAccountTransfer, isRegularTransaction, backdatedTxnsAllowedTill, true);
|
||||||
|
|
||||||
final AccountTransferDetails newDetails = AccountTransferDetails.currentToSavingsTransfer(
|
if (accountTransferDetails == null) {
|
||||||
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
accountTransferDetails = AccountTransferDetails.currentToSavingsTransfer(
|
||||||
toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount,
|
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
||||||
accountTransferDTO.getTransferType());
|
toSavingsAccount.office(), toSavingsAccount.getClient(), toSavingsAccount,
|
||||||
|
accountTransferDTO.getTransferType());
|
||||||
|
}
|
||||||
final Money amount = Money.of(toSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
final Money amount = Money.of(toSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
||||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
accountTransferDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||||
newDetails, null, deposit, accountTransferDTO.getTransactionDate(), amount,
|
accountTransferDetails, null, deposit, accountTransferDTO.getTransactionDate(), amount,
|
||||||
accountTransferDTO.getDescription()));
|
accountTransferDTO.getDescription()));
|
||||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
||||||
transferTransactionId = newDetails.getId();
|
transferTransactionId = accountTransferDetails.getId();
|
||||||
|
|
||||||
} else if (isSavingsToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
} else if (isSavingsToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||||
// --- Savings Account → Current Account ---
|
// --- Savings Account → Current Account ---
|
||||||
@@ -494,27 +504,33 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
||||||
|
|
||||||
|
// skipAccountingPostings=true: current account deposit leg (C-txn) posts
|
||||||
|
// DR SAVING INDIVIDUAL / CR CURRENT ACCOUNT directly. Savings withdrawal GL entries
|
||||||
|
// would produce a self-cancelling DR+CR on the same SAVING INDIVIDUAL GL — suppress them.
|
||||||
final SavingsTransactionBooleanValues txnBooleans = new SavingsTransactionBooleanValues(isAccountTransfer,
|
final SavingsTransactionBooleanValues txnBooleans = new SavingsTransactionBooleanValues(isAccountTransfer,
|
||||||
isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), false,
|
isRegularTransaction, fromSavingsAccount.isWithdrawalFeeApplicableForTransfer(), false,
|
||||||
accountTransferDTO.isExceptionForBalanceCheck());
|
accountTransferDTO.isExceptionForBalanceCheck(), true);
|
||||||
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
|
final SavingsAccountTransaction withdrawal = this.savingsAccountDomainService.handleWithdrawal(fromSavingsAccount,
|
||||||
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
accountTransferDTO.getFmt(), accountTransferDTO.getTransactionDate(),
|
||||||
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
|
||||||
txnBooleans, backdatedTxnsAllowedTill);
|
txnBooleans, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getTransactionDate());
|
accountTransferDTO.getTransactionDate(),
|
||||||
|
fromSavingsAccount.productId(), fromSavingsAccount.getId());
|
||||||
|
|
||||||
final AccountTransferDetails newDetails = AccountTransferDetails.savingsToCurrentTransfer(
|
if (accountTransferDetails == null) {
|
||||||
fromSavingsAccount.office(), fromSavingsAccount.getClient(), fromSavingsAccount,
|
accountTransferDetails = AccountTransferDetails.savingsToCurrentTransfer(
|
||||||
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
fromSavingsAccount.office(), fromSavingsAccount.getClient(), fromSavingsAccount,
|
||||||
accountTransferDTO.getTransferType());
|
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
||||||
|
accountTransferDTO.getTransferType());
|
||||||
|
}
|
||||||
final Money amount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
final Money amount = Money.of(fromSavingsAccount.getCurrency(), accountTransferDTO.getTransactionAmount());
|
||||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
accountTransferDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||||
newDetails, withdrawal, null, accountTransferDTO.getTransactionDate(), amount,
|
accountTransferDetails, withdrawal, null, accountTransferDTO.getTransactionDate(), amount,
|
||||||
accountTransferDTO.getDescription()));
|
accountTransferDTO.getDescription()));
|
||||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
||||||
transferTransactionId = newDetails.getId();
|
transferTransactionId = accountTransferDetails.getId();
|
||||||
|
|
||||||
} else if (isCurrentToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
} else if (isCurrentToCurrentAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||||
// --- Current Account → Current Account ---
|
// --- Current Account → Current Account ---
|
||||||
@@ -528,22 +544,26 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getToAccountId()));
|
||||||
|
|
||||||
performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getTransactionDate());
|
accountTransferDTO.getTransactionDate(),
|
||||||
|
toCurrentAccount.getProductId(), toCurrentAccount.getId());
|
||||||
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
performCurrentDeposit(toCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getTransactionDate());
|
accountTransferDTO.getTransactionDate(),
|
||||||
|
fromCurrentAccount.getProductId(), fromCurrentAccount.getId());
|
||||||
|
|
||||||
final AccountTransferDetails newDetails = AccountTransferDetails.currentToCurrentTransfer(
|
if (accountTransferDetails == null) {
|
||||||
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
accountTransferDetails = AccountTransferDetails.currentToCurrentTransfer(
|
||||||
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
fromCurrentAccount.getOffice(), fromCurrentAccount.getClient(), fromCurrentAccount,
|
||||||
accountTransferDTO.getTransferType());
|
toCurrentAccount.getOffice(), toCurrentAccount.getClient(), toCurrentAccount,
|
||||||
|
accountTransferDTO.getTransferType());
|
||||||
|
}
|
||||||
final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(),
|
final MonetaryCurrency currency = new MonetaryCurrency(fromCurrentAccount.getCurrencyCode(),
|
||||||
fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf());
|
fromCurrentAccount.getCurrencyDigits(), fromCurrentAccount.getCurrencyMultiplesOf());
|
||||||
final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount());
|
final Money amount = Money.of(currency, accountTransferDTO.getTransactionAmount());
|
||||||
newDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
accountTransferDetails.addAccountTransferTransaction(AccountTransferTransaction.savingsToSavingsTransfer(
|
||||||
newDetails, null, null, accountTransferDTO.getTransactionDate(), amount,
|
accountTransferDetails, null, null, accountTransferDTO.getTransactionDate(), amount,
|
||||||
accountTransferDTO.getDescription()));
|
accountTransferDTO.getDescription()));
|
||||||
this.accountTransferDetailRepository.saveAndFlush(newDetails);
|
this.accountTransferDetailRepository.saveAndFlush(accountTransferDetails);
|
||||||
transferTransactionId = newDetails.getId();
|
transferTransactionId = accountTransferDetails.getId();
|
||||||
|
|
||||||
} else if (isCurrentToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
} else if (isCurrentToLoanAccountTransfer(accountTransferDTO.getFromAccountType(), accountTransferDTO.getToAccountType())) {
|
||||||
// --- Current Account → Loan Account ---
|
// --- Current Account → Loan Account ---
|
||||||
@@ -553,7 +573,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId()));
|
"error.msg.currentaccount.not.found", "Current account not found: " + accountTransferDTO.getFromAccountId()));
|
||||||
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
final Loan toLoanAccount = this.loanAccountAssembler.assembleFrom(accountTransferDTO.getToAccountId());
|
||||||
|
|
||||||
performCurrentWithdrawal(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
performCurrentWithdrawalForLoan(fromCurrentAccount, accountTransferDTO.getTransactionAmount(),
|
||||||
accountTransferDTO.getTransactionDate());
|
accountTransferDTO.getTransactionDate());
|
||||||
|
|
||||||
ExternalId externalId = externalIdFactory.create(accountTransferDTO.getTxnExternalId().getValue());
|
ExternalId externalId = externalIdFactory.create(accountTransferDTO.getTxnExternalId().getValue());
|
||||||
@@ -656,10 +676,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Withdraw amount from a current account and save the transaction.
|
* Withdraw for Current → Loan transfers. Loan domain service handles the loan-side GL,
|
||||||
* Throws {@link GeneralPlatformDomainRuleException} if balance is insufficient.
|
* so current account side uses LIABILITY_TRANSFER as the counterpart (classic approach).
|
||||||
*/
|
*/
|
||||||
private CurrentAccountTransaction performCurrentWithdrawal(final CurrentAccount account, final BigDecimal amount,
|
private CurrentAccountTransaction performCurrentWithdrawalForLoan(final CurrentAccount account, final BigDecimal amount,
|
||||||
final LocalDate txnDate) {
|
final LocalDate txnDate) {
|
||||||
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
||||||
final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO;
|
final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO;
|
||||||
@@ -677,6 +697,40 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
account.setTotalWithdrawalsDerived(
|
account.setTotalWithdrawalsDerived(
|
||||||
(account.getTotalWithdrawalsDerived() != null ? account.getTotalWithdrawalsDerived() : BigDecimal.ZERO).add(amount));
|
(account.getTotalWithdrawalsDerived() != null ? account.getTotalWithdrawalsDerived() : BigDecimal.ZERO).add(amount));
|
||||||
currentAccountRepository.save(account);
|
currentAccountRepository.save(account);
|
||||||
|
if (account.getProductId() != null && txn.getId() != null) {
|
||||||
|
final String currentTxnId = "CA" + txn.getId();
|
||||||
|
accountingProcessorHelper.createCashBasedJournalEntriesAndReversalsForSavings(account.getOffice(),
|
||||||
|
account.getCurrencyCode(), CashAccountsForSavings.SAVINGS_CONTROL.getValue(),
|
||||||
|
FinancialActivity.LIABILITY_TRANSFER.getValue(), account.getProductId(), null,
|
||||||
|
account.getId(), currentTxnId, txnDate, amount, false);
|
||||||
|
}
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Withdraw amount from a current account and save the transaction.
|
||||||
|
* Throws {@link GeneralPlatformDomainRuleException} if balance is insufficient.
|
||||||
|
*/
|
||||||
|
private CurrentAccountTransaction performCurrentWithdrawal(final CurrentAccount account, final BigDecimal amount,
|
||||||
|
final LocalDate txnDate, final Long counterpartProductId, final Long counterpartEntityId) {
|
||||||
|
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
||||||
|
final BigDecimal onHold = account.getOnHoldFundsDerived() != null ? account.getOnHoldFundsDerived() : BigDecimal.ZERO;
|
||||||
|
final BigDecimal available = balance.subtract(onHold);
|
||||||
|
if (!account.isAllowOverdraft() && amount.compareTo(available) > 0) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance",
|
||||||
|
"Insufficient balance in current account " + account.getId()
|
||||||
|
+ ". Available: " + available + ", Requested: " + amount, account.getId());
|
||||||
|
}
|
||||||
|
final CurrentAccountTransaction txn = CurrentAccountTransaction.withdrawal(account, account.getOffice(), amount, txnDate, null);
|
||||||
|
final BigDecimal newBalance = balance.subtract(amount);
|
||||||
|
txn.setRunningBalanceDerived(newBalance);
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||||
|
account.setAccountBalanceDerived(newBalance);
|
||||||
|
account.setTotalWithdrawalsDerived(
|
||||||
|
(account.getTotalWithdrawalsDerived() != null ? account.getTotalWithdrawalsDerived() : BigDecimal.ZERO).add(amount));
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
// Post journal entries: DR current account GL, CR counterpart GL — each with correct entity/account number
|
||||||
|
postCurrentAccountTransferJournalEntries(account, txn, amount, txnDate, true, counterpartProductId, counterpartEntityId);
|
||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,7 +738,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
* Deposit amount into a current account and save the transaction.
|
* Deposit amount into a current account and save the transaction.
|
||||||
*/
|
*/
|
||||||
private CurrentAccountTransaction performCurrentDeposit(final CurrentAccount account, final BigDecimal amount,
|
private CurrentAccountTransaction performCurrentDeposit(final CurrentAccount account, final BigDecimal amount,
|
||||||
final LocalDate txnDate) {
|
final LocalDate txnDate, final Long counterpartProductId, final Long counterpartEntityId) {
|
||||||
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
final BigDecimal balance = account.getAccountBalanceDerived() != null ? account.getAccountBalanceDerived() : BigDecimal.ZERO;
|
||||||
final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, account.getOffice(), amount, txnDate, null);
|
final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, account.getOffice(), amount, txnDate, null);
|
||||||
final BigDecimal newBalance = balance.add(amount);
|
final BigDecimal newBalance = balance.add(amount);
|
||||||
@@ -694,9 +748,67 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
|
|||||||
account.setTotalDepositsDerived(
|
account.setTotalDepositsDerived(
|
||||||
(account.getTotalDepositsDerived() != null ? account.getTotalDepositsDerived() : BigDecimal.ZERO).add(amount));
|
(account.getTotalDepositsDerived() != null ? account.getTotalDepositsDerived() : BigDecimal.ZERO).add(amount));
|
||||||
currentAccountRepository.save(account);
|
currentAccountRepository.save(account);
|
||||||
|
// Post journal entries: DR counterpart GL, CR current account GL — each with correct entity/account number
|
||||||
|
postCurrentAccountTransferJournalEntries(account, txn, amount, txnDate, false, counterpartProductId, counterpartEntityId);
|
||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Posts double-entry journal entries for a current account leg of an account transfer.
|
||||||
|
* <p>
|
||||||
|
* Current Product GL mappings are stored under {@code PortfolioProductType.SAVING}, so the
|
||||||
|
* savings JE helper resolves them correctly.
|
||||||
|
* <p>
|
||||||
|
* Transaction id is prefixed with "C" (e.g. {@code C57}) so the entries are NOT grouped with
|
||||||
|
* savings entries that share the same numeric id (those use prefix "S"). The helper only
|
||||||
|
* appends its own "S" prefix when the id is numeric; passing "C57" keeps it as-is.
|
||||||
|
*
|
||||||
|
* @param isWithdrawal {@code true} for the from-leg (DR control / CR liability transfer);
|
||||||
|
* {@code false} for the to-leg (DR liability transfer / CR control)
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Posts direct transfer journal entries between a current account and a counterpart savings/current account.
|
||||||
|
* Each entry carries the correct entity ID so the correct account number appears on each GL line.
|
||||||
|
*
|
||||||
|
* For withdrawal (current → savings):
|
||||||
|
* DR: current product GL (entity = current account)
|
||||||
|
* CR: counterpart product GL (entity = savings/current account)
|
||||||
|
*
|
||||||
|
* For deposit (savings → current):
|
||||||
|
* DR: counterpart product GL (entity = savings/current account)
|
||||||
|
* CR: current product GL (entity = current account)
|
||||||
|
*
|
||||||
|
* @param counterpartProductId savings/current product ID of the other account
|
||||||
|
* @param counterpartEntityId savings/current account ID of the other account
|
||||||
|
*/
|
||||||
|
private void postCurrentAccountTransferJournalEntries(final CurrentAccount account, final CurrentAccountTransaction txn,
|
||||||
|
final BigDecimal amount, final LocalDate txnDate, final boolean isWithdrawal,
|
||||||
|
final Long counterpartProductId, final Long counterpartEntityId) {
|
||||||
|
if (account.getProductId() == null || txn.getId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Prefix "CA" so:
|
||||||
|
// 1. The helper does NOT re-prefix with "S" (avoids txn id collision with savings "S" prefix)
|
||||||
|
// 2. JournalEntryReadPlatformServiceImpl joins m_current_account (transaction_id LIKE 'CA%')
|
||||||
|
// so the correct current account number appears on the DR/CR entry.
|
||||||
|
final String currentTxnId = "CA" + txn.getId();
|
||||||
|
if (isWithdrawal) {
|
||||||
|
// DR: current account GL CR: counterpart (savings/current) GL
|
||||||
|
accountingProcessorHelper.createDirectTransferJournalEntriesForSavings(
|
||||||
|
account.getOffice(), account.getCurrencyCode(),
|
||||||
|
account.getProductId(), account.getId(),
|
||||||
|
counterpartProductId, counterpartEntityId,
|
||||||
|
currentTxnId, txnDate, amount);
|
||||||
|
} else {
|
||||||
|
// DR: counterpart (savings/current) GL CR: current account GL
|
||||||
|
accountingProcessorHelper.createDirectTransferJournalEntriesForSavings(
|
||||||
|
account.getOffice(), account.getCurrencyCode(),
|
||||||
|
counterpartProductId, counterpartEntityId,
|
||||||
|
account.getProductId(), account.getId(),
|
||||||
|
currentTxnId, txnDate, amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public CommandProcessingResult refundByTransfer(JsonCommand command) {
|
public CommandProcessingResult refundByTransfer(JsonCommand command) {
|
||||||
|
|||||||
+4
-2
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.account.starter;
|
package org.apache.fineract.portfolio.account.starter;
|
||||||
|
|
||||||
|
import org.apache.fineract.accounting.journalentry.service.AccountingProcessorHelper;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
||||||
@@ -94,12 +95,13 @@ public class AccountConfiguration {
|
|||||||
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
AccountTransferDetailRepository accountTransferDetailRepository, LoanReadPlatformService loanReadPlatformService,
|
||||||
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
GSIMRepositoy gsimRepository, ConfigurationDomainService configurationDomainService, ExternalIdFactory externalIdFactory,
|
||||||
FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
|
FineractProperties fineractProperties, CurrentAccountRepository currentAccountRepository,
|
||||||
CurrentAccountTransactionRepository currentAccountTransactionRepository) {
|
CurrentAccountTransactionRepository currentAccountTransactionRepository,
|
||||||
|
AccountingProcessorHelper accountingProcessorHelper) {
|
||||||
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
return new AccountTransfersWritePlatformServiceImpl(accountTransfersDataValidator, accountTransferAssembler,
|
||||||
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
accountTransferRepository, savingsAccountAssembler, savingsAccountDomainService, loanAccountAssembler,
|
||||||
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
loanAccountDomainService, savingsAccountWritePlatformService, accountTransferDetailRepository, loanReadPlatformService,
|
||||||
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
|
gsimRepository, configurationDomainService, externalIdFactory, fineractProperties,
|
||||||
currentAccountRepository, currentAccountTransactionRepository);
|
currentAccountRepository, currentAccountTransactionRepository, accountingProcessorHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+356
-333
@@ -1,333 +1,356 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.savings.domain;
|
package org.apache.fineract.portfolio.savings.domain;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsDepositBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsDepositBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsWithdrawalBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.savings.transaction.SavingsWithdrawalBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
|
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
|
||||||
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
|
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
|
||||||
import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException;
|
import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainService {
|
public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainService {
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
private final SavingsAccountRepositoryWrapper savingsAccountRepository;
|
||||||
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
|
private final SavingsAccountTransactionRepository savingsAccountTransactionRepository;
|
||||||
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
|
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
|
||||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||||
private final ConfigurationDomainService configurationDomainService;
|
private final ConfigurationDomainService configurationDomainService;
|
||||||
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
|
private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository;
|
||||||
private final BusinessEventNotifierService businessEventNotifierService;
|
private final BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
|
public SavingsAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
|
||||||
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
|
final SavingsAccountTransactionRepository savingsAccountTransactionRepository,
|
||||||
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
|
||||||
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
final JournalEntryWritePlatformService journalEntryWritePlatformService,
|
||||||
final ConfigurationDomainService configurationDomainService, final PlatformSecurityContext context,
|
final ConfigurationDomainService configurationDomainService, final PlatformSecurityContext context,
|
||||||
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository,
|
final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository,
|
||||||
final BusinessEventNotifierService businessEventNotifierService) {
|
final BusinessEventNotifierService businessEventNotifierService) {
|
||||||
this.savingsAccountRepository = savingsAccountRepository;
|
this.savingsAccountRepository = savingsAccountRepository;
|
||||||
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
|
this.savingsAccountTransactionRepository = savingsAccountTransactionRepository;
|
||||||
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
|
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
|
||||||
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
||||||
this.configurationDomainService = configurationDomainService;
|
this.configurationDomainService = configurationDomainService;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
|
this.depositAccountOnHoldTransactionRepository = depositAccountOnHoldTransactionRepository;
|
||||||
this.businessEventNotifierService = businessEventNotifierService;
|
this.businessEventNotifierService = businessEventNotifierService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt,
|
public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt,
|
||||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
||||||
final SavingsTransactionBooleanValues transactionBooleanValues, final boolean backdatedTxnsAllowedTill) {
|
final SavingsTransactionBooleanValues transactionBooleanValues, final boolean backdatedTxnsAllowedTill) {
|
||||||
context.authenticatedUser();
|
context.authenticatedUser();
|
||||||
account.validateForAccountBlock();
|
account.validateForAccountBlock();
|
||||||
account.validateForDebitBlock();
|
account.validateForDebitBlock();
|
||||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||||
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
||||||
final boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed();
|
final boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed();
|
||||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||||
if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) {
|
if (transactionBooleanValues.isRegularTransaction() && !account.allowWithdrawal()) {
|
||||||
throw new DepositAccountTransactionNotAllowedException(account.getId(), "withdraw", account.depositAccountType());
|
throw new DepositAccountTransactionNotAllowedException(account.getId(), "withdraw", account.depositAccountType());
|
||||||
}
|
}
|
||||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||||
final LocalDate postInterestOnDate = null;
|
final LocalDate postInterestOnDate = null;
|
||||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||||
|
|
||||||
if (backdatedTxnsAllowedTill) {
|
if (backdatedTxnsAllowedTill) {
|
||||||
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
} else {
|
} else {
|
||||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer accountType = null;
|
Integer accountType = null;
|
||||||
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
|
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
|
||||||
paymentDetail, null, accountType);
|
paymentDetail, null, accountType);
|
||||||
UUID refNo = UUID.randomUUID();
|
UUID refNo = UUID.randomUUID();
|
||||||
final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, transactionBooleanValues.isApplyWithdrawFee(),
|
final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, transactionBooleanValues.isApplyWithdrawFee(),
|
||||||
backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo.toString());
|
backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo.toString());
|
||||||
final MathContext mc = MathContext.DECIMAL64;
|
final MathContext mc = MathContext.DECIMAL64;
|
||||||
|
|
||||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||||
|
|
||||||
if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) {
|
if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) {
|
||||||
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd,
|
account.postInterest(mc, today, transactionBooleanValues.isInterestTransfer(), isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||||
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
||||||
} else {
|
} else {
|
||||||
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
|
account.calculateInterestUsing(mc, today, transactionBooleanValues.isInterestTransfer(),
|
||||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill,
|
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill,
|
||||||
postReversals);
|
postReversals);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
|
||||||
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) {
|
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
|
||||||
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
.findBySavingsAccountAndReversedFalseOrderByCreatedDateAsc(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(),
|
account.validateAccountBalanceDoesNotBecomeNegative(transactionAmount, transactionBooleanValues.isExceptionForBalanceCheck(),
|
||||||
depositAccountOnHoldTransactions, backdatedTxnsAllowedTill);
|
depositAccountOnHoldTransactions, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
saveTransactionToGenerateTransactionId(withdrawal);
|
saveTransactionToGenerateTransactionId(withdrawal);
|
||||||
if (backdatedTxnsAllowedTill) {
|
if (backdatedTxnsAllowedTill) {
|
||||||
// Update transactions separately
|
// Update transactions separately
|
||||||
saveUpdatedTransactionsOfSavingsAccount(account.getSavingsAccountTransactionsWithPivotConfig());
|
saveUpdatedTransactionsOfSavingsAccount(account.getSavingsAccountTransactionsWithPivotConfig());
|
||||||
}
|
}
|
||||||
this.savingsAccountRepository.save(account);
|
this.savingsAccountRepository.save(account);
|
||||||
|
|
||||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, transactionBooleanValues.isAccountTransfer(),
|
if (!transactionBooleanValues.isSkipAccountingPostings()) {
|
||||||
backdatedTxnsAllowedTill);
|
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds,
|
||||||
|
transactionBooleanValues.isAccountTransfer(), backdatedTxnsAllowedTill);
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new SavingsWithdrawalBusinessEvent(withdrawal));
|
}
|
||||||
return withdrawal;
|
|
||||||
}
|
businessEventNotifierService.notifyPostBusinessEvent(new SavingsWithdrawalBusinessEvent(withdrawal));
|
||||||
|
return withdrawal;
|
||||||
@Transactional
|
}
|
||||||
@Override
|
|
||||||
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
@Transactional
|
||||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
@Override
|
||||||
final boolean isAccountTransfer, final boolean isRegularTransaction, final boolean backdatedTxnsAllowedTill) {
|
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
||||||
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DEPOSIT;
|
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
||||||
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
final boolean isAccountTransfer, final boolean isRegularTransaction, final boolean backdatedTxnsAllowedTill) {
|
||||||
savingsAccountTransactionType, backdatedTxnsAllowedTill);
|
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DEPOSIT;
|
||||||
}
|
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
||||||
|
savingsAccountTransactionType, backdatedTxnsAllowedTill, false);
|
||||||
private SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
}
|
||||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
|
||||||
final boolean isAccountTransfer, final boolean isRegularTransaction,
|
@Override
|
||||||
final SavingsAccountTransactionType savingsAccountTransactionType, final boolean backdatedTxnsAllowedTill) {
|
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
||||||
context.authenticatedUser();
|
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
||||||
account.validateForAccountBlock();
|
final boolean isAccountTransfer, final boolean isRegularTransaction, final boolean backdatedTxnsAllowedTill,
|
||||||
account.validateForCreditBlock();
|
final boolean skipAccountingPostings) {
|
||||||
|
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DEPOSIT;
|
||||||
// Global configurations
|
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
||||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
savingsAccountTransactionType, backdatedTxnsAllowedTill, skipAccountingPostings);
|
||||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
}
|
||||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
|
||||||
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
private SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
||||||
if (isRegularTransaction && !account.allowDeposit()) {
|
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
||||||
throw new DepositAccountTransactionNotAllowedException(account.getId(), "deposit", account.depositAccountType());
|
final boolean isAccountTransfer, final boolean isRegularTransaction,
|
||||||
}
|
final SavingsAccountTransactionType savingsAccountTransactionType, final boolean backdatedTxnsAllowedTill) {
|
||||||
boolean isInterestTransfer = false;
|
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
||||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
savingsAccountTransactionType, backdatedTxnsAllowedTill, false);
|
||||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
}
|
||||||
|
|
||||||
if (backdatedTxnsAllowedTill) {
|
private SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
|
||||||
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
|
||||||
} else {
|
final boolean isAccountTransfer, final boolean isRegularTransaction,
|
||||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
final SavingsAccountTransactionType savingsAccountTransactionType, final boolean backdatedTxnsAllowedTill,
|
||||||
}
|
final boolean skipAccountingPostings) {
|
||||||
|
context.authenticatedUser();
|
||||||
Integer accountType = null;
|
account.validateForAccountBlock();
|
||||||
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
|
account.validateForCreditBlock();
|
||||||
paymentDetail, null, accountType);
|
|
||||||
UUID refNo = UUID.randomUUID();
|
// Global configurations
|
||||||
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType, backdatedTxnsAllowedTill,
|
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||||
relaxingDaysConfigForPivotDate, refNo.toString());
|
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||||
final LocalDate postInterestOnDate = null;
|
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||||
final MathContext mc = MathContext.DECIMAL64;
|
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
||||||
|
if (isRegularTransaction && !account.allowDeposit()) {
|
||||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
throw new DepositAccountTransactionNotAllowedException(account.getId(), "deposit", account.depositAccountType());
|
||||||
boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed();
|
}
|
||||||
if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) {
|
boolean isInterestTransfer = false;
|
||||||
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
|
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||||
postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||||
} else {
|
|
||||||
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
|
if (backdatedTxnsAllowedTill) {
|
||||||
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
}
|
} else {
|
||||||
|
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
saveTransactionToGenerateTransactionId(deposit);
|
}
|
||||||
|
|
||||||
if (backdatedTxnsAllowedTill) {
|
Integer accountType = null;
|
||||||
// Update transactions separately
|
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
|
||||||
saveUpdatedTransactionsOfSavingsAccount(account.getSavingsAccountTransactionsWithPivotConfig());
|
paymentDetail, null, accountType);
|
||||||
}
|
UUID refNo = UUID.randomUUID();
|
||||||
|
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType, backdatedTxnsAllowedTill,
|
||||||
this.savingsAccountRepository.saveAndFlush(account);
|
relaxingDaysConfigForPivotDate, refNo.toString());
|
||||||
|
final LocalDate postInterestOnDate = null;
|
||||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
final MathContext mc = MathContext.DECIMAL64;
|
||||||
businessEventNotifierService.notifyPostBusinessEvent(new SavingsDepositBusinessEvent(deposit));
|
|
||||||
return deposit;
|
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||||
}
|
boolean postReversals = this.configurationDomainService.isReversalTransactionAllowed();
|
||||||
|
if (account.isBeforeLastPostingPeriod(transactionDate, backdatedTxnsAllowedTill)) {
|
||||||
@Transactional
|
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
|
||||||
@Override
|
postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
||||||
public SavingsAccountTransaction handleHold(final SavingsAccount account, BigDecimal amount, LocalDate transactionDate,
|
} else {
|
||||||
Boolean lienAllowed) {
|
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||||
return SavingsAccountTransaction.holdAmount(account, account.office(), null, transactionDate,
|
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
||||||
Money.of(account.getCurrency(), amount), lienAllowed);
|
}
|
||||||
}
|
|
||||||
|
saveTransactionToGenerateTransactionId(deposit);
|
||||||
@Override
|
|
||||||
public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate,
|
if (backdatedTxnsAllowedTill) {
|
||||||
final BigDecimal transactionAmount, final boolean backdatedTxnsAllowedTill) {
|
// Update transactions separately
|
||||||
final DateTimeFormatter fmt = null;
|
saveUpdatedTransactionsOfSavingsAccount(account.getSavingsAccountTransactionsWithPivotConfig());
|
||||||
final PaymentDetail paymentDetail = null;
|
}
|
||||||
final boolean isAccountTransfer = false;
|
|
||||||
final boolean isRegularTransaction = true;
|
this.savingsAccountRepository.saveAndFlush(account);
|
||||||
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DIVIDEND_PAYOUT;
|
|
||||||
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
if (!skipAccountingPostings) {
|
||||||
savingsAccountTransactionType, backdatedTxnsAllowedTill);
|
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
||||||
}
|
}
|
||||||
|
businessEventNotifierService.notifyPostBusinessEvent(new SavingsDepositBusinessEvent(deposit));
|
||||||
private void updateExistingTransactionsDetails(SavingsAccount account, Set<Long> existingTransactionIds,
|
return deposit;
|
||||||
Set<Long> existingReversedTransactionIds) {
|
}
|
||||||
existingTransactionIds.addAll(account.findExistingTransactionIds());
|
|
||||||
existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds());
|
@Transactional
|
||||||
}
|
@Override
|
||||||
|
public SavingsAccountTransaction handleHold(final SavingsAccount account, BigDecimal amount, LocalDate transactionDate,
|
||||||
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
|
Boolean lienAllowed) {
|
||||||
this.savingsAccountTransactionRepository.saveAndFlush(transaction);
|
return SavingsAccountTransaction.holdAmount(account, account.office(), null, transactionDate,
|
||||||
return transaction.getId();
|
Money.of(account.getCurrency(), amount), lienAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveUpdatedTransactionsOfSavingsAccount(final List<SavingsAccountTransaction> savingsAccountTransactions) {
|
@Override
|
||||||
this.savingsAccountTransactionRepository.saveAll(savingsAccountTransactions);
|
public SavingsAccountTransaction handleDividendPayout(final SavingsAccount account, final LocalDate transactionDate,
|
||||||
}
|
final BigDecimal transactionAmount, final boolean backdatedTxnsAllowedTill) {
|
||||||
|
final DateTimeFormatter fmt = null;
|
||||||
private void updateTransactionDetailsWithPivotConfig(final SavingsAccount account, Set<Long> existingTransactionIds,
|
final PaymentDetail paymentDetail = null;
|
||||||
Set<Long> existingReversedTransactionIds) {
|
final boolean isAccountTransfer = false;
|
||||||
existingTransactionIds.addAll(account.findCurrentTransactionIdsWithPivotDateConfig());
|
final boolean isRegularTransaction = true;
|
||||||
existingReversedTransactionIds.addAll(account.findCurrentReversedTransactionIdsWithPivotDateConfig());
|
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.DIVIDEND_PAYOUT;
|
||||||
}
|
return handleDeposit(account, fmt, transactionDate, transactionAmount, paymentDetail, isAccountTransfer, isRegularTransaction,
|
||||||
|
savingsAccountTransactionType, backdatedTxnsAllowedTill);
|
||||||
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
|
}
|
||||||
final Set<Long> existingReversedTransactionIds, boolean isAccountTransfer, final boolean backdatedTxnsAllowedTill) {
|
|
||||||
|
private void updateExistingTransactionsDetails(SavingsAccount account, Set<Long> existingTransactionIds,
|
||||||
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
|
Set<Long> existingReversedTransactionIds) {
|
||||||
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
existingTransactionIds.addAll(account.findExistingTransactionIds());
|
||||||
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
|
existingReversedTransactionIds.addAll(account.findExistingReversedTransactionIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
|
||||||
@Override
|
this.savingsAccountTransactionRepository.saveAndFlush(transaction);
|
||||||
public void postJournalEntries(final SavingsAccount account, final Set<Long> existingTransactionIds,
|
return transaction.getId();
|
||||||
final Set<Long> existingReversedTransactionIds, final boolean backdatedTxnsAllowedTill) {
|
}
|
||||||
|
|
||||||
final boolean isAccountTransfer = false;
|
private void saveUpdatedTransactionsOfSavingsAccount(final List<SavingsAccountTransaction> savingsAccountTransactions) {
|
||||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
this.savingsAccountTransactionRepository.saveAll(savingsAccountTransactions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void updateTransactionDetailsWithPivotConfig(final SavingsAccount account, Set<Long> existingTransactionIds,
|
||||||
public SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
|
Set<Long> existingReversedTransactionIds) {
|
||||||
boolean backdatedTxnsAllowedTill) {
|
existingTransactionIds.addAll(account.findCurrentTransactionIdsWithPivotDateConfig());
|
||||||
|
existingReversedTransactionIds.addAll(account.findCurrentReversedTransactionIdsWithPivotDateConfig());
|
||||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
}
|
||||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
|
||||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
private void postJournalEntries(final SavingsAccount savingsAccount, final Set<Long> existingTransactionIds,
|
||||||
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
final Set<Long> existingReversedTransactionIds, boolean isAccountTransfer, final boolean backdatedTxnsAllowedTill) {
|
||||||
final boolean postReversals = true;
|
|
||||||
final Set<Long> existingTransactionIds = new HashSet<>();
|
final Map<String, Object> accountingBridgeData = savingsAccount.deriveAccountingBridgeData(savingsAccount.getCurrency().getCode(),
|
||||||
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
||||||
|
this.journalEntryWritePlatformService.createJournalEntriesForSavings(accountingBridgeData);
|
||||||
if (backdatedTxnsAllowedTill) {
|
}
|
||||||
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
|
||||||
} else {
|
@Transactional
|
||||||
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
@Override
|
||||||
}
|
public void postJournalEntries(final SavingsAccount account, final Set<Long> existingTransactionIds,
|
||||||
List<SavingsAccountTransaction> newTransactions = new ArrayList<>();
|
final Set<Long> existingReversedTransactionIds, final boolean backdatedTxnsAllowedTill) {
|
||||||
SavingsAccountTransaction reversal = null;
|
|
||||||
|
final boolean isAccountTransfer = false;
|
||||||
Set<SavingsAccountChargePaidBy> chargePaidBySet = null;
|
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, backdatedTxnsAllowedTill);
|
||||||
for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) {
|
}
|
||||||
reversal = SavingsAccountTransaction.reversal(savingsAccountTransaction);
|
|
||||||
chargePaidBySet = savingsAccountTransaction.getSavingsAccountChargesPaid();
|
@Override
|
||||||
reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet);
|
public SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
|
||||||
account.undoTransaction(savingsAccountTransaction);
|
boolean backdatedTxnsAllowedTill) {
|
||||||
if (postReversals) {
|
|
||||||
newTransactions.add(reversal);
|
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||||
}
|
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||||
}
|
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||||
|
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
|
||||||
boolean isInterestTransfer = false;
|
final boolean postReversals = true;
|
||||||
LocalDate postInterestOnDate = null;
|
final Set<Long> existingTransactionIds = new HashSet<>();
|
||||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
final Set<Long> existingReversedTransactionIds = new HashSet<>();
|
||||||
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
|
|
||||||
for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) {
|
if (backdatedTxnsAllowedTill) {
|
||||||
if (savingsAccountTransaction.isPostInterestCalculationRequired()
|
updateTransactionDetailsWithPivotConfig(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill)) {
|
} else {
|
||||||
|
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
|
||||||
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
|
}
|
||||||
postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
List<SavingsAccountTransaction> newTransactions = new ArrayList<>();
|
||||||
} else {
|
SavingsAccountTransaction reversal = null;
|
||||||
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
|
|
||||||
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
Set<SavingsAccountChargePaidBy> chargePaidBySet = null;
|
||||||
}
|
for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) {
|
||||||
account.validatePivotDateTransaction(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill,
|
reversal = SavingsAccountTransaction.reversal(savingsAccountTransaction);
|
||||||
relaxingDaysConfigForPivotDate, "savingsaccount");
|
chargePaidBySet = savingsAccountTransaction.getSavingsAccountChargesPaid();
|
||||||
account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(), false);
|
reversal.getSavingsAccountChargesPaid().addAll(chargePaidBySet);
|
||||||
account.activateAccountBasedOnBalance();
|
account.undoTransaction(savingsAccountTransaction);
|
||||||
}
|
if (postReversals) {
|
||||||
this.savingsAccountRepository.save(account);
|
newTransactions.add(reversal);
|
||||||
newTransactions.addAll(account.getSavingsAccountTransactionsWithPivotConfig());
|
}
|
||||||
this.savingsAccountTransactionRepository.saveAll(newTransactions);
|
}
|
||||||
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, false, backdatedTxnsAllowedTill);
|
|
||||||
|
boolean isInterestTransfer = false;
|
||||||
return reversal;
|
LocalDate postInterestOnDate = null;
|
||||||
}
|
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||||
}
|
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
|
||||||
|
for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) {
|
||||||
|
if (savingsAccountTransaction.isPostInterestCalculationRequired()
|
||||||
|
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill)) {
|
||||||
|
|
||||||
|
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
|
||||||
|
postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
||||||
|
} else {
|
||||||
|
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||||
|
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
|
||||||
|
}
|
||||||
|
account.validatePivotDateTransaction(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill,
|
||||||
|
relaxingDaysConfigForPivotDate, "savingsaccount");
|
||||||
|
account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(), false);
|
||||||
|
account.activateAccountBasedOnBalance();
|
||||||
|
}
|
||||||
|
this.savingsAccountRepository.save(account);
|
||||||
|
newTransactions.addAll(account.getSavingsAccountTransactionsWithPivotConfig());
|
||||||
|
this.savingsAccountTransactionRepository.saveAll(newTransactions);
|
||||||
|
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, false, backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
|
return reversal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+70
-59
@@ -1,59 +1,70 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.savings;
|
package org.apache.fineract.portfolio.savings;
|
||||||
|
|
||||||
public class SavingsTransactionBooleanValues {
|
public class SavingsTransactionBooleanValues {
|
||||||
|
|
||||||
private final boolean isAccountTransfer;
|
private final boolean isAccountTransfer;
|
||||||
private final boolean isRegularTransaction;
|
private final boolean isRegularTransaction;
|
||||||
private final boolean isApplyWithdrawFee;
|
private final boolean isApplyWithdrawFee;
|
||||||
private final boolean isInterestTransfer;
|
private final boolean isInterestTransfer;
|
||||||
private final boolean isExceptionForBalanceCheck;
|
private final boolean isExceptionForBalanceCheck;
|
||||||
|
private final boolean skipAccountingPostings;
|
||||||
public SavingsTransactionBooleanValues(final boolean isAccountTransfer, final boolean isRegularTransaction,
|
|
||||||
final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isExceptionForBalanceCheck) {
|
public SavingsTransactionBooleanValues(final boolean isAccountTransfer, final boolean isRegularTransaction,
|
||||||
|
final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isExceptionForBalanceCheck) {
|
||||||
this.isAccountTransfer = isAccountTransfer;
|
this(isAccountTransfer, isRegularTransaction, isApplyWithdrawFee, isInterestTransfer, isExceptionForBalanceCheck, false);
|
||||||
this.isRegularTransaction = isRegularTransaction;
|
}
|
||||||
this.isApplyWithdrawFee = isApplyWithdrawFee;
|
|
||||||
this.isInterestTransfer = isInterestTransfer;
|
public SavingsTransactionBooleanValues(final boolean isAccountTransfer, final boolean isRegularTransaction,
|
||||||
this.isExceptionForBalanceCheck = isExceptionForBalanceCheck;
|
final boolean isApplyWithdrawFee, final boolean isInterestTransfer, final boolean isExceptionForBalanceCheck,
|
||||||
}
|
final boolean skipAccountingPostings) {
|
||||||
|
this.isAccountTransfer = isAccountTransfer;
|
||||||
public boolean isAccountTransfer() {
|
this.isRegularTransaction = isRegularTransaction;
|
||||||
return this.isAccountTransfer;
|
this.isApplyWithdrawFee = isApplyWithdrawFee;
|
||||||
}
|
this.isInterestTransfer = isInterestTransfer;
|
||||||
|
this.isExceptionForBalanceCheck = isExceptionForBalanceCheck;
|
||||||
public boolean isRegularTransaction() {
|
this.skipAccountingPostings = skipAccountingPostings;
|
||||||
return this.isRegularTransaction;
|
}
|
||||||
}
|
|
||||||
|
public boolean isAccountTransfer() {
|
||||||
public boolean isApplyWithdrawFee() {
|
return this.isAccountTransfer;
|
||||||
return this.isApplyWithdrawFee;
|
}
|
||||||
}
|
|
||||||
|
public boolean isRegularTransaction() {
|
||||||
public boolean isInterestTransfer() {
|
return this.isRegularTransaction;
|
||||||
return this.isInterestTransfer;
|
}
|
||||||
}
|
|
||||||
|
public boolean isApplyWithdrawFee() {
|
||||||
public boolean isExceptionForBalanceCheck() {
|
return this.isApplyWithdrawFee;
|
||||||
return this.isExceptionForBalanceCheck;
|
}
|
||||||
}
|
|
||||||
|
public boolean isInterestTransfer() {
|
||||||
}
|
return this.isInterestTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExceptionForBalanceCheck() {
|
||||||
|
return this.isExceptionForBalanceCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipAccountingPostings() {
|
||||||
|
return this.skipAccountingPostings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+55
-51
@@ -1,51 +1,55 @@
|
|||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
* distributed with this work for additional information
|
* distributed with this work for additional information
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
* to you under the Apache License, Version 2.0 (the
|
* to you under the Apache License, Version 2.0 (the
|
||||||
* "License"); you may not use this file except in compliance
|
* "License"); you may not use this file except in compliance
|
||||||
* with the License. You may obtain a copy of the License at
|
* with the License. You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing,
|
* Unless required by applicable law or agreed to in writing,
|
||||||
* software distributed under the License is distributed on an
|
* software distributed under the License is distributed on an
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
* KIND, either express or implied. See the License for the
|
* KIND, either express or implied. See the License for the
|
||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.savings.service;
|
package org.apache.fineract.portfolio.savings.service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
|
||||||
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
|
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||||
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
|
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
|
||||||
|
|
||||||
public interface SavingsAccountDomainService {
|
public interface SavingsAccountDomainService {
|
||||||
|
|
||||||
SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
|
SavingsAccountTransaction handleWithdrawal(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
|
||||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, SavingsTransactionBooleanValues transactionBooleanValues,
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, SavingsTransactionBooleanValues transactionBooleanValues,
|
||||||
boolean backdatedTxnsAllowedTill);
|
boolean backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
|
SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
|
||||||
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction,
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction,
|
||||||
boolean backdatedTxnsAllowedTill);
|
boolean backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
void postJournalEntries(SavingsAccount savingsAccount, Set<Long> existingTransactionIds, Set<Long> existingReversedTransactionIds,
|
SavingsAccountTransaction handleDeposit(SavingsAccount account, DateTimeFormatter fmt, LocalDate transactionDate,
|
||||||
boolean backdatedTxnsAllowedTill);
|
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isAccountTransfer, boolean isRegularTransaction,
|
||||||
|
boolean backdatedTxnsAllowedTill, boolean skipAccountingPostings);
|
||||||
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount,
|
|
||||||
boolean backdatedTxnsAllowedTill);
|
void postJournalEntries(SavingsAccount savingsAccount, Set<Long> existingTransactionIds, Set<Long> existingReversedTransactionIds,
|
||||||
|
boolean backdatedTxnsAllowedTill);
|
||||||
SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
|
|
||||||
boolean backdatedTxnsAllowedTill);
|
SavingsAccountTransaction handleDividendPayout(SavingsAccount account, LocalDate transactionDate, BigDecimal transactionAmount,
|
||||||
|
boolean backdatedTxnsAllowedTill);
|
||||||
SavingsAccountTransaction handleHold(SavingsAccount account, BigDecimal amount, LocalDate transactionDate, Boolean lienAllowed);
|
|
||||||
}
|
SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
|
||||||
|
boolean backdatedTxnsAllowedTill);
|
||||||
|
|
||||||
|
SavingsAccountTransaction handleHold(SavingsAccount account, BigDecimal amount, LocalDate transactionDate, Boolean lienAllowed);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user