Validation Failure Feedback Loop
Status: Design voltooid Priority: After stub system is complete Date: 2026-02-01
Problem Statement
When data synchronization fails due to validation issues, neither system (Odoo nor webshop) is notified. Data sits in a failed state silently, requiring manual intervention to discover and resolve.
Failure Scenarios
- Webshop → Odoo failures
- Data sent from webshop cannot be processed by Odoo
- Webshop has no way to know the sync failed
-
Example: Product data with invalid field types
-
Odoo → Webshop failures
- Data pushed from Odoo cannot be processed by middleware/webshop
- Odoo has no way to know the sync failed
- Examples observed:
- Title too long for database column
- Text value where number expected
- Type conflicts in JSON data
Legacy Middleware Probleem
De legacy middleware is een "dom data vat":
Odoo → POST update → Legacy Middleware (slaat alleen op)
↓
Webshop cron ← GET /updated-* ← pulls data
↓
Verwerk lokaal
↓
Succes? → DELETE → Update weg (geen historie)
Faal? → Geen DELETE → Blijft staan → Eindeloze retry loop
Problemen: - Bij falen: eindeloze retry loop (zelfde data faalt steeds opnieuw) - Bij succes: data is weg (geen history, geen audit trail) - Geen notificatie naar Odoo dat iets faalt in de webshop - Webshop kijkt niet naar HTTP status codes
Gekozen Oplossing: Push Notifications met Centrale Fout-Registry
Overzicht
De middleware wordt een centrale fout-registry met bidirectionele communicatie:
Odoo ←──webhook──┐ ┌──webhook──→ Webshop
│ │
↓ ↓
┌─────────────────────┐
│ Middleware │
│ (fout-registry) │
└─────────────────────┘
↑ ↑
POST /failures POST /failures
│ │
Odoo Webshop
"update X faalde" "update Y faalde"
Nieuwe Flows
1. OPHALEN (bestaand, uitgebreid)
- Response bevatidempotency_key per record
- Webshop/Odoo kan checken of ze deze key al verwerkt hebben
2. SUCCES BEVESTIGEN (nieuw)
- Markeert update als verwerkt - Resolved automatisch openstaande fouten voor deze entity3. FOUT MELDEN (nieuw)
- Slaat fout op in registry - Triggert direct webhook naar andere kantIdentifier Flexibiliteit
Bij het melden van een fout kan de bron identificeren via:
- middleware_id - als de bron die kent (uit de GET response)
- external_id + entity_type - als fallback (bijv. SKU, order nummer)
Timeout Mechanisme
- Update opgehaald maar geen ACK/FAIL binnen 4 uur → opnieuw beschikbaar
- Idempotency key voorkomt dubbele verwerking bij retry
- Webshop/Odoo houdt bij welke keys al verwerkt zijn
Webhook Systeem
Directe Notificatie
Zodra een fout binnenkomt, stuurt de middleware direct een webhook naar de geconfigureerde URL:
Webshop meldt fout → Middleware → Webhook naar Odoo
Odoo meldt fout → Middleware → Webhook naar webshop
Webhook Payload
{
"event": "sync_failure",
"entity_type": "product",
"external_id": "SKU-12345",
"middleware_id": "upd_abc123",
"error_message": "Prijs is verplicht maar ontbreekt",
"error_details": [
{
"field": "price",
"expected": "number",
"actual": null,
"message": "Veld is verplicht"
}
],
"failed_at": "2026-02-01T14:32:00Z",
"source": "webshop"
}
De error_details zijn gestructureerd voor programmatische afhandeling. De raw_data wordt niet meegestuurd (kan groot zijn) maar is opvraagbaar via de API.
Authenticatie
- HMAC-SHA256 signature in
X-Webhook-Signatureheader - Ontvanger valideert met shared secret (geconfigureerd per webhook)
- Standaard aanpak (zoals GitHub, Stripe, etc.)
Retry Queue bij Falen
Als de webhook niet afgeleverd kan worden:
| Poging | Wachttijd |
|---|---|
| 1 | 1 minuut |
| 2 | 5 minuten |
| 3 | 30 minuten |
| 4 | 2 uur |
| 5 | 8 uur |
| 6 | 24 uur |
- Admin kan individuele berichten of hele queue verwijderen
- Na max retries: markeer als
delivery_failed, stop met pogingen
Admin Interface
Webhook Configuratie
- Per entity type een webhook URL configureren
- Aparte configuratie voor Odoo-kant en webshop-kant
- Shared secret per webhook (voor HMAC signing)
- Test-knop: stuurt dummy payload, toont response status
┌─────────────────────────────────────────────────────────────┐
│ Webhook Configuratie │
├─────────────────────────────────────────────────────────────┤
│ Entity Type │ Richting │ URL │ Actions │
│ product │ → Odoo │ https://odoo.../hook │ 🧪 ✏️ 🗑️ │
│ product │ → Webshop │ https://shop.../hook │ 🧪 ✏️ 🗑️ │
│ sale_order │ → Odoo │ https://odoo.../hook │ 🧪 ✏️ 🗑️ │
└─────────────────────────────────────────────────────────────┘
Fout-Registry Overzicht
- Lijst van alle gemelde fouten
- Filters: entity type, status (open/resolved), bron (Odoo/webshop), datum
- Detail view: volledige error_details + raw_data
- Actie: handmatig resolven of verwijderen
Webhook Queue Beheer
- Lijst van pending/failed webhook deliveries
- Retry now / Delete opties
- Bulk actions: "verwijder alle failed voor webhook X"
Retentie Instellingen
Configureerbaar via admin interface: - Resolved fouten: X dagen (default 30) - Unresolved fouten: Y dagen (default 90)
Automatische Resolutie
Wanneer een ACK binnenkomt voor een entity, worden alle openstaande fouten voor die entity automatisch resolved:
1. Webshop meldt: "product SKU-123 faalde - prijs ontbreekt"
2. Fout staat open in registry
3. Odoo fixt het probleem, stuurt update opnieuw
4. Webshop verwerkt succesvol, stuurt ACK
5. Middleware: markeer alle open fouten voor SKU-123 als "resolved"
Database Model
SyncFailure
| Kolom | Type | Beschrijving |
|---|---|---|
| id | uuid | Primary key |
| entity_type | string | product, sale_order, partner, ... |
| external_id | string | SKU, order nummer, ... |
| middleware_id | uuid (nullable) | Link naar originele update |
| source | enum | odoo, webshop |
| status | enum | open, resolved |
| error_message | string | Leesbare foutmelding |
| error_details | json | Gestructureerde errors per veld |
| raw_data | json (nullable) | Originele payload voor debugging |
| failed_at | datetime | Wanneer de fout optrad |
| resolved_at | datetime (nullable) | Wanneer opgelost |
| created_at | datetime | Record aangemaakt |
WebhookConfig
| Kolom | Type | Beschrijving |
|---|---|---|
| id | uuid | Primary key |
| entity_type | string | Voor welk entity type |
| direction | enum | to_odoo, to_webshop |
| url | string | Webhook endpoint URL |
| secret | string (encrypted) | HMAC shared secret |
| is_active | boolean | Webhook actief? |
WebhookDelivery
| Kolom | Type | Beschrijving |
|---|---|---|
| id | uuid | Primary key |
| webhook_config_id | fk | Link naar config |
| sync_failure_id | fk | Link naar failure |
| status | enum | pending, delivered, failed |
| attempts | integer | Aantal pogingen |
| next_retry_at | datetime (nullable) | Volgende retry |
| last_error | string (nullable) | Laatste foutmelding |
| payload | json | Verstuurde data |
API Endpoints
Fout Melden
POST /api/failures
Authorization: Bearer {token}
Content-Type: application/json
{
"entity_type": "product",
"middleware_id": "upd_abc123",
"external_id": "SKU-12345",
"error_message": "Prijs is verplicht maar ontbreekt",
"error_details": [
{ "field": "price", "expected": "number", "actual": null }
],
"raw_data": { ... }
}
Response:
Succes Bevestigen
Response:
Fouten Ophalen
Response:
Enkele Fout Ophalen
Response: volledige failure inclusief raw_data.
Beantwoording Originele Vragen
1. Should Odoo be able to acknowledge/dismiss failures? → Ja, via de admin interface kunnen fouten handmatig als "resolved" worden gemarkeerd.
2. Should there be automatic retry after X hours? → Ja, maar voor de updates, niet de fouten. Updates zonder ACK/FAIL worden na 4 uur opnieuw beschikbaar gesteld. Idempotency key voorkomt dubbele verwerking.
3. Who receives email/Slack notifications? Configurable per entity type? → Buiten scope voor nu. Focus is op webhooks. De ontvangende systemen (Odoo/webshop) kunnen zelf bepalen wat ze met de webhook doen (bijv. Slack notificatie triggeren).
4. Should we track "resolution" - when a previously failed record succeeds? → Ja, automatisch. Wanneer ACK binnenkomt voor een entity, worden alle openstaande fouten voor die entity als "resolved" gemarkeerd.
Related
- Stub system (implements first) - handles missing dependencies
- This system handles validation/processing failures