CANCERYS\kw093 2 часов назад
Родитель
Сommit
3c43d03fcc
6 измененных файлов: 75 добавлений и 27 удалений
  1. +1
    -1
      src/main/java/com/ffii/fpsms/modules/deliveryOrder/service/DeliveryOrderService.kt
  2. +3
    -3
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JoPickOrderService.kt
  3. +13
    -2
      src/main/java/com/ffii/fpsms/modules/pickOrder/service/PickOrderService.kt
  4. +1
    -1
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  5. +22
    -11
      src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt
  6. +35
    -9
      src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt

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

@@ -1536,7 +1536,7 @@ open class DeliveryOrderService(
this.type = "do"
this.consoPickOrderCode = consoCode
this.status = StockOutStatus.PENDING.status
this.handler = request.userId
//this.handler = request.userId
}
val savedStockOut = stockOutRepository.saveAndFlush(stockOut)



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

@@ -457,8 +457,8 @@ open class JoPickOrderService(
val lotAvailability = when {
il.expiryDate != null && il.expiryDate!!.isBefore(LocalDate.now()) -> "expired"
sol?.status == "rejected" -> "rejected"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
ill.status == InventoryLotLineStatus.UNAVAILABLE -> "status_unavailable"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
else -> "available"
}

@@ -685,8 +685,8 @@ open class JoPickOrderService(
val lotAvailability = when {
il.expiryDate != null && il.expiryDate!!.isBefore(LocalDate.now()) -> "expired"
sol?.status == "rejected" -> "rejected"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
ill.status == InventoryLotLineStatus.UNAVAILABLE -> "status_unavailable"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
else -> "available"
}
@@ -2175,8 +2175,8 @@ open fun getJobOrderLotsHierarchicalByPickOrderId(pickOrderId: Long): JobOrderLo
val lotAvailability = when {
il.expiryDate != null && il.expiryDate!!.isBefore(LocalDate.now()) -> "expired"
sol?.status == "rejected" -> "rejected"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
ill.status == InventoryLotLineStatus.UNAVAILABLE -> "status_unavailable"
availableQty != null && availableQty <= BigDecimal.ZERO -> "insufficient_stock"
else -> "available"
}


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

@@ -920,8 +920,8 @@ open class PickOrderService(
val lotAvailability = when {
isExpired -> "expired"
sol?.status == "rejected" -> "rejected"
availableQty <= zero -> "insufficient_stock"
ill.status?.value == "unavailable" -> "status_unavailable"
availableQty <= zero -> "insufficient_stock"
else -> "available"
}
@@ -4061,6 +4061,17 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto
id = null, name = "New lot line not found", code = "ERROR", type = "pickorder",
message = "Cannot resolve new inventory lot line", errorPosition = null
)

if (newIll.status == InventoryLotLineStatus.UNAVAILABLE) {
return MessageResponse(
id = null,
name = "Lot line unavailable",
code = "LOT_UNAVAILABLE",
type = "pickorder",
message = "Cannot switch to unavailable inventory lot line",
errorPosition = null
)
}
// Item consistency check
val newItemId = newIll.inventoryLot?.item?.id
@@ -4578,8 +4589,8 @@ println("DEBUG sol polIds in linesResults: " + linesResults.mapNotNull { it["sto
lotAvailability = when {
isExpired -> "expired"
stockOutLine?.status == "rejected" -> "rejected"
availableQty <= zero -> "insufficient_stock"
illEntity.status?.value == "unavailable" -> "status_unavailable"
availableQty <= zero -> "insufficient_stock"
else -> "available"
},
processingStatus = when {


+ 1
- 1
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt Просмотреть файл

@@ -137,7 +137,7 @@ interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long
SELECT ill FROM InventoryLotLine ill
JOIN ill.inventoryLot il
WHERE il.expiryDate < :today
AND ill.inQty != ill.outQty
AND coalesce(ill.inQty, 0) <> coalesce(ill.outQty, 0)
AND ill.deleted = false
ORDER BY il.expiryDate ASC
""")


+ 22
- 11
src/main/java/com/ffii/fpsms/modules/stock/service/InventoryLotLineService.kt Просмотреть файл

@@ -83,19 +83,34 @@ open class InventoryLotLineService(
return RecordsRes<InventoryLotLineInfo>(records, total.toInt());
}

/**
* Same rules as [saveInventoryLotLine]: only stay AVAILABLE if previously AVAILABLE and remaining > 0.
*/
open fun deriveInventoryLotLineStatus(
previousStatus: InventoryLotLineStatus?,
inQty: BigDecimal?,
outQty: BigDecimal?,
holdQty: BigDecimal?
): InventoryLotLineStatus {
val remainingQty =
(inQty ?: BigDecimal.ZERO) - (outQty ?: BigDecimal.ZERO) - (holdQty ?: BigDecimal.ZERO)
val status = previousStatus
val qtyStatus =
if (remainingQty > BigDecimal.ZERO) InventoryLotLineStatus.AVAILABLE else InventoryLotLineStatus.UNAVAILABLE
return when {
status == InventoryLotLineStatus.AVAILABLE && qtyStatus == InventoryLotLineStatus.AVAILABLE ->
InventoryLotLineStatus.AVAILABLE
else -> InventoryLotLineStatus.UNAVAILABLE
}
}

open fun saveInventoryLotLine(request: SaveInventoryLotLineRequest): InventoryLotLine {
val inventoryLotLine =
request.id?.let { inventoryLotLineRepository.findById(it).getOrNull() } ?: InventoryLotLine()
val inventoryLot = request.inventoryLotId?.let { inventoryLotRepository.findById(it).getOrNull() }
val warehouse = request.warehouseId?.let { warehouseRepository.findById(it).getOrNull() }
val stockUom = request.stockUomId?.let { itemUomRespository.findById(it).getOrNull() }
val remainingQty =
(request.inQty ?: BigDecimal(0)) - (request.outQty ?: BigDecimal(0)) - (request.holdQty ?: BigDecimal(0))
val status = request.status?.let { _status -> InventoryLotLineStatus.entries.find { it.value == _status } }
val qtyStatus = when (remainingQty > BigDecimal(0)) {
true -> InventoryLotLineStatus.AVAILABLE
else -> InventoryLotLineStatus.UNAVAILABLE
}

println("status: ${request.status}")
println("status123: ${status?.value}")
@@ -107,11 +122,7 @@ open class InventoryLotLineService(
outQty = request.outQty
holdQty = request.holdQty
this.stockUom = stockUom
this.status =
when (status == InventoryLotLineStatus.AVAILABLE && qtyStatus == InventoryLotLineStatus.AVAILABLE) {
true -> InventoryLotLineStatus.AVAILABLE
else -> InventoryLotLineStatus.UNAVAILABLE
}
this.status = deriveInventoryLotLineStatus(status, request.inQty, request.outQty, request.holdQty)
remarks = request.remarks
}



+ 35
- 9
src/main/java/com/ffii/fpsms/modules/stock/service/StockOutLineService.kt Просмотреть файл

@@ -1012,6 +1012,17 @@ open fun updateStockOutLineStatusByQRCodeAndLotNo(request: UpdateStockOutLineSta
errorPosition = null
)
}
if (resolved.status == InventoryLotLineStatus.UNAVAILABLE) {
println(" Reject noLot bind: resolved InventoryLotLine id=${resolved.id} is UNAVAILABLE")
return MessageResponse(
id = null,
name = "Lot line unavailable",
code = "LOT_UNAVAILABLE",
type = "error",
message = "Cannot confirm scan: target inventory lot line is unavailable",
errorPosition = null
)
}
// Bind the lot line to this stockOutLine so subsequent operations can proceed
stockOutLine.inventoryLotLine = resolved
stockOutLine.item = stockOutLine.item ?: resolved.inventoryLot?.item
@@ -1487,19 +1498,29 @@ if (submitQty > BigDecimal.ZERO && actualInventoryLotLineId != null) {
open fun createStockOut(request: StockOutRequest): StockOutLine {

val inventoryLotLine = inventoryLotLineRepository.findById(request.inventoryLotLineId).orElseThrow()
val qtyBd = BigDecimal.valueOf(request.qty)
val oldHold = inventoryLotLine.holdQty ?: BigDecimal.ZERO

// Step 1: Increase outQty in inventory_lot_line table
// Step 1: Increase outQty in inventory_lot_line table and release hold (same idea as pick / QR scan)
val updatedInventoryLotLine = inventoryLotLine.apply {
val currentOutQty = this.outQty ?: BigDecimal.ZERO
val newOutQty = currentOutQty + BigDecimal.valueOf(request.qty)
val newOutQty = currentOutQty + qtyBd
this.outQty = newOutQty

val currentInQty = this.inQty ?: BigDecimal.ZERO
if (newOutQty.compareTo(currentInQty) == 0) {
this.status = InventoryLotLineStatus.UNAVAILABLE
}
val newHold = oldHold.subtract(qtyBd).coerceAtLeast(BigDecimal.ZERO)
this.holdQty = newHold
this.status = inventoryLotLineService.deriveInventoryLotLineStatus(
this.status,
this.inQty,
this.outQty,
this.holdQty
)
}
inventoryLotLineRepository.save(updatedInventoryLotLine)
// inventory.onHandQty / onHoldQty / unavailableQty: 由 DB trigger `inventory_lot_line_AFTER_UPDATE`
// 依 old/new 的 in、out、hold、status 同步;此處勿再手動改 inventory.onHoldQty,否則與 trigger 重複扣會變負數。

val itemId = updatedInventoryLotLine.inventoryLot?.item?.id
?: throw IllegalArgumentException("InventoryLotLine must have an associated item")

// Step 2: Create a row of stock_out
val currentUser = SecurityUtils.getUser().orElseThrow()
@@ -1512,8 +1533,6 @@ if (submitQty > BigDecimal.ZERO && actualInventoryLotLineId != null) {
val savedStockOut = stockOutRepository.save(stockOut)

// Step 3: Create a row in stock_out_line table
val itemId = updatedInventoryLotLine.inventoryLot?.item?.id
?: throw IllegalArgumentException("InventoryLotLine must have an associated item")
val item = itemRepository.findById(itemId).orElseThrow()

val stockOutLine = StockOutLine().apply {
@@ -1917,10 +1936,17 @@ fun applyStockOutLineDelta(
if (isIssuePosting) {
val latestLotLine = inventoryLotLineRepository.findById(lotLine.id!!).orElse(null)
if (latestLotLine != null) {
val prevStatus = latestLotLine.status
val currentHoldQty = latestLotLine.holdQty ?: BigDecimal.ZERO
val currentOutQty = latestLotLine.outQty ?: BigDecimal.ZERO
latestLotLine.holdQty = currentHoldQty.subtract(deltaQty).coerceAtLeast(BigDecimal.ZERO)
latestLotLine.outQty = currentOutQty.add(deltaQty)
latestLotLine.status = inventoryLotLineService.deriveInventoryLotLineStatus(
prevStatus,
latestLotLine.inQty,
latestLotLine.outQty,
latestLotLine.holdQty
)
latestLotLine.modified = eventTime
if (!operator.isNullOrBlank()) {
latestLotLine.modifiedBy = operator


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