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 = "savingTransactionId", ignore = true)
|
||||
@Mapping(target = "externalAssetOwner", ignore = true)
|
||||
@Mapping(target = "savingsAccountNo", ignore = true)
|
||||
JournalEntryData map(JournalEntry journalEntry);
|
||||
|
||||
@Named("entityType")
|
||||
|
||||
+6
-2
@@ -59,6 +59,8 @@ public class JournalEntryData {
|
||||
@SuppressWarnings("unused")
|
||||
private Long entityId;
|
||||
@SuppressWarnings("unused")
|
||||
private String savingsAccountNo;
|
||||
@SuppressWarnings("unused")
|
||||
private Long createdByUserId;
|
||||
@SuppressWarnings("unused")
|
||||
private OffsetDateTime createdDate;
|
||||
@@ -193,7 +195,8 @@ public class JournalEntryData {
|
||||
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 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.officeId = officeId;
|
||||
this.officeName = officeName;
|
||||
@@ -221,6 +224,7 @@ public class JournalEntryData {
|
||||
this.transactionDetails = transactionDetailData;
|
||||
this.currency = currency;
|
||||
this.externalAssetOwner = externalAssetOwner;
|
||||
this.savingsAccountNo = savingsAccountNo;
|
||||
}
|
||||
|
||||
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,
|
||||
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
|
||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner);
|
||||
runningBalanceComputed, transactionDetailData, currency, externalAssetOwner, null);
|
||||
}
|
||||
|
||||
public Integer getRowIndex() {
|
||||
|
||||
+17
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.core.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -44,6 +45,7 @@ public class SearchParameters {
|
||||
private String orderBy;
|
||||
private String sortOrder;
|
||||
private String accountNo;
|
||||
private Integer depositType;
|
||||
private String currencyCode;
|
||||
private Long staffId;
|
||||
private Long loanId;
|
||||
@@ -57,6 +59,21 @@ public class SearchParameters {
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Boolean isSelfUser;
|
||||
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() {
|
||||
if (limit == null) {
|
||||
|
||||
+44
@@ -146,4 +146,48 @@ public class CurrentAccountTransaction extends AbstractPersistableCustom<Long> {
|
||||
txn.submittedOnDate = transactionDate;
|
||||
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;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -26,4 +27,9 @@ public interface CurrentAccountReadPlatformService {
|
||||
Map<String, Object> retrieveOne(Long accountId);
|
||||
|
||||
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 accrualBasedAccountingEnabled;
|
||||
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 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 PROVISIONING_TRANSACTION_IDENTIFIER = "P";
|
||||
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 ProductToGLAccountMappingRepository accountMappingRepository;
|
||||
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
|
||||
@@ -203,6 +223,9 @@ public class AccountingProcessorHelper {
|
||||
final Long loanProductId = (Long) accountingBridgeData.get("savingsProductId");
|
||||
final Long officeId = (Long) accountingBridgeData.get("officeId");
|
||||
final String currencyCode = (String) accountingBridgeData.get("currencyCode");
|
||||
final int depositAccountType = accountingBridgeData.containsKey("depositAccountType")
|
||||
? (Integer) accountingBridgeData.get("depositAccountType")
|
||||
: 100;
|
||||
final List<SavingsTransactionDTO> newSavingsTransactions = new ArrayList<>();
|
||||
boolean isAccountTransfer = (Boolean) accountingBridgeData.get("isAccountTransfer");
|
||||
|
||||
@@ -264,7 +287,7 @@ public class AccountingProcessorHelper {
|
||||
}
|
||||
|
||||
return new SavingsDTO(loanId, loanProductId, officeId, currencyCode, cashBasedAccountingEnabled, accrualBasedAccountingEnabled,
|
||||
newSavingsTransactions);
|
||||
newSavingsTransactions, depositAccountType);
|
||||
}
|
||||
|
||||
public SharesDTO populateSharesDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
|
||||
@@ -898,7 +921,7 @@ public class AccountingProcessorHelper {
|
||||
String modifiedTransactionId = transactionId;
|
||||
if (StringUtils.isNumeric(transactionId)) {
|
||||
savingsAccountTransactionId = Long.parseLong(transactionId);
|
||||
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
|
||||
modifiedTransactionId = getSavingsTransactionIdentifier() + transactionId;
|
||||
}
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
||||
transactionDate, JournalEntryType.CREDIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
||||
@@ -964,7 +987,7 @@ public class AccountingProcessorHelper {
|
||||
String modifiedTransactionId = transactionId;
|
||||
if (StringUtils.isNumeric(transactionId)) {
|
||||
savingsAccountTransactionId = Long.parseLong(transactionId);
|
||||
modifiedTransactionId = SAVINGS_TRANSACTION_IDENTIFIER + transactionId;
|
||||
modifiedTransactionId = getSavingsTransactionIdentifier() + transactionId;
|
||||
}
|
||||
final JournalEntry journalEntry = JournalEntry.createNew(office, null, account, currencyCode, modifiedTransactionId, manualEntry,
|
||||
transactionDate, JournalEntryType.DEBIT, amount, null, PortfolioProductType.SAVING.getValue(), savingsId, null, null,
|
||||
|
||||
+5
@@ -40,6 +40,8 @@ public class AccrualBasedAccountingProcessorForSavings implements AccountingProc
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||
try {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
@@ -281,5 +283,8 @@ public class AccrualBasedAccountingProcessorForSavings implements AccountingProc
|
||||
paymentTypeId, savingsId, transactionId, transactionDate, amount, isReversal, feePayments);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
AccountingProcessorHelper.clearDepositAccountTypeContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -39,6 +39,8 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
||||
|
||||
@Override
|
||||
public void createJournalEntriesForSavings(final SavingsDTO savingsDTO) {
|
||||
AccountingProcessorHelper.setDepositAccountTypeContext(savingsDTO.getDepositAccountType());
|
||||
try {
|
||||
final GLClosure latestGLClosure = this.helper.getLatestClosureByBranch(savingsDTO.getOfficeId());
|
||||
final Long savingsProductId = savingsDTO.getSavingsProductId();
|
||||
final Long savingsId = savingsDTO.getSavingsId();
|
||||
@@ -251,5 +253,8 @@ public class CashBasedAccountingProcessorForSavings implements AccountingProcess
|
||||
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.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(" 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()) {
|
||||
sb.append(" ,journalEntry.is_running_balance_calculated as runningBalanceComputed, ")
|
||||
.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(" 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 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()) {
|
||||
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 ")
|
||||
@@ -235,11 +242,12 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
||||
transactionDetailData = new TransactionDetailData(transaction, paymentDetailData, noteData, transactionTypeEnumData);
|
||||
}
|
||||
final String externalAssetOwner = rs.getString("externalAssetOwner");
|
||||
final String savingsAccountNo = rs.getString("savingsAccountNo");
|
||||
|
||||
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glCode, accountType, transactionDate,
|
||||
entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, submittedOnDate,
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.MediaType;
|
||||
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 org.apache.commons.lang3.StringUtils;
|
||||
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.DefaultToApiJsonSerializer;
|
||||
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.DepositAccountType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/currentaccounts/{accountId}/transactions")
|
||||
@@ -55,11 +65,58 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor
|
||||
public class CurrentAccountTransactionsApiResource {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final DefaultToApiJsonSerializer<SavingsAccountTransactionData> toApiJsonSerializer;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
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
|
||||
@Path("{transactionId}")
|
||||
|
||||
+13
-2
@@ -38,6 +38,7 @@ import jakarta.ws.rs.core.UriInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -101,10 +102,20 @@ public class CurrentAccountsApiResource {
|
||||
@QueryParam("offset") final Integer offset,
|
||||
@QueryParam("limit") final Integer limit,
|
||||
@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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -43,6 +43,7 @@ import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -159,7 +160,13 @@ public class FixedDepositAccountsApiResource {
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||
@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);
|
||||
sqlValidator.validate(orderBy);
|
||||
@@ -170,7 +177,11 @@ public class FixedDepositAccountsApiResource {
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
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,
|
||||
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.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -140,7 +141,13 @@ public class RecurringDepositAccountsApiResource {
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||
@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);
|
||||
sqlValidator.validate(orderBy);
|
||||
@@ -151,7 +158,11 @@ public class RecurringDepositAccountsApiResource {
|
||||
|
||||
if (paginationParameters.isPaged()) {
|
||||
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,
|
||||
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.UriInfo;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
@@ -130,7 +131,14 @@ public class SavingsAccountsApiResource {
|
||||
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
|
||||
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
|
||||
@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);
|
||||
|
||||
@@ -138,7 +146,12 @@ public class SavingsAccountsApiResource {
|
||||
sqlValidator.validate(sortOrder);
|
||||
sqlValidator.validate(externalId);
|
||||
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);
|
||||
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountTransactionWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "ADJUSTTRANSACTION")
|
||||
public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CurrentAccountTransactionAdjustmentCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public CurrentAccountTransactionAdjustmentCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,6 @@ public class CurrentAccountTransactionAdjustmentCommandHandler implements NewCom
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "DEPOSIT")
|
||||
public class DepositCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DepositCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public DepositCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService 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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "HOLDAMOUNT")
|
||||
public class HoldAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public HoldAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public HoldAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService 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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "RELEASEAMOUNT")
|
||||
public class ReleaseAmountCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ReleaseAmountCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ReleaseAmountCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService 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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "REVERSETRANSACTION")
|
||||
public class ReverseTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public ReverseTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public ReverseTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService 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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "UNDOTRANSACTION")
|
||||
public class UndoTransactionCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UndoTransactionCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public UndoTransactionCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService 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.infrastructure.core.api.JsonCommand;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -31,10 +31,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@CommandType(entity = "CURRENTACCOUNT", action = "WITHDRAWAL")
|
||||
public class WithdrawCurrentAccountCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SavingsAccountWritePlatformService writePlatformService;
|
||||
private final CurrentAccountTransactionWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public WithdrawCurrentAccountCommandHandler(final SavingsAccountWritePlatformService writePlatformService) {
|
||||
public WithdrawCurrentAccountCommandHandler(final CurrentAccountTransactionWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
|
||||
+98
@@ -22,11 +22,13 @@ import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.portfolio.currentaccount.exception.CurrentAccountNotFoundException;
|
||||
import org.apache.fineract.portfolio.currentaccount.service.CurrentAccountReadPlatformService;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -226,6 +228,102 @@ public class CurrentAccountReadPlatformServiceImpl implements CurrentAccountRead
|
||||
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) {
|
||||
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;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
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,
|
||||
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);
|
||||
|
||||
DepositAccountData retrieveOne(DepositAccountType depositAccountType, Long accountId);
|
||||
|
||||
+88
@@ -29,9 +29,11 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
|
||||
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -182,6 +184,92 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
|
||||
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
|
||||
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(" where o.hierarchy like ?");
|
||||
|
||||
final Object[] objectArray = new Object[3];
|
||||
final Object[] objectArray = new Object[15];
|
||||
objectArray[0] = hierarchySearchString;
|
||||
int arrayPos = 1;
|
||||
if (searchParameters != null) {
|
||||
|
||||
if (StringUtils.isNotBlank(searchParameters.getStatus())) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos] = Integer.parseInt(searchParameters.getStatus());
|
||||
arrayPos = arrayPos + 1;
|
||||
final Integer statusEnum = resolveStatusEnum(searchParameters.getStatus());
|
||||
if (statusEnum != null) {
|
||||
sqlBuilder.append(" and sa.status_enum = ?");
|
||||
objectArray[arrayPos++] = statusEnum;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(searchParameters.getExternalId())) {
|
||||
@@ -186,6 +188,30 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append(" and c.office_id = ?");
|
||||
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()) {
|
||||
sqlBuilder.append(" order by ").append(searchParameters.getOrderBy());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy());
|
||||
@@ -194,6 +220,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
sqlBuilder.append(' ').append(searchParameters.getSortOrder());
|
||||
this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getSortOrder());
|
||||
}
|
||||
} else {
|
||||
sqlBuilder.append(" order by sa.id desc");
|
||||
}
|
||||
|
||||
if (searchParameters.hasLimit()) {
|
||||
@@ -209,6 +237,27 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
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
|
||||
public SavingsAccountData retrieveOne(final Long accountId) {
|
||||
|
||||
|
||||
+1
@@ -1894,6 +1894,7 @@ public class SavingsAccount extends AbstractAuditableWithUTCDateTimeCustom<Long>
|
||||
accountingBridgeData.put("cashBasedAccountingEnabled", isCashBasedAccountingEnabledOnSavingsProduct());
|
||||
accountingBridgeData.put("accrualBasedAccountingEnabled", isAccrualBasedAccountingEnabledOnSavingsProduct());
|
||||
accountingBridgeData.put("isAccountTransfer", isAccountTransfer);
|
||||
accountingBridgeData.put("depositAccountType", depositAccountType().getValue());
|
||||
|
||||
final List<Map<String, Object>> newSavingsTransactions = new ArrayList<>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user