From 1122a860bd1488a30a52eedcffd806230bb6e089 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 15 Sep 2026 14:12:43 +0800 Subject: [PATCH] 1.0 fix --- .../master/service/StockLedgerFixService.kt | 62 +++++++++++++++++-- .../master/web/models/StockLedgerFixModels.kt | 1 + 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt index bea9364..1051ec2 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/StockLedgerFixService.kt @@ -118,8 +118,10 @@ open class StockLedgerFixService( /** * A: keep existing inventory ids; - * backfill stockUomId from lot stock UOM (and set uomId to base); + * backfill stockUomId from lot stock UOM (and set uomId to base) — one row per + * (itemId, stockUomId) so uk_inventory_item_stock_uom is not violated; * INSERT missing (itemId, stockUomId); + * soft-delete leftover NULL stockUomId rows on items that already have a bucket; * recalc onHand/unavailable from Σ lot qty by item+stock UOM (0 if no lots). * Does not write inventory.onHoldQty (hold stays on lot line). Does not UPDATE inventory_lot_line. */ @@ -156,6 +158,7 @@ open class StockLedgerFixService( val patchedSingleLot = jdbcDao.executeUpdate(BACKFILL_STOCK_UOM_SINGLE_LOT_SQL) val patchedStockUomId = patchedFromUom + patchedSingleLot val inserted = jdbcDao.executeUpdate(INSERT_MISSING_INVENTORY_SQL) + val orphansDeleted = jdbcDao.executeUpdate(SOFT_DELETE_ORPHAN_NULL_STOCK_UOM_SQL) val updated = jdbcDao.executeUpdate(UPDATE_INVENTORY_QTY_SQL) val missingAfter = toInt( jdbcDao.queryForList(MISSING_UOM_PAIR_COUNT_SQL).firstOrNull()?.get("c"), @@ -164,9 +167,10 @@ open class StockLedgerFixService( jdbcDao.queryForList(NULL_STOCK_UOM_COUNT_SQL).firstOrNull()?.get("c"), ) log.info( - "stock-ledger-fix inventory-1.0 patchedStockUomId={} inserted={} updated={} missingAfter={} nullStockUomIdAfter={}", + "stock-ledger-fix inventory-1.0 patchedStockUomId={} inserted={} orphansDeleted={} updated={} missingAfter={} nullStockUomIdAfter={}", patchedStockUomId, inserted, + orphansDeleted, updated, missingAfter, nullStockUomIdAfter, @@ -177,6 +181,7 @@ open class StockLedgerFixService( missingUomPairsAfter = missingAfter, patchedStockUomId = patchedStockUomId, nullStockUomIdAfter = nullStockUomIdAfter, + orphansDeleted = orphansDeleted, ) } finally { inFlight.remove(inventoryLock) @@ -3463,6 +3468,8 @@ open class StockLedgerFixService( /** * Old row: uomId already equals a lot stock UOM (stock, or base when base=stock). * Fill stockUomId from that lot stock; move uomId to item base. + * One keeper per (itemId, uomId); skip if that stockUomId is already taken + * (including soft-deleted rows that still occupy the unique key). */ private const val BACKFILL_STOCK_UOM_WHEN_UOM_MATCHES_LOT_SQL = """ UPDATE inventory i @@ -3477,10 +3484,21 @@ open class StockLedgerFixService( AND il.itemId IS NOT NULL AND iu.uomId IS NOT NULL ) b ON b.itemId = i.itemId AND b.stockUomId = i.uomId + INNER JOIN ( + SELECT itemId, uomId, MIN(id) AS keepId + FROM inventory + WHERE IFNULL(deleted, 0) = 0 + AND stockUomId IS NULL + GROUP BY itemId, uomId + ) keep ON keep.keepId = i.id LEFT JOIN item_uom baseIu ON baseIu.itemId = i.itemId AND baseIu.baseUnit = 1 AND IFNULL(baseIu.deleted, 0) = 0 + LEFT JOIN inventory taken + ON taken.itemId = i.itemId + AND taken.stockUomId = b.stockUomId + AND taken.id <> i.id SET i.stockUomId = b.stockUomId, i.uomId = COALESCE(baseIu.uomId, i.uomId), @@ -3488,11 +3506,12 @@ open class StockLedgerFixService( i.modifiedBy = 'stock-ledger-fix' WHERE IFNULL(i.deleted, 0) = 0 AND i.stockUomId IS NULL + AND taken.id IS NULL """ /** * Remaining NULL stockUomId: item has exactly one lot stock UOM - * (uomId already base, stock may differ). Skip if that bucket row already exists. + * (uomId already base, stock may differ). Keep MIN(id) only. */ private const val BACKFILL_STOCK_UOM_SINGLE_LOT_SQL = """ UPDATE inventory i @@ -3509,6 +3528,13 @@ open class StockLedgerFixService( GROUP BY il.itemId HAVING COUNT(DISTINCT iu.uomId) = 1 ) b ON b.itemId = i.itemId + INNER JOIN ( + SELECT itemId, MIN(id) AS keepId + FROM inventory + WHERE IFNULL(deleted, 0) = 0 + AND stockUomId IS NULL + GROUP BY itemId + ) keep ON keep.keepId = i.id LEFT JOIN item_uom baseIu ON baseIu.itemId = i.itemId AND baseIu.baseUnit = 1 @@ -3516,7 +3542,6 @@ open class StockLedgerFixService( LEFT JOIN inventory taken ON taken.itemId = i.itemId AND taken.stockUomId = b.stockUomId - AND IFNULL(taken.deleted, 0) = 0 AND taken.id <> i.id SET i.stockUomId = b.stockUomId, @@ -3565,8 +3590,14 @@ open class StockLedgerFixService( AND IFNULL(baseIu.deleted, 0) = 0 LEFT JOIN inventory existing ON existing.itemId = src.itemId - AND COALESCE(existing.stockUomId, existing.uomId) = src.uomId AND IFNULL(existing.deleted, 0) = 0 + AND ( + existing.stockUomId = src.uomId + OR (existing.stockUomId IS NULL AND existing.uomId = src.uomId) + ) + LEFT JOIN inventory occupied + ON occupied.itemId = src.itemId + AND occupied.stockUomId = src.uomId LEFT JOIN inventory tpl ON tpl.id = ( SELECT MIN(i.id) @@ -3575,6 +3606,27 @@ open class StockLedgerFixService( AND IFNULL(i.deleted, 0) = 0 ) WHERE existing.id IS NULL + AND occupied.id IS NULL + """ + + /** + * Extra old UOM rows left NULL after a keeper bucket exists for the item + * (e.g. 18918 id 5261). Soft-delete so search / COALESCE fallbacks ignore them. + */ + private const val SOFT_DELETE_ORPHAN_NULL_STOCK_UOM_SQL = """ + UPDATE inventory i + INNER JOIN ( + SELECT DISTINCT itemId + FROM inventory + WHERE IFNULL(deleted, 0) = 0 + AND stockUomId IS NOT NULL + ) keeper ON keeper.itemId = i.itemId + SET + i.deleted = 1, + i.modified = NOW(), + i.modifiedBy = 'stock-ledger-fix' + WHERE IFNULL(i.deleted, 0) = 0 + AND i.stockUomId IS NULL """ private const val UPDATE_INVENTORY_QTY_SQL = """ diff --git a/src/main/java/com/ffii/fpsms/modules/master/web/models/StockLedgerFixModels.kt b/src/main/java/com/ffii/fpsms/modules/master/web/models/StockLedgerFixModels.kt index 4086266..aecdac0 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/web/models/StockLedgerFixModels.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/web/models/StockLedgerFixModels.kt @@ -69,6 +69,7 @@ data class StockLedgerFixInventoryResponse( val missingUomPairsAfter: Int, val patchedStockUomId: Int, val nullStockUomIdAfter: Int, + val orphansDeleted: Int = 0, ) data class StockLedgerFixSearchInventoryHit(