diff --git a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt index 02e0946..91baa39 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt @@ -790,9 +790,9 @@ fun searchMaterialStockOutTraceabilityReport( } /** - * Queries the database for Stock Balance Report data. + * Queries the database for Stock Balance Report data (one summarized row per item). * Uses stock_ledger with report period (fromDate/toDate): opening = before fromDate, cum in/out = in period, current = up to toDate. - * Unit from uom_conversion.udfudesc; includes per-lot and per-item totals plus in/out breakdown by source (PO, JO, StockTake, Adj) and type (Miss, Bad, Adj, StockTake, DO, JO, Consumable). + * totalStockBalance = SUM(pol.qty * pol.up) per item via purchase_order_line; avgUnitPrice = totalStockBalance / totalCurrentBalance. */ fun searchStockBalanceReport( stockCategory: String?, @@ -825,137 +825,136 @@ fun searchMaterialStockOutTraceabilityReport( val sql = """ SELECT '' as stockSubCategory, - COALESCE(it.code, '') as itemNo, - COALESCE(it.name, '') as itemName, - COALESCE(uc.udfudesc, uc.code, '') as unitOfMeasure, - COALESCE(lot.lotNo, '') as lotNo, - COALESCE(DATE_FORMAT(lot.expiryDate, '%Y-%m-%d'), '') as expiryDate, - FORMAT(ROUND(COALESCE(agg.openingBalance, 0), 0), 0) as openingBalance, - FORMAT(ROUND(COALESCE(agg.cumStockIn, 0), 0), 0) as cumStockIn, - FORMAT(ROUND(COALESCE(agg.cumStockOut, 0), 0), 0) as cumStockOut, - FORMAT(ROUND(COALESCE(agg.currentBalance, 0), 0), 0) as currentBalance, - '' as reOrderLevel, + COALESCE(item_agg.itemNo, '') as itemNo, + COALESCE(item_agg.itemName, '') as itemName, + COALESCE(item_agg.unitOfMeasure, '') as unitOfMeasure, + '' as lotNo, + '' as expiryDate, + '' as openingBalance, + '' as cumStockIn, + '' as cumStockOut, + '' as currentBalance, '' as reOrderQty, - COALESCE(agg.storeLocation, '') as storeLocation, - COALESCE(DATE_FORMAT(agg.lastInDate, '%Y-%m-%d'), '') as lastInDate, - COALESCE(DATE_FORMAT(agg.lastOutDate, '%Y-%m-%d'), '') as lastOutDate, - FORMAT(ROUND(SUM(COALESCE(agg.openingBalance, 0)) OVER w_item, 0), 0) as totalOpeningBalance, - FORMAT(ROUND(SUM(COALESCE(agg.cumStockIn, 0)) OVER w_item, 0), 0) as totalCumStockIn, - FORMAT(ROUND(SUM(COALESCE(agg.cumStockOut, 0)) OVER w_item, 0), 0) as totalCumStockOut, - FORMAT(ROUND(SUM(COALESCE(agg.currentBalance, 0)) OVER w_item, 0), 0) as totalCurrentBalance, - FORMAT(ROUND(COALESCE(agg.cumStockOutMiss, 0), 0), 0) as misInputAndLost, - FORMAT(ROUND(COALESCE(agg.cumStockOutBad, 0), 0), 0) as defectiveGoods, - FORMAT(ROUND(COALESCE(agg.cumStockOutAdjStockTake, 0), 0), 0) as variance, + COALESCE(item_agg.storeLocation, '') as storeLocation, + COALESCE(DATE_FORMAT(item_agg.lastInDate, '%Y-%m-%d'), '') as lastInDate, + COALESCE(DATE_FORMAT(item_agg.lastOutDate, '%Y-%m-%d'), '') as lastOutDate, + FORMAT(ROUND(COALESCE(item_agg.totalOpeningBalance, 0), 0), 0) as totalOpeningBalance, + FORMAT(ROUND(COALESCE(item_agg.totalCumStockIn, 0), 0), 0) as totalCumStockIn, + FORMAT(ROUND(COALESCE(item_agg.totalCumStockOut, 0), 0), 0) as totalCumStockOut, + FORMAT(ROUND(COALESCE(item_agg.totalCurrentBalance, 0), 0), 0) as totalCurrentBalance, + '' as misInputAndLost, + '' as defectiveGoods, + '' as variance, + FORMAT(ROUND(COALESCE(item_agg.totalMisInputAndLost, 0), 0), 0) as totalMisInputAndLost, + FORMAT(ROUND(COALESCE(item_agg.totalVariance, 0), 0), 0) as totalVariance, + FORMAT(ROUND(COALESCE(item_agg.totalDefectiveGoods, 0), 0), 0) as totalDefectiveGoods, + FORMAT(ROUND(COALESCE(ipv.totalStockBalanceRaw, 0), 2), 0) as totalStockBalance, CASE - WHEN agg.lastInDate IS NULL AND agg.lastOutDate IS NULL THEN '' - WHEN agg.lastInDate IS NULL THEN DATE_FORMAT(agg.lastOutDate, '%Y-%m-%d') - WHEN agg.lastOutDate IS NULL THEN DATE_FORMAT(agg.lastInDate, '%Y-%m-%d') - ELSE DATE_FORMAT(GREATEST(agg.lastInDate, agg.lastOutDate), '%Y-%m-%d') - END as lastMovementDate, - FORMAT(ROUND(COALESCE(agg.cumStockInByPO, 0), 0), 0) as cumStockInByPurchaseOrder, - FORMAT(ROUND(COALESCE(agg.cumStockInByJO, 0), 0), 0) as cumStockInByJobOrder, - FORMAT(ROUND(COALESCE(agg.cumStockInByStockTake, 0), 0), 0) as cumStockInByStockTake, - FORMAT(ROUND(COALESCE(agg.cumStockInByAdj, 0), 0), 0) as cumStockInByAdj, - FORMAT(ROUND(COALESCE(agg.cumStockOutMiss, 0), 0), 0) as cumStockOutMissQty, - FORMAT(ROUND(COALESCE(agg.cumStockOutBad, 0), 0), 0) as cumStockOutBadQty, - FORMAT(ROUND(COALESCE(agg.cumStockOutAdj, 0), 0), 0) as cumStockOutAdj, - FORMAT(ROUND(COALESCE(agg.cumStockOutAdjTransfer, 0), 0), 0) as cumStockOutAdjTransfer, - FORMAT(ROUND(COALESCE(agg.cumStockOutAdjStockTake, 0), 0), 0) as cumStockOutAdjStockTake, - FORMAT(ROUND(COALESCE(agg.cumStockOutStockTake, 0), 0), 0) as cumStockOutStockTake, - FORMAT(ROUND(COALESCE(agg.cumStockOutByDO, 0), 0), 0) as cumStockOutByDO, - FORMAT(ROUND(COALESCE(agg.cumStockOutByJO, 0), 0), 0) as cumStockOutByJO, - FORMAT(ROUND(COALESCE(agg.cumStockOutByConsumable, 0), 0), 0) as cumStockOutByConsumable + WHEN COALESCE(item_agg.totalCurrentBalance, 0) > 0 AND ipv.totalStockBalanceRaw IS NOT NULL AND ipv.totalStockBalanceRaw <> 0 + THEN FORMAT(ROUND(ipv.totalStockBalanceRaw / item_agg.totalCurrentBalance, 2), 2) + ELSE '0.00' + END as avgUnitPrice FROM ( SELECT - sl.itemCode, - sl.itemId, - COALESCE(il_in.id, il_out.id) AS lotId, - SUM(CASE WHEN DATE(sl.date) < :fromDate THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0) ELSE 0 END) AS openingBalance, - SUM( - CASE - WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate - AND sil.stockTakeLineId IS NULL -- 不是「由盤點」產生的入庫 - THEN COALESCE(sl.inQty, 0) - ELSE 0 - END -) AS cumStockIn, - --- 累計付出量:排除 type = 'stocktake' 的出庫 (專門的盤點出庫) -SUM( - CASE - WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate - AND COALESCE(sl.outQty, 0) > 0 - AND NOT ( - -- 1) 盤點正式出庫 - LOWER(TRIM(COALESCE(sl.type, ''))) = 'stocktake' - -- 2) 盤點差異調整出庫(你已經算在 cumStockOutAdjStockTake) - OR ( - LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' - AND (sol.stockTransferId IS NULL OR sol.id IS NULL) - ) - -- 3) 錯誤輸入或遺失出庫 - OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss' - OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss' - -- 4) 不良品棄置出庫 - OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad' - OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad' - ) - THEN COALESCE(sl.outQty, 0) - ELSE 0 - END -) AS cumStockOut, - SUM(CASE WHEN DATE(sl.date) <= :toDate THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0) ELSE 0 END) AS currentBalance, - MAX(CASE WHEN COALESCE(sl.inQty, 0) > 0 THEN sl.date END) AS lastInDate, - MAX(CASE WHEN COALESCE(sl.outQty, 0) > 0 THEN sl.date END) AS lastOutDate, - MAX(lot_wh.storeLocation) AS storeLocation, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND sil.purchaseOrderId IS NOT NULL THEN sl.inQty ELSE 0 END) AS cumStockInByPO, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND sil.jobOrderId IS NOT NULL THEN sl.inQty ELSE 0 END) AS cumStockInByJO, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND sil.stockTakeLineId IS NOT NULL THEN sl.inQty ELSE 0 END) AS cumStockInByStockTake, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' THEN sl.inQty ELSE 0 END) AS cumStockInByAdj, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss') THEN sl.outQty ELSE 0 END) AS cumStockOutMiss, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad') THEN sl.outQty ELSE 0 END) AS cumStockOutBad, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' THEN sl.outQty ELSE 0 END) AS cumStockOutAdj, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' AND sol.stockTransferId IS NOT NULL THEN sl.outQty ELSE 0 END) AS cumStockOutAdjTransfer, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' AND (sol.stockTransferId IS NULL OR sol.id IS NULL) THEN sl.outQty ELSE 0 END) AS cumStockOutAdjStockTake, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'stocktake' THEN sl.outQty ELSE 0 END) AS cumStockOutStockTake, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(po.type, ''))) = 'do' THEN sl.outQty ELSE 0 END) AS cumStockOutByDO, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(po.type, ''))) = 'jo' THEN sl.outQty ELSE 0 END) AS cumStockOutByJO, - SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(po.type, ''))) = 'consumable' THEN sl.outQty ELSE 0 END) AS cumStockOutByConsumable - FROM stock_ledger sl - LEFT JOIN stock_in_line sil ON sl.stockInLineId = sil.id AND sil.deleted = 0 - LEFT JOIN inventory_lot il_in ON sil.inventoryLotId = il_in.id AND il_in.deleted = 0 - LEFT JOIN stock_out_line sol ON sl.stockOutLineId = sol.id AND sol.deleted = 0 - LEFT JOIN inventory_lot_line ill_out ON sol.inventoryLotLineId = ill_out.id AND ill_out.deleted = 0 - LEFT JOIN inventory_lot il_out ON ill_out.inventoryLotId = il_out.id AND il_out.deleted = 0 - LEFT JOIN pick_order_line pol ON sol.pickOrderLineId = pol.id AND pol.deleted = 0 - LEFT JOIN pick_order po ON pol.poId = po.id AND po.deleted = 0 - LEFT JOIN ( - SELECT il.id AS lotId, MAX(wh.code) AS storeLocation - FROM inventory_lot il - LEFT JOIN inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0 - LEFT JOIN warehouse wh ON ill.warehouseId = wh.id AND wh.deleted = 0 - GROUP BY il.id - ) lot_wh ON lot_wh.lotId = COALESCE(il_in.id, il_out.id) - WHERE sl.deleted = 0 - AND sl.itemCode IS NOT NULL AND sl.itemCode <> '' - AND DATE(sl.date) <= :toDate - AND COALESCE(il_in.id, il_out.id) IS NOT NULL - $itemCodeSql - GROUP BY sl.itemCode, sl.itemId, COALESCE(il_in.id, il_out.id) - ) agg - LEFT JOIN items it ON agg.itemId = it.id AND it.deleted = 0 - LEFT JOIN item_uom iu ON it.id = iu.itemId AND iu.stockUnit = 1 AND iu.deleted = 0 - LEFT JOIN uom_conversion uc ON iu.uomId = uc.id - LEFT JOIN (SELECT id, lotNo, expiryDate FROM inventory_lot WHERE deleted = 0) lot ON lot.id = agg.lotId - WHERE 1 = 1 - $stockCategorySql - $storeLocationSql - HAVING 1=1 - WINDOW w_item AS (PARTITION BY agg.itemCode) + agg.itemCode AS itemNo, + agg.itemId, + it.name AS itemName, + COALESCE(uc.udfudesc, uc.code, '') AS unitOfMeasure, + SUM(agg.openingBalance) AS totalOpeningBalance, + SUM(agg.cumStockIn) AS totalCumStockIn, + SUM(agg.cumStockOut) AS totalCumStockOut, + SUM(agg.currentBalance) AS totalCurrentBalance, + SUM(agg.cumStockOutMiss) AS totalMisInputAndLost, + SUM(agg.cumStockOutAdjStockTake) AS totalVariance, + SUM(agg.cumStockOutBad) AS totalDefectiveGoods, + MAX(agg.storeLocation) AS storeLocation, + MAX(agg.lastInDate) AS lastInDate, + MAX(agg.lastOutDate) AS lastOutDate + FROM ( + SELECT + sl.itemCode, + sl.itemId, + COALESCE(il_in.id, il_out.id) AS lotId, + SUM(CASE WHEN DATE(sl.date) < :fromDate THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0) ELSE 0 END) AS openingBalance, + SUM( + CASE + WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate + AND sil.stockTakeLineId IS NULL + THEN COALESCE(sl.inQty, 0) + ELSE 0 + END + ) AS cumStockIn, + SUM( + CASE + WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate + AND COALESCE(sl.outQty, 0) > 0 + AND NOT ( + LOWER(TRIM(COALESCE(sl.type, ''))) = 'stocktake' + OR ( + LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' + AND (sol.stockTransferId IS NULL OR sol.id IS NULL) + ) + OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss' + OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss' + OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad' + OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad' + ) + THEN COALESCE(sl.outQty, 0) + ELSE 0 + END + ) AS cumStockOut, + SUM(CASE WHEN DATE(sl.date) <= :toDate THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0) ELSE 0 END) AS currentBalance, + MAX(CASE WHEN COALESCE(sl.inQty, 0) > 0 THEN sl.date END) AS lastInDate, + MAX(CASE WHEN COALESCE(sl.outQty, 0) > 0 THEN sl.date END) AS lastOutDate, + MAX(lot_wh.storeLocation) AS storeLocation, + SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss') THEN sl.outQty ELSE 0 END) AS cumStockOutMiss, + SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad') THEN sl.outQty ELSE 0 END) AS cumStockOutBad, + SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' AND (sol.stockTransferId IS NULL OR sol.id IS NULL) THEN sl.outQty ELSE 0 END) AS cumStockOutAdjStockTake + FROM stock_ledger sl + LEFT JOIN stock_in_line sil ON sl.stockInLineId = sil.id AND sil.deleted = 0 + LEFT JOIN inventory_lot il_in ON sil.inventoryLotId = il_in.id AND il_in.deleted = 0 + LEFT JOIN stock_out_line sol ON sl.stockOutLineId = sol.id AND sol.deleted = 0 + LEFT JOIN inventory_lot_line ill_out ON sol.inventoryLotLineId = ill_out.id AND ill_out.deleted = 0 + LEFT JOIN inventory_lot il_out ON ill_out.inventoryLotId = il_out.id AND il_out.deleted = 0 + LEFT JOIN ( + SELECT il.id AS lotId, MAX(wh.code) AS storeLocation + FROM inventory_lot il + LEFT JOIN inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0 + LEFT JOIN warehouse wh ON ill.warehouseId = wh.id AND wh.deleted = 0 + GROUP BY il.id + ) lot_wh ON lot_wh.lotId = COALESCE(il_in.id, il_out.id) + WHERE sl.deleted = 0 + AND sl.itemCode IS NOT NULL AND sl.itemCode <> '' + AND DATE(sl.date) <= :toDate + AND COALESCE(il_in.id, il_out.id) IS NOT NULL + $itemCodeSql + GROUP BY sl.itemCode, sl.itemId, COALESCE(il_in.id, il_out.id) + ) agg + LEFT JOIN items it ON agg.itemId = it.id AND it.deleted = 0 + LEFT JOIN item_uom iu ON it.id = iu.itemId AND iu.stockUnit = 1 AND iu.deleted = 0 + LEFT JOIN uom_conversion uc ON iu.uomId = uc.id + WHERE 1 = 1 + $stockCategorySql + $storeLocationSql + GROUP BY agg.itemCode, agg.itemId, it.id, it.code, it.name, uc.udfudesc, uc.code + HAVING 1=1 + ) item_agg + LEFT JOIN ( + SELECT itemId, SUM(pol_qty_up) AS totalStockBalanceRaw + FROM ( + SELECT sil.itemId, MAX(COALESCE(pol.qty, 0) * COALESCE(pol.up, 0)) AS pol_qty_up + FROM stock_in_line sil + LEFT JOIN purchase_order_line pol ON sil.purchaseOrderLineId = pol.id AND pol.deleted = 0 + WHERE sil.deleted = 0 + GROUP BY sil.itemId, pol.id + ) t + GROUP BY itemId + ) ipv ON item_agg.itemId = ipv.itemId + WHERE 1=1 """.trimIndent() val havingConditions = mutableListOf() - val currentBalanceExpr = "COALESCE(agg.currentBalance, 0)" + val currentBalanceExpr = "COALESCE(totalCurrentBalance, 0)" if (!balanceFilterStart.isNullOrBlank()) { args["balanceFilterStart"] = balanceFilterStart.toDoubleOrNull() ?: 0.0 @@ -968,30 +967,30 @@ SUM( if (!lastInDateStart.isNullOrBlank()) { val formattedDate = lastInDateStart.replace("/", "-") args["lastInDateStart"] = formattedDate - havingConditions.add("(agg.lastInDate IS NOT NULL AND DATE(agg.lastInDate) >= DATE(:lastInDateStart))") + havingConditions.add("(lastInDate IS NOT NULL AND DATE(lastInDate) >= DATE(:lastInDateStart))") } if (!lastInDateEnd.isNullOrBlank()) { val formattedDate = lastInDateEnd.replace("/", "-") args["lastInDateEnd"] = formattedDate - havingConditions.add("(agg.lastInDate IS NOT NULL AND DATE(agg.lastInDate) <= DATE(:lastInDateEnd))") + havingConditions.add("(lastInDate IS NOT NULL AND DATE(lastInDate) <= DATE(:lastInDateEnd))") } if (!lastOutDateStart.isNullOrBlank()) { val formattedDate = lastOutDateStart.replace("/", "-") args["lastOutDateStart"] = formattedDate - havingConditions.add("(agg.lastOutDate IS NOT NULL AND DATE(agg.lastOutDate) >= DATE(:lastOutDateStart))") + havingConditions.add("(lastOutDate IS NOT NULL AND DATE(lastOutDate) >= DATE(:lastOutDateStart))") } if (!lastOutDateEnd.isNullOrBlank()) { val formattedDate = lastOutDateEnd.replace("/", "-") args["lastOutDateEnd"] = formattedDate - havingConditions.add("(agg.lastOutDate IS NOT NULL AND DATE(agg.lastOutDate) <= DATE(:lastOutDateEnd))") + havingConditions.add("(lastOutDate IS NOT NULL AND DATE(lastOutDate) <= DATE(:lastOutDateEnd))") } val finalSql = if (havingConditions.isNotEmpty()) { sql.replace("HAVING 1=1", "HAVING ${havingConditions.joinToString(" AND ")}") } else { - sql.replace("HAVING 1=1", "") + sql.replace("GROUP BY agg.itemCode, agg.itemId, it.id, it.code, it.name, uc.udfudesc, uc.code\n HAVING 1=1", "GROUP BY agg.itemCode, agg.itemId, it.id, it.code, it.name, uc.udfudesc, uc.code") } - return jdbcDao.queryForList("$finalSql ORDER BY itemNo, lotNo", args) + return jdbcDao.queryForList("$finalSql ORDER BY itemNo", args) } /** * Compiles and fills a Jasper Report, then exports to Excel (.xlsx). Same layout/columns as the report template. diff --git a/src/main/resources/jasper/StockBalanceReport.jrxml b/src/main/resources/jasper/StockBalanceReport.jrxml index 4bdc237..072560b 100644 --- a/src/main/resources/jasper/StockBalanceReport.jrxml +++ b/src/main/resources/jasper/StockBalanceReport.jrxml @@ -1,5 +1,5 @@ - + @@ -60,83 +60,132 @@ - + - + - - - - - + + + + + + + - + - - - - - 1]]> - - - + + - - - + + - - + + - + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + - + + - + - - - - + + + + - + - - + + + + + + + + + + + + + + + + - - - + + + + + + + + - + + + + + + + - + 1]]> - + @@ -147,7 +196,7 @@ - + @@ -159,7 +208,7 @@ - + @@ -170,18 +219,7 @@ - - - - - - - - - - - - + @@ -191,29 +229,7 @@ - - - - - - - - - - - - - - - - - - - - - - - + @@ -221,10 +237,11 @@ - + - + @@ -314,23 +331,12 @@ - + - - - - - - - - - - - - + @@ -341,7 +347,7 @@ - + @@ -351,7 +357,7 @@ - + @@ -361,103 +367,61 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - + + - - - - + + + + + + - - + + - - - - + + + + + + - - + + - - - - + + + + + + - - - - - - - - + - - - - + + + + + + - - - - - - - - + - - + + - +