feat(cashier): add server-side pagination and date ordering to GL entries endpoint
- Add offset/limit query params with defaults (limit=20, offset=0) - Return totalFilteredRecords + pageItems response structure - Compute totalDr/totalCr from all records (not just current page) - Fix ordering: ORDER BY MAX(created_at) for correct chronological sort Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
19639c9e02
commit
db94056144
+47
-25
@@ -558,34 +558,59 @@ public class CashReportApiResource {
|
|||||||
@Path("gl-entries")
|
@Path("gl-entries")
|
||||||
public String glEntries(@QueryParam("date") String date,
|
public String glEntries(@QueryParam("date") String date,
|
||||||
@QueryParam("cashierId") Long cashierId,
|
@QueryParam("cashierId") Long cashierId,
|
||||||
@QueryParam("tellerId") Long tellerId) {
|
@QueryParam("tellerId") Long tellerId,
|
||||||
|
@QueryParam("offset") Integer offset,
|
||||||
|
@QueryParam("limit") Integer limit) {
|
||||||
|
|
||||||
final String txnDate = (date != null && !date.isBlank()) ? date : LocalDate.now().toString();
|
final String txnDate = (date != null && !date.isBlank()) ? date : LocalDate.now().toString();
|
||||||
|
final int resolvedLimit = (limit != null && limit > 0) ? limit : 20;
|
||||||
|
final int resolvedOffset = (offset != null && offset >= 0) ? offset : 0;
|
||||||
|
|
||||||
// Build cashier officer_id filter
|
// Build cashier officer_id filter
|
||||||
String officerFilter;
|
String officerFilter;
|
||||||
Object[] params;
|
Object[] baseParams;
|
||||||
|
|
||||||
if (cashierId != null) {
|
if (cashierId != null) {
|
||||||
officerFilter = "AND std.officer_id IN ("
|
officerFilter = "AND std.officer_id IN ("
|
||||||
+ "SELECT au.id FROM m_appuser au "
|
+ "SELECT au.id FROM m_appuser au "
|
||||||
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id WHERE mc.id = ?)";
|
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id WHERE mc.id = ?)";
|
||||||
params = new Object[] { txnDate, cashierId };
|
baseParams = new Object[] { txnDate, cashierId };
|
||||||
} else if (tellerId != null) {
|
} else if (tellerId != null) {
|
||||||
officerFilter = "AND std.officer_id IN ("
|
officerFilter = "AND std.officer_id IN ("
|
||||||
+ "SELECT au.id FROM m_appuser au "
|
+ "SELECT au.id FROM m_appuser au "
|
||||||
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id "
|
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id "
|
||||||
+ "JOIN m_tellers mt ON mt.id = mc.teller_id WHERE mt.id = ?)";
|
+ "JOIN m_tellers mt ON mt.id = mc.teller_id WHERE mt.id = ?)";
|
||||||
params = new Object[] { txnDate, tellerId };
|
baseParams = new Object[] { txnDate, tellerId };
|
||||||
} else {
|
} else {
|
||||||
officerFilter = "AND std.officer_id IN ("
|
officerFilter = "AND std.officer_id IN ("
|
||||||
+ "SELECT au.id FROM m_appuser au "
|
+ "SELECT au.id FROM m_appuser au "
|
||||||
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id)";
|
+ "JOIN m_cashiers mc ON mc.staff_id = au.staff_id)";
|
||||||
params = new Object[] { txnDate };
|
baseParams = new Object[] { txnDate };
|
||||||
}
|
}
|
||||||
|
|
||||||
// One row per transaction — account no, account holder name, dr/cr amount, date, teller, payment type
|
// Base grouped query (reused for count, totals, and paged data)
|
||||||
final String sql = "SELECT "
|
final String baseGroupSql = "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, std.cif_no ";
|
||||||
|
|
||||||
|
// 1. Count + totals for all matching records (no pagination)
|
||||||
|
final String totalsSql = "SELECT COUNT(*) AS total_count, "
|
||||||
|
+ " COALESCE(SUM(dr_amount), 0) AS total_dr, "
|
||||||
|
+ " COALESCE(SUM(cr_amount), 0) AS total_cr "
|
||||||
|
+ "FROM (SELECT "
|
||||||
|
+ " 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 "
|
||||||
|
+ baseGroupSql + ") sub";
|
||||||
|
|
||||||
|
final Map<String, Object> totalsRow = jdbcTemplate.queryForMap(totalsSql, baseParams);
|
||||||
|
final long totalFilteredRecords = ((Number) totalsRow.get("total_count")).longValue();
|
||||||
|
final java.math.BigDecimal totalDr = (java.math.BigDecimal) totalsRow.get("total_dr");
|
||||||
|
final java.math.BigDecimal totalCr = (java.math.BigDecimal) totalsRow.get("total_cr");
|
||||||
|
|
||||||
|
// 2. Paged data query
|
||||||
|
final String dataSql = "SELECT "
|
||||||
+ " std.txn_id AS voucher_no, "
|
+ " std.txn_id AS voucher_no, "
|
||||||
+ " std.account_no AS ac_no, "
|
+ " std.account_no AS ac_no, "
|
||||||
+ " COALESCE(mc.display_name, std.cif_no, '') AS ac_name, "
|
+ " COALESCE(mc.display_name, std.cif_no, '') AS ac_name, "
|
||||||
@@ -596,15 +621,18 @@ public class CashReportApiResource {
|
|||||||
+ " CASE WHEN MAX(std.client_id) IS NULL THEN 'GL' ELSE 'SAVINGS' END AS txn_source, "
|
+ " CASE WHEN MAX(std.client_id) IS NULL THEN 'GL' ELSE 'SAVINGS' END AS txn_source, "
|
||||||
+ " SUM(CASE WHEN std.txn_type = 'WITHDRAWAL' THEN std.total_amount ELSE 0 END) AS dr_amount, "
|
+ " 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 "
|
+ " SUM(CASE WHEN std.txn_type = 'DEPOSIT' THEN std.total_amount ELSE 0 END) AS cr_amount "
|
||||||
+ "FROM m_savings_txn_denomination std "
|
+ baseGroupSql
|
||||||
+ "LEFT JOIN m_client mc ON mc.id = std.client_id "
|
+ "ORDER BY MAX(std.created_at) "
|
||||||
+ "WHERE std.txn_date = CAST(? AS DATE) "
|
+ "LIMIT ? OFFSET ?";
|
||||||
+ officerFilter
|
|
||||||
+ " GROUP BY std.txn_id, std.account_no, mc.display_name, std.cif_no "
|
|
||||||
+ "ORDER BY std.txn_id NULLS LAST, MAX(std.created_at)";
|
|
||||||
|
|
||||||
List<Map<String, Object>> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
|
// Append limit and offset to params
|
||||||
Map<String, Object> row = new LinkedHashMap<>();
|
final Object[] pagedParams = new Object[baseParams.length + 2];
|
||||||
|
System.arraycopy(baseParams, 0, pagedParams, 0, baseParams.length);
|
||||||
|
pagedParams[baseParams.length] = resolvedLimit;
|
||||||
|
pagedParams[baseParams.length + 1] = resolvedOffset;
|
||||||
|
|
||||||
|
final List<Map<String, Object>> pageItems = jdbcTemplate.query(dataSql, (rs, rowNum) -> {
|
||||||
|
final Map<String, Object> row = new LinkedHashMap<>();
|
||||||
row.put("voucherNo", rs.getLong("voucher_no"));
|
row.put("voucherNo", rs.getLong("voucher_no"));
|
||||||
row.put("acNo", rs.getString("ac_no"));
|
row.put("acNo", rs.getString("ac_no"));
|
||||||
row.put("acName", rs.getString("ac_name"));
|
row.put("acName", rs.getString("ac_name"));
|
||||||
@@ -616,18 +644,12 @@ public class CashReportApiResource {
|
|||||||
row.put("dr", rs.getBigDecimal("dr_amount"));
|
row.put("dr", rs.getBigDecimal("dr_amount"));
|
||||||
row.put("cr", rs.getBigDecimal("cr_amount"));
|
row.put("cr", rs.getBigDecimal("cr_amount"));
|
||||||
return row;
|
return row;
|
||||||
}, params);
|
}, pagedParams);
|
||||||
|
|
||||||
java.math.BigDecimal totalDr = rows.stream()
|
final Map<String, Object> result = new LinkedHashMap<>();
|
||||||
.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("date", txnDate);
|
||||||
result.put("rows", rows);
|
result.put("totalFilteredRecords", totalFilteredRecords);
|
||||||
|
result.put("pageItems", pageItems);
|
||||||
result.put("totalDr", totalDr);
|
result.put("totalDr", totalDr);
|
||||||
result.put("totalCr", totalCr);
|
result.put("totalCr", totalCr);
|
||||||
return gson.toJson(result);
|
return gson.toJson(result);
|
||||||
|
|||||||
Reference in New Issue
Block a user