A sync job can look healthy while quietly creating duplicate ERP records.
We have seen this class of problem when an external system retries an ERP consulting services request after a timeout. The client sees a failed request, retries it, and the ERP receives both requests. The result can be duplicate customers, orders, invoices, or payments.
This is where ERP consulting services need to go beyond module selection. The integration contract, retry behavior, API version, and record identity should be decided before implementation starts.
This article walks through one practical pattern: designing ERP integrations around idempotency, bounded retries, and explicit API contracts. The examples use Odoo because its current API direction makes this decision especially relevant.
Odoo 19 introduced its External JSON-2 API. Odoo also deprecated the older XML-RPC and JSON-RPC external APIs in version 19.
That API change is not just a developer concern. It changes what an ERP implementation team should review during technical discovery.
Step 1: Find the failure before adding retries
The first mistake is usually simple:
The integration receives a timeout and assumes the ERP rejected the transaction.
A timeout only tells us that the caller did not receive the response. It does not prove that the server did not process the request.
Consider this simplified Python example.
import requests
ERP consulting services should identify whether a retry can duplicate a write.
response = requests.post(
"https://erp.example.com/json/2/res.partner/create",
headers={"Authorization": "bearer YOUR_API_KEY"},
json={"name": "Acme Ltd"},
timeout=10,
)
response.raise_for_status()
The problem is not the HTTP request itself.
The problem is what happens when the client receives a 504, connection reset, or local timeout after the ERP has already created the record.
Retrying the same write can create a second record.
Before adding retry logic, we therefore need a stable business identifier. For example, an external customer ID can become the identity boundary between the CRM and ERP.
That gives us the first design rule:
Never make retry behavior independent of record identity.
Step 2: Make the operation idempotent
Once the failure mode is clear, the next decision is where duplicate protection belongs.
For integrations, we generally want the middleware to know whether an external event has already been processed. A simple database table can hold the source system, event ID, target record ID, and processing state.
The write then becomes two operations:
Check whether the event already exists.
Process it only when it has not been committed.
A simplified implementation looks like this:
from dataclasses import dataclass
@dataclass
class SyncEvent:
event_id: str
external_id: str
status: str = "pending"
ERP consulting services should define this identity before building the connector.
events: dict[str, SyncEvent] = {}
def should_process(event_id: str) -> bool:
return event_id not in events
def mark_processed(event_id: str, external_id: str) -> None:
events[event_id] = SyncEvent(
event_id=event_id,
external_id=external_id,
status="processed",
)
This example uses memory for clarity. Production middleware should store the event state in a durable database with a unique constraint on event_id.
That constraint matters more than the Python dictionary.
Two workers can read “not found” at almost the same time. A database uniqueness constraint gives us the final guard against concurrent processing.
For a financial transaction, we would also store the ERP record identifier after successful creation. That makes reconciliation possible without searching by description or amount.
Step 3: Treat retries as part of the API contract
Idempotency prevents duplicate processing, but it does not solve transient failures.
The next question is how aggressively the connector should retry.
Microsoft's Business Central documentation explicitly calls out 429, 503, and 504 as conditions that integrations need to handle. It recommends retry strategies such as exponential backoff and queue-based traffic smoothing.
That gives us a useful production pattern:
import time
import requests
RETRYABLE = {429, 503, 504}
def post_with_retry(url, payload, headers, attempts=4):
# Retry only transient failures, while keeping the operation idempotent.
for attempt in range(attempts):
response = requests.post(
url,
json=payload,
headers=headers,
timeout=30,
)
if response.status_code not in RETRYABLE:
response.raise_for_status()
return response
if attempt == attempts - 1:
response.raise_for_status()
time.sleep(2 ** attempt)
raise RuntimeError("Request failed after retries")
The important part is not 2 ** attempt.
The important part is the boundary around it.
We should not automatically retry every 400 or 401. Those usually require a payload, authentication, or configuration fix.
We should also avoid retrying a non-idempotent operation simply because the network failed.
For high-volume integrations, a queue is usually easier to operate than synchronous retries inside the web request. Microsoft specifically recommends queueing to flatten traffic spikes against Business Central web services.
Step 4: Check the ERP API version before development
The retry design is only useful if the connector targets an API that will remain supported.
Odoo's current documentation introduces /json/2 for external integrations in version 19. Requests use a model and method in the URL, with named arguments in the JSON body. Authentication uses a bearer API key.
For example:
import os
import requests
url = "https://mycompany.example.com/json/2/res.partner/search_read"
headers = {
"Authorization": f"bearer {os.environ['ODOO_API_KEY']}",
"Content-Type": "application/json",
}
payload = {
"domain": [["is_company", "=", True]],
"fields": ["name"],
"limit": 10,
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
print(response.json())
The important consulting decision happens before this code.
If an existing connector still depends on XML-RPC or JSON-RPC, we need to establish its upgrade path. Odoo states that those external RPC APIs are deprecated in version 19 and scheduled for removal in later releases.
That means an ERP assessment should inspect API dependencies alongside business requirements.
A connector that works today can still be the wrong implementation choice.
Real-World Application: Business Central Scope Before Build
That API and retry trade-off becomes easier to understand when the integration work sits inside a larger ERP implementation.
We applied this sequencing in a Business Central implementation for a graphics operations business. The project covered finance, inventory, operational workflows, and approximately 62 AL extensions.
We initially treated the extension list as development scope. The more useful approach was to separate standard Business Central capability from genuine extension requirements before sprint planning.
We then structured delivery into an 18-week plan with nine two-week sprints. Each sprint could be reviewed against defined functional decisions instead of allowing integration and customization requests to expand the scope informally.
The quantified outcome available from the implementation scope was therefore an 18-week delivery boundary and approximately 62 extensions.
The lesson for integration engineers is straightforward. API behavior should be treated as part of ERP architecture, not as a coding detail discovered after functional requirements are approved.
This is the point where Oodles treats ERP consulting services differently from a software configuration exercise. The consulting output has to survive contact with development, testing, migration, and operations.
- ERP consulting services should identify integration failure modes before development, especially timeout and retry scenarios.
- Idempotency needs a durable business identifier, not just a retry counter.
- Database uniqueness constraints protect against concurrent duplicate processing when multiple workers consume the same event.
- Retry logic should target transient failures, while permanent client errors should enter an error workflow.
- API version changes belong in ERP technical assessments, because 6. deprecated interfaces can turn a working connector into future migration work.
If your ERP scope is growing faster than your delivery plan, discuss your requirements with our ERP Consulting Services team before the next change request becomes development work.
What do ERP consulting services include?
ERP consulting services typically cover process discovery, requirements definition, fit-gap analysis, platform evaluation, implementation planning, data migration strategy, integration architecture, testing, training, and post-go-live optimization. The exact scope should depend on whether the organization needs selection, implementation, recovery, or optimization support.
How do ERP consultants prevent scope creep?
They establish decision boundaries before development. Requirements are classified by business priority and technical treatment, while integrations, data dependencies, acceptance criteria, and change-control rules are documented early. This makes new requests visible as scope changes rather than allowing them to enter development informally.
Should companies customize their ERP?
Customization should address a genuine business or regulatory requirement that standard functionality cannot reasonably support. ERP consulting services should compare customization against configuration, process change, integration, maintenance, and future upgrade costs before recommending an extension.
When should data migration planning begin?
ERP consulting services Data migration planning should begin during process discovery, not immediately before go-live. Teams need early decisions about data ownership, required history, cleansing rules, transformation logic, validation, and reconciliation. Those decisions can affect the design of finance, inventory, sales, purchasing, and reporting workflows.
How long does ERP consulting take?
The duration depends on the number of processes, entities, ERP platforms, integrations, and migration requirements. A focused discovery can take weeks, while enterprise programs may require months of advisory and implementation planning. The useful measure is decision coverage, not simply elapsed consulting time.
Top comments (0)