Преглед на файлове

updated bag

reset-do-picking-order
CANCERYS\kw093 преди 2 седмици
родител
ревизия
bdcb3cab3a
променени са 3 файла, в които са добавени 16 реда и са изтрити 739 реда
  1. +6
    -4
      src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt
  2. +1
    -732
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  3. +9
    -3
      src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt

+ 6
- 4
src/main/java/com/ffii/fpsms/modules/bag/service/bagService.kt Целия файл

@@ -97,20 +97,22 @@ fun createJoBagConsumption(request: CreateJoBagConsumptionRequest): MessageRespo
entity = null,
)
}
val balanceQty = bagLotLine.balanceQty ?: 0
val startQty = bagLotLine.startQty ?: 0
if (startQty < request.consumedQty + request.scrapQty) {

if (balanceQty < request.consumedQty + request.scrapQty) {
return MessageResponse(
id = null,
code = null,
name = null,
type = "error",
message = "Insufficient balance. Available: $startQty, Requested: ${request.consumedQty + request.scrapQty}",
message = "Insufficient balance. Available: $balanceQty, Requested: ${request.consumedQty + request.scrapQty}",
errorPosition = null,
entity = null,
)
}

val newBalanceQty = startQty - request.consumedQty - request.scrapQty
val newBalanceQty = balanceQty - request.consumedQty - request.scrapQty
if (bagLotLine.firstUseDate == null)
{
bagLotLine.firstUseDate = LocalDate.now()
@@ -134,7 +136,7 @@ fun createJoBagConsumption(request: CreateJoBagConsumptionRequest): MessageRespo
this.jobId = request.jobId
this.stockOutLineId = bagLotLine.stockOutLineId
this.jobOrderCode=jobOrder?.code?:""
this.startQty = startQty
this.startQty = balanceQty
this.consumedQty = request.consumedQty
this.scrapQty = request.scrapQty
this.endQty = newBalanceQty


+ 1
- 732
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt Целия файл

@@ -1533,16 +1533,7 @@ open class PickOrderService(
return pickOrderGroupRepository.findByPickOrderIdAndDeletedIsFalse(pickOrderId)
}

fun getAllGroupsFromDatabase(): List<Map<String, Any>> {
val sql = """
SELECT id, name, target_date, pick_order_id
FROM pick_order_group
WHERE deleted = false
ORDER BY name ASC
""".trimIndent()

return jdbcDao.queryForList(sql, emptyMap<String, Any>())
}

open fun allPickOrdersGroup(): List<PickOrderGroupInfo> {
return pickOrderGroupRepository.findAllByDeletedIsFalse()
@@ -2455,279 +2446,7 @@ open class PickOrderService(
)
}
}
/*
@Transactional(rollbackFor = [java.lang.Exception::class])
open fun autoAssignAndReleasePickOrderByStore(userId: Long, storeId: String): MessageResponse {
try {
println("=== DEBUG: autoAssignAndReleasePickOrderByStore ===")
println("userId: $userId, storeId: $storeId")

val zero = BigDecimal.ZERO
val releasedBy = SecurityUtils.getUser().getOrNull()
val user = userService.find(userId).orElse(null)
if (user == null) {
return MessageResponse(
id = null, name = "User not found", code = "ERROR", type = "pickorder",
message = "User with ID $userId not found", errorPosition = null
)
}

val sql = """
SELECT DISTINCT dpo.pick_order_id AS pickOrderId
FROM do_pick_order dpo
WHERE dpo.deleted = false
AND dpo.ticket_status = 'pending'
AND dpo.store_id = :storeId
AND dpo.pick_order_id IS NOT NULL
""".trimIndent()
val idRows = jdbcDao.queryForList(sql, mapOf("storeId" to storeId))
val pickOrderIdsByStore = idRows.mapNotNull { row ->
when (val id = row["pickOrderId"]) {
is Number -> id.toLong()
is String -> id.toLongOrNull()
else -> null
}
}.toSet()
println("Candidate pickOrderIds by store $storeId: $pickOrderIdsByStore")

if (pickOrderIdsByStore.isEmpty()) {
return MessageResponse(
id = null, name = "No pick orders", code = "NO_ORDERS", type = "pickorder",
message = "No pending pick orders found for store $storeId", errorPosition = null
)
}

// Check if user already has pick orders in progress
val userExistingOrders = pickOrderRepository.findAllByAssignToAndStatusIn(
user,
listOf(PickOrderStatus.PENDING, PickOrderStatus.RELEASED)
).filter { it.deliveryOrder != null }

if (userExistingOrders.isNotEmpty()) {
println(" DEBUG: User $userId already has ${userExistingOrders.size} pick orders in progress:")
userExistingOrders.forEach { po ->
println(" DEBUG: Existing order ${po.id}: code=${po.code}, status=${po.status}")
}
return MessageResponse(
id = null,
name = "User already has pick orders",
code = "USER_BUSY",
type = "pickorder",
message = "User $userId already has ${userExistingOrders.size} pick orders in progress. Cannot assign new orders.",
errorPosition = null,
entity = mapOf(
"existingOrders" to userExistingOrders.map {
mapOf(
"id" to it.id,
"code" to it.code,
"status" to it.status?.value
)
}
)
)
}

val availablePickOrders = pickOrderRepository
.findAll()
.filter { it.id != null && pickOrderIdsByStore.contains(it.id!!) }
.filter { it.deliveryOrder != null }
.filter { it.assignTo == null }
.filter { it.status == PickOrderStatus.PENDING || it.status == PickOrderStatus.RELEASED }
.sortedBy { it.targetDate }
.take(1)

if (availablePickOrders.isEmpty()) {
return MessageResponse(
id = null, name = "No available pick orders", code = "NO_ORDERS", type = "pickorder",
message = "No unassigned pick orders available for store $storeId", errorPosition = null
)
}

val selected = availablePickOrders.first()
val currUser = SecurityUtils.getUser().orElseThrow()

// If still PENDING, perform full release flow
if (selected.status == PickOrderStatus.PENDING) {
val newConsoCode = assignConsoCode()

val stockOut = StockOut().apply {
this.type = "job"
this.consoPickOrderCode = newConsoCode
this.status = StockOutStatus.PENDING.status
this.handler = currUser.id
}
val savedStockOut = stockOutRepository.saveAndFlush(stockOut)

selected.apply {
this.releasedBy = releasedBy
status = PickOrderStatus.RELEASED
this.consoCode = newConsoCode
}

val suggestions = suggestedPickLotService.suggestionForPickOrders(
SuggestedPickLotForPoRequest(pickOrders = listOf(selected))
)
val saveSuggestedPickLots = suggestedPickLotService.saveAll(suggestions.suggestedList)
pickOrderRepository.saveAndFlush(selected)

val inventoryLotLines = inventoryLotLineRepository.findAllByIdIn(
saveSuggestedPickLots.mapNotNull { it.suggestedLotLine?.id }
)
saveSuggestedPickLots.forEach { lot ->
val lotLineId = lot.suggestedLotLine?.id
if (lotLineId != null) {
val idx = inventoryLotLines.indexOf(lot.suggestedLotLine)
if (idx >= 0) {
val currentHold = inventoryLotLines[idx].holdQty ?: zero
val addHold = lot.qty ?: zero
inventoryLotLines[idx].holdQty = currentHold.plus(addHold)
}
}
}
inventoryLotLineRepository.saveAll(inventoryLotLines)

var precreated = 0
saveSuggestedPickLots.forEach { lot ->
val polId = lot.pickOrderLine?.id
val illId = lot.suggestedLotLine?.id
if (polId != null && illId != null) {
val existing = stockOutLIneRepository.findByPickOrderLineIdAndInventoryLotLineIdAndDeletedFalse(
polId,
illId
)
if (existing.isEmpty()) {
val pol = pickOrderLineRepository.findById(polId).orElse(null)
val ill = inventoryLotLineRepository.findById(illId).orElse(null)
if (pol != null && ill != null) {
val line = com.ffii.fpsms.modules.stock.entity.StockOutLine().apply {
this.stockOut = savedStockOut
this.pickOrderLine = pol
this.inventoryLotLine = ill
this.item = pol.item
this.status =
com.ffii.fpsms.modules.stock.web.model.StockOutLineStatus.PENDING.status
this.qty = 0.0
}
stockOutLIneRepository.save(line)
precreated++
}
}
}
}
println("Precreated $precreated stock out lines for store $storeId")
}

// Assign user (both pending->released and already released)
selected.assignTo = user
pickOrderRepository.saveAndFlush(selected)

// 更新 DoPickOrderRecord,填充新字段
val deliveryOrder = selected.deliveryOrder
if (deliveryOrder != null) {
val targetDate = deliveryOrder.estimatedArrivalDate?.toLocalDate() ?: LocalDate.now()
val datePrefix = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd"))

println(" DEBUG: Target date: $targetDate, Date prefix: $datePrefix")

// Find truck by shop ID with earliest departure time
val truck = deliveryOrder.shop?.id?.let { shopId ->
println(" DEBUG: Looking for truck with shop ID: $shopId")
val trucks = truckRepository.findByShopIdAndDeletedFalse(shopId)
println(" DEBUG: Found ${trucks.size} trucks for shop $shopId")
val selectedTruck = trucks.minByOrNull { it.departureTime ?: LocalTime.MAX }
println(" DEBUG: Selected truck: ID=${selectedTruck?.id}, DepartureTime=${selectedTruck?.departureTime}")
selectedTruck
}

// 根据 truck 的 Store_id 字段确定 storeId
val determinedStoreId = when (truck?.storeId) {
4 -> "4/F"
2 -> "2/F"
else -> "2/F"
}

// Get next ticket number for this date and store
val nextTicketNumber = doPickOrderService.getNextTicketNumber(datePrefix, determinedStoreId)
println(" DEBUG: Next ticket number: $nextTicketNumber")

println(" DEBUG: Processing ${deliveryOrder.deliveryOrderLines.size} delivery order lines")

// UPDATE existing do_pick_order_record entries instead of creating new ones
val existingRecords = doPickOrderRecordRepository.findByPickOrderId(selected.id!!)
println(" DEBUG: Found ${existingRecords.size} existing DoPickOrderRecord entries for pick order ${selected.id}")

if (existingRecords.isNotEmpty()) {
// Update existing records
existingRecords.forEach { record ->
record.handledBy = user.id
record.ticketStatus = DoPickOrderStatus.released
record.ticketReleaseTime = LocalDateTime.now() // 设置 release time
// 填充新字段
record.truckLanceCode = truck?.truckLanceCode
record.shopCode = deliveryOrder.shop?.code
record.shopName = deliveryOrder.shop?.name
record.requiredDeliveryDate = targetDate
println(" DEBUG: Updating existing DoPickOrderRecord ID: ${record.id} - handledBy: ${user.id}, status: released")
}
doPickOrderRecordRepository.saveAll(existingRecords)
println(" Updated ${existingRecords.size} existing DoPickOrderRecord entries")
} else {
// Only create new records if none exist (fallback)
println("⚠️ No existing DoPickOrderRecord entries found, creating new ones")
deliveryOrder.deliveryOrderLines.forEach { line ->
println(" DEBUG: Processing line - Store ID: $determinedStoreId")

val doPickOrderRecord = DoPickOrderRecord(
id=dopickorder.id,
storeId = determinedStoreId,
ticketNo = nextTicketNumber,
ticketStatus = DoPickOrderStatus.released,
truckId = truck?.id,
pickOrderId = selected.id,
truckDepartureTime = truck?.departureTime,
shopId = deliveryOrder.shop?.id,
handledBy = user.id,
// 填充新增字段
truckLanceCode = truck?.truckLanceCode,
shopCode = deliveryOrder.shop?.code,
shopName = deliveryOrder.shop?.name,
requiredDeliveryDate = targetDate
)

println(" DEBUG: Creating new DoPickOrderRecord - Store: $determinedStoreId, Ticket: $nextTicketNumber, Truck: ${truck?.id}")

val savedDoPickOrderRecord = doPickOrderRecordRepository.save(doPickOrderRecord)
println(" DEBUG: Saved new DoPickOrderRecord - ID: ${savedDoPickOrderRecord.id}")
}
}
}

doPickOrderService.updateHandledByForPickOrder(selected.id!!, user.id!!)
doPickOrderService.updateRecordHandledByForPickOrder(selected.id!!, user.id!!) // 添加这行
println(" Updated DoPickOrder and DoPickOrderRecord handledBy to user $userId for pick order ${selected.id}")

return MessageResponse(
id = null,
name = "Pick order assigned",
code = "SUCCESS",
type = "pickorder",
message = "Assigned to user $userId for store $storeId",
errorPosition = null,
entity = mapOf(
"pickOrderIds" to listOf(selected.id!!),
"storeId" to storeId,
"status" to selected.status?.value
)
)
} catch (e: Exception) {
e.printStackTrace()
return MessageResponse(
id = null, name = "Failed to auto-assign by store", code = "ERROR", type = "pickorder",
message = "Failed to auto-assign by store: ${e.message}", errorPosition = null
)
}
}
*/
open fun getAllPickOrderLotsWithDetailsWithAutoAssign(userId: Long): List<Map<String, Any>> {
println("=== Debug: getAllPickOrderLotsWithDetailsWithAutoAssign ===")
println("today: ${LocalDate.now()}")
@@ -4791,227 +4510,7 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto
}
}

open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<String, Any?> {
println("=== Debug: getLotDetailsByDoPickOrderRecordId (Hierarchical Format) ===")
println("doPickOrderRecordId: $doPickOrderRecordId")

// 获取 FG 信息(从 record 表)
val doPickOrderRecordSql = """
SELECT
dpor.id as do_pick_order_record_id,
dpor.ticket_no,
dpor.store_id,
dpor.TruckLanceCode,
dpor.truck_departure_time,
dpor.ShopCode,
dpor.ShopName
FROM fpsmsdb.do_pick_order_record dpor
WHERE dpor.id = :doPickOrderRecordId
AND dpor.deleted = false
""".trimIndent()

val doPickOrderRecordInfo = jdbcDao.queryForMap(doPickOrderRecordSql, mapOf("doPickOrderRecordId" to doPickOrderRecordId)).orElse(null)
if (doPickOrderRecordInfo == null) {
println("❌ No do_pick_order_record found with ID: $doPickOrderRecordId")
return mapOf(
"fgInfo" to null,
"pickOrders" to emptyList<Any>()
)
}
// 构建 FG 信息
val fgInfo = mapOf(
"doPickOrderId" to doPickOrderRecordId,
"ticketNo" to doPickOrderRecordInfo["ticket_no"],
"storeId" to doPickOrderRecordInfo["store_id"],
"shopCode" to doPickOrderRecordInfo["ShopCode"],
"shopName" to doPickOrderRecordInfo["ShopName"],
"truckLanceCode" to doPickOrderRecordInfo["TruckLanceCode"],
"departureTime" to doPickOrderRecordInfo["truck_departure_time"]
)
// Step 2: 获取该 do_pick_order_record 下的所有 pick orders
val pickOrdersSql = """
SELECT DISTINCT
dpolr.pick_order_id,
dpolr.pick_order_code,
dpolr.do_order_id,
dpolr.delivery_order_code,
po.consoCode,
po.status,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as targetDate
FROM fpsmsdb.do_pick_order_line_record dpolr
INNER JOIN fpsmsdb.pick_order po ON po.id = dpolr.pick_order_id
WHERE dpolr.do_pick_order_id = :doPickOrderRecordId
AND dpolr.deleted = 0
AND po.deleted = false
ORDER BY dpolr.pick_order_id
""".trimIndent()
val pickOrdersInfo = jdbcDao.queryForList(pickOrdersSql, mapOf("doPickOrderRecordId" to doPickOrderRecordId))
println(" Found ${pickOrdersInfo.size} pick orders in do_pick_order_record")
// Step 3: 为每个 pick order 获取 lines 和 lots
val pickOrders = pickOrdersInfo.map { poInfo ->
val pickOrderId = (poInfo["pick_order_id"] as? Number)?.toLong()
// 查询该 pick order 的所有 lines 和 lots(复用之前的 SQL)
val linesSql = """
SELECT
po.id as pickOrderId,
po.code as pickOrderCode,
po.consoCode as pickOrderConsoCode,
DATE_FORMAT(po.targetDate, '%Y-%m-%d') as pickOrderTargetDate,
po.type as pickOrderType,
po.status as pickOrderStatus,
pol.id as pickOrderLineId,
pol.qty as pickOrderLineRequiredQty,
pol.status as pickOrderLineStatus,
i.id as itemId,
i.code as itemCode,
i.name as itemName,
uc.code as uomCode,
uc.udfudesc as uomDesc,
uc.udfShortDesc as uomShortDesc,
ill.id as lotId,
il.lotNo,
DATE_FORMAT(il.expiryDate, '%Y-%m-%d') as expiryDate,
w.name as location,
COALESCE(uc.udfudesc, 'N/A') as stockUnit,
w.`order` as routerIndex,
w.code as routerRoute,
CASE
WHEN sol.status = 'rejected' THEN NULL
ELSE (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0))
END as availableQty,
COALESCE(spl.qty, 0) as requiredQty,
COALESCE(sol.qty, 0) as actualPickQty,
spl.id as suggestedPickLotId,
ill.status as lotStatus,
sol.id as stockOutLineId,
sol.status as stockOutLineStatus,
COALESCE(sol.qty, 0) as stockOutLineQty,
COALESCE(ill.inQty, 0) as inQty,
COALESCE(ill.outQty, 0) as outQty,
COALESCE(ill.holdQty, 0) as holdQty,
CASE
WHEN (il.expiryDate IS NOT NULL AND il.expiryDate < CURDATE()) THEN 'expired'
WHEN sol.status = 'rejected' THEN 'rejected'
WHEN (COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0) - COALESCE(ill.holdQty, 0)) <= 0 THEN 'insufficient_stock'
WHEN ill.status = 'unavailable' THEN 'status_unavailable'
ELSE 'available'
END as lotAvailability,
CASE
WHEN sol.status = 'completed' THEN 'completed'
WHEN sol.status = 'rejected' THEN 'rejected'
ELSE 'pending'
END as processingStatus
FROM fpsmsdb.pick_order po
JOIN fpsmsdb.pick_order_line pol ON pol.poId = po.id AND pol.deleted = false
JOIN fpsmsdb.items i ON i.id = pol.itemId
LEFT JOIN fpsmsdb.uom_conversion uc ON uc.id = pol.uomId
LEFT JOIN fpsmsdb.suggested_pick_lot spl ON pol.id = spl.pickOrderLineId
LEFT JOIN fpsmsdb.stock_out_line sol
ON sol.pickOrderLineId = pol.id AND sol.inventoryLotLineId = spl.suggestedLotLineId AND sol.deleted = false
LEFT JOIN fpsmsdb.inventory_lot_line ill ON spl.suggestedLotLineId = ill.id AND ill.deleted = false
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId AND il.deleted = false
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
WHERE po.id = :pickOrderId
AND po.deleted = false
ORDER BY
COALESCE(w.`order`, 999999) ASC,
pol.id ASC,
il.lotNo ASC
""".trimIndent()
val linesResults = jdbcDao.queryForList(linesSql, mapOf("pickOrderId" to pickOrderId))
// 按 pickOrderLineId 分组构建层级结构(与 all-lots-hierarchical 相同)
val lineGroups = linesResults.groupBy { (it["pickOrderLineId"] as? Number)?.toLong() }
val pickOrderLines = lineGroups.map { (lineId, lineRows) ->
val firstLineRow = lineRows.firstOrNull()
if (firstLineRow == null) {
null
} else {
val lots = if (lineRows.any { it["lotId"] != null }) {
lineRows.filter { it["lotId"] != null }.map { lotRow ->
mapOf(
"id" to lotRow["lotId"],
"lotNo" to lotRow["lotNo"],
"expiryDate" to lotRow["expiryDate"],
"location" to lotRow["location"],
"stockUnit" to lotRow["stockUnit"],
"availableQty" to lotRow["availableQty"],
"requiredQty" to lotRow["requiredQty"],
"actualPickQty" to lotRow["actualPickQty"],
"inQty" to lotRow["inQty"],
"outQty" to lotRow["outQty"],
"holdQty" to lotRow["holdQty"],
"lotStatus" to lotRow["lotStatus"],
"lotAvailability" to lotRow["lotAvailability"],
"processingStatus" to lotRow["processingStatus"],
"suggestedPickLotId" to lotRow["suggestedPickLotId"],
"stockOutLineId" to lotRow["stockOutLineId"],
"stockOutLineStatus" to lotRow["stockOutLineStatus"],
"stockOutLineQty" to lotRow["stockOutLineQty"],
"router" to mapOf(
"id" to null,
"index" to lotRow["routerIndex"],
"route" to lotRow["routerRoute"],
"area" to lotRow["routerRoute"]
)
)
}
} else {
emptyList()
}
mapOf(
"id" to lineId,
"requiredQty" to firstLineRow["pickOrderLineRequiredQty"],
"status" to firstLineRow["pickOrderLineStatus"],
"item" to mapOf(
"id" to firstLineRow["itemId"],
"code" to firstLineRow["itemCode"],
"name" to firstLineRow["itemName"],
"uomCode" to firstLineRow["uomCode"],
"uomDesc" to firstLineRow["uomDesc"],
"uomShortDesc" to firstLineRow["uomShortDesc"]
),
"lots" to lots
)
}
}.filterNotNull()
mapOf(
"pickOrderId" to pickOrderId,
"pickOrderCode" to poInfo["pick_order_code"],
"doOrderId" to poInfo["do_order_id"],
"deliveryOrderCode" to poInfo["delivery_order_code"],
"consoCode" to poInfo["consoCode"],
"status" to poInfo["status"],
"targetDate" to poInfo["targetDate"],
"pickOrderLines" to pickOrderLines
)
}
return mapOf(
"fgInfo" to fgInfo,
"pickOrders" to pickOrders
)
}
private fun numToBigDecimal(n: Number?): BigDecimal {
return when (n) {
null -> BigDecimal.ZERO
@@ -5019,162 +4518,7 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri
else -> BigDecimal.valueOf(n.toDouble())
}
}
open fun getLotDetailsByDoPickOrderRecordId2(doPickOrderRecordId: Long): Map<String, Any?> {
// 1) 头信息(来自 record 表)
val dpor = doPickOrderRecordRepository.findById(doPickOrderRecordId).orElse(null)
?: return mapOf("fgInfo" to null, "pickOrders" to emptyList<Any>())

val fgInfo = mapOf(
"doPickOrderId" to dpor.id,
"ticketNo" to dpor.ticketNo,
"storeId" to dpor.storeId,
"shopCode" to dpor.shopCode,
"shopName" to dpor.shopName,
"truckLanceCode" to dpor.truckLanceCode,
"departureTime" to dpor.truckDepartureTime
)

// 2) 取该 record 下所有 pick orders(通过行记录聚合)
val lineRecords = doPickOrderLineRecordRepository.findByDoPickOrderIdAndDeletedFalse(dpor.id!!)
val pickOrderIds = lineRecords.mapNotNull { it.pickOrderId }.distinct()
if (pickOrderIds.isEmpty()) {
return mapOf("fgInfo" to fgInfo, "pickOrders" to emptyList<Any>())
}
val pickOrders = pickOrderRepository.findAllById(pickOrderIds)

// 3) 逐个 pick order 组装行与批次/出库行
val pickOrderDtos = pickOrders.map { po ->
val lines = po.pickOrderLines

val lineDtos = lines.map { pol ->
val item = pol.item
val uom = pol.uom

// 建议批次
val lots = pol.suggestedPickLots.mapNotNull { spl ->
val ill = spl.suggestedLotLine ?: return@mapNotNull null
val il = ill.inventoryLot
val w = ill.warehouse
val today = LocalDate.now()
val isExpired = il?.expiryDate?.let { exp -> exp.isBefore(today) } == true

mapOf(
"id" to ill.id,
"lotNo" to il?.lotNo,
"expiryDate" to il?.expiryDate,
"location" to w?.code,
"stockUnit" to (ill.stockUom?.uom?.udfudesc ?: uom?.udfudesc ?: "N/A"),
"availableQty" to ((ill.inQty ?: BigDecimal.ZERO)
.minus(ill.outQty ?: BigDecimal.ZERO)
.minus(ill.holdQty ?: BigDecimal.ZERO)),
"requiredQty" to spl.qty,
"actualPickQty" to null, // 稍后回填
"inQty" to ill.inQty,
"outQty" to ill.outQty,
"holdQty" to ill.holdQty,
"lotStatus" to ill.status?.value,
"lotAvailability" to when {
isExpired -> "expired"
ill.status?.value == "unavailable" -> "status_unavailable"
((ill.inQty ?: BigDecimal.ZERO)
.minus(ill.outQty ?: BigDecimal.ZERO)
.minus(ill.holdQty ?: BigDecimal.ZERO)) <= BigDecimal.ZERO -> "insufficient_stock"
else -> "available"
},
"processingStatus" to "pending",
"suggestedPickLotId" to spl.id,
"stockOutLineId" to null,
"stockOutLineStatus" to null,
"stockOutLineQty" to null,
"router" to mapOf(
"id" to null,
"index" to w?.order,
"route" to w?.code,
"area" to w?.code
)
)
}

// 出库行(投影):支持 inventoryLotLineId 为空
val stockOutLines = pol.id?.let {
// 使用你已有的方法;若该方法返回投影类型(如 StockOutLineInfo),则以下基于字段名访问
stockOutLIneRepository.findAllByPickOrderLineIdAndDeletedFalse(it)
} ?: emptyList()

// 如果是投影,通常字段:id, qty(Double?), status(String?), inventoryLotLineId(Long?)
// 计算每个 ILL 的已拣数量(BigDecimal)
val actualQtyByIllId: Map<Long, BigDecimal> = stockOutLines
.mapNotNull { sol ->
val illId = sol.inventoryLotLineId ?: return@mapNotNull null
val qtyBD = numToBigDecimal(sol.qty as Number?)
illId to qtyBD
}
.groupBy({ it.first }, { it.second })
.mapValues { (_, list) -> list.fold(BigDecimal.ZERO) { acc, v -> acc + v } }

val lotsWithActual = lots.map { lot ->
val illId = lot["id"] as? Long
val actual = illId?.let { actualQtyByIllId[it] }
lot.toMutableMap().apply { if (actual != null) put("actualPickQty", actual) }
}

// 构建 stockouts,若有 lotLineId 则加载 ILL 以补充 lotNo/location/availableQty
val stockouts = stockOutLines.map { sol ->
val illId = sol.inventoryLotLineId
val ill = illId?.let { inventoryLotLineRepository.findById(it).orElse(null) }
val il = ill?.inventoryLot
val w = ill?.warehouse
val available = if (ill == null) null else
(ill.inQty ?: BigDecimal.ZERO)
.minus(ill.outQty ?: BigDecimal.ZERO)
.minus(ill.holdQty ?: BigDecimal.ZERO)

mapOf(
"id" to sol.id,
"status" to sol.status,
"qty" to numToBigDecimal(sol.qty as Number?),
"lotId" to ill?.id,
"lotNo" to (il?.lotNo ?: ""),
"location" to (w?.code ?: ""),
"availableQty" to available,
"noLot" to (ill == null)
)
}

mapOf(
"id" to pol.id,
"requiredQty" to pol.qty,
"status" to pol.status?.value,
"item" to mapOf(
"id" to item?.id,
"code" to item?.code,
"name" to item?.name,
"uomCode" to uom?.code,
"uomDesc" to uom?.udfudesc,
"uomShortDesc" to uom?.udfShortDesc
),
"lots" to lotsWithActual,
"stockouts" to stockouts
)
}

mapOf(
"pickOrderId" to po.id,
"pickOrderCode" to po.code,
"doOrderId" to po.deliveryOrder?.id,
"deliveryOrderCode" to po.deliveryOrder?.code,
"consoCode" to po.consoCode,
"status" to po.status?.value,
"targetDate" to po.targetDate?.toLocalDate(),
"pickOrderLines" to lineDtos
)
}

return mapOf(
"fgInfo" to fgInfo,
"pickOrders" to pickOrderDtos
)
}
open fun getLotDetailsByDoPickOrderRecordId3(doPickOrderRecordId: Long): LotDetailsByDoPickOrderRecordResponse {
println("=== Debug: getLotDetailsByDoPickOrderRecordId3 (Repository-based) ===")
println("doPickOrderRecordId: $doPickOrderRecordId")
@@ -5463,81 +4807,6 @@ open fun getLotDetailsByDoPickOrderRecordId(doPickOrderRecordId: Long): Map<Stri
pickOrders = pickOrderDtos
)
}
// 新增:从 do_pick_order_record 表查询(用于已完成的 pick orders)
open fun getFgPickOrdersFromRecordByPickOrderId(pickOrderId: Long): List<Map<String, Any?>> {
try {
println(" Starting getFgPickOrdersFromRecordByPickOrderId with pickOrderId: $pickOrderId")

val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null)
if (pickOrder == null) {
println("❌ Pick order not found with ID: $pickOrderId")
return emptyList()
}

val deliveryOrder = pickOrder.deliveryOrder
val shop = deliveryOrder?.shop
val supplier = deliveryOrder?.supplier

// 从 do_pick_order_record 和 do_pick_order_line_record 表查询
val ticketNo = try {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
doPickOrderRecords.firstOrNull()?.ticketNo ?: ""
} catch (e: Exception) {
println("⚠️ Error getting ticket number from record: ${e.message}")
""
}
val dpoStoreId = try {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
doPickOrderRecords.firstOrNull()?.storeId ?: ""
} catch (e: Exception) {
""
}

val truckLanceCode = try {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
doPickOrderRecords.firstOrNull()?.truckLanceCode ?: ""
} catch (e: Exception) {
""
}

val departureTime = try {
val doPickOrderRecords = doPickOrderRecordRepository.findByPickOrderId(pickOrderId)
doPickOrderRecords.firstOrNull()?.truckDepartureTime?.toString() ?: ""
} catch (e: Exception) {
""
}

val result = mapOf(
"pickOrderId" to (pickOrder.id ?: 0L),
"pickOrderCode" to (pickOrder.code ?: ""),
"pickOrderConsoCode" to (pickOrder.consoCode ?: ""),
"pickOrderTargetDate" to (pickOrder.targetDate?.toString() ?: ""),
"pickOrderStatus" to (pickOrder.status?.value ?: ""),
"deliveryOrderId" to (deliveryOrder?.id ?: 0L),
"deliveryNo" to (deliveryOrder?.code ?: ""),
"deliveryDate" to (deliveryOrder?.orderDate?.toString() ?: ""),
"shopId" to (shop?.id ?: 0L),
"shopCode" to (shop?.code ?: ""),
"shopName" to (shop?.name ?: ""),
"shopAddress" to buildShopAddress(shop),
"shopPoNo" to (supplier?.code ?: ""),
"numberOfCartons" to (pickOrder.pickOrderLines.size),
"truckLanceCode" to truckLanceCode, // 从 record 表
"DepartureTime" to departureTime, // 从 record 表
"ticketNo" to ticketNo, // 从 record 表
"storeId" to dpoStoreId, // 从 record 表
"qrCodeData" to (pickOrder.id ?: 0L)
)

println(" FG Pick Orders from record: 1")
return listOf(result)

} catch (e: Exception) {
println("❌ Error in getFgPickOrdersFromRecordByPickOrderId: ${e.message}")
e.printStackTrace()
return emptyList()
}
}
}


+ 9
- 3
src/main/java/com/ffii/fpsms/modules/stock/service/SuggestedPickLotService.kt Целия файл

@@ -148,7 +148,9 @@ open class SuggestedPickLotService(
println("calculateRemainingQtyForInfo(lotLine) ${calculateRemainingQtyForInfo(lotLine)}")
val inventoryLotLine = lotLine.id?.let { inventoryLotLineService.findById(it).getOrNull() }
val warehouseStoreId=inventoryLotLine?.warehouse?.store_id
if ( warehouseStoreId == "3F") {
// 3F filter only applies to delivery order release; no limit for job order, AssignAndRelease, etc.
val isDeliveryOrderPick = line.pickOrder?.type?.value == "do"
if (warehouseStoreId == "3F" && isDeliveryOrderPick) {
return@forEachIndexed
}
// 修复:计算可用数量,转换为销售单位
@@ -1223,10 +1225,11 @@ private fun generateOptimalSuggestionsForAllPickOrders(
remainingQty > zero
}.toMutableList()
val isDeliveryOrderContext = pickOrderLines.any { it.pickOrder?.type?.value == "do" }
lotEntities.forEach { lot ->
if (remainingPickOrderLines.isEmpty()) return@forEach
val warehouseStoreId=lot.warehouse?.store_id
if ( warehouseStoreId == "3F") {
if (warehouseStoreId == "3F" && isDeliveryOrderContext) {
return@forEach
}
val totalQty = lot.inQty ?: zero
@@ -1281,6 +1284,7 @@ private fun generateOptimalSuggestionsForAllPickOrders(
return suggestions
}

private fun updateInventoryTableAfterResuggest(pickOrder: PickOrder) {
try {
// Get all item IDs from the pick order
@@ -1503,8 +1507,10 @@ private fun generateCorrectSuggestionsWithExistingHolds(pickOrder: PickOrder): L

val lot = lotInfo.id?.let { inventoryLotLineRepository.findById(it).orElse(null) }
?: return@forEach
val warehouseStoreId=lot.warehouse?.store_id
if ( warehouseStoreId == "3F") {
// 3F filter only for delivery order
if (warehouseStoreId == "3F" && pickOrder.type?.value == "do") {
return@forEach
}
val totalQty = lot.inQty ?: zero


Зареждане…
Отказ
Запис