From 4ba583fddd0a7dfbd514e366d5508c6fa1c3de1b Mon Sep 17 00:00:00 2001 From: "Tommy\\2Fi-Staff" Date: Mon, 16 Mar 2026 02:39:47 +0800 Subject: [PATCH 1/3] reports update --- .../modules/report/service/ReportService.kt | 8 ++++++-- .../service/StockTakeVarianceReportService.kt | 10 +++++----- .../web/StockTakeVarianceReportController.kt | 16 ++++++++-------- .../jasper/StockItemConsumptionTrendReport.jrxml | 2 +- .../jasper/StockTakeVarianceReport.jrxml | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) 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 dce6b60..5d9d980 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 @@ -876,12 +876,16 @@ fun searchMaterialStockOutTraceabilityReport( 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(item_agg.total_in_value, 0) - COALESCE(item_agg.total_out_value, 0), 2), 2) as totalStockBalance, CASE WHEN COALESCE(item_agg.totalCurrentBalance, 0) > 0 THEN FORMAT(ROUND((COALESCE(item_agg.total_in_value, 0) - COALESCE(item_agg.total_out_value, 0)) / item_agg.totalCurrentBalance, 2), 2) ELSE '0.00' - END as avgUnitPrice + END as avgUnitPrice, + CASE + WHEN COALESCE(item_agg.totalCurrentBalance, 0) > 0 + THEN FORMAT(ROUND(item_agg.totalCurrentBalance * (COALESCE(item_agg.total_in_value, 0) - COALESCE(item_agg.total_out_value, 0)) / item_agg.totalCurrentBalance, 2), 2) + ELSE '0.00' + END as totalStockBalance FROM ( SELECT agg.itemCode AS itemNo, 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 2986ec6..c4cdbec 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 @@ -38,8 +38,8 @@ open class StockTakeVarianceReportService( stockCategory: String?, itemCode: String?, storeLocation: String?, - lastInDateStart: String?, // 前端 startDateStart - lastInDateEnd: String?, // 前端 startDateEnd + stockTakeDateStart: String?, + stockTakeDateEnd: String?, ): List> { val args = mutableMapOf() @@ -64,12 +64,12 @@ open class StockTakeVarianceReportService( } // 1) toDate:有填用傳入,沒填用今天 - val toDate = (lastInDateEnd?.replace("/", "-") + val toDate = (stockTakeDateEnd?.replace("/", "-") ?: LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))) // 2) fromDate:有填用傳入,沒填查 stock_ledger 最早日期 - val fromDate = if (!lastInDateStart.isNullOrBlank()) { - lastInDateStart.replace("/", "-") + val fromDate = if (!stockTakeDateStart.isNullOrBlank()) { + stockTakeDateStart.replace("/", "-") } else { val minDateSql = """ SELECT DATE_FORMAT(MIN(sl.date), '%Y-%m-%d') AS firstDate diff --git a/src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt b/src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt index 1a694ab..4bd9159 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt @@ -28,15 +28,15 @@ class StockTakeVarianceReportController( * - stockCategory * - itemCode * - storeLocation - * - lastInDateStart / lastInDateEnd + * - stockTakeDateStart / stockTakeDateEnd */ @GetMapping("/print-stock-take-variance") fun generateStockTakeVarianceReport( @RequestParam(required = false) stockCategory: String?, @RequestParam(required = false) itemCode: String?, @RequestParam(required = false) storeLocation: String?, - @RequestParam(required = false) lastInDateStart: String?, - @RequestParam(required = false) lastInDateEnd: String?, + @RequestParam(required = false) stockTakeDateStart: String?, + @RequestParam(required = false) stockTakeDateEnd: String?, ): ResponseEntity { val parameters = mutableMapOf() @@ -51,9 +51,9 @@ class StockTakeVarianceReportController( parameters["balanceFilterStart"] = "" parameters["balanceFilterEnd"] = "" - parameters["lastInDateStart"] = lastInDateStart ?: "" - parameters["lastInDateEnd"] = lastInDateEnd ?: "" - parameters["lastOutDateStart"] = "" + parameters["stockTakeDateStart"] = stockTakeDateStart ?: "" + parameters["stockTakeDateEnd"] = stockTakeDateEnd ?: "" + parameters["lastInDateEnd"] = "" parameters["lastOutDateEnd"] = "" @@ -62,8 +62,8 @@ class StockTakeVarianceReportController( stockCategory = stockCategory, itemCode = itemCode, storeLocation = storeLocation, - lastInDateStart = lastInDateStart, - lastInDateEnd = lastInDateEnd, + stockTakeDateStart = stockTakeDateStart, + stockTakeDateEnd = stockTakeDateEnd, ) val stockTakeDateDisplay = dbData .mapNotNull { it["stockTakeDate"] as? String } diff --git a/src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml b/src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml index 51411c0..97e2741 100644 --- a/src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml +++ b/src/main/resources/jasper/StockItemConsumptionTrendReport.jrxml @@ -461,7 +461,7 @@ - + diff --git a/src/main/resources/jasper/StockTakeVarianceReport.jrxml b/src/main/resources/jasper/StockTakeVarianceReport.jrxml index 74b9632..bd69230 100644 --- a/src/main/resources/jasper/StockTakeVarianceReport.jrxml +++ b/src/main/resources/jasper/StockTakeVarianceReport.jrxml @@ -164,7 +164,7 @@ - + From 9bf793085f7e1e67846bf1f74db88317698b4266 Mon Sep 17 00:00:00 2001 From: "B.E.N.S.O.N" Date: Mon, 16 Mar 2026 11:56:45 +0800 Subject: [PATCH 2/3] PO Item QR Code Update --- .../resources/qrCodeLabel/poItemPDF.jrxml | 380 +++++++++--------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/src/main/resources/qrCodeLabel/poItemPDF.jrxml b/src/main/resources/qrCodeLabel/poItemPDF.jrxml index 44606a4..fa0c8dd 100644 --- a/src/main/resources/qrCodeLabel/poItemPDF.jrxml +++ b/src/main/resources/qrCodeLabel/poItemPDF.jrxml @@ -1,192 +1,192 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2493bd182947462b0fe063317f38755687387612 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 16 Mar 2026 12:15:43 +0800 Subject: [PATCH 3/3] update --- .../common/internalSetup/SetupController.kt | 65 +++++++++++++ .../common/internalSetup/inventorySetup.kt | 93 +++++++++++++++++++ .../entity/InventoryLotLineRepository.kt | 13 ++- 3 files changed, 170 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ffii/fpsms/modules/common/internalSetup/SetupController.kt b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/SetupController.kt index f21a9b4..960aa27 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/internalSetup/SetupController.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/SetupController.kt @@ -274,4 +274,69 @@ class SetupController( ) } } + @PostMapping("/inventory/print-lot-stockin-labels-by-store-ids") +fun printLotStockInLabelsByStoreIds( + @RequestBody request: Map +): ResponseEntity> { + + val printerId = (request["printerId"] as? Number)?.toLong() + val printQty = (request["printQty"] as? Number)?.toInt() ?: 1 + val fromIndex = (request["fromIndex"] as? Number)?.toInt() + val toIndex = (request["toIndex"] as? Number)?.toInt() + // storeIds: ["2F","4F"] + val storeIds = (request["storeIds"] as? List<*>)?.mapNotNull { it?.toString() } ?: emptyList() + + if (printerId == null) { + return ResponseEntity.badRequest().body( + mapOf( + "success" to false, + "message" to "printerId is required" + ) + ) + } + if (storeIds.isEmpty()) { + return ResponseEntity.badRequest().body( + mapOf( + "success" to false, + "message" to "storeIds is required" + ) + ) + } + + return try { + val result = inventorySetup.printLotStockInLabelsByStoreIdsV1( + printerId = printerId, + storeIds = storeIds, + printQty = printQty, + fromIndex = fromIndex, + toIndex = toIndex + ) + + val body = + if (result.success) { + mapOf( + "success" to true, + "message" to "Lot stock-in labels printed successfully", + "lastIndex" to result.lastIndex, + "totalLots" to result.totalLots + ) + } else { + mapOf( + "success" to false, + "message" to (result.errorMessage ?: "Printer error"), + "lastIndex" to result.lastIndex, + "totalLots" to result.totalLots + ) + } + + ResponseEntity.ok(body) + } catch (e: Exception) { + ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body( + mapOf( + "success" to false, + "message" to (e.message ?: "Unknown error occurred while printing lot stock-in labels") + ) + ) + } +} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/common/internalSetup/inventorySetup.kt b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/inventorySetup.kt index 2fb88c6..fdc84d6 100644 --- a/src/main/java/com/ffii/fpsms/modules/common/internalSetup/inventorySetup.kt +++ b/src/main/java/com/ffii/fpsms/modules/common/internalSetup/inventorySetup.kt @@ -471,4 +471,97 @@ open class InventorySetup { errorMessage = null ) } + @Transactional(rollbackFor = [Exception::class]) +open fun printLotStockInLabelsByStoreIdsV1( + printerId: Long, + storeIds: List, + printQty: Int = 1, + fromIndex: Int? = null, + toIndex: Int? = null +): PrintProgressResult { + + if (storeIds.isEmpty()) { + return PrintProgressResult( + success = false, + lastIndex = -1, + totalLots = 0, + errorMessage = "storeIds is empty" + ) + } + + // 1. 查出对应 store_id 的 lot line + val allInventoryLotLines = inventoryLotLineRepository + .findAllByStoreIdsAndHasStockInLine(storeIds) + + if (allInventoryLotLines.isEmpty()) { + return PrintProgressResult( + success = false, + lastIndex = -1, + totalLots = 0, + errorMessage = "no lots found for given storeIds" + ) + } + + val effectiveTotal = allInventoryLotLines.size + + // 2. 计算要打印的 index 范围 + val startIndex = (fromIndex ?: 0).coerceAtLeast(0) + val endIndex = (toIndex ?: (effectiveTotal - 1)).coerceAtMost(effectiveTotal - 1) + + if (startIndex > endIndex || startIndex >= effectiveTotal) { + return PrintProgressResult( + success = false, + lastIndex = startIndex - 1, + totalLots = effectiveTotal, + errorMessage = "invalid index range" + ) + } + + var lastIndex = startIndex - 1 + + // 3. 逐张打印,遇到打印机错误就立即返回,支持断点续打 + for (globalIndex in startIndex..endIndex) { + val inventoryLotLine = allInventoryLotLines[globalIndex] + val stockInLineId = inventoryLotLine.inventoryLot?.stockInLine?.id + ?: return PrintProgressResult( + success = false, + lastIndex = lastIndex, + totalLots = effectiveTotal, + errorMessage = "Stock in line missing for inventoryLotLineId=${inventoryLotLine.id}" + ) + + try { + stockInLineService.printQrCode( + PrintQrCodeForSilRequest( + stockInLineId = stockInLineId, + printerId = printerId, + printQty = printQty + ) + ) + lastIndex = globalIndex + } catch (e: Exception) { + val msg = when { + e.message?.contains("paper", ignoreCase = true) == true -> + "Printer error (maybe out of paper): ${e.message}" + else -> + "Printer error: ${e.message}" + } + + return PrintProgressResult( + success = false, + lastIndex = lastIndex, + totalLots = effectiveTotal, + errorMessage = msg + ) + } + } + + // 全部打印成功 + return PrintProgressResult( + success = true, + lastIndex = lastIndex, + totalLots = effectiveTotal, + errorMessage = null + ) +} } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt index 36be207..b5c6711 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt @@ -106,5 +106,16 @@ fun findExpiredItems(@Param("today") today: LocalDate): List """) fun findAllByDeletedIsFalseAndHasStockInLine(): List - + @Query(""" + SELECT ill FROM InventoryLotLine ill + JOIN ill.warehouse w + JOIN ill.inventoryLot il + WHERE w.store_id IN :storeIds + AND ill.deleted = false + AND il.stockInLine IS NOT NULL + ORDER BY w.store_id, il.item.code, il.lotNo +""") +fun findAllByStoreIdsAndHasStockInLine( + @Param("storeIds") storeIds: List +): List } \ No newline at end of file