[{"data":1,"prerenderedAt":798},["ShallowReactive",2],{"blog-pimcore-erp-delta-sync-en":3},{"slug":4,"published":5,"minutes":6,"category":7,"tags":8,"keywords":13,"about":24,"sources":33,"cover":67,"og":68,"expertise":69,"locales":70,"lang":71,"title":74,"description":75,"coverAlt":76,"metaTitle":77,"takeaways":78,"faq":84,"toc":103,"blocks":137,"others":519},"pimcore-erp-delta-sync","2026-10-02",12,"web",[9,10,11,12],"Pimcore","SAP integration","PIM ERP sync","Symfony Messenger",[14,15,16,17,18,19,20,21,22,23],"Pimcore ERP delta sync","Pimcore developer","Pimcore SAP integration","PIM ERP Schnittstelle","Pimcore SAP Schnittstelle","PIM ERP sync best practices","Pimcore Symfony Messenger","delta sync vs full sync","idempotent import Pimcore","Pimcore Infor integration",[25,27,30],{"name":9,"url":26},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPimcore",{"name":28,"url":29},"SAP ERP","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSAP_ERP",{"name":31,"url":32},"Product information management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct_information_management",[34,37,40,43,46,49,52,55,58,61,64],{"title":35,"url":36},"Pimcore docs: Symfony Messenger","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FGetting_Started\u002FInstallation\u002FAdvanced_Installation_Topics\u002FSymfony_Messenger\u002F",{"title":38,"url":39},"Pimcore docs: Platform Versions","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FPimcore_Platform\u002FPlatform_Versions\u002F",{"title":41,"url":42},"Pimcore docs: Pimcore Editions","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FPimcore_Platform\u002FPimcore_Editions\u002F",{"title":44,"url":45},"Pimcore docs: Data Importer","https:\u002F\u002Fdocs.pimcore.com\u002Fplatform\u002FData_Importer\u002F",{"title":47,"url":48},"Pimcore on GitHub: Release 2026.3.0","https:\u002F\u002Fgithub.com\u002Fpimcore\u002Fpimcore\u002Freleases\u002Ftag\u002Fv2026.3.0",{"title":50,"url":51},"Symfony docs: Messenger, Sync & Queued Message Handling","https:\u002F\u002Fsymfony.com\u002Fdoc\u002Fcurrent\u002Fmessenger.html",{"title":53,"url":54},"SAP Library: Change Pointer (Master Data Distribution)","https:\u002F\u002Fhelp.sap.com\u002Fdoc\u002Fsaphelp_nw73ehp1\u002F7.31.19\u002Fen-us\u002F4a\u002Fbb1e253536478be10000000a421937\u002Fcontent.htm?no_cache=true",{"title":56,"url":57},"SAP Library: Change Pointer (IDoc Interface\u002FALE)","https:\u002F\u002Fhelp.sap.com\u002Fdoc\u002Fsaphelp_em700_ehp01\u002F7.0.1\u002Fen-US\u002F12\u002F83e03c19758e71e10000000a114084\u002Fcontent.htm?no_cache=true",{"title":59,"url":60},"Infor docs: Infor ION and M3 BODs","https:\u002F\u002Fdocs.infor.com\u002Fm3cs\u002F10.2.3\u002Fen-us\u002Fm3csbom\u002Ffabsog\u002Fgoi1495809108914.html",{"title":62,"url":63},"Infor Developer Portal: Integration with ION","https:\u002F\u002Fdeveloper.infor.com\u002Ftutorials\u002Fintegration-with-ion",{"title":65,"url":66},"Debezium: open source distributed platform for change data capture","https:\u002F\u002Fdebezium.io\u002F","\u002Fimages\u002Fblog\u002Fpimcore-erp-delta-sync\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fpimcore-erp-delta-sync\u002Fog.jpg","b2b-ecommerce-developer",[71,72,73],"en","de","hu","Pimcore ERP delta sync: syncing product data between SAP or Infor, PIM and shop","How to sync product data between SAP or Infor, Pimcore and a shop: delta vs full sync, change detection, idempotent imports, Messenger queues, ownership and replays.","Diagram: product data flows from an SAP or Infor ERP through a delta extract and a message queue into Pimcore, where quality gates run, and on to the shop.","Pimcore ERP delta sync: a design that holds · Balázs Csorba",[79,80,81,82,83],"Decide ownership per attribute before writing any import: the ERP owns commercial and logistics data, the PIM owns content and classification, and nothing is written by two systems.","Use a delta sync for the daily flow and keep a full sync as a scheduled reconciliation, not as the normal path. Detect changes at the source (SAP change pointers, Infor Sync BODs, CDC) and confirm them with a content hash.","Messages are delivered at least once, so every handler must be idempotent: a stable key per business event, an upsert instead of an insert, and a hash check before writing.","Put a queue between ERP and PIM. In Pimcore that is Symfony Messenger with the pimcore_core queue and a failure transport, ideally on RabbitMQ in production.","Quality gates, a failure queue you actually watch, and a replay command turn a fragile import into an operable pipeline. Check the current Pimcore platform version, edition and support window before you build.",[85,88,91,94,97,100],{"q":86,"a":87},"How do I sync product data from SAP to Pimcore?","Extract changes from SAP as events instead of reading the full material master every time, for example with change pointers (activated in transaction BD61 and processed by report RBDMIDOC) that produce IDocs. Put the messages on a queue, import them into Pimcore with an idempotent handler that upserts by article number and skips records whose content hash is unchanged, and keep a scheduled full reconciliation as a safety net.",{"q":89,"a":90},"What is the difference between a full sync and a delta sync?","A full sync reads and compares all records every run. It is simple and self-healing but slow and heavy on the ERP. A delta sync transfers only what changed since the last run, which is fast and cheap but can silently drift if a change is missed. I run delta for the daily flow and a full reconciliation weekly or nightly to catch drift.",{"q":92,"a":93},"How do I detect changed records between ERP and PIM?","Four options exist: a changed-at timestamp, source-side events such as SAP change pointers or Infor Sync BODs, change data capture on the database, and a content hash that you compute yourself. Timestamps and events tell you what to look at; the hash tells you whether anything relevant actually changed. Combining an event or timestamp with a hash gives the best results.",{"q":95,"a":96},"Can Pimcore use a message queue for imports?","Yes. Pimcore uses Symfony Messenger and defines the pimcore_core queue for background tasks and an optional pimcore_failed_jobs transport for failures. The default backend is Doctrine, and AMQP (RabbitMQ) and Redis are supported. The documentation recommends RabbitMQ for production and workers are started with bin\u002Fconsole messenger:consume pimcore_core.",{"q":98,"a":99},"What does Pimcore Data Importer offer for delta imports?","The Data Importer can read CSV, JSON, XML, XLSX and SQL sources, run on a schedule, from the command line or on push through a queue, and includes a delta check that skips unchanged records plus a cleanup that removes objects that left the source. It is a good fit for file-based feeds. For event-driven ERP flows with per-attribute rules I usually add custom Messenger handlers.",{"q":101,"a":102},"How do I find a Pimcore developer for an SAP or ERP integration?","Look for someone who has shipped a production sync, not only a demo: ask how they handle ownership per attribute, idempotency, failed messages and replays. Experience with Symfony Messenger, the ERP side (IDocs, BODs, OData or REST) and the shop on the other end matters more than any single tool.",[104,107,110,113,116,119,122,125,128,131,134],{"id":105,"title":106},"why-it-is-hard","Why this sync is harder than it looks",{"id":108,"title":109},"ownership","Decide who owns each attribute first",{"id":111,"title":112},"data-flow","The data flow",{"id":114,"title":115},"delta-vs-full","Full vs delta sync, and how to detect a change",{"id":117,"title":118},"idempotent-imports","Idempotent imports",{"id":120,"title":121},"queues","Queues: Symfony Messenger in Pimcore",{"id":123,"title":124},"quality-gates","Data quality gates",{"id":126,"title":127},"monitoring-replays","Monitoring and replays",{"id":129,"title":130},"verify-2026","What to verify about Pimcore in 2026",{"id":132,"title":133},"my-approach","How I approach it in projects",{"id":135,"title":136},"sources","Sources",[138,142,145,159,162,165,168,169,172,226,229,230,233,242,245,246,249,252,299,302,310,311,314,348,351,352,355,358,365,368,369,372,394,402,403,406,429,432,433,436,463,466,467,479,482,483],{"type":139,"content":140},"paragraph",[141],"Almost every B2B commerce project I work on has the same backbone: an ERP such as SAP or Infor holds the truth about articles, prices and stock, a PIM such as Pimcore turns that data into something a customer can understand, and a shop sells it. The three systems agree on a Monday and disagree by Friday. When a sales rep asks why the shop shows an old price, the answer is almost never \"the sync is broken\". It is that nobody decided who owns that field, what counts as a change, or what happens when one message fails.",{"type":139,"content":143},[144],"This article is the design I would use today for the sync between ERP, Pimcore and shop: ownership first, then a delta flow with a full reconciliation next to it, change detection that does not trust a single signal, idempotent handlers behind a queue, quality gates, and the monitoring and replay tooling that makes it operable. At the end is a short list of things to verify about Pimcore versions and editions before you commit, because that landscape has moved in 2026.",{"type":139,"content":146},[147,148,153,154,158],"If you are weighing an AI layer on top of clean product data, the same foundation matters: structured, trustworthy catalogue data is what ",{"tag":149,"to":150,"children":151},"link","\u002Fblog\u002Fagentic-commerce-protocols-ucp-acp-guide",[152],"agentic commerce protocols"," and ",{"tag":149,"to":155,"children":156},"\u002Fblog\u002Fgenerative-engine-optimization-audit",[157],"generative engine optimization"," both depend on.",{"type":160,"level":161,"id":105,"text":106},"heading",2,{"type":139,"content":163},[164],"On paper it is a pipe: read from the ERP, write to the PIM, publish to the shop. In practice there are four problems hiding in it. The first is volume and cadence: a material master with hundreds of thousands of records cannot be re-read every few minutes, but prices and stock change constantly. The second is that \"changed\" is ambiguous: an ERP record can be touched without any attribute that matters to the shop changing. The third is that systems fail halfway: a network timeout, a locked object or a validation error leaves you with some records updated and others not. The fourth is that two systems often write the same field, and the last write wins by accident.",{"type":139,"content":166},[167],"Each of these has a boring, well-understood answer. The point of the design below is to apply them consistently, so that the sync can be explained on one page and debugged in ten minutes.",{"type":160,"level":161,"id":108,"text":109},{"type":139,"content":170},[171],"Before any import code, I write down an ownership table: every attribute group, which system is the single writer, and in which direction it flows. A field has exactly one owner. The other systems may read it, display it or cache it, but never write it back. This one rule removes most of the \"ping-pong\" bugs where an import overwrites a manual edit, or an editor fixes a value that is overwritten again the next night.",{"type":173,"head":174,"rows":183},"table",[175,177,179,181],[176],"Attribute group",[178],"Owner",[180],"Direction",[182],"Rule",[184,193,201,210,217],[185,187,189,191],[186],"Article number, base unit, status, weights, GTIN",[188],"ERP",[190],"ERP → PIM",[192],"Read-only in the PIM editing UI",[194,196,197,199],[195],"Prices, stock, availability, customer-specific conditions",[188],[198],"ERP → shop",[200],"Often bypasses the PIM, or is looked up live",[202,204,206,208],[203],"Titles, descriptions, images, documents, translations",[205],"PIM",[207],"PIM → shop",[209],"Never imported from the ERP once maintained",[211,213,214,215],[212],"Classification, filterable attributes, variants, cross-sell",[205],[207],[216],"Seeded from the ERP once, owned by the PIM afterwards",[218,220,222,224],[219],"Sorting, SEO URL, merchandising flags",[221],"Shop",[223],"Stays in shop",[225],"Not synced back",{"type":139,"content":227},[228],"This split is typical, not universal. Some companies keep long texts in the ERP, some keep dimensions in the PIM. What matters is that your table exists, that it is enforced in code (the import simply has no mapping for fields it does not own), and that the PIM UI marks ERP-owned fields as read-only so editors stop trying to change them. For the shared grey zone, such as a name that starts in the ERP and gets polished in the PIM, make the hand-over explicit: the ERP value seeds the field once, and a flag records that the PIM owns it from then on.",{"type":160,"level":161,"id":111,"text":112},{"type":139,"content":231},[232],"The shape I use is a short chain with a queue in the middle and a failure path next to it. The ERP side produces change events; a thin extract step normalises them; a queue decouples the speed of the ERP from the speed of Pimcore; Pimcore validates, enriches and stores; and the publish step feeds the shop.",{"type":234,"attrs":235,"inner":239,"caption":240},"diagram",{"viewBox":236,"role":237,"aria-labelledby":238},"0 0 740 290","img","d1-pimerp-t d1-pimerp-d","\u003Ctitle id=\"d1-pimerp-t\">Product data flow from ERP to shop\u003C\u002Ftitle>\u003Cdesc id=\"d1-pimerp-d\">Five boxes in a row: ERP, extract, queue, Pimcore PIM and shop, with arrows between them, and a dashed box below for the failure queue, replay and reconciliation.\u003C\u002Fdesc>\u003Ctext x=\"20\" y=\"28\" class=\"d-title\">Product data flow from ERP to shop\u003C\u002Ftext>\u003Ctext x=\"700\" y=\"28\" text-anchor=\"end\" class=\"d-label\">ERP → PIM → shop\u003C\u002Ftext>\u003Crect x=\"20\" y=\"80\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-sky\" \u002F>\u003Ctext x=\"79\" y=\"114\" text-anchor=\"middle\" class=\"d-text\">ERP\u003C\u002Ftext>\u003Ctext x=\"79\" y=\"135\" text-anchor=\"middle\" class=\"d-small\">SAP or Infor\u003C\u002Ftext>\u003Cpath d=\"M138 118 H155.5\" class=\"d-line\" \u002F>\u003Cpath d=\"M163.5 118 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"163.5\" y=\"80\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-box\" \u002F>\u003Ctext x=\"222.5\" y=\"114\" text-anchor=\"middle\" class=\"d-text\">Extract\u003C\u002Ftext>\u003Ctext x=\"222.5\" y=\"135\" text-anchor=\"middle\" class=\"d-small\">delta + hash\u003C\u002Ftext>\u003Cpath d=\"M281.5 118 H299\" class=\"d-line\" \u002F>\u003Cpath d=\"M307 118 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"307\" y=\"80\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-accent\" \u002F>\u003Ctext x=\"366\" y=\"114\" text-anchor=\"middle\" class=\"d-text\">Queue\u003C\u002Ftext>\u003Ctext x=\"366\" y=\"135\" text-anchor=\"middle\" class=\"d-small\">Messenger\u003C\u002Ftext>\u003Cpath d=\"M425 118 H442.5\" class=\"d-line\" \u002F>\u003Cpath d=\"M450.5 118 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"450.5\" y=\"80\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-mint\" \u002F>\u003Ctext x=\"509.5\" y=\"114\" text-anchor=\"middle\" class=\"d-text\">Pimcore PIM\u003C\u002Ftext>\u003Ctext x=\"509.5\" y=\"135\" text-anchor=\"middle\" class=\"d-small\">gates, enrich\u003C\u002Ftext>\u003Cpath d=\"M568.5 118 H586\" class=\"d-line\" \u002F>\u003Cpath d=\"M594 118 l-9 -5 v10 z\" class=\"d-head\" \u002F>\u003Crect x=\"594\" y=\"80\" width=\"118\" height=\"76\" rx=\"10\" class=\"d-gold\" \u002F>\u003Ctext x=\"653\" y=\"114\" text-anchor=\"middle\" class=\"d-text\">Shop\u003C\u002Ftext>\u003Ctext x=\"653\" y=\"135\" text-anchor=\"middle\" class=\"d-small\">publish\u003C\u002Ftext>\u003Cpath d=\"M509.5 156 V196\" class=\"d-line d-dash\" \u002F>\u003Cpath d=\"M366 156 V196\" class=\"d-line d-dash\" \u002F>\u003Crect x=\"20\" y=\"196\" width=\"700\" height=\"46\" rx=\"10\" class=\"d-box d-dash\" \u002F>\u003Ctext x=\"370\" y=\"224\" text-anchor=\"middle\" class=\"d-small\">Failure queue · replay · nightly full reconciliation · dashboards\u003C\u002Ftext>\u003Ctext x=\"370\" y=\"272\" text-anchor=\"middle\" class=\"d-label\">The ERP owns commercial data, the PIM owns content\u003C\u002Ftext>",[241],"The sync as a chain with a queue in the middle. The failure path, replay tooling and reconciliation run next to it and are part of the design, not an afterthought.",{"type":139,"content":243},[244],"Three details make this shape work. First, the extract step is the only place that knows about SAP or Infor: it speaks IDoc, BOD, OData or REST and emits one internal message format. Swapping the ERP, or adding a second one, then touches one component. Second, the queue is a real queue, not a table you poll, so back-pressure and retries are handled by infrastructure you did not write. Third, the PIM does not publish blindly: it holds a record back when it fails a gate, and the shop only sees what passed.",{"type":160,"level":161,"id":114,"text":115},{"type":139,"content":247},[248],"A full sync reads every record on every run and compares it with what you have. It is simple, it heals itself, and it is the right tool for the first load and for periodic reconciliation. It is also slow and puts load on the ERP. A delta sync only transfers what changed since the last successful run. It is fast, but it has one failure mode that a full sync does not: if a change is missed, it stays missed. So I use both: delta for the daily flow, a full reconciliation on a schedule to catch drift.",{"type":139,"content":250},[251],"The harder question is how you know something changed. There are four common signals, and none of them is enough alone.",{"type":173,"head":253,"rows":262},[254,256,258,260],[255],"Signal",[257],"How it works",[259],"Strength",[261],"Weakness",[263,272,281,290],[264,266,268,270],[265],"Changed-at timestamp",[267],"Query records modified since the last watermark",[269],"Easy to build",[271],"Clock skew, back-dated edits, no delete information",[273,275,277,279],[274],"Source events",[276],"SAP change pointers produce IDocs; Infor ION publishes Sync BODs from the data owner",[278],"Reports the change itself, includes deletes",[280],"Needs setup and housekeeping on the ERP",[282,284,286,288],[283],"Change data capture",[285],"A tool such as Debezium streams row-level changes in commit order",[287],"Complete and ordered",[289],"Operates at table level, not business level",[291,293,295,297],[292],"Content hash",[294],"You hash the normalised, relevant attributes and compare",[296],"Ignores irrelevant touches, safe to repeat",[298],"You compute and store it yourself",{"type":139,"content":300},[301],"On the SAP side, change pointers are the classic mechanism for master data. They are activated generally in transaction BD61 and per message type, for example MATMAS for materials. The report RBDMIDOC (transaction BD21) reads unprocessed pointers, generates IDocs and marks the pointers as processed, and RBDCPCLR (BD22) removes processed ones so the tables BDCP and BDCPS stay small. Plan both jobs; a delta feed that is never cleaned up becomes its own performance problem. On the Infor side, ION routes business object documents, and a Sync BOD is sent by the owner of the data to any application that needs the change.",{"type":139,"content":303},[304,305,309],"My default is therefore a ",{"tag":306,"children":307},"strong",[308],"two-step check",": an event or a watermark tells me which records to look at, and a hash of the normalised, owned attributes tells me whether to write. Normalise before hashing: trim whitespace, fix number formats and decimal places, sort lists and drop fields the ERP touches without business meaning. Otherwise a timestamp bump on the ERP side causes thousands of pointless writes and re-indexing in Pimcore.",{"type":160,"level":161,"id":117,"text":118},{"type":139,"content":312},[313],"Message systems deliver at least once. Symfony documents this plainly: a message can be delivered more than once, so handlers must be safe to run repeatedly. Add retries, a worker restart and a replay, and every record will eventually be processed twice. Idempotency is not an optimisation, it is a requirement.",{"type":315,"ordered":316,"items":317},"list",false,[318,323,328,333,338,343],[319,322],{"tag":306,"children":320},[321],"Stable key per business event."," Derive the key from the business meaning (article number plus ERP change number or content hash), never from a random id generated at send time.",[324,327],{"tag":306,"children":325},[326],"Upsert by natural key."," Look up the Pimcore object by article number and update it, or create it, in one handler. Never blindly create.",[329,332],{"tag":306,"children":330},[331],"Hash before write."," If the hash of the incoming owned attributes equals the stored one, do nothing. This also stops re-indexing and cache invalidation storms.",[334,337],{"tag":306,"children":335},[336],"Order tolerance."," Carry a version or timestamp from the source and ignore a message older than what you have stored. Queues do not guarantee global order.",[339,342],{"tag":306,"children":340},[341],"Deletes as state."," Model deactivation and deletion as a status change with a source version, not as a missing record, so they survive replays.",[344,347],{"tag":306,"children":345},[346],"Database constraints as the last line."," A unique key on the article number turns a race between two workers into an error you can see.",{"type":139,"content":349},[350],"Pimcore's Data Importer follows the same idea for file-based feeds: its resolver decides whether a record updates an existing object or creates a new one, and a delta check skips unchanged records. If your feed is a CSV or JSON from the ERP, that may be all you need. I reach for custom handlers when the flow is event-driven, when ownership is per attribute and needs logic, or when one ERP message fans out into several Pimcore objects.",{"type":160,"level":161,"id":120,"text":121},{"type":139,"content":353},[354],"Pimcore uses Symfony Messenger for background work. The documentation defines the queue pimcore_core for background tasks and an optional pimcore_failed_jobs transport for failed messages. The backend is chosen by the PIMCORE_MESSENGER_TRANSPORT_DSN_PREFIX environment variable: Doctrine is the default, and AMQP (RabbitMQ) and Redis are supported. The documentation states that RabbitMQ is the recommended message queue for production, and workers run as `bin\u002Fconsole messenger:consume pimcore_core`, supervised by supervisor or systemd.",{"type":139,"content":356},[357],"For an ERP sync I would add a dedicated transport next to pimcore_core, so a burst of price updates cannot starve the editors' own background jobs, and I would size workers per queue. On the Symfony side, retry behaviour is configured per transport (max_retries, delay, multiplier, max_delay, jitter); by default a message is retried three times with exponential backoff before it goes to the failure transport. Think about which errors deserve a retry: a locked object or a timeout does, a validation error does not, and retrying it only delays the alert.",{"type":359,"variant":360,"title":361,"body":362},"callout","tip","Do not put the whole ERP record in the message",[363],[364],"Send a small message with the key, the source version and the hash, and let the handler fetch the full record if needed. Large payloads bloat the queue, and by the time a retry runs the payload may already be stale. A thin message plus a fresh read is easier to replay and easier to reason about.",{"type":139,"content":366},[367],"Keep the queue honest. messenger:stats shows how many messages are waiting per transport, and the failure transport is only useful if someone looks at it, which brings us to monitoring.",{"type":160,"level":161,"id":123,"text":124},{"type":139,"content":370},[371],"A sync that faithfully copies bad data is worse than one that stops. I put gates between \"received\" and \"published\", and a record that fails a gate is held and reported, not dropped and not published.",{"type":315,"ordered":316,"items":373},[374,379,384,389],[375,378],{"tag":306,"children":376},[377],"Structural gate:"," required fields present, types and units valid, referenced objects (brand, category, unit) exist.",[380,383],{"tag":306,"children":381},[382],"Business gate:"," sensible ranges (price above zero, weight not ten thousand times the median), status transitions that make sense, no sudden mass deactivation.",[385,388],{"tag":306,"children":386},[387],"Completeness gate:"," a product is only published to the shop when mandatory content (title, image, translation) exists in every required language.",[390,393],{"tag":306,"children":391},[392],"Volume gate:"," if a run would change or delete far more records than usual, pause and ask for confirmation instead of applying it. This catches an ERP misconfiguration before it empties your shop.",{"type":139,"content":395},[396,397,401],"Gates give you a clear status model: received, valid, enriched, published, held. If you plan to use an LLM for enrichment, such as drafting descriptions or classifying products, treat its output as just another gate input and test it the way you would test any model feature, as described in ",{"tag":149,"to":398,"children":399},"\u002Fblog\u002Fllm-evals-for-product-features",[400],"LLM evals for product features",".",{"type":160,"level":161,"id":126,"text":127},{"type":139,"content":404},[405],"The measure of a sync is not how it behaves on a good day but how fast you recover on a bad one. I want four things in place before go-live.",{"type":315,"ordered":407,"items":408},true,[409,414,419,424],[410,413],{"tag":306,"children":411},[412],"A failure queue with an owner."," Failed messages land in a separate transport (pimcore_failed_jobs in Pimcore). Inspect with messenger:failed:show, re-run with messenger:failed:retry, discard with messenger:failed:remove. Alert when the count is above zero for longer than a set time.",[415,418],{"tag":306,"children":416},[417],"Lag and throughput."," Queue depth per transport, age of the oldest message, and records per minute. A flat line is as suspicious as a spike.",[420,423],{"tag":306,"children":421},[422],"A replay command."," Given an article number, a time window or an ERP change number, re-extract and re-send. Because handlers are idempotent, replays are safe to run in production.",[425,428],{"tag":306,"children":426},[427],"A reconciliation report."," The full compare between ERP and PIM that lists differences, not just a pass or fail. Run it nightly or weekly and review the trend: a growing difference count means a delta signal is being missed.",{"type":139,"content":430},[431],"Log one structured line per message: key, source version, hash, outcome (written, skipped unchanged, held by gate, failed) and duration. Most support questions then become a search instead of an investigation.",{"type":160,"level":161,"id":129,"text":130},{"type":139,"content":434},[435],"Pimcore changed its release model recently, and some of it affects planning. These are the points I would check against the current documentation before a project starts, not assume from memory.",{"type":315,"ordered":407,"items":437},[438,443,448,453,458],[439,442],{"tag":306,"children":440},[441],"Platform version and support window."," Pimcore versions are Major.Minor, with minor versions roughly quarterly, and since 2026.1 all modules share the platform version number. The 2026.3 release appeared on 29 September 2026 and is not an LTS. Community support for a platform version ends when the next one is released.",[444,447],{"tag":306,"children":445},[446],"LTS target."," The documentation lists 2025.4 as LTS until December 2028 and 2024.4 until December 2026. For a long-lived B2B shop I would plan on an LTS line, or budget for regular minor upgrades.",[449,452],{"tag":306,"children":450},[451],"Edition and modules."," Data Hub and Data Importer are listed in the Community edition; Professional adds the TinyMCE editor; Enterprise includes all modules, for example Workflow Designer and the E-Commerce Framework. Confirm which modules your design actually relies on and which edition and license they need.",[454,457],{"tag":306,"children":455},[456],"Release notes, not just version numbers."," The 2026.3 notes mention security fixes and the removal of legacy admin controllers in favour of the Studio API. If you have custom admin code or integrations, read the upgrade notes first.",[459,462],{"tag":306,"children":460},[461],"Messenger setup."," Confirm the transport DSN prefix, the failure transport configuration and how workers are supervised in your hosting, because this is where sync reliability is decided.",{"type":139,"content":464},[465],"I deliberately do not quote prices or support-contract details here: those depend on your agreement with Pimcore or your partner, and they change.",{"type":160,"level":161,"id":132,"text":133},{"type":139,"content":468},[469,470,474,475,401],"This design is how I think about a Pimcore and SAP delta sync, and I have a Pimcore plus SAP delta-sync reference project, which you can find on the ",{"tag":149,"to":471,"children":472},"\u002Freferences",[473],"references page",". If you need a Pimcore developer for an SAP or Infor integration, a PIM and ERP interface (in German, a \"PIM ERP Schnittstelle\"), or a review of a sync that is drifting, have a look at my ",{"tag":149,"to":476,"children":477},"\u002Fexpertise\u002Fb2b-ecommerce-developer",[478],"B2B e-commerce developer profile",{"type":139,"content":480},[481],"The shortest version of my advice: write the ownership table first, make every handler idempotent, hash what you own, put a queue and a failure path in the middle, and rehearse the replay before you need it.",{"type":160,"level":161,"id":135,"text":136},{"type":315,"ordered":407,"items":484},[485,489,492,495,498,501,504,507,510,513,516],[486],{"tag":487,"href":36,"children":488},"a",[35],[490],{"tag":487,"href":39,"children":491},[38],[493],{"tag":487,"href":42,"children":494},[41],[496],{"tag":487,"href":45,"children":497},[44],[499],{"tag":487,"href":48,"children":500},[47],[502],{"tag":487,"href":51,"children":503},[50],[505],{"tag":487,"href":54,"children":506},[53],[508],{"tag":487,"href":57,"children":509},[56],[511],{"tag":487,"href":60,"children":512},[59],[514],{"tag":487,"href":63,"children":515},[62],[517],{"tag":487,"href":66,"children":518},[65],[520,606,663,751],{"slug":521,"published":5,"minutes":522,"category":7,"tags":523,"keywords":528,"about":539,"sources":548,"cover":600,"og":601,"expertise":69,"locales":602,"lang":71,"title":603,"description":604,"coverAlt":605},"european-accessibility-act-b2b-shop",13,[524,525,526,527],"European Accessibility Act","BFSG","WCAG 2.2","B2B e-commerce",[529,530,531,532,533,534,535,536,537,538],"European Accessibility Act B2B shop","BFSG B2B Onlineshop","Barrierefreiheit Onlineshop","Spryker barrierefrei","barrierefrei Webshop","BaFG Österreich Webshop","EN 301 549 WCAG 2.2","BFSG Kleinstunternehmen Ausnahme","Pimcore TYPO3 Barrierefreiheit","EAA e-commerce checklist",[540,542,545],{"name":524,"url":541},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEuropean_Accessibility_Act",{"name":543,"url":544},"Web Content Accessibility Guidelines","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FWeb_Content_Accessibility_Guidelines",{"name":546,"url":547},"E-commerce","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FE-commerce",[549,552,555,558,561,564,567,570,573,576,579,582,585,588,591,594,597],{"title":550,"url":551},"BFSG section 1: purpose and scope (gesetze-im-internet.de)","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__1.html",{"title":553,"url":554},"BFSG section 2: definitions, consumer, microenterprise, e-commerce services","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__2.html",{"title":556,"url":557},"BFSG section 3: accessibility and the microenterprise exemption","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__3.html",{"title":559,"url":560},"BFSG section 14: duties of the service provider","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__14.html",{"title":562,"url":563},"BFSG section 32: rights of consumers and associations in the administrative procedure","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__32.html",{"title":565,"url":566},"BFSG section 37: fines","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__37.html",{"title":568,"url":569},"BFSG section 38: transitional provisions","https:\u002F\u002Fwww.gesetze-im-internet.de\u002Fbfsg\u002F__38.html",{"title":571,"url":572},"Bundesfachstelle Barrierefreiheit: FAQ on the BFSG (B2B, microenterprises, EN 301 549)","https:\u002F\u002Fwww.bundesfachstelle-barrierefreiheit.de\u002FDE\u002FFachwissen\u002FProdukte-und-Dienstleistungen\u002FBarrierefreiheitsstaerkungsgesetz\u002FFAQ\u002Ffaq_node.html",{"title":574,"url":575},"AccessibleEU: The European accessibility standard EN 301 549 has been updated (7 September 2026)","https:\u002F\u002Faccessible-eu-centre.ec.europa.eu\u002Fcontent-corner\u002Fnews\u002Feuropean-accessibility-standard-en-301-549-has-been-updated-2026-09-07_en",{"title":577,"url":578},"W3C: Web Content Accessibility Guidelines (WCAG) 2.2","https:\u002F\u002Fwww.w3.org\u002FTR\u002FWCAG22\u002F",{"title":580,"url":581},"Deque: Automated testing identifies 57 percent of digital accessibility issues","https:\u002F\u002Fwww.deque.com\u002Fblog\u002Fautomated-testing-study-identifies-57-percent-of-digital-accessibility-issues\u002F",{"title":583,"url":584},"sitebrunch: What is the market surveillance authority for accessibility (MLBF)?","https:\u002F\u002Fwww.sitebrunch.com\u002Fnews\u002Fmlbf-marktueberwachungsstelle-barrierefreiheit",{"title":586,"url":587},"Marcus Herrmann: June 2026, how the MLBF intends to check","https:\u002F\u002Fmarcus-herrmann.com\u002Fblog\u002Fmlbf-verraet-wie-sie-pruefen-will",{"title":589,"url":590},"axes4: BFSG in practice, what has happened since the deadline (2026)","https:\u002F\u002Fwww.axes4.com\u002Fde\u002Fblog\u002Fpost\u002F2026\u002Fbfsg-in-der-praxis-was-sich-seit-dem-stichtag-getan-hat",{"title":592,"url":593},"ODC Legal: BFSG for websites, what companies must check in 2026","https:\u002F\u002Fwww.odclegal.de\u002Fblog\u002Fbfsg-website-pflicht",{"title":595,"url":596},"WKO: Information on the Austrian Barrierefreiheitsgesetz","https:\u002F\u002Fwww.wko.at\u002Fce-kennzeichnung-normen\u002Finformationen-zum-barrierefreiheitsgesetz",{"title":598,"url":599},"Web Crossing: One year of the Barrierefreiheitsgesetz, where online shops must improve","https:\u002F\u002Fwww.web-crossing.com\u002Fnews\u002Fdetail\u002Fein-jahr-barrierefreiheitsgesetz-wo-onlineshops-und-websites-jetzt-nachbessern-muessen\u002F","\u002Fimages\u002Fblog\u002Feuropean-accessibility-act-b2b-shop\u002Fcover.webp","\u002Fimages\u002Fblog\u002Feuropean-accessibility-act-b2b-shop\u002Fog.jpg",[71,72,73],"European Accessibility Act for B2B shops: what Spryker, Pimcore and TYPO3 teams must fix","Does the European Accessibility Act apply to a B2B shop? Scope under BFSG and BaFG, the microenterprise exemption, 2026 enforcement, WCAG 2.2 and a fix plan.","Diagram: a B2B shop fans out to the consumer-scope question under BFSG and BaFG, WCAG 2.2 AA conformity, the accessibility statement and market surveillance.",{"slug":607,"published":5,"minutes":6,"category":7,"tags":608,"keywords":612,"about":623,"sources":630,"cover":657,"og":658,"expertise":69,"locales":659,"lang":71,"title":660,"description":661,"coverAlt":662},"headless-product-configurator-b2b",[609,527,610,611],"Product configurator","Nuxt","Headless commerce",[613,614,615,616,617,618,619,620,621,622],"B2B product configurator","Produktkonfigurator","Produktkonfigurator TYPO3","B2B Konfigurator","headless product configurator","CPQ configure price quote","rule engine vs constraint solver","Nuxt Spryker Glue API","product configurator 100000 variants","accessible product configurator",[624,626,628],{"name":609,"url":625},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FProduct_configurator",{"name":611,"url":627},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHeadless_commerce",{"name":610,"url":629},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNuxt",[631,633,636,639,642,645,648,651,654],{"title":632,"url":625},"Wikipedia: Knowledge-based configuration (product configurator)",{"title":634,"url":635},"Google OR-Tools: The CP-SAT solver","https:\u002F\u002Fdevelopers.google.com\u002Foptimization\u002Fcp\u002Fcp_solver",{"title":637,"url":638},"Spryker documentation: Glue API, Product Configuration feature integration","https:\u002F\u002Fdocs.spryker.com\u002Fdocs\u002Fscos\u002Fdev\u002Ffeature-integration-guides\u002F202108.0\u002Fglue-api\u002Fglue-api-product-configuration-feature-integration.html",{"title":640,"url":641},"OWASP: Input Validation Cheat Sheet","https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FInput_Validation_Cheat_Sheet.html",{"title":643,"url":644},"web.dev: Virtualize large lists","https:\u002F\u002Fweb.dev\u002Farticles\u002Fvirtualize-long-lists-react-window",{"title":646,"url":647},"W3C: Understanding SC 3.3.1 Error Identification (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Ferror-identification.html",{"title":649,"url":650},"W3C: Understanding SC 4.1.3 Status Messages (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Fstatus-messages.html",{"title":652,"url":653},"W3C: Understanding SC 2.5.8 Target Size (Minimum) (WCAG 2.2)","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FWCAG22\u002FUnderstanding\u002Ftarget-size-minimum.html",{"title":655,"url":656},"W3C WAI-ARIA Authoring Practices: Combobox pattern","https:\u002F\u002Fwww.w3.org\u002FWAI\u002FARIA\u002Fapg\u002Fpatterns\u002Fcombobox\u002F","\u002Fimages\u002Fblog\u002Fheadless-product-configurator-b2b\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fheadless-product-configurator-b2b\u002Fog.jpg",[71,72,73],"Headless B2B product configurator: rules, pricing and Nuxt on a commerce API","How to build a B2B product configurator headless: rule engine or solver, where rules live, server-side pricing, Nuxt on a commerce API, TYPO3 and 100k+ variants.","Diagram: a Nuxt front end talks to a configuration service that reads rules from the PIM and prices from the ERP, then hands a validated configuration to the commerce API.",{"slug":664,"published":5,"minutes":522,"category":7,"tags":665,"keywords":668,"about":679,"sources":686,"cover":744,"og":745,"expertise":746,"locales":747,"lang":71,"title":748,"description":749,"coverAlt":750},"nuxt-core-web-vitals-performance",[666,610,667,546],"Core Web Vitals","Performance",[669,670,671,672,673,674,675,676,677,678],"Core Web Vitals Nuxt","Nuxt performance optimization","improve LCP Nuxt","INP Nuxt hydration","fix CLS Vue","Nuxt lazy hydration","Nuxt Image LCP","Nuxt Scripts third-party scripts","Nuxt prerender vs SSR vs ISR","Core Web Vitals e-commerce",[680,682,683],{"name":666,"url":681},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCore_Web_Vitals",{"name":610,"url":629},{"name":684,"url":685},"Vue.js","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FVue.js",[687,690,693,696,699,702,705,708,711,714,717,720,723,726,729,732,735,738,741],{"title":688,"url":689},"web.dev: Web Vitals","https:\u002F\u002Fweb.dev\u002Farticles\u002Fvitals",{"title":691,"url":692},"web.dev: Largest Contentful Paint (LCP)","https:\u002F\u002Fweb.dev\u002Farticles\u002Flcp",{"title":694,"url":695},"web.dev: Optimize Largest Contentful Paint","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-lcp",{"title":697,"url":698},"web.dev: Interaction to Next Paint (INP)","https:\u002F\u002Fweb.dev\u002Farticles\u002Finp",{"title":700,"url":701},"web.dev: Optimize Cumulative Layout Shift","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-cls",{"title":703,"url":704},"web.dev: Optimize long tasks","https:\u002F\u002Fweb.dev\u002Farticles\u002Foptimize-long-tasks",{"title":706,"url":707},"Chrome for Developers: Chrome UX Report (CrUX)","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fcrux",{"title":709,"url":710},"Chrome for Developers: CrUX methodology","https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fcrux\u002Fmethodology",{"title":712,"url":713},"GitHub: GoogleChrome\u002Fweb-vitals","https:\u002F\u002Fgithub.com\u002FGoogleChrome\u002Fweb-vitals",{"title":715,"url":716},"Nuxt docs: Rendering modes","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fconcepts\u002Frendering",{"title":718,"url":719},"Nuxt docs: Components (Lazy prefix, delayed hydration, client components)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fdirectory-structure\u002Fapp\u002Fcomponents",{"title":721,"url":722},"Nuxt docs: Experimental features (lazyHydration, payloadExtraction)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fguide\u002Fgoing-further\u002Fexperimental-features",{"title":724,"url":725},"Nuxt docs: Data fetching (pick, transform, lazy)","https:\u002F\u002Fnuxt.com\u002Fdocs\u002F4.x\u002Fgetting-started\u002Fdata-fetching",{"title":727,"url":728},"Nuxt blog: Nuxt 4.5","https:\u002F\u002Fnuxt.com\u002Fblog\u002Fv4-5",{"title":730,"url":731},"GitHub: Nuxt releases","https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases",{"title":733,"url":734},"Nuxt Image: NuxtImg","https:\u002F\u002Fimage.nuxt.com\u002Fusage\u002Fnuxt-img",{"title":736,"url":737},"Nuxt Fonts module","https:\u002F\u002Fnuxt.com\u002Fmodules\u002Ffonts",{"title":739,"url":740},"Nuxt Scripts: Getting started","https:\u002F\u002Fscripts.nuxt.com\u002Fdocs\u002Fgetting-started",{"title":742,"url":743},"Nuxt Scripts: Script triggers","https:\u002F\u002Fscripts.nuxt.com\u002Fdocs\u002Fguides\u002Fscript-triggers","\u002Fimages\u002Fblog\u002Fnuxt-core-web-vitals-performance\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fnuxt-core-web-vitals-performance\u002Fog.jpg","vue-nuxt-developer",[71,72,73],"Core Web Vitals for Nuxt sites and shops: fixing LCP, INP and CLS","How to fix LCP, INP and CLS in Nuxt 4 sites and shops: images, hydration, fonts, third-party scripts, prerender vs SSR vs ISR, and measuring real users.","Diagram: a Nuxt page load from server HTML through hero image, hydration and interaction, with the LCP, INP and CLS thresholds marked on the stages.",{"slug":752,"published":753,"minutes":754,"category":7,"tags":755,"keywords":761,"about":770,"sources":779,"cover":792,"og":793,"expertise":746,"locales":794,"lang":71,"title":795,"description":796,"coverAlt":797},"home-assistant-ev-charging-energy-manager","2026-09-29",9,[756,757,758,759,760],"Home Assistant","EPEX Spot","Energy prices","Tesla","Energy management",[762,763,764,765,766,767,768,769],"EPEX Austria prices","spot tariff Austria EV charging","Home Assistant EV charging","cheapest hours charging","day-ahead price Austria","negative electricity prices Austria","Tesla charging spot prices","load management 3 phase",[771,774,776],{"name":772,"url":773},"EPEX SPOT","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FEPEX_SPOT",{"name":756,"url":775},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHome_Assistant",{"name":777,"url":778},"Load management","https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLoad_management",[780,783,786,789],{"title":781,"url":782},"aWATTar Austria: market data API","https:\u002F\u002Fwww.awattar.at\u002Fservices\u002Fapi",{"title":784,"url":785},"EPEX SPOT: market results","https:\u002F\u002Fwww.epexspot.com\u002Fen\u002Fmarket-results",{"title":787,"url":788},"Home Assistant developer docs: apps","https:\u002F\u002Fdevelopers.home-assistant.io\u002Fdocs\u002Fapps\u002F",{"title":790,"url":791},"Home Assistant: Tesla Fleet integration","https:\u002F\u002Fwww.home-assistant.io\u002Fintegrations\u002Ftesla_fleet\u002F","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fcover.webp","\u002Fimages\u002Fblog\u002Fhome-assistant-ev-charging-energy-manager\u002Fog.jpg",[71,72,73],"Charging on EPEX Austria prices: what my Home Assistant app saves","A year of hourly EPEX prices for Austria, replayed for a Tesla and a water boiler: cheapest-hour charging costs 6.5 instead of 18.9 ct\u002FkWh, close to €590 a year, without blowing a 20 A fuse.","Cover: three bars comparing 18.9, 13.1 and 6.5 ct\u002FkWh for charging at 18:00, overnight and in the cheapest hours.",1791009037059]