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 91baa39..b61de1c 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 @@ -266,13 +266,45 @@ return result } + fun getDistinctHandlersForFGStockOutTraceability(): List { + val sql = """ + SELECT DISTINCT COALESCE(picker_user.name, modified_user.name, '') AS handler + FROM stock_out_line sol + INNER JOIN stock_out so ON sol.stockOutId = so.id AND so.deleted = 0 AND so.type = 'do' + LEFT JOIN user picker_user ON sol.handled_by = picker_user.id AND picker_user.deleted = 0 + LEFT JOIN user modified_user ON sol.modifiedBy = modified_user.staffNo AND modified_user.deleted = 0 AND sol.handled_by IS NULL + WHERE sol.deleted = 0 + ORDER BY handler + """.trimIndent() + return jdbcDao.queryForList(sql, emptyMap()).map { row -> (row["handler"]?.toString() ?: "").trim() }.filter { it.isNotBlank() } +} + + fun getDistinctHandlersForMaterialStockOutTraceability(): List { + val sql = """ + SELECT DISTINCT COALESCE(picker_user.name, jpo_handler_user.name, created_user.name, modified_user.name, '') AS handler + FROM stock_out_line sol + INNER JOIN stock_out so ON sol.stockOutId = so.id AND so.deleted = 0 AND so.type = 'job' + INNER JOIN pick_order_line pol ON sol.pickOrderLineId = pol.id AND pol.deleted = 0 + INNER JOIN pick_order po ON pol.poId = po.id AND po.deleted = 0 AND po.type IN ('jo', 'JOB_ORDER') AND po.joId IS NOT NULL + LEFT JOIN user picker_user ON sol.handled_by = picker_user.id AND picker_user.deleted = 0 + LEFT JOIN jo_pick_order jpo ON po.id = jpo.pick_order_id AND pol.itemId = jpo.item_id AND jpo.deleted = 0 + LEFT JOIN user jpo_handler_user ON jpo.handled_by = jpo_handler_user.id AND jpo_handler_user.deleted = 0 + LEFT JOIN user created_user ON sol.createdBy = created_user.username AND created_user.deleted = 0 + LEFT JOIN user modified_user ON sol.modifiedBy = modified_user.staffNo AND modified_user.deleted = 0 AND sol.handled_by IS NULL + WHERE sol.deleted = 0 + ORDER BY handler + """.trimIndent() + return jdbcDao.queryForList(sql, emptyMap()).map { row -> (row["handler"]?.toString() ?: "").trim() }.filter { it.isNotBlank() } +} + fun searchFGStockOutTraceabilityReport( stockCategory: String?, stockSubCategory: String?, itemCode: String?, year: String?, lastOutDateStart: String?, - lastOutDateEnd: String? + lastOutDateEnd: String?, + handler: String? ): List> { val args = mutableMapOf() @@ -316,6 +348,13 @@ return result "AND DATE(IFNULL(dpor.RequiredDeliveryDate, do.estimatedArrivalDate)) <= DATE(:lastOutDateEnd)" } else "" + val handlerSql = buildMultiValueExactClause( + handler, + "COALESCE(picker_user.name, modified_user.name, '')", + "handler", + args + ) + val sql = """ SELECT IFNULL(DATE_FORMAT( @@ -416,6 +455,7 @@ return result $yearSql $lastOutDateStartSql $lastOutDateEndSql + $handlerSql GROUP BY sol.id, dpor.RequiredDeliveryDate, @@ -469,7 +509,8 @@ fun searchMaterialStockOutTraceabilityReport( itemCode: String?, year: String?, lastOutDateStart: String?, - lastOutDateEnd: String? + lastOutDateEnd: String?, + handler: String? ): List> { val args = mutableMapOf() @@ -514,6 +555,13 @@ fun searchMaterialStockOutTraceabilityReport( "" } + val handlerSql = buildMultiValueExactClause( + handler, + "COALESCE(picker_user.name, jpo_handler_user.name, created_user.name, modified_user.name, '')", + "handler", + args + ) + val sql = """ SELECT IFNULL(it.code, '') AS itemNo, @@ -597,6 +645,7 @@ fun searchMaterialStockOutTraceabilityReport( $yearSql $lastOutDateStartSql $lastOutDateEndSql + $handlerSql ORDER BY it.code, il.lotNo, @@ -706,7 +755,6 @@ fun searchMaterialStockOutTraceabilityReport( } else "" val sql = """ - SELECT COALESCE(it.code, '') as itemNo, COALESCE(it.name, '') as itemName, @@ -715,25 +763,13 @@ fun searchMaterialStockOutTraceabilityReport( COALESCE(sil.lotNo, il.lotNo, '') as lotNo, COALESCE(DATE_FORMAT(COALESCE(sil.expiryDate, il.expiryDate), '%Y-%m-%d'), '') as expiryDate, CASE WHEN COALESCE(qr_agg.qcFailed, 0) = 1 THEN '0' - ELSE TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE( - CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END, 0), 2))) - END as stockInQty, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE( - CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END, 0), 2))) as iqcSampleQty, + ELSE TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(sil.acceptedQty, 0), 2))) + END as stockInQty, + TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(sil.acceptedQty, 0), 2))) as iqcSampleQty, TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(qr_agg.failQtySum, 0), 2))) as iqcDefectQty, TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT( - CASE WHEN COALESCE( - CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END, 0) > 0 - THEN COALESCE(qr_agg.failQtySum, 0) / - (CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END) * 100 + CASE WHEN COALESCE(sil.acceptedQty, 0) > 0 + THEN COALESCE(qr_agg.failQtySum, 0) / sil.acceptedQty * 100 ELSE 0 END, 2))) as iqcDefectPercentage, CASE WHEN COALESCE(qr_agg.qcFailed, 0) = 1 THEN '不合格' ELSE '已接受' END as iqcResult, @@ -742,24 +778,14 @@ fun searchMaterialStockOutTraceabilityReport( COALESCE(wh.code, '') as storeLocation, COALESCE(sp_si.code, sp_po.code, '') as supplierID, COALESCE(sp_si.name, sp_po.name, '') as supplierName, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE( - CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END, 0)) OVER (PARTITION BY it.id), 2))) as totalStockInQty, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE( - CASE WHEN sil.purchaseOrderId IS NOT NULL AND iu_purchase.id IS NOT NULL AND iu.id IS NOT NULL - THEN sil.acceptedQty * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu.ratioN / NULLIF(iu.ratioD, 0)) - ELSE sil.acceptedQty END, 0)) OVER (PARTITION BY it.id), 2))) as totalIqcSampleQty + TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(sil.acceptedQty, 0)) OVER (PARTITION BY it.id), 2))) as totalStockInQty, + TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(sil.acceptedQty, 0)) OVER (PARTITION BY it.id), 2))) as totalIqcSampleQty FROM stock_in_line sil LEFT JOIN stock_in si ON sil.stockInId = si.id LEFT JOIN purchase_order po ON sil.purchaseOrderId = po.id LEFT JOIN items it ON sil.itemId = it.id LEFT JOIN item_uom iu ON it.id = iu.itemId AND iu.stockUnit = true LEFT JOIN uom_conversion uc ON iu.uomId = uc.id - LEFT JOIN item_uom iu_purchase - ON it.id = iu_purchase.itemId - AND iu_purchase.purchaseUnit = 1 - AND iu_purchase.deleted = 0 LEFT JOIN inventory_lot il ON sil.inventoryLotId = il.id LEFT JOIN inventory_lot_line ill ON il.id = ill.inventoryLotId LEFT JOIN warehouse wh ON ill.warehouseId = wh.id @@ -838,17 +864,17 @@ fun searchMaterialStockOutTraceabilityReport( 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, + CASE WHEN COALESCE(item_agg.totalOpeningBalance, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalOpeningBalance, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalOpeningBalance, 0), 0) END as totalOpeningBalance, + CASE WHEN COALESCE(item_agg.totalCumStockIn, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalCumStockIn, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalCumStockIn, 0), 0) END as totalCumStockIn, + CASE WHEN COALESCE(item_agg.totalCumStockOut, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalCumStockOut, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalCumStockOut, 0), 0) END as totalCumStockOut, + CASE WHEN COALESCE(item_agg.totalCurrentBalance, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalCurrentBalance, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalCurrentBalance, 0), 0) END 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 COALESCE(item_agg.totalMisInputAndLost, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalMisInputAndLost, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalMisInputAndLost, 0), 0) END as totalMisInputAndLost, + CASE WHEN COALESCE(item_agg.totalVariance, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalVariance, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalVariance, 0), 0) END as totalVariance, + CASE WHEN COALESCE(item_agg.totalDefectiveGoods, 0) < 0 THEN CONCAT('(', FORMAT(-item_agg.totalDefectiveGoods, 0), ')') ELSE FORMAT(COALESCE(item_agg.totalDefectiveGoods, 0), 0) END as totalDefectiveGoods, + FORMAT(ROUND(COALESCE(ipv.totalStockBalanceRaw, 0), 2), 2) as totalStockBalance, CASE 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) diff --git a/src/main/java/com/ffii/fpsms/modules/report/service/StockLedgerReportService.kt b/src/main/java/com/ffii/fpsms/modules/report/service/StockLedgerReportService.kt index 3f34050..4ab4978 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/service/StockLedgerReportService.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/service/StockLedgerReportService.kt @@ -255,35 +255,27 @@ SELECT reOrderLevel, reOrderQty, - -- jrxml 需要 String,避免 BigDecimal cast/trim 爆炸 - CAST(CAST(inQty AS DECIMAL(20,0)) AS CHAR) AS stockIn, - CAST(CAST(outQty AS DECIMAL(20,0)) AS CHAR) AS stockOut, + -- jrxml 需要 String;負數括號顯示,無小數 + CASE WHEN COALESCE(inQty, 0) < 0 THEN CONCAT('(', FORMAT(-inQty, 0), ')') ELSE FORMAT(COALESCE(inQty, 0), 0) END AS stockIn, + CASE WHEN COALESCE(outQty, 0) < 0 THEN CONCAT('(', FORMAT(-outQty, 0), ')') ELSE FORMAT(COALESCE(outQty, 0), 0) END AS stockOut, -- 累計存量(跨 lot:只用 itemCode 分區) - CAST(CAST( - (openingBeforeStart - + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId) - ) AS DECIMAL(20,0) - ) AS CHAR) AS cumBalance, + CASE WHEN (openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId)) < 0 + THEN CONCAT('(', FORMAT(-(openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId)), 0), ')') + ELSE FORMAT(openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId), 0) END AS cumBalance, -- 累計期初存量 = 本行累計 - 本行異動 - CAST(CAST( - (openingBeforeStart - + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId) - - delta - ) AS DECIMAL(20,0) - ) AS CHAR) AS cumOpeningBal, + CASE WHEN (openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId) - delta) < 0 + THEN CONCAT('(', FORMAT(-(openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId) - delta), 0), ')') + ELSE FORMAT(openingBeforeStart + SUM(delta) OVER (PARTITION BY itemCode ORDER BY trnDateRaw, slId) - delta, 0) END AS cumOpeningBal, -- footer totals(同樣輸出 String) - CAST(openingBeforeStart AS DECIMAL(20,0)) AS totalCumOpeningBal, - CAST(SUM(inQty) OVER (PARTITION BY itemCode) AS DECIMAL(20,0)) AS totalStockIn, - CAST(SUM(outQty) OVER (PARTITION BY itemCode) AS DECIMAL(20,0)) AS totalStockOut, - CAST( - (openingBeforeStart - + SUM(inQty) OVER (PARTITION BY itemCode) - - SUM(outQty) OVER (PARTITION BY itemCode) - ) AS DECIMAL(20,0) - ) AS totalCumBalance + CASE WHEN COALESCE(openingBeforeStart, 0) < 0 THEN CONCAT('(', FORMAT(-openingBeforeStart, 0), ')') ELSE FORMAT(COALESCE(openingBeforeStart, 0), 0) END AS totalCumOpeningBal, + CASE WHEN SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemCode) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemCode), 0), ')') ELSE FORMAT(SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemCode), 0) END AS totalStockIn, + CASE WHEN SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemCode) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemCode), 0), ')') ELSE FORMAT(SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemCode), 0) END AS totalStockOut, + CASE WHEN (openingBeforeStart + SUM(inQty) OVER (PARTITION BY itemCode) - SUM(outQty) OVER (PARTITION BY itemCode)) < 0 + THEN CONCAT('(', FORMAT(-(openingBeforeStart + SUM(inQty) OVER (PARTITION BY itemCode) - SUM(outQty) OVER (PARTITION BY itemCode)), 0), ')') + ELSE FORMAT(openingBeforeStart + SUM(inQty) OVER (PARTITION BY itemCode) - SUM(outQty) OVER (PARTITION BY itemCode), 0) END AS totalCumBalance FROM period ORDER BY diff --git a/src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt b/src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt index e562305..0adccd5 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt @@ -248,41 +248,42 @@ SELECT expiryDate, storeLocation, - /* 期初/累計/現存:jrxml 欄位全是 String */ - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(openingQty, 0), 2))) AS openingBalance, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(inQty, 0), 2))) AS cumStockIn, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(outQty, 0), 2))) AS cumStockOut, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(currentQty, 0), 2))) AS currentBookBalance, + /* 期初/累計/現存:jrxml 欄位全是 String;負數括號顯示,無小數 */ + CASE WHEN COALESCE(openingQty, 0) < 0 THEN CONCAT('(', FORMAT(-openingQty, 0), ')') ELSE FORMAT(COALESCE(openingQty, 0), 0) END AS openingBalance, + CASE WHEN COALESCE(inQty, 0) < 0 THEN CONCAT('(', FORMAT(-inQty, 0), ')') ELSE FORMAT(COALESCE(inQty, 0), 0) END AS cumStockIn, + CASE WHEN COALESCE(outQty, 0) < 0 THEN CONCAT('(', FORMAT(-outQty, 0), ')') ELSE FORMAT(COALESCE(outQty, 0), 0) END AS cumStockOut, + CASE WHEN COALESCE(currentQty, 0) < 0 THEN CONCAT('(', FORMAT(-currentQty, 0), ')') ELSE FORMAT(COALESCE(currentQty, 0), 0) END AS currentBookBalance, COALESCE(DATE_FORMAT(lastInDateRaw, '%Y-%m-%d'), '') AS lastInDate, COALESCE(DATE_FORMAT(lastOutDateRaw, '%Y-%m-%d'), '') AS lastOutDate, COALESCE(DATE_FORMAT(stockTakeDateRaw, '%Y-%m-%d'), '') AS stockTakeDate, - /* 取貨量 = approverStockTakeQty(無盤點紀錄就空白) */ + /* 取貨量 = approverStockTakeQty(無盤點紀錄顯示 0) */ CASE - WHEN stkApproverQty IS NULL THEN '' - ELSE TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(stkApproverQty, 0), 2))) + WHEN stkApproverQty IS NULL THEN '0' + WHEN COALESCE(stkApproverQty, 0) < 0 THEN CONCAT('(', FORMAT(-stkApproverQty, 0), ')') + ELSE FORMAT(COALESCE(stkApproverQty, 0), 0) END AS stockTakeQty, - /* 超撿量 = varianceQty(無盤點紀錄就空白) */ + /* 超撿量 = varianceQty(無盤點紀錄顯示 0) */ CASE - WHEN stkVarianceQty IS NULL THEN '' - ELSE TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(COALESCE(stkVarianceQty, 0), 2))) + WHEN stkVarianceQty IS NULL THEN '0' + WHEN COALESCE(stkVarianceQty, 0) < 0 THEN CONCAT('(', FORMAT(-stkVarianceQty, 0), ')') + ELSE FORMAT(COALESCE(stkVarianceQty, 0), 0) END AS variance, + /* 盤盈虧百分比(無盤點或 bookQty=0 顯示 0%) */ CASE - WHEN stkVarianceQty IS NULL THEN '' - WHEN COALESCE(stkBookQty, 0) = 0 THEN '' - ELSE CONCAT( - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT((COALESCE(stkVarianceQty, 0) / stkBookQty) * 100, 2))), - '%' - ) + WHEN stkVarianceQty IS NULL THEN '0%' + WHEN COALESCE(stkBookQty, 0) = 0 THEN '0%' + WHEN (COALESCE(stkVarianceQty, 0) / stkBookQty) * 100 < 0 THEN CONCAT('(', FORMAT(-(COALESCE(stkVarianceQty, 0) / stkBookQty) * 100, 0), '%)') + ELSE CONCAT(FORMAT((COALESCE(stkVarianceQty, 0) / stkBookQty) * 100, 0), '%') END AS variancePercentage, - /* item totals(footer 用) */ - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo), 2))) AS totalOpeningBalance, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo), 2))) AS totalCumStockIn, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo), 2))) AS totalCumStockOut, - TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM FORMAT(SUM(COALESCE(currentQty, 0)) OVER (PARTITION BY itemNo), 2))) AS totalCurrentBalance + /* item totals(footer 用);負數括號顯示,無小數 */ + CASE WHEN SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalOpeningBalance, + CASE WHEN SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCumStockIn, + CASE WHEN SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCumStockOut, + CASE WHEN SUM(COALESCE(currentQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(currentQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(currentQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCurrentBalance FROM data ORDER BY diff --git a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt index ac2f408..008eaa9 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt @@ -164,6 +164,14 @@ class ReportController( return ResponseEntity(pdfBytes, headers, HttpStatus.OK) } + @GetMapping("/fg-stock-out-traceability-handlers") + fun getFGStockOutTraceabilityHandlers(): List = + reportService.getDistinctHandlersForFGStockOutTraceability() + + @GetMapping("/material-stock-out-traceability-handlers") + fun getMaterialStockOutTraceabilityHandlers(): List = + reportService.getDistinctHandlersForMaterialStockOutTraceability() + @GetMapping("/print-fg-stock-out-traceability") fun generateFGStockOutTraceabilityReport( @RequestParam(required = false) stockCategory: String?, @@ -171,7 +179,8 @@ class ReportController( @RequestParam(required = false) itemCode: String?, @RequestParam(required = false) year: String?, @RequestParam(required = false) lastOutDateStart: String?, - @RequestParam(required = false) lastOutDateEnd: String? + @RequestParam(required = false) lastOutDateEnd: String?, + @RequestParam(required = false) handler: String? ): ResponseEntity { val parameters = mutableMapOf() @@ -193,7 +202,8 @@ class ReportController( itemCode, year, lastOutDateStart, - lastOutDateEnd + lastOutDateEnd, + handler ) val pdfBytes = reportService.createPdfResponse( @@ -217,7 +227,8 @@ class ReportController( @RequestParam(required = false) itemCode: String?, @RequestParam(required = false) year: String?, @RequestParam(required = false) lastOutDateStart: String?, - @RequestParam(required = false) lastOutDateEnd: String? + @RequestParam(required = false) lastOutDateEnd: String?, + @RequestParam(required = false) handler: String? ): ResponseEntity { val parameters = mutableMapOf() @@ -239,7 +250,8 @@ class ReportController( itemCode, year, lastOutDateStart, - lastOutDateEnd + lastOutDateEnd, + handler ) val pdfBytes = reportService.createPdfResponse( diff --git a/src/main/resources/jasper/StockLedgarReport.jrxml b/src/main/resources/jasper/StockLedgarReport.jrxml index 70f7923..5bf97d7 100644 --- a/src/main/resources/jasper/StockLedgarReport.jrxml +++ b/src/main/resources/jasper/StockLedgarReport.jrxml @@ -57,10 +57,10 @@ - - - - + + + + @@ -100,32 +100,32 @@ - + - + - + - + - + - + @@ -381,20 +381,14 @@ - + - + @@ -403,10 +397,7 @@ - + @@ -452,10 +443,7 @@ - +