How to integrate Salesforce with legacy ERP systems like SAP
One owner for the customer master. One owner for the invoice amount. Failures go to a queue a person reads.
Platform & ArchitectureIntegrating Salesforce with an ERP such as SAP is an ownership problem before it is a mapping problem. One system owns the customer master. One system owns the invoice amount. Salesforce can hold the relationship and the ERP can hold the money, or the reverse, if you write it down. Write the moment a record is created and the moment it is retired. Two systems that both believe they own the customer will overwrite each other. Two systems that both write the invoice amount will disagree, and finance will be right to trust neither.
The failure is a Tuesday. On Monday a clerk corrects a billing address in the ERP, because that is where the invoice is actually addressed. Overnight a copy from Salesforce writes the old address back, because the CRM had a newer timestamp on an unrelated edit and the interface treats newest as winner. Tuesday's invoices go to the old place. The nightly file that carried the bad write transferred its rows, so the morning check is green. The file from Monday is already gone. Nobody owns the queue, because nobody built one. Finance finds the error from a customer, which is the most expensive monitoring you can run.
Salesforce describes Data 360 as a way to ingest, harmonise, unify, and analyse streaming and batch data. Data Cloud was renamed Data 360 on 14 October 2025. Landing a copy of the customer there does not appoint an owner. The owner is still the system you named. Data 360 lets you see both sides. It is not a third master, and it is not a tie-break. A profile that shows both addresses is a picture of the Tuesday failure. It does not decide which address the invoice uses.
The first release is two facts
Start with the customer master and the invoice amount. The account, the identifier the ERP already uses, and the amount finance will defend. Leave orders, deliveries, and price conditions for a later release unless a named process stops without them. A first release that maps "the order" usually means nobody agreed the owner, so the spreadsheet grew until it looked like progress. Progress, in that workshop, is a row count. The row count is not an owner.
Write creation and retirement as events, not as a hope that the systems will stay aligned. The ERP creates the legal entity when finance opens it. Salesforce may create a prospect before that, and the prospect is not the legal entity. The moment the legal entity exists, the identifier flows one way, and Salesforce stores it on the account it is allowed to keep. Retirement is the same event in reverse. One system closes the master. The other system may no longer create transactions against it. A sync that recreates a retired customer because a rep edited a contact is how closed accounts come back and receive invoices.
The identifier is the contract. It is the ERP's identifier, stored on the Salesforce account, unique, required before any money message is sent. A name match is not an identifier. Names collide, change, and get punctuated differently by each system. If the identifier is missing, the message fails onto the queue. It does not fuzzy-match its way onto a likely account. Fuzzy matching is how two legal entities become one customer for a night, and the night is enough to send the wrong invoice.
If the same programme also schedules technicians, that work sits in Agentforce Field Service and Operations, the field service hub formerly called Field Service. Work orders are that product's records. They do not belong inside the customer-master message. One owner for the customer. One owner for the invoice amount. One owner for the work order, if you have work orders. Three decisions. A message that tries to carry all three will be owned by nobody, because each team will say the other team's fields are why it failed.
Do not begin from a field-by-field map of an IDoc nobody in the room has read. Agree the owner, the identifier, and the failure path. Then map the few fields those decisions require, from the specification you actually have open. A field you cannot point to in that specification does not go in the first release. You can add it when someone has a process that stops without it. Until then it is decoration, and decoration is what overwrites the address.
A queue a person looks at
Every failed message goes to a queue with an owner and a person who reads it on a schedule you can point at. The message stays until that person replays it or rejects it. A dashboard that is green when the channel is up, and silent when a record is skipped, is not a queue. It is a light. The Tuesday failure had a green light. The address was still wrong.
A nightly file can fail and still leave yesterday's data looking acceptable in the morning. Nobody notices until a customer is wrong on a Tuesday and the file from Monday is already gone. An event that fails in the open, with an alert and a row on the queue, is easier to trust. Prefer events for the customer and the invoice. The pattern, including the condition that rules a quiet batch out, is the one in a working catalogue of Salesforce integration patterns. If a file is unavoidable for now, make the failure loud yourself: the file arrived, the count matches, and a miss raises the same queue. A successful transfer of zero rows is a failure. Write that test down. Run it every morning, including mornings when someone expects the file to be empty. Empty and failed look the same unless you stored the expected count.
The person who reads the queue is named, and they have time in the day to reject a message, not only to replay it. Replay-everything is how the bad address goes out again. Rejection is a decision: this message is wrong, the owner’s value stands, the copy will be corrected from the owner. That decision needs a person who knows which system owns the field. A queue watched by someone who can only click retry is a delay, not a control.
Count the queue. A spike after a release is the release. A spike every Monday is a file. A queue that is always empty is either a very quiet interface or a dashboard that does not receive the failures. Both are worth an hour to distinguish. Send a deliberately bad message in the first week, a missing identifier, and confirm it arrives as a row a person can see. If it does not, you do not have a queue. You have a log.
What done means
A customer is done when both systems show the same identifier and only the owning system can change the legal name. An invoice amount is done when the owning system is the only writer and the other system stores a copy it does not edit. When they disagree, the owner's figure wins and the copy is corrected. Write that sentence into the design before anyone builds the connector. After go-live it becomes a political argument. Before go-live it is a line in a document.
The non-owner's copy is labelled as a copy in the interface, not presented as a second place to edit. A rep who can type over the legal name will type over it, with good intentions, the day a customer says the name on the case is wrong. The edit feels local. The overnight write makes it global. Remove the edit. Offer a request to the owning system instead, if you need a path for corrections. The request is a message. It is not a silent overwrite.
The same sentence covers the address that caused Tuesday. Whoever owns the billing address, named, is the only writer. The other system may display it. If Salesforce owns the relationship address and the ERP owns the billing address, those are two fields, and they are allowed to differ. Forcing them to be one field is how a delivery instruction overwrites an invoice address. Split them. Map each to its owner. Do not call both "address" in the interface and hope the context survives.
When the programme also has a billing product in the path, the invoice amount still has one owner. That decision, between Revenue Cloud and a third-party billing system, is the same shape, and it is set out in billing and Revenue Cloud. Do not let the SAP design and the billing design each pick a winner. One sentence, shared, before either connector is built. Two sentences that disagree will surface at close, and close is the worst week to find them.
The order of the work
First the owners, the identifier, and the creation and retirement events, with finance and the CRM owner in the room. Then the queue, including the bad-message test and the zero-row test. Then the few fields the specification supports. Then one direction of traffic, for the customer only, until the identifier matches on a sample you read by hand. The invoice amount follows, one writer, copy on the other side, disagreement resolved by the sentence you already wrote.
Do not start with the full message because a diagram of the full message exists. The diagram is a wish. The first release is two facts and a queue a person reads. Everything else can wait until those are dull. Dull means a week in which the only address changes are the ones the owner made, and every failed message is a row someone rejected or replayed on purpose.
Read a sample before you call it live
After the first week of the customer message, read twenty accounts by hand. The identifier matches. The legal name matches the owner. The billing address matches the owner, and it is not the relationship address unless you deliberately made them the same field. Any mismatch is a defect in the writer, not a rounding error to tolerate. Twenty is enough to see a systematic overwrite. It is not a survey. One overwritten address is enough to stop and fix the direction before invoices use it.
Do the same for the amount, on twenty documents, when that message starts. The copy equals the owner. The non-owner's screen does not offer an edit. If a person can change the figure, the interface is not finished, whatever the mapping spreadsheet says. Fix the screen before you widen the traffic. A wider message on a broken writer multiplies Tuesday. It does not dilute it.



