From d5990fef2f7332163d7e6ddbbf1aebc9fb2ba2a6 Mon Sep 17 00:00:00 2001 From: Fai Luk Date: Sun, 22 Mar 2026 03:00:11 +0800 Subject: [PATCH] need to resyn the po if the m18Lot not match, if not the whole PO cannot create GRN --- .../stock/service/StockInLineService.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) 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 51f25d4..1d1f8c9 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 @@ -63,6 +63,7 @@ import com.ffii.fpsms.m18.model.GoodsReceiptNoteAntValue import com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLog import com.ffii.fpsms.m18.entity.M18GoodsReceiptNoteLogRepository import com.ffii.fpsms.m18.service.M18GoodsReceiptNoteService +import com.ffii.fpsms.m18.service.M18PurchaseOrderService import com.ffii.fpsms.m18.utils.CommonUtils import com.google.gson.Gson import org.slf4j.LoggerFactory @@ -97,6 +98,7 @@ open class StockInLineService( private val inventoryRepository: InventoryRepository, private val m18GoodsReceiptNoteService: M18GoodsReceiptNoteService, private val m18GoodsReceiptNoteLogRepository: M18GoodsReceiptNoteLogRepository, + private val m18PurchaseOrderService: M18PurchaseOrderService, ) : AbstractBaseEntityService(jdbcDao, stockInLineRepository) { private val logger = LoggerFactory.getLogger(StockInLineService::class.java) @@ -502,6 +504,46 @@ open class StockInLineService( return poRepository.saveAndFlush(po) } + /** + * Calls M18 GET `/root/api/read/po?id={m18PoId}` (same as [M18PurchaseOrderService.getPurchaseOrder]) using + * [PurchaseOrder.m18DataLog].m18Id, compares each local POL's [PurchaseOrderLine.m18Lot] to M18 `pot[].lot` + * (matched by POL [com.ffii.fpsms.m18.entity.M18DataLog.m18Id] == M18 line id), and persists updates when different. + * Run before building the GRN so `sourceLot` matches M18. + */ + private fun syncPurchaseOrderLineM18LotFromM18(po: PurchaseOrder) { + val m18PoId = po.m18DataLog?.m18Id ?: run { + logger.debug("[GRN m18Lot sync] PO id=${po.id} code=${po.code} has no m18DataLog.m18Id, skip") + return + } + val response = try { + m18PurchaseOrderService.getPurchaseOrder(m18PoId) + } catch (e: Exception) { + logger.warn("[GRN m18Lot sync] read/po failed m18PoId=$m18PoId PO=${po.code}: ${e.message}") + null + } ?: return + + val pot = response.data?.pot ?: return + if (pot.isEmpty()) return + + val lotByM18LineId = pot.associate { it.id to (it.lot ?: "").trim() } + val pols = polRepository.findAllByPurchaseOrderIdAndDeletedIsFalseAndM18DataLogIsNotNull(po.id!!) + var changed = 0 + for (pol in pols) { + val m18LineId = pol.m18DataLog?.m18Id ?: continue + if (!lotByM18LineId.containsKey(m18LineId)) continue + val remoteLot = lotByM18LineId[m18LineId]!! + val localLot = (pol.m18Lot ?: "").trim() + if (localLot == remoteLot) continue + logger.info("[GRN m18Lot sync] PO ${po.code} POL id=${pol.id} m18LineId=$m18LineId: local m18Lot='$localLot' -> M18 lot='$remoteLot'") + pol.m18Lot = if (remoteLot.isEmpty()) null else remoteLot + polRepository.saveAndFlush(pol) + changed++ + } + if (changed > 0) { + logger.info("[GRN m18Lot sync] Updated m18Lot on $changed POL row(s) for PO id=${po.id} code=${po.code}") + } + } + /** * Builds M18 Goods Receipt Note (AN) request from completed PO and its completed stock-in lines. */ @@ -631,6 +673,9 @@ open class StockInLineService( logger.info("[updatePurchaseOrderStatus] savedPo id=${savedPo.id}, status=${savedPo.status}") // TODO: For test only - normally check savedPo.status == PurchaseOrderStatus.COMPLETED and use only COMPLETE lines try { + // Align POL.m18Lot with M18 before GRN (sourceLot must match M18 PO line lot or AN save may fail). + syncPurchaseOrderLineM18LotFromM18(savedPo) + // Defensive: load only completed stock-in lines for the PO, so GRN payload can't include pending/escalated. val linesForGrn = stockInLineRepository.findCompletedByPurchaseOrderIdAndDeletedFalseWithItemNames(savedPo.id!!) if (linesForGrn.isEmpty()) {