From 7f62902a624ed3b32eed57777764acbf464cd9db Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 30 Jun 2026 22:56:04 +0800 Subject: [PATCH] po detail ui add deleted button and ui update --- .../stock/service/StockInLineService.kt | 69 +++++++++++++++++-- .../stock/web/StockInLineController.kt | 5 ++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt index 6fb83bb..59ff83a 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt @@ -31,6 +31,7 @@ import com.ffii.fpsms.modules.jobOrder.enums.JobOrderStatus import com.ffii.fpsms.modules.master.entity.ItemUomRespository import com.ffii.fpsms.modules.master.entity.WarehouseRepository import com.ffii.fpsms.modules.master.service.ItemUomService +//import com.ffii.fpsms.modules.master.service.BomOutputQtyService import com.ffii.fpsms.modules.master.service.PrinterService import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine @@ -110,6 +111,7 @@ open class StockInLineService( @Value("\${scheduler.m18Grn.createEnabled:false}") private val m18GrnCreateEnabled: Boolean, /** Recent window for PO stock-in alerts (pending / receiving) in nav / list UI. */ @Value("\${fpsms.purchase-stock-in-alert.lookback-days:7}") private val purchaseStockInAlertLookbackDays: Int, + // private val bomOutputQtyService: BomOutputQtyService, ) : AbstractBaseEntityService(jdbcDao, stockInLineRepository) { private val logger = LoggerFactory.getLogger(StockInLineService::class.java) @@ -124,6 +126,63 @@ open class StockInLineService( return stockInLineRepository.findStockInLineInfoByIdAndStatusAndDeletedFalse(id = stockInLineId, status = StockInLineStatus.RECEIVED.status).orElseThrow() } + /** + * Soft-delete a PO stock-in line that has not been put away yet. + * Also soft-deletes linked inventory lot / lot lines when no stock was received into inventory. + */ + @Transactional + open fun softDelete(id: Long): MessageResponse { + val sil = stockInLineRepository.findById(id).orElseThrow { + ResponseStatusException(HttpStatus.NOT_FOUND, "stockInLineId $id not found") + } + if (sil.deleted == true) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Stock in line already deleted") + } + + val status = (sil.status ?: "").lowercase() + if (status == StockInLineStatus.COMPLETE.status || status == StockInLineStatus.PARTIALLY_COMPLETE.status) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot delete: stock in line already put away") + } + + val lotId = sil.inventoryLot?.id + val putAwayQty = if (lotId != null) { + inventoryLotLineRepository.findAllByInventoryLotId(lotId) + .sumOf { it.inQty ?: BigDecimal.ZERO } + } else { + BigDecimal.ZERO + } + if (putAwayQty > BigDecimal.ZERO) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot delete: stock in line already put away") + } + + sil.deleted = true + saveAndFlush(sil) + + sil.inventoryLot?.let { lot -> + lot.deleted = true + inventoryLotRepository.saveAndFlush(lot) + inventoryLotLineRepository.findAllByInventoryLotId(lot.id!!).forEach { line -> + line.deleted = true + inventoryLotLineRepository.save(line) + } + } + + sil.purchaseOrderLine?.let { pol -> + updatePurchaseOrderLineStatus(pol) + pol.purchaseOrder?.let { updatePurchaseOrderStatus(it) } + } + + return MessageResponse( + id = sil.id, + code = sil.itemNo, + name = sil.item?.name, + type = "stock in line deleted", + message = "delete success", + errorPosition = null, + entity = null, + ) + } + open fun purchaseStockInAlertSinceDays(overrideDays: Int?): LocalDateTime { val d = (overrideDays ?: purchaseStockInAlertLookbackDays).coerceIn(1, 90) return LocalDateTime.now().minusDays(d.toLong()) @@ -366,9 +425,8 @@ open class StockInLineService( } // Set demandQty based on source if (jo != null && jo?.bom != null) { - // For job orders, demandQty comes from BOM's outputQty + //this.demandQty = bomOutputQtyService.stockOutputQty(jo?.bom) this.demandQty = jo?.bom?.outputQty - } else if (pol != null && item.id != null) { val m18UomId = pol.uomM18?.id val qtyM18 = pol.qtyM18 @@ -936,6 +994,7 @@ open class StockInLineService( // Set demandQty based on source if (this.jobOrder != null && this.jobOrder?.bom != null) { // For job orders, demandQty comes from BOM's outputQty + //this.demandQty = this.jobOrder?.bom?.let { bomOutputQtyService.stockOutputQty(it) } ?: this.demandQty this.demandQty = this.jobOrder?.bom?.outputQty ?: this.demandQty } else if (this.purchaseOrderLine != null && this.item?.id != null) { val itemId = this.item!!.id!! @@ -1020,7 +1079,7 @@ open class StockInLineService( if (putAwayStockQty >= requiredStockQty) { val _tStatus = System.nanoTime() stockInLine.apply { - val isWipJobOrder = stockInLine.jobOrder?.bom?.description == "WIP" + val isWipJobOrder = stockInLine.jobOrder?.bom?.bomKind == "WIP" this.status = if (isWipJobOrder) { StockInLineStatus.COMPLETE.status } else { @@ -1315,8 +1374,8 @@ open class StockInLineService( val item = stockInLine.item ?: return val _tInv = System.nanoTime() - val inventory = inventoryRepository.findFirstByItemIdAndDeletedIsFalseOrderByIdAsc(item.id!!) ?: return - _logStep("inventoryRepository.findFirstByItemIdAndDeletedIsFalseOrderByIdAsc", _tInv) + val inventory = itemUomService.findInventoryForItemBaseUom(item.id!!) ?: return + _logStep("inventoryRepository.findInventoryForItemBaseUom", _tInv) // ✅ 修复:查询最新的 stock_ledger 记录,基于前一笔 balance 计算 val _tLatest = System.nanoTime() diff --git a/src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt b/src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt index aea4d51..38b4dcf 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt @@ -63,6 +63,11 @@ class StockInLineController( return stockInLineService.update(newItem) } + @PostMapping("/delete/{stockInLineId}") + fun delete(@PathVariable stockInLineId: Long): MessageResponse { + return stockInLineService.softDelete(stockInLineId) + } + @PostMapping("/download-label") @Throws(UnsupportedEncodingException::class, NoSuchMessageException::class, ParseException::class, Exception::class) fun printLabel(@Valid @RequestBody request: ExportQrCodeRequest, response: HttpServletResponse) {