Skip to content
Data migration·Prophet 21

The P21 item load that updates instead of creating

Prophet 21 can resolve an incoming item against an existing record by supplier part number and update it rather than create a new one. The load reports success and your catalog is quietly wrong.

This is the most expensive quiet failure in a Prophet 21 data load, because nothing about it looks like a failure.

You push a batch of items. The load returns success. The counts look approximately right. Weeks later somebody notices that an item's description, unit of measure, or supplier link is not what it should be, and there is no obvious reason why.

The mechanism

P21's item service does not treat your load as strictly create-only. Given a record that carries a supplier part number matching an existing item, it can resolve to that item and apply your payload as an update.

From the API's point of view this is correct behaviour. It found a match and honoured it. From your point of view, a row that was supposed to become a new item instead overwrote a different one.

Two things make this dangerous:

  1. It reports success. There is no error to catch, no rejected row to review, no suspense file to read. Your load log says the row went in.
  2. The damage is invisible at the row level. The item you clobbered still exists and still looks like an item. Nothing is null, nothing is orphaned. You would have to compare before and after to see it, and by then the before is gone.

The failure surfaces later as a pricing mistake, a wrong unit on a purchase order, or a customer receiving the wrong product.

Where it bites hardest

Migrations where the client is re-keying their catalog are the worst case. That is common: a distributor moving to P21 often takes the opportunity to adopt a new item numbering convention, so legacy IDs and new IDs are disjoint by design.

The team is then holding two sets of identifiers with no overlap, and the temptation is to match on whatever field the two files share. Supplier part number and UPC are the usual candidates. Both are exactly the fields that trigger resolution against existing records.

Guardrails

Match deliberately, not incidentally. Decide the key you are matching on and make it explicit in the payload. If you are creating, do not send fields that can be used to resolve to an existing record unless you intend that.

Snapshot before you load. Pull the current state of every item you are about to touch, plus the ones you are not. A load that quietly modified 40 records you did not name is only detectable against a baseline.

Verify by count and by diff. Item count went up by exactly the number of rows you sent, or it did not. If the count is short, the missing rows did not fail, they landed on top of something. Find out what.

Cross-reference before you cut over. When the client is re-keying, build the legacy-to-new map as a real artifact and reconcile it before pricing and history work starts. Do not let it live in someone's head or in a spreadsheet column.

Beware the tempting fuzzy match. When two catalogs do not share an identifier, description matching looks like a rescue. It is not. Size is usually the differentiator in distribution catalogs and it is exactly what token-based similarity throws away. Two conduit fittings that differ only by trade size can score as a perfect match. Prefer a normalized structural match on the identifier itself, accept the lower hit rate, and work the remainder by hand.

The rule

On any load into a live catalog: the acceptance test is the delta, not the return code. The API telling you it succeeded is a statement about the API, not about your data.

Seeing this on your system?

Describe the symptom in your own words. Diagnosing it is our job, and an engineer replies within one business day.