feat(cashier): GL entries endpoint + remove vault-cashier GL postings

- GET /cashreports/gl-entries: returns voucher_no, ac_no, ac_name
  (customer name), dr (withdrawal), cr (deposit) for cashier transactions
  with optional cashierId/tellerId filter
- TellerWritePlatformServiceJpaImpl: removed ALLOCATE/SETTLE GL entries
- OrganisationTellerConfiguration: removed obsolete GL constructor args

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-02 10:47:22 +05:30
co-authored by Claude Sonnet 4.6
parent 6bdcac7b77
commit 7c4cf6260c
2 changed files with 144 additions and 71 deletions
@@ -18,8 +18,6 @@
*/ */
package org.apache.fineract.organisation.teller.starter; package org.apache.fineract.organisation.teller.starter;
import org.apache.fineract.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
@@ -61,11 +59,9 @@ public class OrganisationTellerConfiguration {
public TellerWritePlatformService tellerWritePlatformService(PlatformSecurityContext context, public TellerWritePlatformService tellerWritePlatformService(PlatformSecurityContext context,
TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer, TellerRepositoryWrapper tellerRepositoryWrapper, TellerCommandFromApiJsonDeserializer fromApiJsonDeserializer, TellerRepositoryWrapper tellerRepositoryWrapper,
OfficeRepositoryWrapper officeRepositoryWrapper, StaffRepository staffRepository, CashierRepository cashierRepository, OfficeRepositoryWrapper officeRepositoryWrapper, StaffRepository staffRepository, CashierRepository cashierRepository,
CashierTransactionRepository cashierTxnRepository, JournalEntryRepository glJournalEntryRepository, CashierTransactionRepository cashierTxnRepository,
FinancialActivityAccountRepositoryWrapper financialActivityAccountRepositoryWrapper,
CashierTransactionDataValidator cashierTransactionDataValidator) { CashierTransactionDataValidator cashierTransactionDataValidator) {
return new TellerWritePlatformServiceJpaImpl(context, fromApiJsonDeserializer, tellerRepositoryWrapper, officeRepositoryWrapper, return new TellerWritePlatformServiceJpaImpl(context, fromApiJsonDeserializer, tellerRepositoryWrapper, officeRepositoryWrapper,
staffRepository, cashierRepository, cashierTxnRepository, glJournalEntryRepository, staffRepository, cashierRepository, cashierTxnRepository, cashierTransactionDataValidator);
financialActivityAccountRepositoryWrapper, cashierTransactionDataValidator);
} }
} }
@@ -518,4 +518,81 @@ public class CashReportApiResource {
entry[2] += count * sign; entry[2] += count * sign;
} }
} }
/**
* GET /cashreports/gl-entries?date=yyyy-MM-dd&cashierId=X&tellerId=Y
* Returns cashier transaction summary: voucher no, account no,
* account holder name, DR (withdrawal), CR (deposit).
* - cashierId: filter by specific cashier
* - tellerId: filter by all cashiers under a teller
* - neither: all cashier transactions for the date
*/
@GET
@Path("gl-entries")
public String glEntries(@QueryParam("date") String date,
@QueryParam("cashierId") Long cashierId,
@QueryParam("tellerId") Long tellerId) {
final String txnDate = (date != null && !date.isBlank()) ? date : LocalDate.now().toString();
// Build cashier officer_id filter
String officerFilter;
Object[] params;
if (cashierId != null) {
officerFilter = "AND std.officer_id IN ("
+ "SELECT au.id FROM m_appuser au "
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id WHERE mc.id = ?)";
params = new Object[] { txnDate, cashierId };
} else if (tellerId != null) {
officerFilter = "AND std.officer_id IN ("
+ "SELECT au.id FROM m_appuser au "
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id "
+ "JOIN m_tellers mt ON mt.id = mc.teller_id WHERE mt.id = ?)";
params = new Object[] { txnDate, tellerId };
} else {
officerFilter = "AND std.officer_id IN ("
+ "SELECT au.id FROM m_appuser au "
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id)";
params = new Object[] { txnDate };
}
// One row per transaction — account no, account holder name, dr/cr amount
final String sql = "SELECT "
+ " std.txn_id AS voucher_no, "
+ " std.account_no AS ac_no, "
+ " COALESCE(mc.display_name, '') AS ac_name, "
+ " SUM(CASE WHEN std.txn_type = 'WITHDRAWAL' THEN std.total_amount ELSE 0 END) AS dr_amount, "
+ " SUM(CASE WHEN std.txn_type = 'DEPOSIT' THEN std.total_amount ELSE 0 END) AS cr_amount "
+ "FROM m_savings_txn_denomination std "
+ "LEFT JOIN m_client mc ON mc.id = std.client_id "
+ "WHERE std.txn_date = CAST(? AS DATE) "
+ officerFilter
+ " GROUP BY std.txn_id, std.account_no, mc.display_name "
+ "ORDER BY std.txn_id";
List<Map<String, Object>> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
Map<String, Object> row = new LinkedHashMap<>();
row.put("voucherNo", rs.getLong("voucher_no"));
row.put("acNo", rs.getString("ac_no"));
row.put("acName", rs.getString("ac_name"));
row.put("dr", rs.getBigDecimal("dr_amount"));
row.put("cr", rs.getBigDecimal("cr_amount"));
return row;
}, params);
java.math.BigDecimal totalDr = rows.stream()
.map(r -> (java.math.BigDecimal) r.get("dr"))
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
java.math.BigDecimal totalCr = rows.stream()
.map(r -> (java.math.BigDecimal) r.get("cr"))
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
Map<String, Object> result = new LinkedHashMap<>();
result.put("date", txnDate);
result.put("rows", rows);
result.put("totalDr", totalDr);
result.put("totalCr", totalCr);
return gson.toJson(result);
}
} }