| @@ -835,7 +835,7 @@ open class StockInLineService( | |||||
| syncPurchaseOrderLineM18LotFromM18(savedPo) | syncPurchaseOrderLineM18LotFromM18(savedPo) | ||||
| // Completed lines for GRN: either this receipt date only (multi-delivery POs) or all completed on PO (legacy). | // Completed lines for GRN: either this receipt date only (multi-delivery POs) or all completed on PO (legacy). | ||||
| val linesForGrn = if (grnReceiptDate != null) { | |||||
| val candidateLines = if (grnReceiptDate != null) { | |||||
| stockInLineRepository.findCompletedByPurchaseOrderIdAndReceiptDateAndDeletedFalseWithItemNames( | stockInLineRepository.findCompletedByPurchaseOrderIdAndReceiptDateAndDeletedFalseWithItemNames( | ||||
| savedPo.id!!, | savedPo.id!!, | ||||
| grnReceiptDate, | grnReceiptDate, | ||||
| @@ -843,20 +843,29 @@ open class StockInLineService( | |||||
| } else { | } else { | ||||
| stockInLineRepository.findCompletedByPurchaseOrderIdAndDeletedFalseWithItemNames(savedPo.id!!) | stockInLineRepository.findCompletedByPurchaseOrderIdAndDeletedFalseWithItemNames(savedPo.id!!) | ||||
| } | } | ||||
| if (linesForGrn.isEmpty()) { | |||||
| if (candidateLines.isEmpty()) { | |||||
| logger.info("[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] DEBUG: Skipping M18 GRN - no stock-in lines for PO id=${savedPo.id} code=${savedPo.code}") | logger.info("[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] DEBUG: Skipping M18 GRN - no stock-in lines for PO id=${savedPo.id} code=${savedPo.code}") | ||||
| return | return | ||||
| } | } | ||||
| if (grnReceiptDate != null && linesForGrn.all { sil -> | |||||
| val id = sil.id ?: return@all false | |||||
| m18GoodsReceiptNoteLogRepository.existsByStockInLineIdAndStatusTrue(id) | |||||
| }) { | |||||
| val linesForGrn = candidateLines.filter { sil -> | |||||
| val id = sil.id ?: return@filter true | |||||
| !m18GoodsReceiptNoteLogRepository.existsByStockInLineIdAndStatusTrue(id) | |||||
| } | |||||
| if (linesForGrn.isEmpty()) { | |||||
| logger.info( | logger.info( | ||||
| "[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] Skipping M18 GRN — all ${linesForGrn.size} line(s) for PO id=${savedPo.id} " + | |||||
| "code=${savedPo.code} on receipt date $grnReceiptDate already have successful GRN logs" | |||||
| "[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] Skipping M18 GRN — all ${candidateLines.size} line(s) for PO id=${savedPo.id} " + | |||||
| "code=${savedPo.code}${grnReceiptDate?.let { " on receipt date $it" } ?: ""} already have successful GRN logs" | |||||
| ) | ) | ||||
| return | return | ||||
| } | } | ||||
| val alreadySyncedCount = candidateLines.size - linesForGrn.size | |||||
| if (alreadySyncedCount > 0) { | |||||
| logger.info( | |||||
| "[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] PO id=${savedPo.id} code=${savedPo.code}: " + | |||||
| "excluding $alreadySyncedCount stock-in line(s) already in successful M18 GRN log; " + | |||||
| "sending ${linesForGrn.size} new line(s)${grnReceiptDate?.let { " for receipt date $it" } ?: ""}" | |||||
| ) | |||||
| } | |||||
| if (savedPo.m18BeId == null || savedPo.supplier?.m18Id == null || savedPo.currency?.m18Id == null) { | if (savedPo.m18BeId == null || savedPo.supplier?.m18Id == null || savedPo.currency?.m18Id == null) { | ||||
| logger.info("[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] DEBUG: Skipping M18 GRN - missing M18 ids for PO id=${savedPo.id} code=${savedPo.code}. m18BeId=${savedPo.m18BeId}, supplier.m18Id=${savedPo.supplier?.m18Id}, currency.m18Id=${savedPo.currency?.m18Id}") | logger.info("[tryUpdatePurchaseOrderAndCreateGrnIfCompleted] DEBUG: Skipping M18 GRN - missing M18 ids for PO id=${savedPo.id} code=${savedPo.code}. m18BeId=${savedPo.m18BeId}, supplier.m18Id=${savedPo.supplier?.m18Id}, currency.m18Id=${savedPo.currency?.m18Id}") | ||||
| return | return | ||||