|
|
|
@@ -19,7 +19,18 @@ import java.time.LocalDate |
|
|
|
import java.time.LocalDateTime |
|
|
|
import com.ffii.fpsms.modules.stock.service.SuggestedPickLotService |
|
|
|
import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus |
|
|
|
|
|
|
|
import com.ffii.fpsms.modules.pickOrder.enums.PickOrderStatus |
|
|
|
import com.ffii.fpsms.modules.pickOrder.entity.PickOrderRepository |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.service.DoPickOrderService |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRepository |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecordRepository |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRepository |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderRecord |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRecord |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus |
|
|
|
import com.ffii.fpsms.modules.deliveryOrder.entity.DoPickOrderLineRecordRepository |
|
|
|
@Service |
|
|
|
open class StockOutLineService( |
|
|
|
private val jdbcDao: JdbcDao, |
|
|
|
@@ -29,8 +40,14 @@ open class StockOutLineService( |
|
|
|
private val itemRepository: ItemsRepository, |
|
|
|
private val inventoryRepository: InventoryRepository, |
|
|
|
private val itemUomRespository: ItemUomRespository, |
|
|
|
private val pickOrderRepository: PickOrderRepository, |
|
|
|
private val inventoryLotLineRepository: InventoryLotLineRepository, |
|
|
|
private val suggestedPickLotService: SuggestedPickLotService |
|
|
|
private val suggestedPickLotService: SuggestedPickLotService, |
|
|
|
private val doPickOrderRepository: DoPickOrderRepository, |
|
|
|
private val doPickOrderRecordRepository: DoPickOrderRecordRepository, |
|
|
|
private val deliveryOrderRepository: DeliveryOrderRepository, |
|
|
|
private val doPickOrderLineRepository: DoPickOrderLineRepository, |
|
|
|
private val doPickOrderLineRecordRepository: DoPickOrderLineRecordRepository, |
|
|
|
): AbstractBaseEntityService<StockOutLine, Long, StockOutLIneRepository>(jdbcDao, stockOutLineRepository) { |
|
|
|
@Throws(IOException::class) |
|
|
|
@Transactional |
|
|
|
@@ -325,6 +342,86 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
private fun completeDoIfAllPickOrdersCompleted(pickOrderId: Long) { |
|
|
|
// 1) 先用 line 关联找 do_pick_order_id |
|
|
|
val lines = doPickOrderLineRepository.findByPickOrderIdAndDeletedFalse(pickOrderId) |
|
|
|
val doPickOrderIds = |
|
|
|
if (lines.isNotEmpty()) lines.mapNotNull { it.doPickOrderId }.distinct() |
|
|
|
else doPickOrderRepository.findByPickOrderId(pickOrderId).mapNotNull { it.id } // 兼容旧设计 |
|
|
|
|
|
|
|
doPickOrderIds.forEach { dpoId -> |
|
|
|
val allLines = doPickOrderLineRepository.findByDoPickOrderIdAndDeletedFalse(dpoId) |
|
|
|
val allPickOrderIdsInDpo = if (allLines.isNotEmpty()) |
|
|
|
allLines.mapNotNull { it.pickOrderId }.distinct() |
|
|
|
else |
|
|
|
doPickOrderRepository.findById(dpoId).orElse(null)?.pickOrderId?.let { listOf(it) } ?: emptyList() |
|
|
|
|
|
|
|
if (allPickOrderIdsInDpo.isEmpty()) return@forEach |
|
|
|
|
|
|
|
// 2) 检查这个 do_pick_order 下所有 pick orders 是否都 COMPLETED |
|
|
|
val statuses = allPickOrderIdsInDpo.map { id -> |
|
|
|
pickOrderRepository.findById(id).orElse(null)?.status |
|
|
|
} |
|
|
|
val allCompleted = statuses.all { it == PickOrderStatus.COMPLETED } |
|
|
|
|
|
|
|
if (!allCompleted) return@forEach |
|
|
|
|
|
|
|
// 3) 全部完成 → 更新 do_pick_order 为 completed,并可复制到 record 表(可选) |
|
|
|
val dpo = doPickOrderRepository.findById(dpoId).orElse(null) ?: return@forEach |
|
|
|
dpo.ticketStatus = DoPickOrderStatus.completed |
|
|
|
dpo.ticketCompleteDateTime = LocalDateTime.now() |
|
|
|
doPickOrderRepository.save(dpo) |
|
|
|
|
|
|
|
// 4) 可选:复制 header 到 record 表,复制 lines 到 line_record,再删除原有行/头(与你在 PickOrderService.completeStockOut 的做法保持一致) |
|
|
|
val dpoRecord = DoPickOrderRecord( |
|
|
|
storeId = dpo.storeId ?: "", |
|
|
|
ticketNo = dpo.ticketNo ?: "", |
|
|
|
ticketStatus = DoPickOrderStatus.completed, |
|
|
|
truckId = dpo.truckId, |
|
|
|
pickOrderId = dpo.pickOrderId, |
|
|
|
truckDepartureTime = dpo.truckDepartureTime, |
|
|
|
shopId = dpo.shopId, |
|
|
|
handledBy = dpo.handledBy, |
|
|
|
handlerName = dpo.handlerName, |
|
|
|
doOrderId = dpo.doOrderId, |
|
|
|
pickOrderCode = dpo.pickOrderCode, |
|
|
|
deliveryOrderCode = dpo.deliveryOrderCode, |
|
|
|
loadingSequence = dpo.loadingSequence, |
|
|
|
ticketReleaseTime = dpo.ticketReleaseTime, |
|
|
|
ticketCompleteDateTime = LocalDateTime.now(), |
|
|
|
truckLanceCode = dpo.truckLanceCode, |
|
|
|
shopCode = dpo.shopCode, |
|
|
|
shopName = dpo.shopName, |
|
|
|
requiredDeliveryDate = dpo.requiredDeliveryDate |
|
|
|
) |
|
|
|
val savedHeader = doPickOrderRecordRepository.save(dpoRecord) |
|
|
|
|
|
|
|
val lineRecords = allLines.map { l -> |
|
|
|
DoPickOrderLineRecord().apply { |
|
|
|
this.doPickOrderId = savedHeader.id |
|
|
|
this.pickOrderId = l.pickOrderId |
|
|
|
this.doOrderId = l.doOrderId |
|
|
|
this.pickOrderCode = l.pickOrderCode |
|
|
|
this.deliveryOrderCode = l.deliveryOrderCode |
|
|
|
this.status = l.status |
|
|
|
} |
|
|
|
} |
|
|
|
if (lineRecords.isNotEmpty()) doPickOrderLineRecordRepository.saveAll(lineRecords) |
|
|
|
|
|
|
|
// 若你流程要求,把原行/头删掉(或保留 completed 状态) |
|
|
|
if (allLines.isNotEmpty()) doPickOrderLineRepository.deleteAll(allLines) |
|
|
|
doPickOrderRepository.delete(dpo) |
|
|
|
|
|
|
|
// 5) 可选:同步 delivery_order → COMPLETED |
|
|
|
dpo.doOrderId?.let { doId -> |
|
|
|
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doId) |
|
|
|
if (deliveryOrder != null && deliveryOrder.status != DeliveryOrderStatus.COMPLETED) { |
|
|
|
deliveryOrder.status = DeliveryOrderStatus.COMPLETED |
|
|
|
deliveryOrderRepository.save(deliveryOrder) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@Transactional |
|
|
|
open fun update(request: UpdateStockOutLineRequest): MessageResponse { |
|
|
|
val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow() |
|
|
|
@@ -373,54 +470,101 @@ private fun getStockOutIdFromPickOrderLine(pickOrderLineId: Long): Long { |
|
|
|
entity = lineInfoList, |
|
|
|
) |
|
|
|
} |
|
|
|
private fun completeDoForPickOrder(pickOrderId: Long) { |
|
|
|
// 1) 原表 do_pick_order → completed |
|
|
|
val dpos = doPickOrderRepository.findByPickOrderId(pickOrderId) |
|
|
|
if (dpos.isNotEmpty()) { |
|
|
|
dpos.forEach { |
|
|
|
it.ticketStatus = com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus.completed |
|
|
|
it.ticketCompleteDateTime = java.time.LocalDateTime.now() |
|
|
|
} |
|
|
|
doPickOrderRepository.saveAll(dpos) |
|
|
|
|
|
|
|
@Transactional |
|
|
|
open fun updateStatus(request: UpdateStockOutLineStatusRequest): MessageResponse { |
|
|
|
try { |
|
|
|
val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow { |
|
|
|
IllegalArgumentException("StockOutLine not found with ID: ${request.id}") |
|
|
|
} |
|
|
|
|
|
|
|
println("Updating StockOutLine ID: ${request.id}") |
|
|
|
println("Current status: ${stockOutLine.status}") |
|
|
|
println("New status: ${request.status}") |
|
|
|
|
|
|
|
// Update status |
|
|
|
stockOutLine.status = request.status |
|
|
|
|
|
|
|
// Update quantity if provided |
|
|
|
if (request.qty != null) { |
|
|
|
val currentQty = stockOutLine.qty?.toDouble() ?: 0.0 |
|
|
|
val newQty = currentQty + request.qty |
|
|
|
stockOutLine.qty = (newQty) |
|
|
|
// 2) 同步相关 delivery_order 状态 |
|
|
|
dpos.forEach { dpo -> |
|
|
|
dpo.doOrderId?.let { doId -> |
|
|
|
val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(doId) |
|
|
|
if (deliveryOrder != null && |
|
|
|
deliveryOrder.status != com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus.COMPLETED |
|
|
|
) { |
|
|
|
deliveryOrder.status = com.ffii.fpsms.modules.deliveryOrder.enums.DeliveryOrderStatus.COMPLETED |
|
|
|
deliveryOrderRepository.save(deliveryOrder) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine) |
|
|
|
println("Updated StockOutLine: ${savedStockOutLine.id} with status: ${savedStockOutLine.status}") |
|
|
|
|
|
|
|
// ✅ FIX: Only call once and add debugging |
|
|
|
if (request.status == "rejected" || request.status == "REJECTED") { |
|
|
|
println("=== TRIGGERING LOT REJECTION LOGIC ===") |
|
|
|
handleLotRejectionFromStockOutLine(savedStockOutLine) |
|
|
|
// 3) 记录表 do_pick_order_record → completed(可选) |
|
|
|
val dporList = doPickOrderRecordRepository.findByPickOrderId(pickOrderId) |
|
|
|
if (dporList.isNotEmpty()) { |
|
|
|
dporList.forEach { |
|
|
|
it.ticketStatus = com.ffii.fpsms.modules.deliveryOrder.enums.DoPickOrderStatus.completed |
|
|
|
it.ticketCompleteDateTime = java.time.LocalDateTime.now() |
|
|
|
} |
|
|
|
doPickOrderRecordRepository.saveAll(dporList) |
|
|
|
} |
|
|
|
} |
|
|
|
open fun updateStatus(request: UpdateStockOutLineStatusRequest): MessageResponse { |
|
|
|
try { |
|
|
|
// 1. 查当前 stockOutLine |
|
|
|
val stockOutLine = stockOutLineRepository.findById(request.id).orElseThrow { |
|
|
|
IllegalArgumentException("StockOutLine not found with ID: ${request.id}") |
|
|
|
} |
|
|
|
|
|
|
|
println("Updating StockOutLine ID: ${request.id}") |
|
|
|
println("Current status: ${stockOutLine.status}") |
|
|
|
println("New status: ${request.status}") |
|
|
|
|
|
|
|
// 2. 更新自身 status/qty |
|
|
|
stockOutLine.status = request.status |
|
|
|
if (request.qty != null) { |
|
|
|
val currentQty = stockOutLine.qty?.toDouble() ?: 0.0 |
|
|
|
val newQty = currentQty + request.qty |
|
|
|
stockOutLine.qty = (newQty) |
|
|
|
} |
|
|
|
val savedStockOutLine = stockOutLineRepository.saveAndFlush(stockOutLine) |
|
|
|
println("Updated StockOutLine: ${savedStockOutLine.id} with status: ${savedStockOutLine.status}") |
|
|
|
|
|
|
|
// 3. 如果被拒绝,触发特殊处理 |
|
|
|
if (request.status == "rejected" || request.status == "REJECTED") { |
|
|
|
println("=== TRIGGERING LOT REJECTION LOGIC ===") |
|
|
|
handleLotRejectionFromStockOutLine(savedStockOutLine) |
|
|
|
} |
|
|
|
|
|
|
|
// 4. 自动刷 pickOrderLine 状态 |
|
|
|
val pickOrderLine = stockOutLine.pickOrderLine |
|
|
|
if (pickOrderLine != null) { |
|
|
|
checkIsStockOutLineCompleted(pickOrderLine.id) |
|
|
|
// 5. 自动刷 pickOrder 状态 |
|
|
|
val pickOrder = pickOrderLine.pickOrder |
|
|
|
if (pickOrder != null) { |
|
|
|
val allLines = pickOrder.pickOrderLines |
|
|
|
val allCompleted = allLines.all { it.status == PickOrderLineStatus.COMPLETED } |
|
|
|
if (allCompleted && allLines.isNotEmpty()) { |
|
|
|
pickOrder.status = PickOrderStatus.COMPLETED |
|
|
|
pickOrderRepository.save(pickOrder) |
|
|
|
completeDoForPickOrder(pickOrder.id!!) |
|
|
|
completeDoIfAllPickOrdersCompleted(pickOrder.id!!) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!) |
|
|
|
return MessageResponse( |
|
|
|
id = savedStockOutLine.id, |
|
|
|
name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo, |
|
|
|
code = savedStockOutLine.stockOut!!.consoPickOrderCode, |
|
|
|
type = savedStockOutLine.status, |
|
|
|
message = "Stock out line status updated successfully", |
|
|
|
errorPosition = null, |
|
|
|
entity = mappedSavedStockOutLine, |
|
|
|
) |
|
|
|
} catch (e: Exception) { |
|
|
|
println("Error updating stock out line status: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
throw e |
|
|
|
} |
|
|
|
|
|
|
|
val mappedSavedStockOutLine = stockOutLineRepository.findStockOutLineInfoById(savedStockOutLine.id!!) |
|
|
|
|
|
|
|
return MessageResponse( |
|
|
|
id = savedStockOutLine.id, |
|
|
|
name = savedStockOutLine.inventoryLotLine!!.inventoryLot!!.lotNo, |
|
|
|
code = savedStockOutLine.stockOut!!.consoPickOrderCode, |
|
|
|
type = savedStockOutLine.status, |
|
|
|
message = "Stock out line status updated successfully", |
|
|
|
errorPosition = null, |
|
|
|
entity = mappedSavedStockOutLine, |
|
|
|
) |
|
|
|
} catch (e: Exception) { |
|
|
|
println("Error updating stock out line status: ${e.message}") |
|
|
|
e.printStackTrace() |
|
|
|
throw e |
|
|
|
} |
|
|
|
} |
|
|
|
// ✅ ADD THIS: Handle lot rejection when stock out line is rejected |
|
|
|
private fun handleLotRejectionFromStockOutLine(stockOutLine: StockOutLine) { |
|
|
|
try { |
|
|
|
|