| @@ -274,4 +274,69 @@ class SetupController( | |||||
| ) | ) | ||||
| } | } | ||||
| } | } | ||||
| @PostMapping("/inventory/print-lot-stockin-labels-by-store-ids") | |||||
| fun printLotStockInLabelsByStoreIds( | |||||
| @RequestBody request: Map<String, Any> | |||||
| ): ResponseEntity<Map<String, Any>> { | |||||
| 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") | |||||
| ) | |||||
| ) | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -471,4 +471,97 @@ open class InventorySetup { | |||||
| errorMessage = null | errorMessage = null | ||||
| ) | ) | ||||
| } | } | ||||
| @Transactional(rollbackFor = [Exception::class]) | |||||
| open fun printLotStockInLabelsByStoreIdsV1( | |||||
| printerId: Long, | |||||
| storeIds: List<String>, | |||||
| 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 | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| @@ -106,5 +106,16 @@ fun findExpiredItems(@Param("today") today: LocalDate): List<InventoryLotLine> | |||||
| """) | """) | ||||
| fun findAllByDeletedIsFalseAndHasStockInLine(): List<InventoryLotLine> | fun findAllByDeletedIsFalseAndHasStockInLine(): List<InventoryLotLine> | ||||
| @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<String> | |||||
| ): List<InventoryLotLine> | |||||
| } | } | ||||