#API REST de Integración — Deltav → Odoo 17

Versión: 1.0 — Julio 2026
Base URL: https://api-integracion.mi-empresa.com/v1
Protocolo: REST/JSON sobre HTTPS
Autenticación: Bearer Token (JWT) obtenido con API Key de Odoo

#1. Autenticación

El sistema externo debe autenticarse con la API Key de Odoo para obtener un token JWT de corta duración.

POST /v1/auth/token

Request:

{
  "api_key": "a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Response 200 OK:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600,
  "odoo_uid": 7
}

Uso en llamadas subsiguientes:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Errores de autenticación:

StatusCodeDescripción
401AUTHENTICATION_ERRORAPI Key inválida o expirada
401TOKEN_EXPIREDEl JWT expiró, re-autenticar

#2. Convenciones

ReglaDetalle
Content-TypeTodas las requests y responses usan application/json
FechasISO 8601: "2026-07-02T15:30:00Z" (datetime) o "2026-07-02" (date)
IDs externosexternal_ref es un string alfanumérico (máx. 255 chars) que identifica de forma única y estable a cada entidad en el sistema externo
MonedasSe usa el ID de res.currency en Odoo (ej. 2 = USD, 3 = EUR, 21 = ARS)
PaísesCódigo ISO 3166-1 alpha-2: "US", "AR", "ES", etc.
POST es upsertSi el external_ref ya existe → se actualiza el registro. Si no → se crea
Campos no enviadosNo se modifican en Odoo (merge parcial)
Orden de carga1º Contactos → 2º Productos → 3º BoMs → 4º Pagos

#3. Endpoints

#3.1 Health Check

GET /v1/health

Response 200 OK:

{
  "status": "healthy",
  "odoo_version": "17.0",
  "odoo_connected": true,
  "timestamp": "2026-07-02T17:00:00Z"
}

#3.2 Contactos

Modelo Odoo: res.partner

#Crear o Actualizar

POST /v1/contacts

Request Body:

{
  "external_ref": "EXT-CUST-001",
  "name": "Acme Corporation",
  "is_company": true,
  "vat": "US123456789",
  "email": "info@acme.com",
  "phone": "+1-555-0100",
  "mobile": "+1-555-0101",
  "website": "https://acme.com",
  "street": "123 Main St",
  "street2": "Suite 400",
  "city": "San Francisco",
  "state": "California",
  "zip": "94105",
  "country_code": "US",
  "is_customer": true,
  "is_supplier": false,
  "payment_term": "immediate",
  "contacts": [
    {
      "name": "John Doe",
      "email": "john@acme.com",
      "phone": "+1-555-0200",
      "position": "CFO"
    }
  ],
  "tags": ["enterprise", "premium"]
}

Parámetros:

CampoTipoRequeridoDefaultDescripción
external_refstring (max 255)ID único del sistema externo
namestringNombre o razón social
is_companybooleanfalsetrue si es empresa
vatstringNº de identificación fiscal
emailstringEmail principal
phonestringTeléfono fijo
mobilestringTeléfono móvil
websitestringURL del sitio web
streetstringDirección (calle y número)
street2stringComplemento de dirección
citystringCiudad
statestringProvincia / Estado
zipstringCódigo postal
country_codestring (ISO 2)Código de país ("US", "AR")
is_customerbooleanfalseMarca como cliente
is_supplierbooleanfalseMarca como proveedor
payment_termstring"immediate", "net_15", "net_30", "net_60"
contactsarrayContactos hijos (personas asociadas)
contacts[].namestring✅ (si se envía)Nombre del contacto
contacts[].emailstringEmail del contacto
contacts[].phonestringTeléfono
contacts[].positionstringCargo / función
tagsarray of stringEtiquetas / categorías

Response 201 Created:

{
  "status": "created",
  "external_ref": "EXT-CUST-001",
  "odoo_id": 42,
  "name": "Acme Corporation",
  "created_at": "2026-07-02T17:00:00Z"
}

Response 200 OK (actualización):

{
  "status": "updated",
  "external_ref": "EXT-CUST-001",
  "odoo_id": 42,
  "name": "Acme Corporation",
  "updated_at": "2026-07-02T17:05:00Z"
}

#Consultar Contacto

GET /v1/contacts/{external_ref}

Parámetros de búsqueda (query string):

ParámetroTipoDescripción
searchstringBúsqueda por nombre (ilike)
is_customerbooleanFiltrar clientes
is_supplierbooleanFiltrar proveedores
country_codestringFiltrar por país
limitintegerMáx. resultados (default: 20)
offsetintegerPaginación (default: 0)

Ejemplo: GET /v1/contacts?search=Acme&is_customer=true&limit=20

Response 200 OK:

{
  "external_ref": "EXT-CUST-001",
  "odoo_id": 42,
  "name": "Acme Corporation",
  "is_company": true,
  "vat": "US123456789",
  "email": "info@acme.com",
  "phone": "+1-555-0100",
  "mobile": "+1-555-0101",
  "website": "https://acme.com",
  "street": "123 Main St",
  "street2": "Suite 400",
  "city": "San Francisco",
  "state": "California",
  "zip": "94105",
  "country": "United States",
  "country_code": "US",
  "is_customer": true,
  "is_supplier": false,
  "payment_term": "immediate",
  "contacts": [
    {
      "odoo_id": 43,
      "name": "John Doe",
      "email": "john@acme.com",
      "phone": "+1-555-0200",
      "position": "CFO"
    }
  ],
  "tags": ["enterprise", "premium"]
}

Errores específicos:

StatusCodeCaso
404NOT_FOUNDexternal_ref no existe
400VALIDATION_ERRORFalta name o external_ref
400INVALID_COUNTRYcountry_code no es un código ISO-2 válido

#3.3 Productos

Modelos Odoo: product.template + product.product

#Crear o Actualizar

POST /v1/products

Request Body (producto simple):

{
  "external_ref": "EXT-PROD-001",
  "name": "Laptop Pro 15\"",
  "default_code": "LP15-2026",
  "barcode": "1234567890123",
  "type": "product",
  "list_price": 1299.99,
  "standard_price": 850.00,
  "currency_id": 2,
  "uom_id": 1,
  "uom_po_id": 1,
  "description": "Laptop profesional 15 pulgadas, 16GB RAM, 512GB SSD",
  "description_purchase": "Laptop Pro 15\" — pedido mayorista",
  "sale_ok": true,
  "purchase_ok": true,
  "active": true,
  "categ_id": "Laptops",
  "taxes_id": ["15% VAT"],
  "supplier_taxes_id": ["15% VAT"],
  "weight": 1.8,
  "volume": 0.005
}

Request Body (producto con variantes):

{
  "external_ref": "EXT-PROD-002",
  "name": "Camiseta Algodón Premium",
  "default_code": "TSH-PRM",
  "type": "product",
  "list_price": 29.99,
  "attributes": [
    { "name": "Talle", "values": ["S", "M", "L", "XL"] },
    { "name": "Color", "values": ["Negro", "Blanco", "Azul"] }
  ]
}

Parámetros:

CampoTipoRequeridoDefaultDescripción
external_refstring (max 255)ID único del sistema externo
namestringNombre del producto
default_codestringSKU / referencia interna
barcodestringCódigo de barras (EAN13/UPC)
typestring"product""product" (stockable), "consu" (consumible), "service"
list_pricenumber0.00Precio de venta
standard_pricenumber0.00Costo estándar
currency_idintegermoneda cia.ID de moneda en Odoo
uom_idinteger1 (Unidades)Unidad de medida de venta
uom_po_idinteger1 (Unidades)Unidad de medida de compra
descriptionstringDescripción para ventas
description_purchasestringDescripción para compras
sale_okbooleantrueHabilitado para venta
purchase_okbooleantrueHabilitado para compra
activebooleantrueProducto activo
categ_idstringCategoría (se busca o crea por nombre)
taxes_idarray of stringImpuestos de venta (por nombre)
supplier_taxes_idarray of stringImpuestos de compra (por nombre)
weightnumberPeso en kg
volumenumberVolumen en m³
attributesarrayAtributos para generar variantes
attributes[].namestring✅ (si se envía)Nombre del atributo (ej. "Color")
attributes[].valuesarray of string✅ (si se envía)Valores del atributo
suppliersarrayInformación de proveedores
suppliers[].partner_external_refstring✅ (si se envía)external_ref del proveedor
suppliers[].pricenumber✅ (si se envía)Precio de compra
suppliers[].min_qtynumber0Cantidad mínima
suppliers[].delayinteger0Días de entrega

Response 201 Created (con variantes):

{
  "status": "created",
  "external_ref": "EXT-PROD-002",
  "odoo_template_id": 16,
  "default_code": "TSH-PRM",
  "name": "Camiseta Algodón Premium",
  "variants": [
    { "odoo_id": 40, "name": "Camiseta Algodón Premium (S, Negro)", "default_code": "TSH-PRM-S-NEG" },
    { "odoo_id": 41, "name": "Camiseta Algodón Premium (S, Blanco)", "default_code": "TSH-PRM-S-BCO" }
  ],
  "variants_count": 12,
  "created_at": "2026-07-02T17:00:00Z"
}

Response 200 OK (actualización):

{
  "status": "updated",
  "external_ref": "EXT-PROD-001",
  "odoo_template_id": 15,
  "updated_at": "2026-07-02T17:05:00Z"
}

#Consultar Producto

GET /v1/products/{external_ref}

Parámetros de búsqueda:

ParámetroTipoDescripción
searchstringBúsqueda por nombre o SKU
typestring"product", "consu", "service"
categ_idstringFiltrar por categoría
activebooleanSolo activos/inactivos
limitintegerDefault: 20
offsetintegerDefault: 0

Response 200 OK:

{
  "external_ref": "EXT-PROD-001",
  "odoo_template_id": 15,
  "name": "Laptop Pro 15\"",
  "default_code": "LP15-2026",
  "barcode": "1234567890123",
  "type": "product",
  "list_price": 1299.99,
  "standard_price": 850.00,
  "currency": { "id": 2, "name": "USD" },
  "uom": { "id": 1, "name": "Units" },
  "categ": { "id": 5, "name": "Laptops" },
  "description": "Laptop profesional 15 pulgadas, 16GB RAM, 512GB SSD",
  "sale_ok": true,
  "purchase_ok": true,
  "active": true,
  "taxes": [{ "id": 1, "name": "15% VAT" }],
  "weight": 1.8,
  "volume": 0.005,
  "qty_available": 45,
  "virtual_available": 30,
  "variants": [
    {
      "odoo_id": 30,
      "default_code": "LP15-SLV-16",
      "name": "Laptop Pro 15\" (Silver, 16GB)",
      "attributes": { "Color": "Silver", "RAM": "16GB" },
      "qty_available": 12
    }
  ],
  "suppliers": [
    {
      "partner_external_ref": "EXT-SUP-001",
      "partner_name": "TechDistributors Inc",
      "price": 780.00,
      "min_qty": 10,
      "delay": 5
    }
  ]
}

Errores específicos:

StatusCodeCaso
404NOT_FOUNDexternal_ref no existe
400VALIDATION_ERRORFalta name o external_ref
400INVALID_TYPEtype no es válido
400DUPLICATE_SKUdefault_code ya existe en otro producto

#3.4 Lista de Materiales (BoM)

Modelos Odoo: mrp.bom + mrp.bom.line

#Crear o Actualizar

POST /v1/boms

Request Body:

{
  "external_ref": "EXT-BOM-001",
  "code": "BOM-LP15-001",
  "product_external_ref": "EXT-PROD-001",
  "product_qty": 1.0,
  "product_uom_id": 1,
  "type": "normal",
  "active": true,
  "lines": [
    {
      "product_external_ref": "EXT-PROD-002",
      "product_qty": 1.0,
      "product_uom_id": 1
    },
    {
      "product_external_ref": "EXT-PROD-003",
      "product_qty": 2.0,
      "product_uom_id": 1
    },
    {
      "product_external_ref": "EXT-PROD-004",
      "product_qty": 0.05,
      "product_uom_id": 5
    }
  ]
}

Parámetros:

CampoTipoRequeridoDefaultDescripción
external_refstring (max 255)ID único del sistema externo
codestringCódigo de referencia de la BoM
product_external_refstringexternal_ref del producto terminado
product_qtynumberCantidad producida por esta BoM
product_uom_idinteger1 (Unidades)Unidad de medida del producto terminado
typestring"normal""normal", "phantom" (kit virtual), "subcontracting"
activebooleantrueBoM activa
linesarrayComponentes de la BoM
lines[].product_external_refstringexternal_ref del componente
lines[].product_qtynumberCantidad necesaria
lines[].product_uom_idinteger1 (Unidades)Unidad de medida del componente
Nota: Las líneas de BoM en Odoo 17 referencian product.product (variante), no product.template. El middleware resuelve automáticamente a la variante por defecto del componente.

Response 201 Created:

{
  "status": "created",
  "external_ref": "EXT-BOM-001",
  "odoo_id": 8,
  "code": "BOM-LP15-001",
  "product": {
    "external_ref": "EXT-PROD-001",
    "name": "Laptop Pro 15\""
  },
  "product_qty": 1.0,
  "type": "normal",
  "lines_count": 3,
  "created_at": "2026-07-02T17:00:00Z"
}

#Consultar BoM

GET /v1/boms/{external_ref}

Parámetros de búsqueda:

ParámetroTipoDescripción
product_external_refstringFiltrar por producto terminado
typestring"normal", "phantom", "subcontracting"
activebooleanSolo activas/inactivas

Response 200 OK:

{
  "external_ref": "EXT-BOM-001",
  "odoo_id": 8,
  "code": "BOM-LP15-001",
  "product": {
    "external_ref": "EXT-PROD-001",
    "odoo_id": 15,
    "name": "Laptop Pro 15\""
  },
  "product_qty": 1.0,
  "product_uom": { "id": 1, "name": "Units" },
  "type": "normal",
  "active": true,
  "lines": [
    {
      "odoo_id": 51,
      "product": { "external_ref": "EXT-PROD-002", "name": "Pantalla 15\" IPS" },
      "product_qty": 1.0,
      "product_uom": { "id": 1, "name": "Units" }
    },
    {
      "odoo_id": 52,
      "product": { "external_ref": "EXT-PROD-003", "name": "Módulo RAM 16GB DDR5" },
      "product_qty": 2.0,
      "product_uom": { "id": 1, "name": "Units" }
    },
    {
      "odoo_id": 53,
      "product": { "external_ref": "EXT-PROD-004", "name": "Pasta Térmica Arctic MX-6" },
      "product_qty": 0.05,
      "product_uom": { "id": 5, "name": "kg" }
    }
  ]
}

Errores específicos:

StatusCodeCaso
404NOT_FOUNDexternal_ref no existe
400DEPENDENCY_NOT_FOUNDEl product_external_ref o un componente no existe
400VALIDATION_ERRORproduct_qty ≤ 0 o lines vacío
400DUPLICATE_BOMYa existe una BoM para ese producto con ese code

#3.5 Pagos

Modelos Odoo: account.payment + account.move

#Registrar Pago

POST /v1/payments

Request Body — Cobro de cliente (inbound):

{
  "external_ref": "EXT-PAY-001",
  "payment_type": "inbound",
  "partner_type": "customer",
  "partner_external_ref": "EXT-CUST-001",
  "amount": 2599.98,
  "currency_id": 2,
  "payment_date": "2026-07-02",
  "journal_external_ref": "BANK-US-01",
  "payment_method": "manual",
  "memo": "Pago facturas INV-001 e INV-002",
  "matched_invoices": [
    { "invoice_external_ref": "INV-001", "amount": 1299.99 },
    { "invoice_external_ref": "INV-002", "amount": 1299.99 }
  ]
}

Request Body — Pago a proveedor (outbound):

{
  "external_ref": "EXT-PAY-002",
  "payment_type": "outbound",
  "partner_type": "supplier",
  "partner_external_ref": "EXT-SUP-001",
  "amount": 8500.00,
  "currency_id": 2,
  "payment_date": "2026-07-01",
  "journal_external_ref": "BANK-US-01",
  "payment_method": "bank_transfer",
  "memo": "Pago lote componentes Q2 2026",
  "matched_invoices": [
    { "invoice_external_ref": "VENDOR-BILL-005", "amount": 8500.00 }
  ]
}

Parámetros:

CampoTipoRequeridoDefaultDescripción
external_refstring (max 255)ID único del sistema externo para este pago
payment_typestring"inbound" (cobro) o "outbound" (pago)
partner_typestring"customer" o "supplier"
partner_external_refstringexternal_ref del cliente o proveedor
amountnumber (> 0)Monto total del pago
currency_idintegermoneda cia.ID de moneda en Odoo
payment_datestring (date)Fecha "YYYY-MM-DD"
journal_external_refstringReferencia del diario contable (mapeado en middleware)
payment_methodstring"manual""manual", "check", "bank_transfer", "credit_card"
memostringNota / referencia del pago
matched_invoicesarrayFacturas a conciliar (si se omite → pago a cuenta)
matched_invoices[].invoice_external_refstring✅ (si se envía)external_ref de la factura
matched_invoices[].amountnumbersaldo pendienteMonto a conciliar
Nota: Las facturas (account.move) deben estar precargadas en Odoo. Este endpoint no las crea.

Response 201 Created:

{
  "status": "created",
  "external_ref": "EXT-PAY-001",
  "odoo_id": 156,
  "payment_type": "inbound",
  "partner_type": "customer",
  "partner": {
    "external_ref": "EXT-CUST-001",
    "name": "Acme Corporation"
  },
  "amount": 2599.98,
  "currency": { "id": 2, "name": "USD" },
  "payment_date": "2026-07-02",
  "state": "posted",
  "memo": "Pago facturas INV-001 e INV-002",
  "matched_invoices": [
    { "invoice_external_ref": "INV-001", "odoo_id": 89, "invoice_number": "INV/2026/0001", "amount_matched": 1299.99 },
    { "invoice_external_ref": "INV-002", "odoo_id": 90, "invoice_number": "INV/2026/0002", "amount_matched": 1299.99 }
  ],
  "created_at": "2026-07-02T17:00:00Z"
}

#Consultar Pago

GET /v1/payments/{external_ref}

Parámetros de búsqueda:

ParámetroTipoDescripción
partner_external_refstringFiltrar por cliente/proveedor
payment_typestring"inbound" o "outbound"
date_fromstring (date)Pagos desde fecha
date_tostring (date)Pagos hasta fecha
statestring"draft", "posted", "cancelled"
limitintegerDefault: 20
offsetintegerDefault: 0

Response 200 OK:

{
  "external_ref": "EXT-PAY-001",
  "odoo_id": 156,
  "payment_type": "inbound",
  "partner_type": "customer",
  "partner": { "external_ref": "EXT-CUST-001", "odoo_id": 42, "name": "Acme Corporation" },
  "amount": 2599.98,
  "currency": { "id": 2, "name": "USD" },
  "payment_date": "2026-07-02",
  "state": "posted",
  "journal": { "id": 5, "name": "Bank US" },
  "payment_method": "manual",
  "memo": "Pago facturas INV-001 e INV-002",
  "matched_invoices": [
    { "invoice_external_ref": "INV-001", "odoo_id": 89, "invoice_number": "INV/2026/0001", "amount_matched": 1299.99, "amount_residual": 0.00 }
  ]
}

Errores específicos:

StatusCodeCaso
404NOT_FOUNDexternal_ref no existe
400DEPENDENCY_NOT_FOUNDpartner_ext_ref, journal_ext_ref o invoice_ext_ref no existe
400VALIDATION_ERRORamount ≤ 0, payment_type inválido
409BUSINESS_RULE_ERRORFactura ya está totalmente pagada
409CONFLICTexternal_ref del pago duplicado
422JOURNAL_MISMATCHEl diario no está configurado para ese payment_type

#4. Manejo de Errores

Todos los errores retornan esta estructura consistente:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Descripción legible en español",
    "details": { "field": "información específica" },
    "request_id": "req_a1b2c3d4e5"
  }
}

#Catálogo completo de errores

HTTPError CodeDescripción
400VALIDATION_ERRORCampos requeridos faltantes o con formato inválido
400INVALID_COUNTRYCódigo de país no válido
400INVALID_TYPETipo de entidad no reconocido
400DUPLICATE_SKUSKU/default_code duplicado
400DUPLICATE_BOMBoM duplicada para el producto
400DEPENDENCY_NOT_FOUNDReferencia externa dependiente no existe
401AUTHENTICATION_ERRORAPI Key inválida o falta autenticación
401TOKEN_EXPIREDJWT expirado
403PERMISSION_DENIEDUsuario sin permisos para la operación
404NOT_FOUNDRecurso no encontrado
409CONFLICTConflicto de estado (ej. pago duplicado)
409BUSINESS_RULE_ERRORRegla de negocio de Odoo violada
422JOURNAL_MISMATCHDiario no configurado para el tipo de pago
429RATE_LIMITDemasiadas peticiones
500ODOO_ERRORError interno de Odoo
502UPSTREAM_UNAVAILABLEOdoo no disponible
504UPSTREAM_TIMEOUTTimeout esperando respuesta de Odoo

#5. Idempotencia y Mapeo

Toda entidad del sistema externo se identifica por su external_ref, que es único, estable e inmutable. El middleware mantiene una tabla interna de mapeo que resuelve external_ref → odoo_id.

#Tabla de mapeo (external_mapping)

ColumnaTipoDescripción
idSERIAL PKIdentificador interno
entity_typeVARCHAR(50)'contact', 'product', 'bom', 'payment'
external_refVARCHAR(255)ID único del sistema externo
odoo_idINTEGERID correspondiente en Odoo
odoo_modelVARCHAR(100)'res.partner', 'product.template', etc.
created_atTIMESTAMPTZFecha de creación del mapeo
updated_atTIMESTAMPTZFecha de última actualización

#Comportamiento del upsert

EscenarioAcción del Middleware
external_ref no existe en mappingCREATE en Odoo, guardar mapping nuevo
external_ref existe en mappingWRITE (actualizar) en Odoo usando el ID mapeado
Misma request repetidaIdempotente: mismo resultado, sin duplicados

#Unicidad


#6. Mapeo de Campos Odoo ↔ API

#Contactos (res.partner)

API FieldOdoo FieldNotas
external_refSolo en mapping
namename
is_companyis_company
vatvat
emailemail
phonephone
mobilemobile
websitewebsite
streetstreet
street2street2
citycity
statestate_idResuelto por nombre
zipzip
country_codecountry_idResuelto por código ISO-2
is_customercustomer_rank> 0
is_suppliersupplier_rank> 0
payment_termproperty_payment_term_idResuelto por nombre
contactschild_idsContactos hijo (res.partner tipo contact)
tagscategory_idResuelto por nombre

#Productos (product.template)

API FieldOdoo FieldNotas
namename
default_codedefault_code
barcodebarcode
typedetailed_typeMapeo: product→product, consu→consu, service→service
list_pricelist_price
standard_pricestandard_price
currency_idcurrency_id
uom_iduom_id
uom_po_iduom_po_id
descriptiondescription_sale
description_purchasedescription_purchase
sale_oksale_ok
purchase_okpurchase_ok
activeactive
categ_idcateg_idBusca o crea por nombre
taxes_idtaxes_idBusca por nombre
supplier_taxes_idsupplier_taxes_idBusca por nombre
weightweight
volumevolume

#Lista de Materiales (mrp.bom)

API FieldOdoo FieldNotas
codecode
product_uom_idproduct_uom_id
typetype"normal", "phantom", "subcontracting"
activeactive

#Pagos (account.payment)

API FieldOdoo FieldNotas
payment_typepayment_type"inbound" o "outbound"
partner_typepartner_type"customer" o "supplier"
partner_external_refpartner_idResuelto vía mapping
amountamount
currency_idcurrency_id
payment_datedate
journal_external_refjournal_idMapeado en middleware
payment_methodpayment_method_line_idMapeado por código
memoref
matched_invoicesConciliación vía account.move.line

#7. Ejemplos de Integración (curl)

#Flujo completo

#1. Autenticación

TOKEN=$(curl -s -X POST https://api-integracion.mi-empresa.com/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{"api_key":"a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}' | jq -r '.access_token')

AUTH="Authorization: Bearer $TOKEN"

#2. Crear Contacto

curl -s -X POST https://api-integracion.mi-empresa.com/v1/contacts \
  -H "Content-Type: application/json" \
  -H "$AUTH" \
  -d '{
    "external_ref": "EXT-CUST-001",
    "name": "Acme Corporation",
    "is_company": true,
    "email": "info@acme.com",
    "is_customer": true,
    "country_code": "US"
  }'

#3. Crear Producto

curl -s -X POST https://api-integracion.mi-empresa.com/v1/products \
  -H "Content-Type: application/json" \
  -H "$AUTH" \
  -d '{
    "external_ref": "EXT-PROD-001",
    "name": "Laptop Pro 15\"",
    "type": "product",
    "list_price": 1299.99
  }'

#4. Crear Producto con variantes

curl -s -X POST https://api-integracion.mi-empresa.com/v1/products \
  -H "Content-Type: application/json" \
  -H "$AUTH" \
  -d '{
    "external_ref": "EXT-PROD-002",
    "name": "Camiseta Algodón Premium",
    "type": "product",
    "list_price": 29.99,
    "attributes": [
      {"name": "Talle", "values": ["S", "M", "L", "XL"]},
      {"name": "Color", "values": ["Negro", "Blanco", "Azul"]}
    ]
  }'

#5. Crear BoM

curl -s -X POST https://api-integracion.mi-empresa.com/v1/boms \
  -H "Content-Type: application/json" \
  -H "$AUTH" \
  -d '{
    "external_ref": "EXT-BOM-001",
    "product_external_ref": "EXT-PROD-001",
    "product_qty": 1.0,
    "lines": [
      {"product_external_ref": "EXT-PROD-002", "product_qty": 1.0},
      {"product_external_ref": "EXT-PROD-003", "product_qty": 2.0}
    ]
  }'

#6. Registrar Pago

curl -s -X POST https://api-integracion.mi-empresa.com/v1/payments \
  -H "Content-Type: application/json" \
  -H "$AUTH" \
  -d '{
    "external_ref": "EXT-PAY-001",
    "payment_type": "inbound",
    "partner_type": "customer",
    "partner_external_ref": "EXT-CUST-001",
    "amount": 2599.98,
    "currency_id": 2,
    "payment_date": "2026-07-02",
    "journal_external_ref": "BANK-US-01",
    "matched_invoices": [
      {"invoice_external_ref": "INV-001", "amount": 1299.99},
      {"invoice_external_ref": "INV-002", "amount": 1299.99}
    ]
  }'

#8. Contrato de Notificaciones Salientes

Como parte del módulo integrador_odoo2delta, Odoo 17 notifica al sistema externo (DeltaV) cuando se crean o confirman documentos de venta y compra. Esta sección define el contrato que el sistema externo debe implementar para recibir dichas notificaciones.

#8.1 Endpoints que el Sistema Externo debe exponer

MétodoEndpointEvento OdooDescripción
POST/notificaciones/ventassale.order → create / action_confirmNotificación de nueva venta o confirmación
POST/notificaciones/compraspurchase.order → create / button_confirmNotificación de nueva orden de compra o confirmación

Autenticación: Bearer Token (JWT) en header Authorization.

Comportamiento esperado del sistema externo:

#8.2 Payload de Notificación — Venta

POST /notificaciones/ventas desde Odoo → Sistema Externo

{
  "tipo": "venta",
  "evento": "confirmacion",
  "id_odoo": 42,
  "numero": "S00042",
  "fecha_creacion": "2026-07-15T10:30:00Z",
  "fecha_confirmacion": "2026-07-15T10:35:00Z",
  "estado": "sale",
  "cliente": {
    "id_odoo": 15,
    "nombre": "Acme Corporation",
    "email": "info@acme.com",
    "vat": "US123456789"
  },
  "lineas": [
    {
      "producto": {
        "id_odoo": 30,
        "nombre": "Laptop Pro 15\"",
        "sku": "LP15-2026",
        "cantidad": 2.0,
        "precio_unitario": 1299.99,
        "subtotal": 2599.98
      }
    }
  ],
  "total_neto": 2599.98,
  "total_impuestos": 389.99,
  "total": 2989.97,
  "moneda": {"id": 2, "codigo": "USD"},
  "notas": "Entrega urgente — prioridad alta"
}

Campos del payload de venta:

CampoTipoDescripción
tipostringSiempre "venta"
eventostring"creacion" o "confirmacion" según el hook disparado
id_odoointegerID del sale.order en Odoo
numerostringNúmero de orden de venta (secuencia)
fecha_creacionstring (ISO 8601)Fecha de creación de la orden
fecha_confirmacionstring (ISO 8601)Fecha de confirmación (null si solo se creó)
estadostringEstado del sale.order: "draft", "sent", "sale", "done", "cancel"
clienteobjectDatos del cliente (res.partner)
cliente.id_odoointegerID en Odoo
cliente.nombrestringRazón social
cliente.emailstringEmail principal
cliente.vatstringNº de identificación fiscal
lineasarrayLíneas de la orden (sale.order.line)
lineas[].productoobjectProducto de la línea
lineas[].producto.id_odoointegerID del producto en Odoo
lineas[].producto.nombrestringNombre del producto
lineas[].producto.skustringSKU / referencia interna
lineas[].cantidadnumberCantidad pedida
lineas[].precio_unitarionumberPrecio unitario
lineas[].subtotalnumberCantidad × precio unitario
total_netonumberSuma de subtotales sin impuestos
total_impuestosnumberSuma de impuestos
totalnumberTotal de la orden (neto + impuestos)
monedaobjectMoneda de la orden
moneda.idintegerID de moneda en Odoo
moneda.codigostringCódigo ISO 4217: "USD", "EUR", "ARS"
notasstringNotas internas de la orden

#8.3 Payload de Notificación — Compra

POST /notificaciones/compras desde Odoo → Sistema Externo

{
  "tipo": "compra",
  "evento": "confirmacion",
  "id_odoo": 15,
  "numero": "P00015",
  "fecha_creacion": "2026-07-15T09:00:00Z",
  "fecha_confirmacion": "2026-07-15T09:05:00Z",
  "fecha_prevista": "2026-07-30",
  "estado": "purchase",
  "proveedor": {
    "id_odoo": 28,
    "nombre": "TechDistributors Inc",
    "email": "orders@techdist.com",
    "vat": "US987654321"
  },
  "lineas": [
    {
      "producto": {
        "id_odoo": 30,
        "nombre": "Laptop Pro 15\"",
        "sku": "LP15-2026"
      },
      "cantidad": 10.0,
      "precio_unitario": 850.00,
      "subtotal": 8500.00
    }
  ],
  "total_neto": 8500.00,
  "total_impuestos": 1275.00,
  "total": 9775.00,
  "moneda": {"id": 2, "codigo": "USD"},
  "notas": "Pedido trimestral Q3 2026"
}

Campos adicionales del payload de compra:

CampoTipoDescripción
tipostringSiempre "compra"
eventostring"creacion" o "confirmacion"
id_odoointegerID del purchase.order en Odoo
numerostringNúmero de orden de compra
fecha_previstastring (date)Fecha prevista de recepción YYYY-MM-DD
proveedorobjectDatos del proveedor (res.partner)
estadostringEstado: "draft", "sent", "to approve", "purchase", "done", "cancel"

#8.4 Configuración del Módulo

El módulo integrador_odoo2delta expone las siguientes opciones de configuración en Settings → Integraciones → DeltaV:

ParámetroDescripciónDefault
URL API DeltaVURL base de la API del sistema externo para notificaciones(vacío — requerido)
API KeyToken de autenticación para las llamadas salientes(vacío — requerido)
Timeout HTTPTimeout máximo para cada intento de notificación15 segundos
Máx. reintentosNúmero máximo de reintentos ante errores de red/5xx4
Backoff inicialEspera inicial entre reintentos (se duplica cada vez)1 segundo
Notificar al crearEnviar notificación en evento create además de confirmfalse
Log nivelNivel de logging para notificacionesINFO

#8.5 Errores y reintentos

EscenarioComportamiento
Sistema externo responde 2xx✅ Notificación exitosa. Se registra en log.
Sistema externo responde 4xx❌ Error de negocio. No se reintenta. Se registra en log + mensaje en chatter del documento.
Sistema externo responde 5xx🔄 Reintento con backoff exponencial (1s, 2s, 4s, 8s). Máximo 4 intentos.
Timeout (> 15s)🔄 Igual que 5xx.
Error de red (DNS, conexión)🔄 Igual que 5xx.
Se agotan los 4 reintentos🚨 Se registra error crítico en log + se notifica al administrador vía chatter.
Nota: Las notificaciones son fire-and-forget desde la perspectiva del usuario de Odoo. La confirmación de la orden no se bloquea si la notificación falla — los reintentos ocurren en segundo plano.

#Fuente original