Merge remote-tracking branch 'self-hosted/feature/account-charges' into feature/Backend-DSC-MFA
This commit is contained in:
+1
@@ -73,6 +73,7 @@ public interface JournalEntryMapper {
|
|||||||
@Mapping(target = "transactionDetails", ignore = true)
|
@Mapping(target = "transactionDetails", ignore = true)
|
||||||
@Mapping(target = "savingTransactionId", ignore = true)
|
@Mapping(target = "savingTransactionId", ignore = true)
|
||||||
@Mapping(target = "externalAssetOwner", ignore = true)
|
@Mapping(target = "externalAssetOwner", ignore = true)
|
||||||
|
@Mapping(target = "savingsAccountNo", ignore = true)
|
||||||
JournalEntryData map(JournalEntry journalEntry);
|
JournalEntryData map(JournalEntry journalEntry);
|
||||||
|
|
||||||
@Named("entityType")
|
@Named("entityType")
|
||||||
|
|||||||
+6
-2
@@ -59,6 +59,8 @@ public class JournalEntryData {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private Long entityId;
|
private Long entityId;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
private String savingsAccountNo;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private Long createdByUserId;
|
private Long createdByUserId;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private OffsetDateTime createdDate;
|
private OffsetDateTime createdDate;
|
||||||
@@ -193,7 +195,8 @@ public class JournalEntryData {
|
|||||||
final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate,
|
final EnumOptionData entityType, final Long entityId, final Long createdByUserId, final OffsetDateTime submittedOnDate,
|
||||||
final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber,
|
final String createdByUserName, final String comments, final Boolean reversed, final String referenceNumber,
|
||||||
final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed,
|
final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance, final Boolean runningBalanceComputed,
|
||||||
final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner) {
|
final TransactionDetailData transactionDetailData, final CurrencyData currency, final String externalAssetOwner,
|
||||||
|
final String savingsAccountNo) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.officeId = officeId;
|
this.officeId = officeId;
|
||||||
this.officeName = officeName;
|
this.officeName = officeName;
|
||||||
@@ -221,6 +224,7 @@ public class JournalEntryData {
|
|||||||
this.transactionDetails = transactionDetailData;
|
this.transactionDetails = transactionDetailData;
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
this.externalAssetOwner = externalAssetOwner;
|
this.externalAssetOwner = externalAssetOwner;
|
||||||
|
this.savingsAccountNo = savingsAccountNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId,
|
public static JournalEntryData importInstance(Long officeId, LocalDate transactionDate, String currencyCode, Long paymentTypeId,
|
||||||
@@ -266,7 +270,7 @@ public class JournalEntryData {
|
|||||||
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
|
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
|
||||||
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||||
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
||||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner);
|
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getRowIndex() {
|
public Integer getRowIndex() {
|
||||||
|
|||||||
+17
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.core.service;
|
package org.apache.fineract.infrastructure.core.service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
@@ -44,6 +45,7 @@ public class SearchParameters {
|
|||||||
private String orderBy;
|
private String orderBy;
|
||||||
private String sortOrder;
|
private String sortOrder;
|
||||||
private String accountNo;
|
private String accountNo;
|
||||||
|
private Integer depositType;
|
||||||
private String currencyCode;
|
private String currencyCode;
|
||||||
private Long staffId;
|
private Long staffId;
|
||||||
private Long loanId;
|
private Long loanId;
|
||||||
@@ -57,6 +59,21 @@ public class SearchParameters {
|
|||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private Boolean isSelfUser;
|
private Boolean isSelfUser;
|
||||||
private Integer legalForm;
|
private Integer legalForm;
|
||||||
|
private LocalDate fromDate;
|
||||||
|
private LocalDate toDate;
|
||||||
|
private LocalDate asOnDate;
|
||||||
|
|
||||||
|
public boolean hasFromDate() {
|
||||||
|
return this.fromDate != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasToDate() {
|
||||||
|
return this.toDate != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAsOnDate() {
|
||||||
|
return this.asOnDate != null;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getLimit() {
|
public Integer getLimit() {
|
||||||
if (limit == null) {
|
if (limit == null) {
|
||||||
|
|||||||
+44
@@ -146,4 +146,48 @@ public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
|
|||||||
txn.submittedOnDate = transactionDate;
|
txn.submittedOnDate = transactionDate;
|
||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transactionTypeEnum = 8 (Hold Amount)
|
||||||
|
public static CurrentAccountTransaction holdAmount(CurrentAccount account, Office office,
|
||||||
|
BigDecimal amount, LocalDate transactionDate, String reasonForBlock) {
|
||||||
|
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||||
|
txn.currentAccount = account;
|
||||||
|
txn.office = office;
|
||||||
|
txn.transactionTypeEnum = 8;
|
||||||
|
txn.amount = amount;
|
||||||
|
txn.transactionDate = transactionDate;
|
||||||
|
txn.lienTransaction = true;
|
||||||
|
txn.reasonForBlock = reasonForBlock;
|
||||||
|
txn.submittedOnDate = transactionDate;
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transactionTypeEnum = 9 (Release Amount)
|
||||||
|
public static CurrentAccountTransaction releaseAmount(CurrentAccount account, Office office,
|
||||||
|
BigDecimal amount, LocalDate transactionDate, Long holdTransactionId) {
|
||||||
|
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||||
|
txn.currentAccount = account;
|
||||||
|
txn.office = office;
|
||||||
|
txn.transactionTypeEnum = 9;
|
||||||
|
txn.amount = amount;
|
||||||
|
txn.transactionDate = transactionDate;
|
||||||
|
txn.releaseIdOfHoldAmountTransaction = holdTransactionId;
|
||||||
|
txn.submittedOnDate = transactionDate;
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// transactionTypeEnum = 7 (Reversal / Undo)
|
||||||
|
public static CurrentAccountTransaction reversal(CurrentAccount account, Office office,
|
||||||
|
BigDecimal amount, LocalDate transactionDate, Long originalTransactionId) {
|
||||||
|
CurrentAccountTransaction txn = new CurrentAccountTransaction();
|
||||||
|
txn.currentAccount = account;
|
||||||
|
txn.office = office;
|
||||||
|
txn.transactionTypeEnum = 7;
|
||||||
|
txn.amount = amount;
|
||||||
|
txn.transactionDate = transactionDate;
|
||||||
|
txn.reversal = true;
|
||||||
|
txn.originalTransactionId = originalTransactionId;
|
||||||
|
txn.submittedOnDate = transactionDate;
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.currentaccount.service;
|
package org.apache.fineract.portfolio.currentaccount.service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -26,4 +27,9 @@ public interface CurrentAccountReadPlatformService {
|
|||||||
Map<String, Object> retrieveOne(Long accountId);
|
Map<String, Object> retrieveOne(Long accountId);
|
||||||
|
|
||||||
List<Map<String, Object>> retrieveAll();
|
List<Map<String, Object>> retrieveAll();
|
||||||
|
|
||||||
|
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName);
|
||||||
|
|
||||||
|
List<Map<String, Object>> retrieveAll(Integer offset, Integer limit, String accountNo, String clientName,
|
||||||
|
String status, LocalDate fromDate, LocalDate toDate, LocalDate asOnDate);
|
||||||
}
|
}
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.currentaccount.service;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
|
||||||
|
public interface CurrentAccountTransactionWritePlatformService {
|
||||||
|
|
||||||
|
CommandProcessingResult deposit(Long accountId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult withdrawal(Long accountId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult holdAmount(Long accountId, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult releaseAmount(Long accountId, Long transactionId);
|
||||||
|
|
||||||
|
CommandProcessingResult undoTransaction(Long accountId, Long transactionId, boolean isReversal);
|
||||||
|
|
||||||
|
CommandProcessingResult reverseTransaction(Long accountId, Long transactionId, boolean isReversal, JsonCommand command);
|
||||||
|
|
||||||
|
CommandProcessingResult adjustTransaction(Long accountId, Long transactionId, JsonCommand command);
|
||||||
|
}
|
||||||
+1
@@ -35,4 +35,5 @@ public class SavingsDTO {
|
|||||||
private boolean cashBasedAccountingEnabled;
|
private boolean cashBasedAccountingEnabled;
|
||||||
private boolean accrualBasedAccountingEnabled;
|
private boolean accrualBasedAccountingEnabled;
|
||||||
private List<SavingsTransactionDTO> newSavingsTransactions;
|
private List<SavingsTransactionDTO> newSavingsTransactions;
|
||||||
|
private int depositAccountType;
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-3
@@ -83,10 +83,30 @@ public class AccountingProcessorHelper {
|
|||||||
|
|
||||||
public static final String LOAN_TRANSACTION_IDENTIFIER = "L";
|
public static final String LOAN_TRANSACTION_IDENTIFIER = "L";
|
||||||
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
|
public static final String SAVINGS_TRANSACTION_IDENTIFIER = "S";
|
||||||
|
public static final String FIXED_DEPOSIT_TRANSACTION_IDENTIFIER = "FD";
|
||||||
|
public static final String RECURRING_DEPOSIT_TRANSACTION_IDENTIFIER = "RD";
|
||||||
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
|
public static final String CLIENT_TRANSACTION_IDENTIFIER = "C";
|
||||||
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P";
|
public static final String PROVISIONING_TRANSACTION_IDENTIFIER = "P";
|
||||||
public static final String SHARE_TRANSACTION_IDENTIFIER = "SH";
|
public static final String SHARE_TRANSACTION_IDENTIFIER = "SH";
|
||||||
|
|
||||||
|
private static final ThreadLocal<Integer> DEPOSIT_ACCOUNT_TYPE_CONTEXT = ThreadLocal.withInitial(() -> 100);
|
||||||
|
|
||||||
|
public static void setDepositAccountTypeContext(int depositAccountType) {
|
||||||
|
DEPOSIT_ACCOUNT_TYPE_CONTEXT.set(depositAccountType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearDepositAccountTypeContext() {
|
||||||
|
DEPOSIT_ACCOUNT_TYPE_CONTEXT.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSavingsTransactionIdentifier() {
|
||||||
|
return switch (DEPOSIT_ACCOUNT_TYPE_CONTEXT.get()) {
|
||||||
|
case 200 -> FIXED_DEPOSIT_TRANSACTION_IDENTIFIER;
|
||||||
|
case 300 -> RECURRING_DEPOSIT_TRANSACTION_IDENTIFIER;
|
||||||
|
default -> SAVINGS_TRANSACTION_IDENTIFIER;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private final JournalEntryRepository glJournalEntryRepository;
|
private final JournalEntryRepository glJournalEntryRepository;
|
||||||
private final ProductToGLAccountMappingRepository accountMappingRepository;
|
private final ProductToGLAccountMappingRepository accountMappingRepository;
|
||||||
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
|
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
|
||||||
@@ -203,6 +223,9 @@ public class AccountingProcessorHelper {
|
|||||||
final Long loanProductId = (Long) accountingBridgeData.get("savingsProductId");
|
final Long loanProductId = (Long) accountingBridgeData.get("savingsProductId");
|
||||||
final Long officeId = (Long) accountingBridgeData.get("officeId");
|
final Long officeId = (Long) accountingBridgeData.get("officeId");
|
||||||
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
|
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
|
||||||
|
final int depositAccountType = accountingBridgeData.containsKey("depositAccountType")
|
||||||
|
? (Integer) accountingBridgeData.get("depositAccountType")
|
||||||
|
: 100;
|
||||||
final List<SavingsTransactionDTO> newSavingsTransactions = new ArrayList<>();
|
final List<SavingsTransactionDTO> newSavingsTransactions = new ArrayList<>();
|
||||||
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
|
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
|
||||||
|
|
||||||
@@ -264,7 +287,7 @@ public class AccountingProcessorHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new SavingsDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, accrualBasedAccountingEnabled,
|
return new SavingsDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, accrualBasedAccountingEnabled,
|
||||||
newSavingsTransactions);
|
newSavingsTransactions, depositAccountType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharesDTO populateSharesDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
public SharesDTO populateSharesDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
||||||
@@ -898,7 +921,7 @@ public class AccountingProcessorHelper {
|
|||||||
String modifiedTransactionId = transactionId;
|
String modifiedTransactionId = transactionId;
|
||||||
if (StringUtils.isNumeric(transactionId)) {
|
if (StringUtils.isNumeric(transactionId)) {
|
||||||
savingsAccountTransactionId = Long.parseLong(transactionId);
|
savingsAccountTransactionId = Long.parseLong(transactionId);
|
||||||
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
|
modifiedTransactionId = getSavingsTransactionIdentifier() + transactionId;
|
||||||
}
|
}
|
||||||
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
||||||
transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
||||||
@@ -964,7 +987,7 @@ public class AccountingProcessorHelper {
|
|||||||
String modifiedTransactionId = transactionId;
|
String modifiedTransactionId = transactionId;
|
||||||
if (StringUtils.isNumeric(transactionId)) {
|
if (StringUtils.isNumeric(transactionId)) {
|
||||||
savingsAccountTransactionId = Long.parseLong(transactionId);
|
savingsAccountTransactionId = Long.parseLong(transactionId);
|
||||||
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
|
modifiedTransactionId = getSavingsTransactionIdentifier() + transactionId;
|
||||||
}
|
}
|
||||||
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
||||||
transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
||||||
|
|||||||
+5
@@ -40,6 +40,8 @@ public class AccrualBasedAccountingProcessorForSavings implements AccountingProc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||||
|
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||||
|
try {
|
||||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||||
final Long savingsId = savingsDTO.getSavingsId();
|
final Long savingsId = savingsDTO.getSavingsId();
|
||||||
@@ -281,5 +283,8 @@ public class AccrualBasedAccountingProcessorForSavings implements AccountingProc
|
|||||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
AccountingProcessorHelper.clearDepositAccountTypeContext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -39,6 +39,8 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||||
|
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||||
|
try {
|
||||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||||
final Long savingsId = savingsDTO.getSavingsId();
|
final Long savingsId = savingsDTO.getSavingsId();
|
||||||
@@ -251,5 +253,8 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
|||||||
savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
AccountingProcessorHelper.clearDepositAccountTypeContext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-3
@@ -104,7 +104,12 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" journalEntry.created_on_utc as submittedOnDate, journalEntry.reversed as reversed, ")
|
.append(" journalEntry.created_on_utc as submittedOnDate, journalEntry.reversed as reversed, ")
|
||||||
.append(" journalEntry.currency_code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ")
|
.append(" journalEntry.currency_code as currencyCode, curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ")
|
||||||
.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(" WHEN journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' THEN ca.account_no ")
|
||||||
|
.append(" WHEN journalEntry.entity_type_enum = 2 THEN sa.account_no ")
|
||||||
|
.append(" ELSE NULL ")
|
||||||
|
.append(" END as savingsAccountNo ");
|
||||||
if (associationParametersData.isRunningBalanceRequired()) {
|
if (associationParametersData.isRunningBalanceRequired()) {
|
||||||
sb.append(" ,journalEntry.is_running_balance_calculated as runningBalanceComputed, ")
|
sb.append(" ,journalEntry.is_running_balance_calculated as runningBalanceComputed, ")
|
||||||
.append(" journalEntry.office_running_balance as officeRunningBalance, ")
|
.append(" journalEntry.office_running_balance as officeRunningBalance, ")
|
||||||
@@ -124,7 +129,9 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
.append(" left join m_appuser as creatingUser on creatingUser.id = journalEntry.created_by ")
|
.append(" left join m_appuser as creatingUser on creatingUser.id = journalEntry.created_by ")
|
||||||
.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_current_account ca on ca.id = journalEntry.entity_id AND journalEntry.entity_type_enum = 2 AND journalEntry.transaction_id LIKE 'CA%' ");
|
||||||
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 ")
|
||||||
@@ -235,11 +242,12 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
transactionDetailData = new TransactionDetailData(transaction, paymentDetailData, noteData, transactionTypeEnumData);
|
transactionDetailData = new TransactionDetailData(transaction, paymentDetailData, noteData, transactionTypeEnumData);
|
||||||
}
|
}
|
||||||
final String externalAssetOwner = rs.getString("externalAssetOwner");
|
final String externalAssetOwner = rs.getString("externalAssetOwner");
|
||||||
|
final String savingsAccountNo = rs.getString("savingsAccountNo");
|
||||||
|
|
||||||
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glCode, accountType, transactionDate,
|
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glCode, accountType, transactionDate,
|
||||||
entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||||
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
||||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner);
|
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner, savingsAccountNo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -278,7 +278,7 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry
|
|||||||
final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId);
|
final EnumOptionData entryType = AccountingEnumerations.journalEntryType(entryTypeId);
|
||||||
|
|
||||||
return new JournalEntryData(id, officeId, null, null, glAccountId, null, accountType, null, entryType, amount, null, null, null,
|
return new JournalEntryData(id, officeId, null, null, glAccountId, null, accountType, null, entryType, amount, null, null, null,
|
||||||
null, null, null, null, null, null, null, null, null, null, null, null, null);
|
null, null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+57
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.savings.api;
|
package org.apache.fineract.portfolio.savings.api;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
@@ -32,6 +34,10 @@ import jakarta.ws.rs.QueryParam;
|
|||||||
import jakarta.ws.rs.core.Context;
|
import jakarta.ws.rs.core.Context;
|
||||||
import jakarta.ws.rs.core.MediaType;
|
import jakarta.ws.rs.core.MediaType;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||||
@@ -43,10 +49,14 @@ import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamE
|
|||||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
|
||||||
|
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadService;
|
||||||
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
import org.apache.fineract.portfolio.savings.DepositsApiConstants;
|
||||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Path("/v1/currentaccounts/{accountId}/transactions")
|
@Path("/v1/currentaccounts/{accountId}/transactions")
|
||||||
@@ -55,11 +65,58 @@ import org.springframework.stereotype.Component;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CurrentAccountTransactionsApiResource {
|
public class CurrentAccountTransactionsApiResource {
|
||||||
|
|
||||||
|
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||||
|
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer;
|
private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer;
|
||||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||||
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
|
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
|
||||||
|
private final PaymentTypeReadService paymentTypeReadService;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("template")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "Retrieve current account transaction template")
|
||||||
|
@ApiResponse(responseCode = "200", description = "OK")
|
||||||
|
public String retrieveTemplate(@PathParam("accountId") final Long accountId, @Context final UriInfo uriInfo) {
|
||||||
|
|
||||||
|
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
|
final String sql = "SELECT ca.currency_code, ca.currency_digits, ca.currency_multiplesof, "
|
||||||
|
+ "mc.display_symbol, mc.name AS currency_name, mc.internationalized_name_code "
|
||||||
|
+ "FROM m_current_account ca "
|
||||||
|
+ "LEFT JOIN m_currency mc ON mc.code = ca.currency_code "
|
||||||
|
+ "WHERE ca.id = ?";
|
||||||
|
|
||||||
|
final Map<String, Object> template;
|
||||||
|
try {
|
||||||
|
template = jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
|
||||||
|
final Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
final LocalDate today = LocalDate.now();
|
||||||
|
map.put("date", new int[] { today.getYear(), today.getMonthValue(), today.getDayOfMonth() });
|
||||||
|
|
||||||
|
final Map<String, Object> currency = new LinkedHashMap<>();
|
||||||
|
currency.put("code", rs.getString("currency_code"));
|
||||||
|
currency.put("decimalPlaces", rs.getInt("currency_digits"));
|
||||||
|
currency.put("inMultiplesOf", rs.getInt("currency_multiplesof"));
|
||||||
|
currency.put("displaySymbol", rs.getString("display_symbol"));
|
||||||
|
currency.put("name", rs.getString("currency_name"));
|
||||||
|
currency.put("nameCode", rs.getString("internationalized_name_code"));
|
||||||
|
map.put("currency", currency);
|
||||||
|
return map;
|
||||||
|
}, accountId);
|
||||||
|
} catch (org.springframework.dao.EmptyResultDataAccessException e) {
|
||||||
|
throw new CurrentAccountNotFoundException(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Collection<PaymentTypeData> paymentTypeOptions = paymentTypeReadService.retrieveAllPaymentTypes();
|
||||||
|
template.put("paymentTypeOptions", paymentTypeOptions);
|
||||||
|
|
||||||
|
return GSON.toJson(template);
|
||||||
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{transactionId}")
|
@Path("{transactionId}")
|
||||||
|
|||||||
+13
-2
@@ -38,6 +38,7 @@ import jakarta.ws.rs.core.UriInfo;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -101,10 +102,20 @@ public class CurrentAccountsApiResource {
|
|||||||
@QueryParam("offset") final Integer offset,
|
@QueryParam("offset") final Integer offset,
|
||||||
@QueryParam("limit") final Integer limit,
|
@QueryParam("limit") final Integer limit,
|
||||||
@QueryParam("orderBy") final String orderBy,
|
@QueryParam("orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") final String sortOrder) {
|
@QueryParam("sortOrder") final String sortOrder,
|
||||||
|
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||||
|
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||||
|
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||||
|
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.CURRENT_ACCOUNT_RESOURCE_NAME);
|
||||||
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll();
|
final List<Map<String, Object>> accounts = currentAccountReadPlatformService.retrieveAll(offset, limit, accountNo, clientName,
|
||||||
|
status,
|
||||||
|
fromDate != null ? LocalDate.parse(fromDate) : null,
|
||||||
|
toDate != null ? LocalDate.parse(toDate) : null,
|
||||||
|
asOnDate != null ? LocalDate.parse(asOnDate) : null);
|
||||||
return GSON.toJson(accounts);
|
return GSON.toJson(accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-2
@@ -43,6 +43,7 @@ import jakarta.ws.rs.core.Response;
|
|||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -159,7 +160,13 @@ public class FixedDepositAccountsApiResource {
|
|||||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||||
|
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||||
|
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||||
|
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||||
|
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||||
sqlValidator.validate(orderBy);
|
sqlValidator.validate(orderBy);
|
||||||
@@ -170,7 +177,11 @@ public class FixedDepositAccountsApiResource {
|
|||||||
|
|
||||||
if (paginationParameters.isPaged()) {
|
if (paginationParameters.isPaged()) {
|
||||||
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||||
.retrieveAllPaged(DepositAccountType.FIXED_DEPOSIT, paginationParameters);
|
.retrieveAllPaged(DepositAccountType.FIXED_DEPOSIT, paginationParameters, accountNo, clientName,
|
||||||
|
status,
|
||||||
|
fromDate != null ? LocalDate.parse(fromDate) : null,
|
||||||
|
toDate != null ? LocalDate.parse(toDate) : null,
|
||||||
|
asOnDate != null ? LocalDate.parse(asOnDate) : null);
|
||||||
return this.toApiJsonSerializer.serialize(settings, account,
|
return this.toApiJsonSerializer.serialize(settings, account,
|
||||||
DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
DepositsApiConstants.FIXED_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -43,6 +43,7 @@ import jakarta.ws.rs.core.MediaType;
|
|||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -140,7 +141,13 @@ public class RecurringDepositAccountsApiResource {
|
|||||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||||
|
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||||
|
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||||
|
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||||
|
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||||
|
|
||||||
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
this.context.authenticatedUser().validateHasReadPermission(DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
|
||||||
sqlValidator.validate(orderBy);
|
sqlValidator.validate(orderBy);
|
||||||
@@ -151,7 +158,11 @@ public class RecurringDepositAccountsApiResource {
|
|||||||
|
|
||||||
if (paginationParameters.isPaged()) {
|
if (paginationParameters.isPaged()) {
|
||||||
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
final Page<DepositAccountData> account = this.depositAccountReadPlatformService
|
||||||
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters);
|
.retrieveAllPaged(DepositAccountType.RECURRING_DEPOSIT, paginationParameters, accountNo, clientName,
|
||||||
|
status,
|
||||||
|
fromDate != null ? LocalDate.parse(fromDate) : null,
|
||||||
|
toDate != null ? LocalDate.parse(toDate) : null,
|
||||||
|
asOnDate != null ? LocalDate.parse(asOnDate) : null);
|
||||||
return this.toApiJsonSerializer.serialize(settings, account,
|
return this.toApiJsonSerializer.serialize(settings, account,
|
||||||
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
DepositsApiConstants.RECURRING_DEPOSIT_ACCOUNT_RESPONSE_DATA_PARAMETERS);
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -40,6 +40,7 @@ import jakarta.ws.rs.core.MediaType;
|
|||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -130,7 +131,14 @@ public class SavingsAccountsApiResource {
|
|||||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||||
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
|
||||||
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
|
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder,
|
||||||
|
@QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo,
|
||||||
|
@QueryParam("clientName") @Parameter(description = "clientName") final String clientName,
|
||||||
|
@QueryParam("depositType") @Parameter(description = "depositType") final Integer depositType,
|
||||||
|
@QueryParam("status") @Parameter(description = "status") final String status,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate (YYYY-MM-DD)") final String fromDate,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate (YYYY-MM-DD)") final String toDate,
|
||||||
|
@QueryParam("asOnDate") @Parameter(description = "asOnDate (YYYY-MM-DD)") final String asOnDate) {
|
||||||
|
|
||||||
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
|
||||||
|
|
||||||
@@ -138,7 +146,12 @@ public class SavingsAccountsApiResource {
|
|||||||
sqlValidator.validate(sortOrder);
|
sqlValidator.validate(sortOrder);
|
||||||
sqlValidator.validate(externalId);
|
sqlValidator.validate(externalId);
|
||||||
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset)
|
||||||
.orderBy(orderBy).sortOrder(sortOrder).build();
|
.orderBy(orderBy).sortOrder(sortOrder).accountNo(accountNo).name(clientName).depositType(depositType)
|
||||||
|
.status(status)
|
||||||
|
.fromDate(fromDate != null ? LocalDate.parse(fromDate) : null)
|
||||||
|
.toDate(toDate != null ? LocalDate.parse(toDate) : null)
|
||||||
|
.asOnDate(asOnDate != null ? LocalDate.parse(asOnDate) : null)
|
||||||
|
.build();
|
||||||
|
|
||||||
final Page<SavingsAccountData> products = savingsAccountReadPlatformService.retrieveAll(searchParameters);
|
final Page<SavingsAccountData> products = savingsAccountReadPlatformService.retrieveAll(searchParameters);
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION")
|
@CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION")
|
||||||
public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
|
public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public CurrentAccountTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public CurrentAccountTransactionAdjustmentCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,6 @@ public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCom
|
|||||||
@Override
|
@Override
|
||||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||||
final Long transactionId = Long.valueOf(command.getTransactionId());
|
final Long transactionId = Long.valueOf(command.getTransactionId());
|
||||||
return this.writePlatformService.adjustSavingsTransaction(command.getSavingsId(), transactionId, command);
|
return this.writePlatformService.adjustTransaction(command.getSavingsId(), transactionId, command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT")
|
@CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT")
|
||||||
public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public DepositCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public DepositCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT")
|
@CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT")
|
||||||
public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public HoldAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public HoldAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT")
|
@CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT")
|
||||||
public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ReleaseAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public ReleaseAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION")
|
@CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION")
|
||||||
public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ReverseTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public ReverseTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION")
|
@CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION")
|
||||||
public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public UndoTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public UndoTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
|||||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
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;
|
||||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL")
|
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL")
|
||||||
public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||||
|
|
||||||
private final SavingsAccountWritePlatformService writePlatformService;
|
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public WithdrawCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
public WithdrawCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||||
this.writePlatformService = writePlatformService;
|
this.writePlatformService = writePlatformService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+98
@@ -22,11 +22,13 @@ import java.math.BigDecimal;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.ResultSetMetaData;
|
import java.sql.ResultSetMetaData;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
@@ -226,6 +228,102 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> retrieveAll(final Integer offset, final Integer limit, final String accountNo,
|
||||||
|
final String clientName) {
|
||||||
|
return retrieveAll(offset, limit, accountNo, clientName, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> retrieveAll(final Integer offset, final Integer limit, final String accountNo,
|
||||||
|
final String clientName, final String status, final LocalDate fromDate, final LocalDate toDate,
|
||||||
|
final LocalDate asOnDate) {
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(BASE_SQL);
|
||||||
|
final List<Object> params = new ArrayList<>();
|
||||||
|
boolean hasWhere = false;
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(accountNo)) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("ca.account_no LIKE ?");
|
||||||
|
params.add("%" + accountNo + "%");
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(clientName)) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("c.display_name LIKE ?");
|
||||||
|
params.add("%" + clientName + "%");
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(status)) {
|
||||||
|
final Integer statusEnum = resolveStatusEnum(status);
|
||||||
|
if (statusEnum != null) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("ca.status_enum = ?");
|
||||||
|
params.add(statusEnum);
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fromDate != null) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("ca.activatedon_date >= ?");
|
||||||
|
params.add(fromDate);
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
if (toDate != null) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("ca.activatedon_date <= ?");
|
||||||
|
params.add(toDate);
|
||||||
|
hasWhere = true;
|
||||||
|
}
|
||||||
|
if (asOnDate != null) {
|
||||||
|
sqlBuilder.append(hasWhere ? " AND " : " WHERE ");
|
||||||
|
sqlBuilder.append("ca.activatedon_date <= ?");
|
||||||
|
params.add(asOnDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlBuilder.append(" ORDER BY ca.id DESC");
|
||||||
|
|
||||||
|
if (limit != null && limit > 0) {
|
||||||
|
sqlBuilder.append(" LIMIT ?");
|
||||||
|
params.add(limit);
|
||||||
|
if (offset != null && offset >= 0) {
|
||||||
|
sqlBuilder.append(" OFFSET ?");
|
||||||
|
params.add(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Object>> rows = jdbcTemplate.query(sqlBuilder.toString(), new FlatRowMapper(), params.toArray());
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
final Map<String, Object> response = buildStructuredResponse(row);
|
||||||
|
final Long id = ((Number) row.get("id")).longValue();
|
||||||
|
response.put("charges", retrieveCharges(id));
|
||||||
|
result.add(response);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer resolveStatusEnum(final String status) {
|
||||||
|
if (status == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return switch (status.toLowerCase()) {
|
||||||
|
case "active" -> 300;
|
||||||
|
case "approved" -> 200;
|
||||||
|
case "submitted", "submittedandpendingapproval" -> 100;
|
||||||
|
case "withdrawn" -> 400;
|
||||||
|
case "rejected" -> 500;
|
||||||
|
case "closed" -> 600;
|
||||||
|
default -> {
|
||||||
|
try {
|
||||||
|
yield Integer.parseInt(status);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
yield null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildStructuredResponse(Map<String, Object> row) {
|
private Map<String, Object> buildStructuredResponse(Map<String, Object> row) {
|
||||||
Map<String, Object> out = new LinkedHashMap<>();
|
Map<String, Object> out = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
|||||||
+468
@@ -0,0 +1,468 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.savings.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.accounting.common.AccountingConstants.CashAccountsForSavings;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
|
||||||
|
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
|
||||||
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||||
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
|
import org.apache.fineract.organisation.office.domain.Office;
|
||||||
|
import org.apache.fineract.portfolio.PortfolioProductType;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccount;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransactionRepository;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||||
|
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProduct;
|
||||||
|
import org.apache.fineract.portfolio.currentproduct.domain.CurrentProductRepository;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CurrentAccountTransactionWritePlatformServiceImpl implements CurrentAccountTransactionWritePlatformService {
|
||||||
|
|
||||||
|
// Savings entity type used for GL journal entries (EntityType.SAVINGS = 2)
|
||||||
|
private static final Integer ENTITY_TYPE_SAVINGS = 2;
|
||||||
|
|
||||||
|
private final PlatformSecurityContext context;
|
||||||
|
private final CurrentAccountRepository currentAccountRepository;
|
||||||
|
private final CurrentAccountTransactionRepository currentAccountTransactionRepository;
|
||||||
|
private final CurrentProductRepository currentProductRepository;
|
||||||
|
private final ProductToGLAccountMappingRepository productToGLAccountMappingRepository;
|
||||||
|
private final JournalEntryRepository journalEntryRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult deposit(final Long accountId, final JsonCommand command) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
final Office office = account.getOffice();
|
||||||
|
|
||||||
|
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
if (txnDate == null) txnDate = LocalDate.now();
|
||||||
|
|
||||||
|
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
validatePositiveAmount(amount, accountId, "deposit");
|
||||||
|
|
||||||
|
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
||||||
|
|
||||||
|
final CurrentAccountTransaction txn = CurrentAccountTransaction.deposit(account, office, amount, txnDate, refNo);
|
||||||
|
|
||||||
|
final BigDecimal oldBalance = safeBalance(account.getAccountBalanceDerived());
|
||||||
|
final BigDecimal newBalance = oldBalance.add(amount);
|
||||||
|
txn.setRunningBalanceDerived(newBalance);
|
||||||
|
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||||
|
|
||||||
|
account.setAccountBalanceDerived(newBalance);
|
||||||
|
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).add(amount));
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
postTransactionGl(account, txn, JournalEntryType.DEBIT, JournalEntryType.CREDIT, "Current Account Deposit");
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(txn.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||||
|
.withOfficeId(office != null ? office.getId() : null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult withdrawal(final Long accountId, final JsonCommand command) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
final Office office = account.getOffice();
|
||||||
|
|
||||||
|
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
if (txnDate == null) txnDate = LocalDate.now();
|
||||||
|
|
||||||
|
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
validatePositiveAmount(amount, accountId, "withdrawal");
|
||||||
|
|
||||||
|
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||||
|
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||||
|
final BigDecimal available = currentBalance.subtract(onHold);
|
||||||
|
|
||||||
|
if (!account.isAllowOverdraft() && amount.compareTo(available) > 0) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance",
|
||||||
|
"Insufficient available balance. Available: " + available + ", Requested: " + amount, accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String refNo = command.stringValueOfParameterNamed("accountNumber");
|
||||||
|
|
||||||
|
final CurrentAccountTransaction txn = CurrentAccountTransaction.withdrawal(account, office, amount, txnDate, refNo);
|
||||||
|
|
||||||
|
final BigDecimal newBalance = currentBalance.subtract(amount);
|
||||||
|
txn.setRunningBalanceDerived(newBalance);
|
||||||
|
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||||
|
|
||||||
|
account.setAccountBalanceDerived(newBalance);
|
||||||
|
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
// Withdrawal GL: DR SAVINGS_CONTROL (liability down), CR SAVINGS_REFERENCE (cash out)
|
||||||
|
postTransactionGl(account, txn, JournalEntryType.CREDIT, JournalEntryType.DEBIT, "Current Account Withdrawal");
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(txn.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||||
|
.withOfficeId(office != null ? office.getId() : null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult holdAmount(final Long accountId, final JsonCommand command) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
final Office office = account.getOffice();
|
||||||
|
|
||||||
|
LocalDate txnDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
if (txnDate == null) txnDate = LocalDate.now();
|
||||||
|
|
||||||
|
final BigDecimal amount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
validatePositiveAmount(amount, accountId, "hold amount");
|
||||||
|
|
||||||
|
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||||
|
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||||
|
final BigDecimal available = currentBalance.subtract(onHold);
|
||||||
|
|
||||||
|
if (amount.compareTo(available) > 0) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.insufficient.balance.for.hold",
|
||||||
|
"Insufficient available balance to place hold. Available: " + available + ", Requested: " + amount, accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String reason = command.stringValueOfParameterNamed("reasonForBlock");
|
||||||
|
|
||||||
|
final CurrentAccountTransaction txn = CurrentAccountTransaction.holdAmount(account, office, amount, txnDate, reason);
|
||||||
|
txn.setRunningBalanceDerived(currentBalance);
|
||||||
|
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(txn);
|
||||||
|
|
||||||
|
account.setOnHoldFundsDerived(onHold.add(amount));
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(txn.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.withClientId(account.getClient() != null ? account.getClient().getId() : null)
|
||||||
|
.withOfficeId(office != null ? office.getId() : null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult releaseAmount(final Long accountId, final Long transactionId) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
final Office office = account.getOffice();
|
||||||
|
|
||||||
|
final CurrentAccountTransaction holdTxn = currentAccountTransactionRepository.findById(transactionId)
|
||||||
|
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||||
|
"error.msg.current.account.transaction.not.found",
|
||||||
|
"Hold transaction not found: " + transactionId, transactionId));
|
||||||
|
|
||||||
|
if (!holdTxn.getCurrentAccount().getId().equals(accountId)) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction does not belong to this account.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holdTxn.isReversed()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||||
|
"Hold transaction is already released/reversed.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal holdAmount = holdTxn.getAmount();
|
||||||
|
final BigDecimal currentBalance = safeBalance(account.getAccountBalanceDerived());
|
||||||
|
|
||||||
|
// Mark hold transaction as reversed
|
||||||
|
holdTxn.setReversed(true);
|
||||||
|
currentAccountTransactionRepository.save(holdTxn);
|
||||||
|
|
||||||
|
// Create release transaction
|
||||||
|
final CurrentAccountTransaction releaseTxn = CurrentAccountTransaction.releaseAmount(
|
||||||
|
account, office, holdAmount, LocalDate.now(), transactionId);
|
||||||
|
releaseTxn.setRunningBalanceDerived(currentBalance);
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(releaseTxn);
|
||||||
|
|
||||||
|
// Reduce onHold
|
||||||
|
final BigDecimal onHold = safeBalance(account.getOnHoldFundsDerived());
|
||||||
|
account.setOnHoldFundsDerived(onHold.subtract(holdAmount).max(BigDecimal.ZERO));
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(releaseTxn.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult undoTransaction(final Long accountId, final Long transactionId, final boolean isReversal) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
|
||||||
|
final CurrentAccountTransaction txn = currentAccountTransactionRepository.findById(transactionId)
|
||||||
|
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||||
|
"error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction not found: " + transactionId, transactionId));
|
||||||
|
|
||||||
|
if (!txn.getCurrentAccount().getId().equals(accountId)) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction does not belong to this account.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txn.isReversed()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||||
|
"Transaction is already reversed.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseTransactionBalances(account, txn);
|
||||||
|
|
||||||
|
txn.setReversed(true);
|
||||||
|
currentAccountTransactionRepository.save(txn);
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(transactionId)
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult reverseTransaction(final Long accountId, final Long transactionId,
|
||||||
|
final boolean isReversal, final JsonCommand command) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
|
||||||
|
final CurrentAccountTransaction original = currentAccountTransactionRepository.findById(transactionId)
|
||||||
|
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||||
|
"error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction not found: " + transactionId, transactionId));
|
||||||
|
|
||||||
|
if (!original.getCurrentAccount().getId().equals(accountId)) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction does not belong to this account.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.isReversed()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||||
|
"Transaction is already reversed.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
reverseTransactionBalances(account, original);
|
||||||
|
original.setReversed(true);
|
||||||
|
currentAccountTransactionRepository.save(original);
|
||||||
|
|
||||||
|
// Create a reversal entry
|
||||||
|
final CurrentAccountTransaction reversalTxn = CurrentAccountTransaction.reversal(
|
||||||
|
account, account.getOffice(), original.getAmount(), LocalDate.now(), transactionId);
|
||||||
|
reversalTxn.setRunningBalanceDerived(safeBalance(account.getAccountBalanceDerived()));
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(reversalTxn);
|
||||||
|
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(reversalTxn.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public CommandProcessingResult adjustTransaction(final Long accountId, final Long transactionId,
|
||||||
|
final JsonCommand command) {
|
||||||
|
context.authenticatedUser();
|
||||||
|
|
||||||
|
final CurrentAccount account = findActiveAccount(accountId);
|
||||||
|
final Office office = account.getOffice();
|
||||||
|
|
||||||
|
final CurrentAccountTransaction original = currentAccountTransactionRepository.findById(transactionId)
|
||||||
|
.orElseThrow(() -> new GeneralPlatformDomainRuleException(
|
||||||
|
"error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction not found: " + transactionId, transactionId));
|
||||||
|
|
||||||
|
if (!original.getCurrentAccount().getId().equals(accountId)) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.not.found",
|
||||||
|
"Transaction does not belong to this account.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original.isReversed()) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.transaction.already.reversed",
|
||||||
|
"Transaction is already reversed.", transactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal newAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
|
||||||
|
validatePositiveAmount(newAmount, accountId, "adjust");
|
||||||
|
|
||||||
|
LocalDate newDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||||
|
if (newDate == null) newDate = original.getTransactionDate();
|
||||||
|
|
||||||
|
// Undo original
|
||||||
|
reverseTransactionBalances(account, original);
|
||||||
|
original.setReversed(true);
|
||||||
|
currentAccountTransactionRepository.save(original);
|
||||||
|
|
||||||
|
// Create adjusted transaction of the same type
|
||||||
|
final CurrentAccountTransaction adjusted;
|
||||||
|
if (original.getTransactionTypeEnum() == 1) {
|
||||||
|
adjusted = CurrentAccountTransaction.deposit(account, office, newAmount, newDate,
|
||||||
|
command.stringValueOfParameterNamed("accountNumber"));
|
||||||
|
} else {
|
||||||
|
adjusted = CurrentAccountTransaction.withdrawal(account, office, newAmount, newDate,
|
||||||
|
command.stringValueOfParameterNamed("accountNumber"));
|
||||||
|
}
|
||||||
|
adjusted.setOriginalTransactionId(transactionId);
|
||||||
|
adjusted.setRunningBalanceDerived(safeBalance(account.getAccountBalanceDerived()));
|
||||||
|
currentAccountTransactionRepository.saveAndFlush(adjusted);
|
||||||
|
|
||||||
|
// Apply new transaction to balance
|
||||||
|
applyTransactionToBalance(account, adjusted);
|
||||||
|
currentAccountRepository.save(account);
|
||||||
|
|
||||||
|
return new CommandProcessingResultBuilder()
|
||||||
|
.withEntityId(adjusted.getId())
|
||||||
|
.withSavingsId(accountId)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private CurrentAccount findActiveAccount(final Long accountId) {
|
||||||
|
final CurrentAccount account = currentAccountRepository.findById(accountId)
|
||||||
|
.orElseThrow(() -> new CurrentAccountNotFoundException(accountId));
|
||||||
|
if (!Integer.valueOf(300).equals(account.getStatusEnum())) {
|
||||||
|
throw new GeneralPlatformDomainRuleException("error.msg.current.account.not.active",
|
||||||
|
"Current account is not in Active state.", accountId);
|
||||||
|
}
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validatePositiveAmount(final BigDecimal amount, final Long accountId, final String operation) {
|
||||||
|
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new GeneralPlatformDomainRuleException(
|
||||||
|
"error.msg.current.account." + operation + ".invalid.amount",
|
||||||
|
"Transaction amount must be greater than zero for " + operation + ".", accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal safeBalance(final BigDecimal value) {
|
||||||
|
return value != null ? value : BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reverseTransactionBalances(final CurrentAccount account, final CurrentAccountTransaction txn) {
|
||||||
|
final BigDecimal amount = txn.getAmount();
|
||||||
|
final Integer type = txn.getTransactionTypeEnum();
|
||||||
|
|
||||||
|
if (Integer.valueOf(1).equals(type)) {
|
||||||
|
// Was a deposit: reverse it
|
||||||
|
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).subtract(amount));
|
||||||
|
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).subtract(amount).max(BigDecimal.ZERO));
|
||||||
|
} else if (Integer.valueOf(2).equals(type)) {
|
||||||
|
// Was a withdrawal: reverse it
|
||||||
|
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).add(amount));
|
||||||
|
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).subtract(amount).max(BigDecimal.ZERO));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyTransactionToBalance(final CurrentAccount account, final CurrentAccountTransaction txn) {
|
||||||
|
final BigDecimal amount = txn.getAmount();
|
||||||
|
final Integer type = txn.getTransactionTypeEnum();
|
||||||
|
|
||||||
|
if (Integer.valueOf(1).equals(type)) {
|
||||||
|
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).add(amount));
|
||||||
|
account.setTotalDepositsDerived(safeBalance(account.getTotalDepositsDerived()).add(amount));
|
||||||
|
} else if (Integer.valueOf(2).equals(type)) {
|
||||||
|
account.setAccountBalanceDerived(safeBalance(account.getAccountBalanceDerived()).subtract(amount));
|
||||||
|
account.setTotalWithdrawalsDerived(safeBalance(account.getTotalWithdrawalsDerived()).add(amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL: cashEntryType=DEBIT/CREDIT for SAVINGS_REFERENCE; controlEntryType for SAVINGS_CONTROL
|
||||||
|
private void postTransactionGl(final CurrentAccount account, final CurrentAccountTransaction txn,
|
||||||
|
final JournalEntryType cashEntryType, final JournalEntryType controlEntryType, final String description) {
|
||||||
|
try {
|
||||||
|
final CurrentProduct product = currentProductRepository.findById(account.getProductId()).orElse(null);
|
||||||
|
if (product == null) return;
|
||||||
|
final Integer accountingType = product.getAccountingType();
|
||||||
|
if (accountingType == null || accountingType == 1) return; // NONE = 1
|
||||||
|
|
||||||
|
final GLAccount cashAccount = findGlAccount(account.getProductId(),
|
||||||
|
CashAccountsForSavings.SAVINGS_REFERENCE.getValue());
|
||||||
|
final GLAccount controlAccount = findGlAccount(account.getProductId(),
|
||||||
|
CashAccountsForSavings.SAVINGS_CONTROL.getValue());
|
||||||
|
|
||||||
|
if (cashAccount == null || controlAccount == null) {
|
||||||
|
log.warn("[GL] Missing GL account mapping for current account product id={}", account.getProductId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String ref = "CAT" + txn.getId();
|
||||||
|
postGlEntry(account, cashAccount, cashEntryType, txn.getAmount(), txn.getTransactionDate(), ref, description);
|
||||||
|
postGlEntry(account, controlAccount, controlEntryType, txn.getAmount(), txn.getTransactionDate(), ref, description);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[GL] Failed GL posting for CA transaction id={}: {}", txn.getId(), e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GLAccount findGlAccount(final Long productId, final int financialAccountType) {
|
||||||
|
final var mapping = productToGLAccountMappingRepository
|
||||||
|
.findByProductIdAndProductTypeAndFinancialAccountTypeAndPaymentTypeId(
|
||||||
|
productId, PortfolioProductType.SAVING.getValue(), financialAccountType, null);
|
||||||
|
return mapping != null ? mapping.getGlAccount() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postGlEntry(final CurrentAccount account, final GLAccount glAccount,
|
||||||
|
final JournalEntryType entryType, final BigDecimal amount,
|
||||||
|
final LocalDate date, final String transactionRef, final String description) {
|
||||||
|
final JournalEntry entry = JournalEntry.createNew(
|
||||||
|
account.getOffice(), null, glAccount,
|
||||||
|
account.getCurrencyCode(), transactionRef,
|
||||||
|
false, date, entryType, amount,
|
||||||
|
description,
|
||||||
|
ENTITY_TYPE_SAVINGS, account.getId(),
|
||||||
|
null, null, null, null, null);
|
||||||
|
journalEntryRepository.save(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.savings.service;
|
package org.apache.fineract.portfolio.savings.service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
|
||||||
@@ -33,6 +34,12 @@ public interface DepositAccountReadPlatformService {
|
|||||||
|
|
||||||
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters);
|
||||||
|
|
||||||
|
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
|
||||||
|
String accountNo, String clientName);
|
||||||
|
|
||||||
|
Page<DepositAccountData> retrieveAllPaged(DepositAccountType depositAccountType, PaginationParameters paginationParameters,
|
||||||
|
String accountNo, String clientName, String status, LocalDate fromDate, LocalDate toDate, LocalDate asOnDate);
|
||||||
|
|
||||||
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
|
Collection<DepositAccountData> retrieveAllForLookup(DepositAccountType depositAccountType);
|
||||||
|
|
||||||
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
|
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
|
||||||
|
|||||||
+88
@@ -29,9 +29,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||||
@@ -182,6 +184,92 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
|||||||
depositAccountMapper);
|
depositAccountMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<DepositAccountData> retrieveAllPaged(final DepositAccountType depositAccountType,
|
||||||
|
final PaginationParameters paginationParameters, final String accountNo, final String clientName) {
|
||||||
|
return retrieveAllPaged(depositAccountType, paginationParameters, accountNo, clientName, null, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<DepositAccountData> retrieveAllPaged(final DepositAccountType depositAccountType,
|
||||||
|
final PaginationParameters paginationParameters, final String accountNo, final String clientName,
|
||||||
|
final String status, final LocalDate fromDate, final LocalDate toDate, final LocalDate asOnDate) {
|
||||||
|
|
||||||
|
this.paginationParametersDataValidator.validateParameterValues(paginationParameters, supportedOrderByValues,
|
||||||
|
depositAccountType.resourceName());
|
||||||
|
|
||||||
|
final DepositAccountMapper depositAccountMapper = this.getDepositAccountMapper(depositAccountType);
|
||||||
|
if (depositAccountMapper == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||||
|
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
|
||||||
|
sqlBuilder.append(depositAccountMapper.schema());
|
||||||
|
sqlBuilder.append(" where sa.deposit_type_enum = ? ");
|
||||||
|
|
||||||
|
final List<Object> params = new ArrayList<>();
|
||||||
|
params.add(depositAccountType.getValue());
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(accountNo)) {
|
||||||
|
sqlBuilder.append(" and sa.account_no like ? ");
|
||||||
|
params.add("%" + accountNo + "%");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(clientName)) {
|
||||||
|
sqlBuilder.append(" and c.display_name like ? ");
|
||||||
|
params.add("%" + clientName + "%");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(status)) {
|
||||||
|
final Integer statusEnum = resolveStatusEnum(status);
|
||||||
|
if (statusEnum != null) {
|
||||||
|
sqlBuilder.append(" and sa.status_enum = ? ");
|
||||||
|
params.add(statusEnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fromDate != null) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date >= ? ");
|
||||||
|
params.add(fromDate);
|
||||||
|
}
|
||||||
|
if (toDate != null) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date <= ? ");
|
||||||
|
params.add(toDate);
|
||||||
|
}
|
||||||
|
if (asOnDate != null) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date <= ? ");
|
||||||
|
params.add(asOnDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paginationParameters.hasOrderBy()) {
|
||||||
|
sqlBuilder.append(paginationParameters.paginationSql());
|
||||||
|
} else {
|
||||||
|
sqlBuilder.append(" order by sa.id desc ");
|
||||||
|
sqlBuilder.append(paginationParameters.limitSql());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), params.toArray(), depositAccountMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer resolveStatusEnum(final String status) {
|
||||||
|
if (status == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return switch (status.toLowerCase()) {
|
||||||
|
case "active" -> 300;
|
||||||
|
case "approved" -> 200;
|
||||||
|
case "submitted", "submittedandpendingapproval" -> 100;
|
||||||
|
case "withdrawn" -> 400;
|
||||||
|
case "rejected" -> 500;
|
||||||
|
case "closed" -> 600;
|
||||||
|
default -> {
|
||||||
|
try {
|
||||||
|
yield Integer.parseInt(status);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
yield null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<DepositAccountData> retrieveAllForLookup(final DepositAccountType depositAccountType) {
|
public Collection<DepositAccountData> retrieveAllForLookup(final DepositAccountType depositAccountType) {
|
||||||
|
|
||||||
|
|||||||
+53
-4
@@ -166,15 +166,17 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
sqlBuilder.append(" join m_office o on o.id = c.office_id");
|
sqlBuilder.append(" join m_office o on o.id = c.office_id");
|
||||||
sqlBuilder.append(" where o.hierarchy like ?");
|
sqlBuilder.append(" where o.hierarchy like ?");
|
||||||
|
|
||||||
final Object[] objectArray = new Object[3];
|
final Object[] objectArray = new Object[15];
|
||||||
objectArray[0] = hierarchySearchString;
|
objectArray[0] = hierarchySearchString;
|
||||||
int arrayPos = 1;
|
int arrayPos = 1;
|
||||||
if (searchParameters != null) {
|
if (searchParameters != null) {
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(searchParameters.getStatus())) {
|
if (StringUtils.isNotBlank(searchParameters.getStatus())) {
|
||||||
sqlBuilder.append(" and sa.status_enum = ?");
|
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||||
objectArray[arrayPos] = Integer.parseInt(searchParameters.getStatus());
|
if (statusEnum != null) {
|
||||||
arrayPos = arrayPos + 1;
|
sqlBuilder.append(" and sa.status_enum = ?");
|
||||||
|
objectArray[arrayPos++] = statusEnum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(searchParameters.getExternalId())) {
|
if (StringUtils.isNotBlank(searchParameters.getExternalId())) {
|
||||||
@@ -186,6 +188,30 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
sqlBuilder.append(" and c.office_id = ?");
|
sqlBuilder.append(" and c.office_id = ?");
|
||||||
objectArray[arrayPos++] = searchParameters.getOfficeId();
|
objectArray[arrayPos++] = searchParameters.getOfficeId();
|
||||||
}
|
}
|
||||||
|
if (StringUtils.isNotBlank(searchParameters.getAccountNo())) {
|
||||||
|
sqlBuilder.append(" and sa.account_no like ?");
|
||||||
|
objectArray[arrayPos++] = "%" + searchParameters.getAccountNo() + "%";
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(searchParameters.getName())) {
|
||||||
|
sqlBuilder.append(" and c.display_name like ?");
|
||||||
|
objectArray[arrayPos++] = "%" + searchParameters.getName() + "%";
|
||||||
|
}
|
||||||
|
if (searchParameters.getDepositType() != null) {
|
||||||
|
sqlBuilder.append(" and sa.deposit_type_enum = ?");
|
||||||
|
objectArray[arrayPos++] = searchParameters.getDepositType();
|
||||||
|
}
|
||||||
|
if (searchParameters.hasFromDate()) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date >= ?");
|
||||||
|
objectArray[arrayPos++] = searchParameters.getFromDate();
|
||||||
|
}
|
||||||
|
if (searchParameters.hasToDate()) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date <= ?");
|
||||||
|
objectArray[arrayPos++] = searchParameters.getToDate();
|
||||||
|
}
|
||||||
|
if (searchParameters.hasAsOnDate()) {
|
||||||
|
sqlBuilder.append(" and sa.activatedon_date <= ?");
|
||||||
|
objectArray[arrayPos++] = searchParameters.getAsOnDate();
|
||||||
|
}
|
||||||
if (searchParameters.hasOrderBy()) {
|
if (searchParameters.hasOrderBy()) {
|
||||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||||
@@ -194,6 +220,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
sqlBuilder.append(" order by sa.id desc");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchParameters.hasLimit()) {
|
if (searchParameters.hasLimit()) {
|
||||||
@@ -209,6 +237,27 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
|||||||
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.savingAccountMapper);
|
return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.savingAccountMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer resolveStatusEnum(final String status) {
|
||||||
|
if (status == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return switch (status.toLowerCase()) {
|
||||||
|
case "active" -> 300;
|
||||||
|
case "approved" -> 200;
|
||||||
|
case "submitted", "submittedandpendingapproval" -> 100;
|
||||||
|
case "withdrawn" -> 400;
|
||||||
|
case "rejected" -> 500;
|
||||||
|
case "closed" -> 600;
|
||||||
|
default -> {
|
||||||
|
try {
|
||||||
|
yield Integer.parseInt(status);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
yield null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SavingsAccountData retrieveOne(final Long accountId) {
|
public SavingsAccountData retrieveOne(final Long accountId) {
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1894,6 +1894,7 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
|||||||
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnSavingsProduct());
|
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnSavingsProduct());
|
||||||
accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnSavingsProduct());
|
accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnSavingsProduct());
|
||||||
accountingBridgeData.put("isAccountTransfer", isAccountTransfer);
|
accountingBridgeData.put("isAccountTransfer", isAccountTransfer);
|
||||||
|
accountingBridgeData.put("depositAccountType", depositAccountType().getValue());
|
||||||
|
|
||||||
final List<Map<String, Object>> newSavingsTransactions = new ArrayList<>();
|
final List<Map<String, Object>> newSavingsTransactions = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user