updade code based on the accounting deatils search
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.accounting.journalentry.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class DayWiseSummaryData {
|
||||||
|
|
||||||
|
private LocalDate date;
|
||||||
|
private BigDecimal openingBalance;
|
||||||
|
private BigDecimal totalDebit;
|
||||||
|
private BigDecimal totalCredit;
|
||||||
|
private BigDecimal netMovement;
|
||||||
|
private BigDecimal closingBalance;
|
||||||
|
private int entryCount;
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.accounting.journalentry.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.Page;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class GLAccountLedgerReportData {
|
||||||
|
|
||||||
|
/** GL Account details (name, code, type, description, usage) */
|
||||||
|
private GLAccountData glAccount;
|
||||||
|
|
||||||
|
/** Balance of the account before the fromDate (0 if no fromDate or no glAccountId) */
|
||||||
|
private BigDecimal openingBalance;
|
||||||
|
|
||||||
|
/** Balance of the account as of toDate = openingBalance + netMovement */
|
||||||
|
private BigDecimal closingBalance;
|
||||||
|
|
||||||
|
/** Sum of all DEBIT entries in the requested period */
|
||||||
|
private BigDecimal totalDebit;
|
||||||
|
|
||||||
|
/** Sum of all CREDIT entries in the requested period */
|
||||||
|
private BigDecimal totalCredit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Net movement: for ASSET/EXPENSE (debit-normal) = totalDebit - totalCredit;
|
||||||
|
* for LIABILITY/INCOME/EQUITY (credit-normal) = totalCredit - totalDebit
|
||||||
|
*/
|
||||||
|
private BigDecimal netMovement;
|
||||||
|
|
||||||
|
/** true for ASSET and EXPENSE accounts (debit-normal balance convention) */
|
||||||
|
private boolean debitNormal;
|
||||||
|
|
||||||
|
private LocalDate fromDate;
|
||||||
|
private LocalDate toDate;
|
||||||
|
|
||||||
|
/** Day-wise breakdown of balances for the period */
|
||||||
|
private List<DayWiseSummaryData> dayWiseSummary;
|
||||||
|
|
||||||
|
/** Total number of journal entries matching the filter (for the KPI card) */
|
||||||
|
private long totalEntries;
|
||||||
|
|
||||||
|
/** Paginated journal entries for the period */
|
||||||
|
private Page<JournalEntryData> entries;
|
||||||
|
}
|
||||||
+8
@@ -19,6 +19,7 @@
|
|||||||
package org.apache.fineract.accounting.journalentry.service;
|
package org.apache.fineract.accounting.journalentry.service;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.GLAccountLedgerReportData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
||||||
@@ -36,4 +37,11 @@ public interface JournalEntryReadPlatformService {
|
|||||||
OfficeOpeningBalancesData retrieveOfficeOpeningBalances(Long officeId, String currencyCode);
|
OfficeOpeningBalancesData retrieveOfficeOpeningBalances(Long officeId, String currencyCode);
|
||||||
|
|
||||||
Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType);
|
Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBS GL Ledger Report: returns opening balance, day-wise summary, and paginated entries for a GL account.
|
||||||
|
* Opening balance is computed as the net balance of all entries before fromDate.
|
||||||
|
*/
|
||||||
|
GLAccountLedgerReportData retrieveGLAccountLedger(Long glAccountId, Long officeId, Boolean onlyManualEntries,
|
||||||
|
LocalDate fromDate, LocalDate toDate, String transactionId, SearchParameters searchParameters);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+811
-22449
File diff suppressed because it is too large
Load Diff
+52
@@ -44,6 +44,7 @@ import java.util.Set;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.accounting.journalentry.command.JournalEntryCommand;
|
import org.apache.fineract.accounting.journalentry.command.JournalEntryCommand;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.GLAccountLedgerReportData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
||||||
@@ -268,6 +269,57 @@ public class JournalEntriesApiResource {
|
|||||||
return this.apiJsonSerializerService.serialize(settings, officeOpeningBalancesData);
|
return this.apiJsonSerializerService.serialize(settings, officeOpeningBalancesData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBS GL Account Ledger Report — returns opening balance, day-wise summary,
|
||||||
|
* period totals, and paginated entries for a given GL account and date range.
|
||||||
|
*
|
||||||
|
* GET /journalentries/ledger?glAccountId=5&fromDate=01 Jan 2025&toDate=31 Jan 2025&dateFormat=dd MMM yyyy&locale=en
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("ledger")
|
||||||
|
@Consumes({ MediaType.APPLICATION_JSON })
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON })
|
||||||
|
@Operation(summary = "GL Account Ledger Report",
|
||||||
|
description = "Returns opening balance, day-wise running balance summary, period totals, and paginated journal entries for a GL account.")
|
||||||
|
public String retrieveGLAccountLedger(@Context final UriInfo uriInfo,
|
||||||
|
@QueryParam("glAccountId") @Parameter(description = "glAccountId") final Long glAccountId,
|
||||||
|
@QueryParam("officeId") @Parameter(description = "officeId") final Long officeId,
|
||||||
|
@QueryParam("manualEntriesOnly") @Parameter(description = "manualEntriesOnly") final Boolean onlyManualEntries,
|
||||||
|
@QueryParam("fromDate") @Parameter(description = "fromDate") final DateParam fromDateParam,
|
||||||
|
@QueryParam("toDate") @Parameter(description = "toDate") final DateParam toDateParam,
|
||||||
|
@QueryParam("transactionId") @Parameter(description = "transactionId") final String transactionId,
|
||||||
|
@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("locale") @Parameter(description = "locale") final String locale,
|
||||||
|
@QueryParam("dateFormat") @Parameter(description = "dateFormat") final String rawDateFormat) {
|
||||||
|
|
||||||
|
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
|
||||||
|
|
||||||
|
final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? null : new DateFormat(rawDateFormat);
|
||||||
|
LocalDate fromDate = null;
|
||||||
|
if (fromDateParam != null) {
|
||||||
|
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
|
||||||
|
}
|
||||||
|
LocalDate toDate = null;
|
||||||
|
if (toDateParam != null) {
|
||||||
|
toDate = toDateParam.getDate("toDate", dateFormat, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlValidator.validate(orderBy);
|
||||||
|
sqlValidator.validate(sortOrder);
|
||||||
|
final SearchParameters searchParameters = SearchParameters.builder()
|
||||||
|
.limit(limit).officeId(officeId).offset(offset)
|
||||||
|
.orderBy(orderBy).sortOrder(sortOrder).build();
|
||||||
|
|
||||||
|
final GLAccountLedgerReportData ledgerData = this.journalEntryReadPlatformService
|
||||||
|
.retrieveGLAccountLedger(glAccountId, officeId, onlyManualEntries, fromDate, toDate, transactionId, searchParameters);
|
||||||
|
|
||||||
|
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||||
|
return this.apiJsonSerializerService.serialize(settings, ledgerData);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean is(final String commandParam, final String commandValue) {
|
private boolean is(final String commandParam, final String commandValue) {
|
||||||
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
|
||||||
}
|
}
|
||||||
|
|||||||
+225
-10
@@ -28,6 +28,7 @@ import java.time.OffsetDateTime;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -37,6 +38,8 @@ import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialA
|
|||||||
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||||
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
|
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.DayWiseSummaryData;
|
||||||
|
import org.apache.fineract.accounting.journalentry.data.GLAccountLedgerReportData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
|
||||||
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
|
||||||
@@ -213,16 +216,20 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
|
|
||||||
TransactionTypeEnumData transactionTypeEnumData = null;
|
TransactionTypeEnumData transactionTypeEnumData = null;
|
||||||
|
|
||||||
if (PortfolioAccountType.fromInt(entityTypeId).isLoanAccount()) {
|
if (entityTypeId != null && PortfolioAccountType.fromInt(entityTypeId).isLoanAccount()) {
|
||||||
final LoanTransactionEnumData loanTransactionType = LoanEnumerations
|
final Integer loanTxnTypeId = JdbcSupport.getInteger(rs, "loanTransactionType");
|
||||||
.transactionType(JdbcSupport.getInteger(rs, "loanTransactionType"));
|
if (loanTxnTypeId != null) {
|
||||||
transactionTypeEnumData = new TransactionTypeEnumData(loanTransactionType.getId(), loanTransactionType.getCode(),
|
final LoanTransactionEnumData loanTransactionType = LoanEnumerations.transactionType(loanTxnTypeId);
|
||||||
loanTransactionType.getValue());
|
transactionTypeEnumData = new TransactionTypeEnumData(loanTransactionType.getId(), loanTransactionType.getCode(),
|
||||||
} else if (PortfolioAccountType.fromInt(entityTypeId).isSavingsAccount()) {
|
loanTransactionType.getValue());
|
||||||
final SavingsAccountTransactionEnumData savingsTransactionType = SavingsEnumerations
|
}
|
||||||
.transactionType(JdbcSupport.getInteger(rs, "savingsTransactionType"));
|
} else if (entityTypeId != null && PortfolioAccountType.fromInt(entityTypeId).isSavingsAccount()) {
|
||||||
transactionTypeEnumData = new TransactionTypeEnumData(savingsTransactionType.getId(), savingsTransactionType.getCode(),
|
final Integer savingsTxnTypeId = JdbcSupport.getInteger(rs, "savingsTransactionType");
|
||||||
savingsTransactionType.getValue());
|
if (savingsTxnTypeId != null) {
|
||||||
|
final SavingsAccountTransactionEnumData savingsTransactionType = SavingsEnumerations.transactionType(savingsTxnTypeId);
|
||||||
|
transactionTypeEnumData = new TransactionTypeEnumData(savingsTransactionType.getId(), savingsTransactionType.getCode(),
|
||||||
|
savingsTransactionType.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transactionDetailData = new TransactionDetailData(transaction, paymentDetailData, noteData, transactionTypeEnumData);
|
transactionDetailData = new TransactionDetailData(transaction, paymentDetailData, noteData, transactionTypeEnumData);
|
||||||
@@ -539,4 +546,212 @@ public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlat
|
|||||||
throw new JournalEntriesNotFoundException(entityId, e);
|
throw new JournalEntriesNotFoundException(entityId, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GLAccountLedgerReportData retrieveGLAccountLedger(final Long glAccountId, final Long officeId,
|
||||||
|
final Boolean onlyManualEntries, final LocalDate fromDate, final LocalDate toDate,
|
||||||
|
final String transactionId, final SearchParameters searchParameters) {
|
||||||
|
|
||||||
|
// ── 1. Fetch GL Account details ──────────────────────────────────────
|
||||||
|
GLAccountData glAccountData = null;
|
||||||
|
boolean isDebitNormal = true; // ASSET / EXPENSE = debit-normal
|
||||||
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
|
glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId,
|
||||||
|
new JournalEntryAssociationParametersData());
|
||||||
|
if (glAccountData != null && glAccountData.getType() != null) {
|
||||||
|
final int typeId = glAccountData.getType().getId().intValue();
|
||||||
|
// 1=ASSET, 2=LIABILITY, 3=EQUITY, 4=INCOME, 5=EXPENSE
|
||||||
|
isDebitNormal = (typeId == 1 || typeId == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 2. Compute opening balance (all entries before fromDate) ─────────
|
||||||
|
BigDecimal openingBalance = BigDecimal.ZERO;
|
||||||
|
if (glAccountId != null && glAccountId > 0 && fromDate != null) {
|
||||||
|
final List<Object> obParams = new ArrayList<>();
|
||||||
|
final StringBuilder obSql = new StringBuilder(
|
||||||
|
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS ob_debit, "
|
||||||
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS ob_credit "
|
||||||
|
+ "FROM acc_gl_journal_entry je "
|
||||||
|
+ "WHERE je.account_id = ? AND je.entry_date < ?");
|
||||||
|
obParams.add(glAccountId);
|
||||||
|
obParams.add(fromDate);
|
||||||
|
if (officeId != null && officeId > 0) {
|
||||||
|
obSql.append(" AND je.office_id = ?");
|
||||||
|
obParams.add(officeId);
|
||||||
|
}
|
||||||
|
final BigDecimal[] ob = this.jdbcTemplate.queryForObject(obSql.toString(),
|
||||||
|
(rs, rowNum) -> new BigDecimal[]{
|
||||||
|
Objects.requireNonNullElse(rs.getBigDecimal("ob_debit"), BigDecimal.ZERO),
|
||||||
|
Objects.requireNonNullElse(rs.getBigDecimal("ob_credit"), BigDecimal.ZERO)
|
||||||
|
}, obParams.toArray());
|
||||||
|
if (ob != null) {
|
||||||
|
openingBalance = isDebitNormal ? ob[0].subtract(ob[1]) : ob[1].subtract(ob[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 3. Day-wise summary for the period ───────────────────────────────
|
||||||
|
final List<DayWiseSummaryData> dayWiseSummary = new ArrayList<>();
|
||||||
|
{
|
||||||
|
final List<Object> dwParams = new ArrayList<>();
|
||||||
|
final StringBuilder dwSql = new StringBuilder(
|
||||||
|
"SELECT je.entry_date AS dw_date, "
|
||||||
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS dw_debit, "
|
||||||
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS dw_credit, "
|
||||||
|
+ "COUNT(*) AS dw_count "
|
||||||
|
+ "FROM acc_gl_journal_entry je "
|
||||||
|
+ "WHERE 1=1");
|
||||||
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
|
dwSql.append(" AND je.account_id = ?");
|
||||||
|
dwParams.add(glAccountId);
|
||||||
|
}
|
||||||
|
if (officeId != null && officeId > 0) {
|
||||||
|
dwSql.append(" AND je.office_id = ?");
|
||||||
|
dwParams.add(officeId);
|
||||||
|
}
|
||||||
|
if (fromDate != null) {
|
||||||
|
dwSql.append(" AND je.entry_date >= ?");
|
||||||
|
dwParams.add(fromDate);
|
||||||
|
}
|
||||||
|
if (toDate != null) {
|
||||||
|
dwSql.append(" AND je.entry_date <= ?");
|
||||||
|
dwParams.add(toDate);
|
||||||
|
}
|
||||||
|
if (onlyManualEntries != null && onlyManualEntries) {
|
||||||
|
dwSql.append(" AND je.manual_entry = true");
|
||||||
|
}
|
||||||
|
dwSql.append(" GROUP BY je.entry_date ORDER BY je.entry_date ASC");
|
||||||
|
|
||||||
|
final boolean debitNormalFinal = isDebitNormal;
|
||||||
|
final List<Map<String, Object>> rows = this.jdbcTemplate.queryForList(dwSql.toString(), dwParams.toArray());
|
||||||
|
|
||||||
|
BigDecimal running = openingBalance;
|
||||||
|
for (final Map<String, Object> row : rows) {
|
||||||
|
// Handle both java.sql.Date (older drivers) and java.time.LocalDate (newer drivers)
|
||||||
|
final Object rawDate = row.get("dw_date");
|
||||||
|
LocalDate date;
|
||||||
|
if (rawDate instanceof LocalDate) {
|
||||||
|
date = (LocalDate) rawDate;
|
||||||
|
} else if (rawDate instanceof java.sql.Date) {
|
||||||
|
date = ((java.sql.Date) rawDate).toLocalDate();
|
||||||
|
} else {
|
||||||
|
date = LocalDate.parse(String.valueOf(rawDate).substring(0, 10));
|
||||||
|
}
|
||||||
|
final BigDecimal dr = Objects.requireNonNullElse((BigDecimal) row.get("dw_debit"), BigDecimal.ZERO);
|
||||||
|
final BigDecimal cr = Objects.requireNonNullElse((BigDecimal) row.get("dw_credit"), BigDecimal.ZERO);
|
||||||
|
final int count = ((Number) row.get("dw_count")).intValue();
|
||||||
|
final BigDecimal net = debitNormalFinal ? dr.subtract(cr) : cr.subtract(dr);
|
||||||
|
final BigDecimal closing = running.add(net);
|
||||||
|
dayWiseSummary.add(DayWiseSummaryData.builder()
|
||||||
|
.date(date)
|
||||||
|
.openingBalance(running)
|
||||||
|
.totalDebit(dr)
|
||||||
|
.totalCredit(cr)
|
||||||
|
.netMovement(net)
|
||||||
|
.closingBalance(closing)
|
||||||
|
.entryCount(count)
|
||||||
|
.build());
|
||||||
|
running = closing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 4. Paginated journal entries — only fetch if limit > 0 ───────────
|
||||||
|
final JournalEntryAssociationParametersData assocParams = new JournalEntryAssociationParametersData(true, true);
|
||||||
|
final int reqLimit = searchParameters != null && searchParameters.getLimit() != null
|
||||||
|
? searchParameters.getLimit() : 0;
|
||||||
|
final Page<JournalEntryData> entries;
|
||||||
|
if (reqLimit > 0) {
|
||||||
|
entries = retrieveAll(searchParameters, glAccountId, onlyManualEntries,
|
||||||
|
fromDate, toDate, null, null, transactionId, null, assocParams);
|
||||||
|
} else {
|
||||||
|
// Summary-only request — return empty page, no SQL needed
|
||||||
|
entries = new Page<>(new ArrayList<>(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 5. Compute period totals ─────────────────────────────────────────
|
||||||
|
BigDecimal totalDebit = BigDecimal.ZERO;
|
||||||
|
BigDecimal totalCredit = BigDecimal.ZERO;
|
||||||
|
{
|
||||||
|
final List<Object> totParams = new ArrayList<>();
|
||||||
|
final StringBuilder totSql = new StringBuilder(
|
||||||
|
"SELECT COALESCE(SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE 0 END), 0) AS totalDebit, "
|
||||||
|
+ "COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE 0 END), 0) AS totalCredit "
|
||||||
|
+ "FROM acc_gl_journal_entry je WHERE 1=1");
|
||||||
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
|
totSql.append(" AND je.account_id = ?");
|
||||||
|
totParams.add(glAccountId);
|
||||||
|
}
|
||||||
|
if (officeId != null && officeId > 0) {
|
||||||
|
totSql.append(" AND je.office_id = ?");
|
||||||
|
totParams.add(officeId);
|
||||||
|
}
|
||||||
|
if (fromDate != null) {
|
||||||
|
totSql.append(" AND je.entry_date >= ?");
|
||||||
|
totParams.add(fromDate);
|
||||||
|
}
|
||||||
|
if (toDate != null) {
|
||||||
|
totSql.append(" AND je.entry_date <= ?");
|
||||||
|
totParams.add(toDate);
|
||||||
|
}
|
||||||
|
if (onlyManualEntries != null && onlyManualEntries) {
|
||||||
|
totSql.append(" AND je.manual_entry = true");
|
||||||
|
}
|
||||||
|
final BigDecimal[] totResult = this.jdbcTemplate.queryForObject(totSql.toString(),
|
||||||
|
(rs, rowNum) -> new BigDecimal[]{
|
||||||
|
Objects.requireNonNullElse(rs.getBigDecimal("totalDebit"), BigDecimal.ZERO),
|
||||||
|
Objects.requireNonNullElse(rs.getBigDecimal("totalCredit"), BigDecimal.ZERO)
|
||||||
|
}, totParams.toArray());
|
||||||
|
if (totResult != null) {
|
||||||
|
totalDebit = totResult[0];
|
||||||
|
totalCredit = totResult[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final BigDecimal netMovement = isDebitNormal ? totalDebit.subtract(totalCredit) : totalCredit.subtract(totalDebit);
|
||||||
|
final BigDecimal closingBalance = openingBalance.add(netMovement);
|
||||||
|
|
||||||
|
// ── 6. Total entries count ────────────────────────────────────────────
|
||||||
|
long totalEntries = 0L;
|
||||||
|
{
|
||||||
|
final List<Object> cntParams = new ArrayList<>();
|
||||||
|
final StringBuilder cntSql = new StringBuilder(
|
||||||
|
"SELECT COUNT(*) FROM acc_gl_journal_entry je WHERE 1=1");
|
||||||
|
if (glAccountId != null && glAccountId > 0) {
|
||||||
|
cntSql.append(" AND je.account_id = ?");
|
||||||
|
cntParams.add(glAccountId);
|
||||||
|
}
|
||||||
|
if (officeId != null && officeId > 0) {
|
||||||
|
cntSql.append(" AND je.office_id = ?");
|
||||||
|
cntParams.add(officeId);
|
||||||
|
}
|
||||||
|
if (fromDate != null) {
|
||||||
|
cntSql.append(" AND je.entry_date >= ?");
|
||||||
|
cntParams.add(fromDate);
|
||||||
|
}
|
||||||
|
if (toDate != null) {
|
||||||
|
cntSql.append(" AND je.entry_date <= ?");
|
||||||
|
cntParams.add(toDate);
|
||||||
|
}
|
||||||
|
if (onlyManualEntries != null && onlyManualEntries) {
|
||||||
|
cntSql.append(" AND je.manual_entry = true");
|
||||||
|
}
|
||||||
|
final Long cnt = this.jdbcTemplate.queryForObject(cntSql.toString(), Long.class, cntParams.toArray());
|
||||||
|
if (cnt != null) { totalEntries = cnt; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return GLAccountLedgerReportData.builder()
|
||||||
|
.glAccount(glAccountData)
|
||||||
|
.openingBalance(openingBalance)
|
||||||
|
.closingBalance(closingBalance)
|
||||||
|
.totalDebit(totalDebit)
|
||||||
|
.totalCredit(totalCredit)
|
||||||
|
.netMovement(netMovement)
|
||||||
|
.debitNormal(isDebitNormal)
|
||||||
|
.fromDate(fromDate)
|
||||||
|
.toDate(toDate)
|
||||||
|
.dayWiseSummary(dayWiseSummary)
|
||||||
|
.totalEntries(totalEntries)
|
||||||
|
.entries(entries)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-7
@@ -595,26 +595,29 @@ public class CurrentAccountWritePlatformServiceImpl implements CurrentAccountWri
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String transactionRef = "CA" + account.getId();
|
// Deposit uses CA{accountId} as transaction ref (one per activation)
|
||||||
|
final String depositRef = "CA" + account.getId();
|
||||||
|
|
||||||
// Deposits: DR SAVINGS_REFERENCE (cash in), CR SAVINGS_CONTROL (liability)
|
// Deposits: DR SAVINGS_REFERENCE (cash in), CR SAVINGS_CONTROL (liability)
|
||||||
if (depositTxns != null) {
|
if (depositTxns != null) {
|
||||||
for (final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn : depositTxns) {
|
for (final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn : depositTxns) {
|
||||||
if (txn.getId() == null || txn.getAmount() == null) continue;
|
if (txn.getId() == null || txn.getAmount() == null) continue;
|
||||||
postEntry(account, cashAccount, JournalEntryType.DEBIT, txn.getAmount(), txn.getTransactionDate(), transactionRef);
|
postEntry(account, cashAccount, JournalEntryType.DEBIT, txn.getAmount(), txn.getTransactionDate(), depositRef);
|
||||||
postEntry(account, controlAccount, JournalEntryType.CREDIT, txn.getAmount(), txn.getTransactionDate(), transactionRef);
|
postEntry(account, controlAccount, JournalEntryType.CREDIT, txn.getAmount(), txn.getTransactionDate(), depositRef);
|
||||||
log.info("[GL] Deposit GL posted for account id={}, amount={}", account.getId(), txn.getAmount());
|
log.info("[GL] Deposit GL posted for account id={}, amount={}", account.getId(), txn.getAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charges: DR SAVINGS_CONTROL (liability reduced), CR INCOME_FROM_FEES
|
// Charges: each charge gets its own transaction ref = "CAC" + txn.getId()
|
||||||
|
// so each charge appears as a separate transaction in journal entries
|
||||||
if (chargeEntries != null && feeIncomeAccount != null) {
|
if (chargeEntries != null && feeIncomeAccount != null) {
|
||||||
for (final ChargeGlEntry entry : chargeEntries) {
|
for (final ChargeGlEntry entry : chargeEntries) {
|
||||||
final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn = entry.txn();
|
final org.apache.fineract.portfolio.currentaccount.domain.CurrentAccountTransaction txn = entry.txn();
|
||||||
if (txn.getId() == null || txn.getAmount() == null) continue;
|
if (txn.getId() == null || txn.getAmount() == null) continue;
|
||||||
postEntry(account, controlAccount, JournalEntryType.DEBIT, txn.getAmount(), txn.getTransactionDate(), transactionRef);
|
final String chargeRef = "CAC" + txn.getId();
|
||||||
postEntry(account, feeIncomeAccount, JournalEntryType.CREDIT, txn.getAmount(), txn.getTransactionDate(), transactionRef);
|
postEntry(account, controlAccount, JournalEntryType.DEBIT, txn.getAmount(), txn.getTransactionDate(), chargeRef);
|
||||||
log.info("[GL] Charge GL posted for account id={}, charge={}, amount={}", account.getId(), entry.charge().getCharge().getName(), txn.getAmount());
|
postEntry(account, feeIncomeAccount, JournalEntryType.CREDIT, txn.getAmount(), txn.getTransactionDate(), chargeRef);
|
||||||
|
log.info("[GL] Charge GL posted for account id={}, charge={}, amount={}, ref={}", account.getId(), entry.charge().getCharge().getName(), txn.getAmount(), chargeRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user