Skip to content

Validation when your ERP has no functional API

Receiving orders

Two kinds of ERP show up in integrations:

Functional APIData API / direct database
How you create an orderPOST /salesOrders or a "create order" functionINSERT into order tables (or a generic "data" API that does the same)
Who validatesthe ERP: unknown item, blocked customer, credit limit, unit conversion…nobody — whatever you write lands; the ERP trips over it later
Failure modeimmediate, explicit error you can pass to the operatorsilent corruption discovered days later by someone else

With a data API, the integration layer has to be the validation layer. That is what the SDK's pipeline is for.

Rendering diagram…

Sanitizers → validators → business rules → adapter. With a data-API ERP the pipeline is not optional.

What to validate, minimum set

CheckWhySDK step
Article exists and is not blockedthe #1 way to create unbookable ordersvalidators.ArticlesInCatalog(erp)
Customer existsorphan ordersvalidators.CustomerResolved(erp)
Customer not blocked / credit holdpolicyrules.CustomerNotBlocked(erp)
Quantities are positive integersAIOTIC sends null for unreadable quantities on override sendsvalidators.PositiveQuantities()
Unit of measure knownstuks vs ST vs PCSsanitizers.MapUnits({...}) (+ reject unknown)
Currency allowedwrong currency = wrong pricevalidators.CurrencyAllowed(["EUR"])
Delivery date plausibletypo detectionvalidators.DeliveryDateSane()
Duplicate PO for the same customerdouble bookingsrules.NoDuplicateOrder(store)
Ship-to address completeundeliverable ordersrules.ShipToAddressComplete()
Line total vs quantity × pricemis-read quantities (18.000 vs 18)validators.LineTotalsConsistent() (warning)

Putting it together

python
from aiotic.erp.data_api import DataApiAdapter
from aiotic.pipeline import Pipeline, sanitizers as S, validators as V, rules as R
from aiotic.receive import ErpReceiver, SqliteStore
import pyodbc

erp = DataApiAdapter(lambda: pyodbc.connect(DSN), paramstyle="qmark")   # implements ErpPort + CatalogPort + CustomerPort
store = SqliteStore("integration.db")

pipeline = Pipeline(
    sanitizers=[S.StripWhitespace(), S.NormalizeCountryCodes(), S.NormalizeCurrency(), S.MapUnits({"doos": "BOX"}), S.FillShippingFromCustomer()],
    validators=[V.RequiredFields(), V.CustomerResolved(erp), V.ArticlesInCatalog(erp), V.PositiveQuantities(), V.CurrencyAllowed(["EUR"])],
    rules=[R.CustomerNotBlocked(erp), R.NoDuplicateOrder(store), R.ShipToAddressComplete()],
)
receiver = ErpReceiver(erp, api_key=RECEIVE_KEY, pipeline=pipeline, store=store)

A rejecting verdict becomes {"success": false, "error": "Unknown article number: PROD-999 (items[2].article_number); Customer 10577 is blocked for new orders"} — the operator reads it in the AIOTIC app, fixes the order (or the master data) and sends again.

Your own rules

Anything that would live in an ERP's "before insert" logic can be a rule:

python
from aiotic.pipeline import Issue
from aiotic.pipeline.rules import rule

@rule("no_weekend_delivery")
def no_weekend(order, ctx):
    from datetime import date
    if order.delivery_date and date.fromisoformat(order.delivery_date).weekday() >= 5:
        yield Issue("weekend_delivery", "Delivery date falls on a weekend", path="delivery_date")

pipeline.rules.append(no_weekend)

ctx carries the request_id and anything earlier steps stored (for example the ERP customer record from CustomerResolved), so rules can use ERP data without a second lookup.

Sanitize before you validate

Order matters. Sanitizers normalise what the document printed (NederlandNL, EUR, 7327aa7327 AA, stuksST); validators then judge the cleaned order. Everything returns a new order — the payload AIOTIC sent is never mutated, so you can log both.

Where the same checks live with a functional API

Keep the pipeline anyway, lighter: sanitizers plus RequiredFields and NoDuplicateOrder. Let the ERP do catalog and customer checks and pass its error text through (ErpRejected). You get one code path for both ERP styles, which is the point.

Documentation revision 3 · Published 8 September 2026 · commit 6862d5e. Verified against AIOTIC API v1.0.0. AIOTIC is a product of DevOps Company.