Builds the PRO100 sync diff/report. Rows fall into three buckets:
:updates— matched rows become per-field change sets (base_price, materialsunit) plus adata["pro100"]round-trip blob:creates— rows with no catalogue match but enough data to become a new item; applied only when the operator opts in on the preview screen:skipped— ambiguous matches, rows belonging to another group, and unmatched rows that cannot be created (no id, no name)
Pure — no DB. All three buckets are computed unconditionally so the preview can show counts before the operator decides what to apply.
Summary
Functions
Splits a PRO100 row name into {category_name_or_nil, item_name}.
Converts the :creates bucket into the plan shape Executor.execute/4 wants.
Functions
Splits a PRO100 row name into {category_name_or_nil, item_name}.
Both parts are trimmed. When there is no whitespace-padded separator — or
when either side of it is blank ("/ Foo", "Group /") — the whole trimmed
string becomes the item name and there is no category. A blank category name
would fail create_category's changeset, which the executor swallows
silently, leaving items uncategorized with no diagnostic.
Converts the :creates bucket into the plan shape Executor.execute/4 wants.
Category linkage runs through the :_category_name placeholder resolved
against the phase-1 lookup, which is built from categories_to_create — a
name present on an item but missing from that list is dropped silently, so
the two must be derived together.