# Deploy note — PO line m18Id rematch (recode) Date: 2026-08-19 Branch / build: uncommitted (QA before prod) Author: agent + QA ## Summary - PO/DO sync failed when M18 recoded a product (`proId` changed, item **code** unchanged), e.g. `PFP002PO26080303` lines `20022` / `20023` / `20024`. - Sync now **links** the new M18 id onto the existing local item (same **code + type**) and **does not** rebuild/delete `item_uom`. ## Scope - Backend: `ItemsService.saveItem`, `M18MasterDataService.saveProduct` / `saveProducts`, remap helper - Frontend: none - DB / Liquibase: none - Config / ops: none (use existing `GET /m18/test/po-by-code`) ## Commits - (not committed yet) ## Safety rules (what this deploy will / will not do) | Situation | Behaviour | |-----------|-----------| | Local item already has that `m18Id` | Unchanged: normal product/PO update | | Same **code + type**, new unused `m18Id` | Update `items.m18Id` only. **No** UOM delete/rebuild, **no** QC/name wipe | | New `m18Id` already on **another** item | Refuse (keep old mapping) | | Same code, **different** type | Will **not** auto-link (same as before for type mismatch) | | Brand-new M18 product, no local code | Creates item + UOMs as before | | Scheduled product sync of already-mapped items | Unchanged full product/UOM sync | | Local PO status not `PENDING` | PO resync is **skipped** (existing rule) | ## Pre-check SQL (prod or staging, before resync) ```sql -- 1) Failed PO lines for this PO SELECT d.id, d.m18Id AS m18_line_id, d.status, d.dataLog, d.createDate FROM m18_data_log d WHERE d.refType = 'Purchase Order Line' AND d.status = 'FAIL' AND d.dataLog LIKE '%PFP002PO26080303%' ORDER BY d.id DESC LIMIT 20; -- 2) Local PO must be PENDING SELECT id, code, status, m18Id FROM purchase_order WHERE code = 'PFP002PO26080303' AND deleted = 0; -- 3) After you know the 3 M18 product codes, confirm one local row each SELECT id, code, name, type, m18Id FROM items WHERE deleted = 0 AND code IN ('CODE1', 'CODE2', 'CODE3'); ``` Confirm each of the three codes has **exactly one** local row, and its current `m18Id` is **not** already `20022`/`20023`/`20024` on a *different* item: ```sql SELECT id, code, m18Id FROM items WHERE deleted = 0 AND m18Id IN (20022, 20023, 20024); ``` Expected before fix: **no rows** (or only unrelated items — if any, **do not** auto-resync; fix mapping manually). ## Test plan | # | Steps (who / where / data) | Expected result | |---|----|-----| | 1 | **Staging first.** Snapshot `items.m18Id` + `item_uom` counts for the 3 item codes. | Baseline recorded | | 2 | Confirm M18 products `20022`/`20023`/`20024` still have the **same codes** as local items. | Codes match; types still map to the same FPSMS `type` | | 3 | Confirm local PO `PFP002PO26080303` status is **PENDING**. | If not pending, stop — resync will skip the whole PO | | 4 | Call `GET /m18/test/po-by-code?code=PFP002PO26080303` (auth as ops). | HTTP 200; `totalFail` for those 3 lines is 0 (or success list includes the PO) | | 5 | SQL: `items.m18Id` for the 3 codes is now 20022/20023/20024. `item_uom` **row count and ids unchanged**. QC category / shelf life unchanged. | Remap-only; no UOM wipe | | 6 | UI: PO workbench / PO detail for `PFP002PO26080303` shows **6 lines** (previously 3 missing). | Failed lines created | | 7 | **Regression:** pick a normal mapped item, run scheduled or `GET /m18/product/{existingM18Id}`. | Item still updates as before; UOMs still sync | | 8 | Confirm no new `PO_LINE FAIL` email for this PO after resync. | Alert not re-sent for these 3 proIds | ## Out of scope / not tested - Recoded products whose **item code also changed** (will not auto-link) - Non-`PENDING` PO overwrite (by design skipped) - DO with the same recode (same `resolveLocalItemId` path; spot-check only if a DO uses these 3 items) - Live M18 UOM id change on the recoded product (PO qty conversion still uses existing purchase UOM) ## Rollback - Revert backend deploy (no Liquibase, no frontend). - If only `items.m18Id` was updated, restore previous `m18Id` from the pre-check snapshot. PO lines already inserted stay; delete/fix only if QA rejects the PO. - Do **not** run a full product resync on the new proIds expecting to undo UOMs — this path does not rewrite UOMs.