#Arquitectura — Integración Deltav → Odoo 17

Diagramas de secuencia en PlantUML del flujo completo: autenticación, contactos, productos, BoMs y pagos.

#Visión general del sistema

Please use CSS style instead of skinparam ParticipantPaddingSistema ExternoMiddlewareOdoo 17Mapping DBSistema Externo(Deltav)Sistema Externo(Deltav)Middleware(REST -> JSON-RPC)Middleware(REST -> JSON-RPC)Odoo 17Odoo 17Mapping DBMapping DBAutenticacionPOST /v1/auth/token {api_key}POST /web/session/authenticateuid + session200 {access_token JWT}ContactosPOST /v1/contacts {name,vat,email,...}SELECT odoo_id WHERE ext_ref=?alt[Registro nuevo]not foundexecute_kw(create) res.partnerpartner_id: 42INSERT mapping (ext_ref,42,partner)[Registro existente]found -> odoo_id=42execute_kw(write) [42]updated201/200 {status,odoo_id,name}ProductosPOST /v1/products {name,price,attributes}lookup product + resolve variant attrscreate product.template + product.product variantstmpl_id: 15 + variants: [30,31,32]INSERT mappings (template + variants)201 {odoo_id:15, variants:[...]}Lista de MaterialesPOST /v1/boms {product, lines:[{comp,qty}]}resolver product_ext_ref + componentscreate mrp.bom + bom_line_idsbom_id: 8INSERT mapping (ext_ref,8,bom)201 {odoo_id:8, lines_count:3}PagosPOST /v1/payments {type,partner,amount,invoices}resolver partner + journal + facturascreate account.payment + action_post()payment_id:156, state:"posted"INSERT mapping201 {odoo_id:156, state:"posted"}

#1. Autenticación — Obtención de JWT

Sistema ExternoMiddlewareOdoo 17Sistema ExternoSistema ExternoMiddlewareMiddlewareOdoo 17Odoo 17POST /v1/auth/token{"api_key": "***"}POST /web/session/authenticatevalidar API Keyuid: 7 + session cookiegenerar JWTexpires_in: 3600 segundos200 OK{"access_token": "***", "token_type": "bearer"}Authorization: Bearer {token}en todas las llamadas subsiguientes

#2. Contactos — Crear, Actualizar, Consultar

Sistema ExternoMiddlewareMapping DBOdoo 17Sistema ExternoSistema ExternoMiddlewareMiddlewareMapping DBMapping DBOdoo 17Odoo 17POST /v1/contacts{"external_ref":"EXT-001","name":"Acme", "is_company":true}SELECT odoo_id FROM mappingWHERE ext_ref='EXT-001'alt[Nuevo - Crear]not foundexecute_kw(res.partner, create){name, is_company, vat, email, ...}partner_id: 42INSERT mapping(EXT-001, 42, partner)201 Created{"status":"created", "odoo_id":42}[Existente - Actualizar]found - odoo_id=42execute_kw(res.partner, write)[[42], {name, email, ...}]True200 OK{"status":"updated", "odoo_id":42}Consulta GETGET /v1/contacts/EXT-001lookup ext_ref - 42execute_kw(res.partner, read)[[42], {fields:[name,email,country_id,...]}]{id:42, name:"Acme", email:"...",country_id:[233,"US"]}resolver country_id -"United States"200 OK{"name":"Acme","country":"United States"}

#3. Productos — Template + Variantes

Sistema ExternoMiddlewareMapping DBOdoo 17Sistema ExternoSistema ExternoMiddlewareMiddlewareMapping DBMapping DBOdoo 17Odoo 17POST /v1/products{"ext_ref":"PROD-001","name":"Laptop Pro","list_price":1299.99,"attributes":[{...}]}lookup ext_ref - existe?alt[Nuevo producto con variantes]not foundcreate product.template{name, detailed_type:"product",list_price, ...}tmpl_id: 15write attribute_line_idsprecio extra + valores- genera variantesvariants created: [30,31,32,33]Mapear template + cada variantePROD-001 -> 15 (template)PROD-001-30 -> 30 (variant)PROD-001-31 -> 31 (variant)PROD-001-32 -> 32 (variant)PROD-001-33 -> 33 (variant)INSERT mappings201 Created{"odoo_id":15, "variants":[{...}]}[Actualizacion]found - tmpl_id=15write([15], {list_price, active, ...})True200 Updated

#4. Lista de Materiales — BoM con dependencias

Sistema ExternoMiddlewareMapping DBOdoo 17Sistema ExternoSistema ExternoMiddlewareMiddlewareMapping DBMapping DBOdoo 17Odoo 17POST /v1/boms{"ext_ref":"BOM-001","product_ext_ref":"PROD-001","lines":[{"comp_ext_ref":"COMP-001","qty":2}, ...]}resolver product_ext_ref - tmpl_idresolver cada component_ext_ref - product_idalt[Dependencias OK]tmpl_id:15components: [30,31]create mrp.bomproduct_tmpl_id: 15bom_line_ids:(0,0,{product_id:30, qty:2})(0,0,{product_id:31, qty:1})bom_id: 8INSERT mapping(BOM-001, 8, bom)201 Created{"odoo_id":8, "lines_count":2}[Dependencia no encontrada]component EXT-999 -not found400 Bad Request{"error":"DEPENDENCY_NOT_FOUND","details":{"ext_ref":"EXT-999"}}

#5. Pagos — Cobro + Conciliación

Sistema ExternoMiddlewareMapping DBOdoo 17Sistema ExternoSistema ExternoMiddlewareMiddlewareMapping DBMapping DBOdoo 17Odoo 17POST /v1/payments{"ext_ref":"PAY-001","payment_type":"inbound","partner_ext_ref":"EXT-001","amount":2599.98,"matched_invoices":["INV-001","INV-002"]}resolver partner_ext_ref - partner_id: 42resolver journal_ext_ref - journal_idresolver matched_invoices - [89,90]create account.payment{payment_type:"inbound",partner_id:42, amount:2599.98}payment_id: 156action_post([156])conciliar facturas 89 y 90alt[Conciliacion exitosa]state:"posted"reconciled with invoices [89,90]INSERT mapping(PAY-001, 156, payment)201 Created{"odoo_id":156, "state":"posted"}[Error de regla de negocio]Error:factura INV-001 ya pagada409 Conflict{"error":"BUSINESS_RULE_ERROR","message":"factura INV-001completamente pagada"}

#6. Orden de carga obligatorio

1. Contactosres.partner2. Productosproduct.template+ product.product3. Listas de Materialesmrp.bom + bom.line4. Pagosaccount.payment+ conciliacionsuppliersnecesitan existirproducto ycomponentespartner, journaly facturas
#EntidadDepende deMotivo
1ContactosSin dependencias
2ProductosContactosSuppliers, taxes
3BoMsProductosProducto + componentes deben existir
4PagosContactos + FacturasPartner, journal e invoices

#Tabla de Mapeo — Mapping DB

┌─────────────┬──────────┬──────────────┬──────────────────────┐
│  ext_ref    │ odoo_id  │    model     │     updated_at       │
├─────────────┼──────────┼──────────────┼──────────────────────┤
│ EXT-001     │    42    │ res.partner  │ 2026-07-02T17:00:00Z │
│ PROD-001    │    15    │ product.tpl  │ 2026-07-02T17:01:00Z │
│ PROD-001-30 │    30    │ product.prod │ 2026-07-02T17:01:00Z │
│ PROD-001-31 │    31    │ product.prod │ 2026-07-02T17:01:00Z │
│ BOM-001     │     8    │ mrp.bom      │ 2026-07-02T17:02:00Z │
│ PAY-001     │   156    │ acct.payment │ 2026-07-02T17:03:00Z │
└─────────────┴──────────┴──────────────┴──────────────────────┘

Unicidad: (model, ext_ref) — cada external_ref es único por tipo de entidad.


#Propiedades del Middleware

PropiedadDescripción
IdempotenciaPOST con mismo external_ref = upsert (crea o actualiza)
Resolución de IDsCada request consulta Mapping DB: ext_ref → odoo_id
Merge parcialSolo los campos enviados se modifican en Odoo
Idempotency-KeyHeader opcional para prevenir duplicados en reintentos
Rate Limit100 req/min por API Key — 429 Too Many Requests
TimeoutREST: 30s — JSON-RPC: 60s — 504 Gateway Timeout

#7. Notificaciones Salientes — integrador_odoo2delta

#Flujo de notificación de venta

Usuario Odoosale.orderintegrador_odoo2deltaOdoo DBAPI DeltaVUsuario OdooUsuario Odoosale.ordersale.orderintegrador_odoo2deltaintegrador_odoo2deltaOdoo DBOdoo DBAPI DeltaV(Sistema Externo)API DeltaV(Sistema Externo)Confirmación de ventaaction_confirm()state = 'sale'hook: _notificar_venta()leer sale.order(campos relevantes)datos de la ordenconstruir payload JSONPOST {deltaV_url}/notificaciones/ventasAuthorization: Bearer {token}Content-Type: application/json {"tipo": "venta","id_odoo": 42,"numero": "S00042","cliente": {...},"lineas": [...],"total": 2599.98,"fecha_confirmacion": "..."}alt[Éxito (2xx)]200 OKregistrar notificación exitosaok[Error de red / timeout]error / timeoutreintentar con backoff(1s, 2s, 4s, 8s)Máx 4 reintentosLuego → log + alerta[Error de negocio (4xx)]400/409/422registrar errorsin reintentar

#Flujo de notificación de compra

Usuario Odoopurchase.orderintegrador_odoo2deltaOdoo DBAPI DeltaVUsuario OdooUsuario Odoopurchase.orderpurchase.orderintegrador_odoo2deltaintegrador_odoo2deltaOdoo DBOdoo DBAPI DeltaV(Sistema Externo)API DeltaV(Sistema Externo)Confirmación de comprabutton_confirm()state = 'purchase'hook: _notificar_compra()leer purchase.order(campos relevantes)datos de la OCconstruir payload JSONPOST {deltaV_url}/notificaciones/comprasAuthorization: Bearer {token}Content-Type: application/json {"tipo": "compra","id_odoo": 15,"numero": "P00015","proveedor": {...},"lineas": [...],"total": 8500.00,"fecha_confirmacion": "..."}alt[Éxito (2xx)]200 OKregistrar notificación exitosaok[Error de red / timeout]error / timeoutreintentar con backoff(1s, 2s, 4s, 8s)Máx 4 reintentosLuego → log + alerta[Error de negocio (4xx)]400/409/422registrar errorsin reintentar

#8. Diagrama de Capas del Sistema

Diagrama de Componentes — Integración Deltav ↔ Odoo 17Sistema Externo (DeltaV)MiddlewareOdoo 17deltav_connectorintegrador_odoo2deltaModelos NativosCliente RESTAPI de Notificaciones/notificaciones/ventas/notificaciones/comprasFastAPIREST EndpointsMapping DB(PostgreSQL)Rate LimiterCircuit BreakerModelosdeltav.*Vistas + MenúControladoresHTTPSync EngineHooksale.orderHookpurchase.orderHTTP Client+ ReintentosConfiguraciónSettingssale.orderpurchase.orderres.partnerproduct.*mrp.bomaccount.*REST/JSON(JWT auth)lookupext_ref → odoo_idJSON-RPCJSON-RPCsynccreate /action_confirmcreate /button_confirmPOSTPOSTHTTP POST(JWT auth)URL, timeout,retry config

📄 Fuente: Especificación original de la API (Gist)