Просмотр исходного кода

jo pick order resuggest reject W402 warehosue

consumable pickOrder new ui like do
consumable if user is 任文華, suggest and reusggest will reject W402 warehosue
production
CANCERYS\kw093 2 дней назад
Родитель
Сommit
3814cd4c79
5 измененных файлов: 155 добавлений и 53 удалений
  1. +40
    -7
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt
  2. +2
    -29
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoWorkbenchMainService.kt
  3. +36
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoWorkbenchPickConstants.kt
  4. +18
    -0
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/ConsumableWorkbenchPickConstants.kt
  5. +59
    -17
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderWorkbenchService.kt

+ 40
- 7
src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DoWorkbenchMainService.kt Просмотреть файл

@@ -30,6 +30,7 @@ import com.ffii.fpsms.modules.pickOrder.enums.PickOrderLineStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderType
import com.ffii.fpsms.modules.pickOrder.service.PickOrderService
import com.ffii.fpsms.modules.pickOrder.service.ConsumableWorkbenchPickConstants
import com.ffii.fpsms.modules.pickOrder.service.assembleHierarchicalFgPayload
import com.ffii.fpsms.modules.stock.entity.Inventory
import com.ffii.fpsms.modules.stock.entity.InventoryLotLine
@@ -61,6 +62,7 @@ import com.ffii.fpsms.modules.deliveryOrder.web.models.ReleasedDoPickOrderListIt
import com.ffii.fpsms.modules.deliveryOrder.web.models.WorkbenchTicketReleaseTableResponse
import com.ffii.fpsms.modules.user.service.UserService
import com.ffii.fpsms.modules.jobOrder.entity.JoPickOrderRepository
import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchPickConstants
import com.ffii.fpsms.modules.pickOrder.entity.TruckRepository
import kotlin.system.measureTimeMillis
import org.slf4j.LoggerFactory
@@ -239,6 +241,29 @@ open class DoWorkbenchMainService(
joPickOrderRepository.save(jpo)
}

/**
* Warehouse exclude list for workbench **re-suggest** after scan-pick (shortfall / lot split).
* - JO: same list as assign ([JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES]).
* - Consumable: hardcoded user [ConsumableWorkbenchPickConstants.HARDCODED_EXCLUDE_USER_ID] → JO list; else `null`.
* - DO / other: pass through request (`null` → service default excludes).
*/
private fun workbenchResuggestExcludeWarehouseCodes(
pickOrderId: Long?,
poType: PickOrderType?,
userId: Long?,
requestExcludeWarehouseCodes: List<String>?,
): List<String>? {
val resolvedType = poType
?: pickOrderId?.let { pickOrderRepository.findById(it).orElse(null)?.type }
if (resolvedType == PickOrderType.JOB_ORDER) {
return JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES.toList()
}
if (resolvedType == PickOrderType.Consumable && userId != null) {
return ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes(userId)
}
return requestExcludeWarehouseCodes
}

/**
* Workbench scan-pick (DO FG):
* 1) Post outbound on scanned inventory lot line first; on failure return a clear message.
@@ -599,10 +624,12 @@ val saveSolMs = lapMs()

val pickOrderId = pol.pickOrder?.id
val poType = pol.pickOrder?.type
val effectiveExcludeWarehouseCodes = when (poType) {
PickOrderType.JOB_ORDER -> request.excludeWarehouseCodes ?: emptyList()
else -> request.excludeWarehouseCodes // null → DO 走 default;有傳則整份取代 default
}
val effectiveExcludeWarehouseCodes = workbenchResuggestExcludeWarehouseCodes(
pickOrderId = pickOrderId,
poType = poType,
userId = request.userId,
requestExcludeWarehouseCodes = request.excludeWarehouseCodes,
)
val ledgerMs = measureTimeMillis { createWorkbenchPickLedger(sol, effectiveDelta) }
registerAfterCommit {
runInNewTransaction {
@@ -2127,6 +2154,12 @@ return MessageResponse(
var postMs = 0L
try {
if (pickOrderId != null) {
val resuggestExcludeWarehouseCodes = workbenchResuggestExcludeWarehouseCodes(
pickOrderId = pickOrderId,
poType = null,
userId = userId,
requestExcludeWarehouseCodes = effectiveExcludeWarehouseCodes,
)
if (hasExplicitQty) {
rebuildMs = measureTimeMillis {
if (explicitRemainder > BigDecimal.ZERO) {
@@ -2135,13 +2168,13 @@ return MessageResponse(
targetQty = explicitRemainder,
storeId = requestStoreId,
excludeInventoryLotLineId = scannedIllId,
excludeWarehouseCodes = effectiveExcludeWarehouseCodes,
excludeWarehouseCodes = resuggestExcludeWarehouseCodes,
)
} else {
suggestedPickLotWorkbenchService.setNoHoldSuggestionsForPickOrderLineExactQty(
pickOrderLineId = polId,
targetQty = BigDecimal.ZERO,
excludeWarehouseCodes = effectiveExcludeWarehouseCodes,
excludeWarehouseCodes = resuggestExcludeWarehouseCodes,
)
}
}
@@ -2168,7 +2201,7 @@ return MessageResponse(
suggestedPickLotWorkbenchService.rebuildNoHoldSuggestionsForPickOrderLine(
pickOrderLineId = polId,
storeId = requestStoreId,
excludeWarehouseCodes = effectiveExcludeWarehouseCodes,
excludeWarehouseCodes = resuggestExcludeWarehouseCodes,
)
}
ensureMs = measureTimeMillis {


+ 2
- 29
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoWorkbenchMainService.kt Просмотреть файл

@@ -50,35 +50,8 @@ open class JoWorkbenchMainService(
private val suggestedPickLotWorkbenchService: SuggestedPickLotWorkbenchService,
private val stockOutLineWorkbenchService: StockOutLineWorkbenchService,
) {
// Keep aligned with SuggestedPickLotWorkbenchService default excludes for diagnosis logs.
private val workbenchDefaultExcludeWarehouseCodes: Set<String> = setOf(
//"2F-W202-01-00",
//"2F-W200-#A-00",
"4F-W402-01-00",
"4F-W402-02-00",
"4F-W402-03-00",
"4F-W402-04-00",
"4F-W402-05-00",
"4F-W402-#A-00",
"4F-W402-#B-00",
"4F-W402-#C-00",
"4F-W402-#D-00",
"4F-W402-#E-00",
"4F-W402-#F-00",
"4F-W402-#G-00",
"4F-W402-#H-00",
"4F-W402-#I-00",
"4F-W402-#J-00",
"4F-W402-#K-00",
"4F-W402-#L-00",
"4F-W402-#M-00",
"4F-W402-#N-00",
"4F-W402-#O-00",
"4F-W402-#P-00",
"4F-W402-#Q-00",
"4F-W402-#R-00",
"4F-W402-#S-00"
)
private val workbenchDefaultExcludeWarehouseCodes: Set<String> =
JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES

private fun debugPrintSuggestionNullReasons(pickOrderId: Long) {
val pickOrder = pickOrderRepository.findById(pickOrderId).orElse(null) ?: return


+ 36
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoWorkbenchPickConstants.kt Просмотреть файл

@@ -0,0 +1,36 @@
package com.ffii.fpsms.modules.jobOrder.service

/**
* JO workbench pick constants.
*
* [DEFAULT_EXCLUDE_WAREHOUSE_CODES] applies on **assign / first prime** ([JoWorkbenchMainService])
* and on **scan-pick re-suggest** ([com.ffii.fpsms.modules.deliveryOrder.service.DoWorkbenchMainService]).
*/
object JoWorkbenchPickConstants {
val DEFAULT_EXCLUDE_WAREHOUSE_CODES: Set<String> = setOf(
"4F-W402-01-00",
"4F-W402-02-00",
"4F-W402-03-00",
"4F-W402-04-00",
"4F-W402-05-00",
"4F-W402-#A-00",
"4F-W402-#B-00",
"4F-W402-#C-00",
"4F-W402-#D-00",
"4F-W402-#E-00",
"4F-W402-#F-00",
"4F-W402-#G-00",
"4F-W402-#H-00",
"4F-W402-#I-00",
"4F-W402-#J-00",
"4F-W402-#K-00",
"4F-W402-#L-00",
"4F-W402-#M-00",
"4F-W402-#N-00",
"4F-W402-#O-00",
"4F-W402-#P-00",
"4F-W402-#Q-00",
"4F-W402-#R-00",
"4F-W402-#S-00",
)
}

+ 18
- 0
src/main/java/com/ffii/fpsms/modules/pickOrder/service/ConsumableWorkbenchPickConstants.kt Просмотреть файл

@@ -0,0 +1,18 @@
package com.ffii.fpsms.modules.pickOrder.service

import com.ffii.fpsms.modules.jobOrder.service.JoWorkbenchPickConstants

/**
* Temporary consumable workbench exclude list until per-user DB config (Scheme A) lands.
*
* - [HARDCODED_EXCLUDE_USER_ID] → same warehouses as JO ([JoWorkbenchPickConstants]).
* - All other users → `null` → DO 2F default excludes in [SuggestedPickLotWorkbenchService].
*/
object ConsumableWorkbenchPickConstants {
const val HARDCODED_EXCLUDE_USER_ID: Long = 246L

fun resolveExcludeWarehouseCodes(userId: Long): List<String>? {
if (userId != HARDCODED_EXCLUDE_USER_ID) return null
return JoWorkbenchPickConstants.DEFAULT_EXCLUDE_WAREHOUSE_CODES.toList()
}
}

+ 59
- 17
src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderWorkbenchService.kt Просмотреть файл

@@ -5,6 +5,7 @@ import com.ffii.fpsms.modules.master.web.models.MessageResponse
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderLineRepository
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderType
import com.ffii.fpsms.modules.pickOrder.web.models.PickOrderLineLotDetailResponse
import com.ffii.fpsms.modules.stock.service.StockOutLineWorkbenchService
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotWorkbenchService
@@ -169,43 +170,76 @@ open class PickOrderWorkbenchService(
sol.id AS stockOutLineId,
sol.status AS stockOutLineStatus,
COALESCE(sol.qty, 0) AS stockOutLineQty,
sol.inventoryLotLineId AS inventoryLotLineId,
ill.id AS lotId,
il.lotNo AS lotNo,
il.stockInLineId AS stockInLineId,
DATE_FORMAT(il.expiryDate, '%Y-%m-%d') AS expiryDate,
w.name AS location,
w.code AS location,
COALESCE(ill.inQty,0) AS inQty,
COALESCE(ill.outQty,0) AS outQty,
COALESCE(ill.holdQty,0) AS holdQty,
(COALESCE(ill.inQty,0) - COALESCE(ill.outQty,0)) AS availableQty
(COALESCE(ill.inQty,0) - COALESCE(ill.outQty,0)) AS availableQty,
ill.status AS lotStatus,
spl.suggestedPickLotId AS suggestedPickLotId,
COALESCE(spl.splQty, sol.qty, 0) AS suggestedQty
FROM fpsmsdb.stock_out_line sol
LEFT JOIN fpsmsdb.inventory_lot_line ill ON ill.id = sol.inventoryLotLineId
LEFT JOIN fpsmsdb.inventory_lot il ON il.id = ill.inventoryLotId
LEFT JOIN fpsmsdb.warehouse w ON w.id = ill.warehouseId
LEFT JOIN (
SELECT
s.stockOutLineId AS solId,
MAX(s.id) AS suggestedPickLotId,
SUM(COALESCE(s.qty, 0)) AS splQty
FROM fpsmsdb.suggested_pick_lot s
WHERE s.pickOrderLineId = :pickOrderLineId
AND s.deleted = false
AND s.stockOutLineId IS NOT NULL
GROUP BY s.stockOutLineId
) spl ON spl.solId = sol.id
WHERE sol.pickOrderLineId = :pickOrderLineId
AND sol.deleted = false
ORDER BY sol.id
""".trimIndent()
val lotRows = jdbcDao.queryForList(sql, mapOf("pickOrderLineId" to polId))
val zero = BigDecimal.ZERO
val lots = lotRows.map { r ->
val isNoLot = r["inventoryLotLineId"] == null
val inQty = toBigDecimal(r["inQty"]) ?: zero
val outQty = toBigDecimal(r["outQty"]) ?: zero
val availableQty = toBigDecimal(r["availableQty"]) ?: inQty.subtract(outQty)
val status = (r["stockOutLineStatus"]?.toString() ?: "").lowercase()
val lotStatusRaw = (r["lotStatus"]?.toString() ?: "").lowercase()
val suggestedQty = toBigDecimal(r["suggestedQty"]) ?: zero
val stockOutLineQty = toBigDecimal(r["stockOutLineQty"]) ?: zero
val lotAvailability = when {
isNoLot && status != "completed" -> "insufficient_stock"
status == "rejected" -> "rejected"
!isNoLot && lotStatusRaw == "unavailable" -> "status_unavailable"
!isNoLot && availableQty <= zero && status != "completed" -> "insufficient_stock"
else -> "available"
}
mapOf(
"id" to r["lotId"],
"lotNo" to r["lotNo"],
"expiryDate" to r["expiryDate"],
"location" to r["location"],
"stockUnit" to (pol.uom?.udfudesc ?: pol.uom?.code ?: ""),
"availableQty" to r["availableQty"],
"requiredQty" to pol.qty,
"actualPickQty" to r["stockOutLineQty"],
"inQty" to r["inQty"],
"outQty" to r["outQty"],
"holdQty" to r["holdQty"],
"lotStatus" to "available",
"lotAvailability" to "available",
"availableQty" to if (isNoLot) null else availableQty,
"requiredQty" to suggestedQty,
"actualPickQty" to stockOutLineQty,
"inQty" to if (isNoLot) null else inQty,
"outQty" to if (isNoLot) null else outQty,
"holdQty" to if (isNoLot) null else toBigDecimal(r["holdQty"]),
"lotStatus" to if (isNoLot) "unavailable" else r["lotStatus"],
"lotAvailability" to lotAvailability,
"noLot" to isNoLot,
"processingStatus" to (r["stockOutLineStatus"] ?: "pending"),
"suggestedPickLotId" to r["suggestedPickLotId"],
"stockOutLineId" to r["stockOutLineId"],
"stockOutLineStatus" to r["stockOutLineStatus"],
"stockOutLineQty" to r["stockOutLineQty"],
"stockOutLineQty" to stockOutLineQty,
"stockInLineId" to r["stockInLineId"],
)
}
@@ -360,6 +394,7 @@ open class PickOrderWorkbenchService(
""".trimIndent()
val noLotRows = jdbcDao.queryForList(noLotSql, mapOf("pickOrderLineId" to pickOrderLineId))
val noLot = noLotRows.map { r ->
val stockOutLineQty = toBigDecimal(r["stockOutLineQty"]) ?: zero
PickOrderLineLotDetailResponse(
lotId = null,
lotNo = null,
@@ -369,19 +404,19 @@ open class PickOrderWorkbenchService(
stockInLineId = null,
stockUnit = uomDesc,
availableQty = null,
requiredQty = pol.qty ?: zero,
requiredQty = stockOutLineQty,
inQty = null,
outQty = null,
holdQty = null,
actualPickQty = toBigDecimal(r["stockOutLineQty"]) ?: zero,
actualPickQty = stockOutLineQty,
suggestedPickLotId = null,
lotStatus = null,
lotStatus = "unavailable",
stockOutLineId = toLong(r["stockOutLineId"]),
stockOutLineStatus = r["stockOutLineStatus"]?.toString(),
stockOutLineQty = toBigDecimal(r["stockOutLineQty"]) ?: zero,
stockOutLineQty = stockOutLineQty,
totalPickedByAllPickOrders = null,
remainingAfterAllPickOrders = null,
lotAvailability = "available",
lotAvailability = "insufficient_stock",
noLot = true,
)
}
@@ -407,10 +442,17 @@ open class PickOrderWorkbenchService(
errorPosition = null
)

val excludeWarehouseCodes =
if (pickOrder.type == PickOrderType.Consumable) {
ConsumableWorkbenchPickConstants.resolveExcludeWarehouseCodes(userId)
} else {
null
}

val suggestSummary = suggestedPickLotWorkbenchService.primeNextSingleLotSuggestionsForPickOrder(
pickOrderId = pickOrderId,
storeId = null,
excludeWarehouseCodes = null,
excludeWarehouseCodes = excludeWarehouseCodes,
)
val stockOutSummary = stockOutLineWorkbenchService.ensureStockOutLinesForPickOrderNoHold(
pickOrderId = pickOrderId,


Загрузка…
Отмена
Сохранить