Repository: SplashSync/Wordpress
Plugin version: 2.1.0 (code identical at current master HEAD)
WooCommerce: 11.0.1 · Target node: Dolibarr module 2.23.3
Summary
When a WooCommerce product disappears without deleted_post firing — a plugin removing rows directly,
a restore from backup, an integration rebuilding its own posts — the Splash link stays active. The
connector then keeps serving that object on every export, but with empty field lists. The target
node receives an update it cannot validate, logs an error, and the same error repeats on every
subsequent sync. Nothing ever reports that the source object is gone.
Concrete case
Dolibarr product EM-14239-8448 (id 984), one order and one invoice attached. On every export:
[Splash Client] Product Attribute Value Name is Not Valid.
[product EM-14239-8448 (id 984) / attribute ticket] (received: '')
Checked on the WooCommerce side:
- no product or variation carries the SKU
EM-14239-8448, nor EM-14239;
- all 404
attribute_ticket values in the database are non-empty — so the empty value is not a
data problem;
- the Events Manager event 14239 still exists and is published, but its WooCommerce product and its
tickets are gone.
So the empty payload is not "bad data", it is "no data": the source object no longer exists, and the
connector still answers for it.
On this single install, 7 Dolibarr products are in this state. 5 of them carry sales, so the
target legitimately refuses to delete them — the link cannot resolve itself, and the error is
permanent.
Why deletion propagation does not cover this
Post\HooksTrait::deleted() does handle products correctly, dispatching by post type and using
getIdsForCommit() to expand a variable parent into its children. That path is fine.
The gap is everything that never reaches deleted_post: direct SQL deletion by another plugin,
database restores, or an integration that manages its own posts. In those cases the link survives
its source, and there is no mechanism to notice.
Suggested improvements
- Fail loudly instead of returning an empty object. When
wc_get_product() returns nothing for
a linked id, the object read should return an explicit "object not found" so the server can mark
the link as dead — rather than handing the target an object whose lists are empty, which produces
a partial write and a misleading validation error.
- Surface dead links. A read-only check in the self-tests — "N linked objects no longer exist on
this node" — would make the problem visible and actionable, instead of appearing as recurring
validation errors on unrelated fields.
Related: a duplicate SKU makes a mapping point at the wrong object
On the same install, five variable products each had one variation carrying the parent's own SKU
(price 0, never sold). The link therefore resolved to the target's variant base, so every write
returned:
[Splash Client] Luke, I am your father... This Product is a Variant base, so you cannot access it directly.
Deleting such a variation is worse than a failed write: it commits Product / SPL_A_DELETE, and the
link resolves to the parent on the target. On the Dolibarr side that is an unguarded deletion of a
product with 14 children and sales — reported separately as a data-loss issue on
SplashSync/Dolibarr (delete() lacks the variant-base guard that load() has).
Refusing to link — or at least warning — when a variation's SKU equals its parent's would remove the
trigger on this side, independently of the fix on the target side.
Repository: SplashSync/Wordpress
Plugin version: 2.1.0 (code identical at current
masterHEAD)WooCommerce: 11.0.1 · Target node: Dolibarr module 2.23.3
Summary
When a WooCommerce product disappears without
deleted_postfiring — a plugin removing rows directly,a restore from backup, an integration rebuilding its own posts — the Splash link stays active. The
connector then keeps serving that object on every export, but with empty field lists. The target
node receives an update it cannot validate, logs an error, and the same error repeats on every
subsequent sync. Nothing ever reports that the source object is gone.
Concrete case
Dolibarr product
EM-14239-8448(id 984), one order and one invoice attached. On every export:Checked on the WooCommerce side:
EM-14239-8448, norEM-14239;attribute_ticketvalues in the database are non-empty — so the empty value is not adata problem;
tickets are gone.
So the empty payload is not "bad data", it is "no data": the source object no longer exists, and the
connector still answers for it.
On this single install, 7 Dolibarr products are in this state. 5 of them carry sales, so the
target legitimately refuses to delete them — the link cannot resolve itself, and the error is
permanent.
Why deletion propagation does not cover this
Post\HooksTrait::deleted()does handle products correctly, dispatching by post type and usinggetIdsForCommit()to expand a variable parent into its children. That path is fine.The gap is everything that never reaches
deleted_post: direct SQL deletion by another plugin,database restores, or an integration that manages its own posts. In those cases the link survives
its source, and there is no mechanism to notice.
Suggested improvements
wc_get_product()returns nothing fora linked id, the object read should return an explicit "object not found" so the server can mark
the link as dead — rather than handing the target an object whose lists are empty, which produces
a partial write and a misleading validation error.
this node" — would make the problem visible and actionable, instead of appearing as recurring
validation errors on unrelated fields.
Related: a duplicate SKU makes a mapping point at the wrong object
On the same install, five variable products each had one variation carrying the parent's own SKU
(price 0, never sold). The link therefore resolved to the target's variant base, so every write
returned:
Deleting such a variation is worse than a failed write: it commits
Product / SPL_A_DELETE, and thelink resolves to the parent on the target. On the Dolibarr side that is an unguarded deletion of a
product with 14 children and sales — reported separately as a data-loss issue on
SplashSync/Dolibarr(delete()lacks the variant-base guard thatload()has).Refusing to link — or at least warning — when a variation's SKU equals its parent's would remove the
trigger on this side, independently of the fix on the target side.