# HITO 3: Carga de Facturas EDI (INVOIC) — Integración EDICOM iPaaS
**Proyecto**: AGRIGEST — Integración EDI con EDICOM iPaaS  
**Documento**: EDICOM-HITO-03-FACTURAS.md  
**Mensaje EDI**: `INVOIC` — Invoice (Factura Electrónica EDI) (UN/EDIFACT D96A)  
**Dirección de flujo**: AGRIGEST → EDICOM → Cliente (Outbound)  
**Versión**: 1.0 — 2026-07-23

---

## 1. Descripción del Hito

La carga de facturas consiste en el **envío automatizado de facturas electrónicas** a los clientes/cadenas a través de la plataforma EDICOM iPaaS en formato EDI `INVOIC`.

Este hito es el más crítico desde el punto de vista fiscal y legal, ya que implica la emisión de documentos con valor tributario. EDICOM actúa como intermediario que transforma la factura al formato EDI requerido por cada cliente.

**Flujo**: `AGRIGEST (ge_facturas) → Construcción JSON canónico → API iPaaS → Plataforma EDICOM → Transformación INVOIC → Cliente EDI`

> ⚠️ **Nota**: En muchas integraciones, la factura EDI se envía **después** del albarán (DESADV) para que el cliente pueda hacer la conciliación automática ORDERS → DESADV → INVOIC (flujo 3 vías).

---

## 2. Requerimientos EDICOM para el Envío de Facturas

### 2.1 Autenticación

```http
Authorization: Bearer <token_acceso>
X-Eipaas-Ds: <codigo_datasource_cliente>
```

### 2.2 Endpoint de Publicación de Factura

```http
POST /api/v1/messages/publish
Authorization: Bearer <token>
X-Eipaas-Ds: <datasource>
Content-Type: application/json

{
  "documentType": "INVOIC",
  "sender": "<GLN-NUESTRA-EMPRESA>",
  "receiver": "<GLN-CLIENTE>",
  "payload": { ... }
}
```

**Respuesta exitosa**:
```json
{
  "messageId": "MSG-INVOIC-20260723-00001",
  "status": "ACCEPTED",
  "createdAt": "2026-07-23T14:00:00Z"
}
```

### 2.3 Estructura Canónica del Payload INVOIC

Campos requeridos para el mensaje `INVOIC` (Factura):

| Campo EDICOM (Canónico) | Descripción | Fuente AGRIGEST |
|---|---|---|
| `invoiceNumber` | Número de factura | `ge_facturas.serie + numero` |
| `invoiceDate` | Fecha de emisión | `ge_facturas.fecha` |
| `invoiceType` | Tipo: `380` (factura normal), `381` (abono) | `ge_facturas.tipo` (si existe) |
| `orderReference` | Nº pedido del cliente | `ge_pedidos.edi_numero_pedido_cliente` |
| `despatchReference` | Nº albarán/DESADV relacionado | `ge_albaranes.serie + numero` |
| `sellerGLN` | GLN del emisor (vendedor) | GLN de la empresa |
| `sellerNIF` | NIF/CIF del vendedor | `ge_empresas.nif` |
| `sellerName` | Razón social del vendedor | `ge_empresas.nombre_fiscal` |
| `sellerAddress` | Dirección del vendedor | `ge_empresas` |
| `buyerGLN` | GLN del comprador | `ge_clientes.ean` |
| `buyerNIF` | NIF del comprador | `ge_clientes.nif` |
| `buyerName` | Razón social del comprador | `ge_clientes.nombre_fiscal` |
| `buyerAddress` | Dirección del comprador | `ge_clientes` (domicilio, cp, poblacion, provincia) |
| `deliveryPointGLN` | GLN del punto de entrega | `ge_clientes_destinos.ean` |
| `paymentTerms` | Condiciones de pago | `ge_formas_pago` → mapeo al código EDI |
| `currency` | Moneda (`EUR`) | Siempre `EUR` |
| `lines[].lineNumber` | Número de línea | `ge_facturas_lineas.posicion` o secuencial |
| `lines[].ean` | EAN del artículo | `ge_articulos.ean` |
| `lines[].description` | Descripción artículo | `ge_facturas_lineas.descripcion` |
| `lines[].invoicedQuantity` | Cantidad facturada | `ge_facturas_lineas.cantidad` |
| `lines[].unitPrice` | Precio unitario neto | `ge_facturas_lineas.precio` |
| `lines[].lineNetAmount` | Importe neto línea | `ge_facturas_lineas.base` |
| `lines[].vatRate` | Tipo IVA (%) | `ge_facturas_lineas.tipo_iva` |
| `lines[].vatAmount` | Cuota IVA línea | `ge_facturas_lineas.iva` |
| `lines[].lineTotalAmount` | Total línea con IVA | `ge_facturas_lineas.total` |
| `lines[].discountPercent` | % Descuento | `ge_facturas_lineas.descuento_porcentaje` |
| `totals.taxableBase` | Base imponible total | `ge_facturas.base_imponible` |
| `totals.vatAmount` | IVA total | `ge_facturas.iva` |
| `totals.invoiceTotal` | Total factura | `ge_facturas.total` |

---

## 3. Análisis de la Base de Datos Actual — Carencias Detectadas

### 3.1 Tabla `ge_facturas` — Estado Actual

Para conocer la estructura exacta, se requiere leer la definición de `ge_facturas` (aproximadamente en línea 2900 del SQL). Los campos que presumiblemente existen son similares a `ge_albaranes`:
- `id`, `empresa_id`, `cliente_id`, `clientedestino_id`
- `serie`, `numero`, `referencia`, `fecha`
- `base_imponible`, `total`, `iva`
- `id_pedido` → Enlace al pedido

**❌ Carencias para la integración EDI (INVOIC)**:

| Campo Faltante | Tipo | Descripción | Necesario Para |
|---|---|---|---|
| `edi_message_id` | `varchar(100)` | ID del mensaje INVOIC enviado a EDICOM | Trazabilidad |
| `edi_estado_envio` | `enum('NO_ENVIADO','ENVIADO','ERROR','CONFIRMADO','RECHAZADO')` | Estado del envío EDI | Control de flujo |
| `edi_enviado_at` | `timestamp` | Fecha/hora de envío a EDICOM | Auditoría |
| `edi_tipo_factura` | `varchar(10)` | `380` (factura), `381` (abono/rectificativa) | Tipo de documento EDI |
| `edi_referencia_albaran_edi` | `varchar(100)` | Message ID del DESADV relacionado | Conciliación 3 vías |
| `edi_referencia_pedido_cliente` | `varchar(50)` | Nº pedido del cliente | Conciliación 3 vías |
| `edi_intentos_envio` | `tinyint(3)` | Contador de intentos de envío | Gestión de reintentos |
| `edi_error_descripcion` | `text` | Descripción del último error EDI | Soporte |

### 3.2 Tabla `ge_facturas_lineas` — Estado Actual

Presumiblemente similar a `ge_albaranes_lineas`. 

**❌ Carencias para la integración EDI (INVOIC)**:

| Campo Faltante | Tipo | Descripción |
|---|---|---|
| `edi_ean_enviado` | `varchar(20)` | EAN del artículo incluido en el INVOIC |
| `edi_numero_linea_pedido` | `int(11)` | Línea del ORDERS al que corresponde |
| `edi_numero_linea_albaran` | `int(11)` | Línea del DESADV al que corresponde |

### 3.3 Campos Necesarios en Otras Tablas

**`ge_empresas`** — Necesita para el INVOIC:
- `nif` → NIF/CIF del vendedor (**verificar que existe**)
- `nombre_fiscal` → (**verificar que existe**)
- `ean` o `gln` → GLN de la empresa emisora (**probablemente FALTA — añadir**)
- `direccion`, `cp`, `poblacion`, `provincia` (**verificar**)

**`ge_formas_pago`** — Necesita:
- Código EDI de la forma de pago (ej. `10`=contador, `20`=transferencia, `60`=pagaré)  
  → Campo `edi_codigo` probablemente **FALTA en la tabla actual**

---

## 4. SQL Propuesto — Campos a Añadir

> ⚠️ **IMPORTANTE**: Este script debe ser revisado y ejecutado **manualmente** por el usuario según la regla de seguridad de la empresa.

```sql
-- ========================================
-- HITO 3: Campos EDI para ge_facturas
-- ========================================

ALTER TABLE `ge_facturas`
  ADD COLUMN `edi_message_id`              varchar(100)  DEFAULT NULL 
      COMMENT 'ID del mensaje INVOIC enviado a EDICOM'                                         AFTER `referencia`,
  ADD COLUMN `edi_estado_envio`            enum('NO_ENVIADO','ENVIADO','ERROR','CONFIRMADO','RECHAZADO') 
      DEFAULT 'NO_ENVIADO' COMMENT 'Estado del envío EDI de la factura'                        AFTER `edi_message_id`,
  ADD COLUMN `edi_enviado_at`              timestamp NULL DEFAULT NULL 
      COMMENT 'Fecha/hora de envío del INVOIC a EDICOM'                                        AFTER `edi_estado_envio`,
  ADD COLUMN `edi_tipo_factura`            varchar(10)   DEFAULT '380' 
      COMMENT '380=Factura, 381=Abono/Rectificativa (código EDIFACT)'                          AFTER `edi_enviado_at`,
  ADD COLUMN `edi_referencia_albaran_edi`  varchar(100)  DEFAULT NULL 
      COMMENT 'messageId del DESADV relacionado (conciliación 3 vías)'                         AFTER `edi_tipo_factura`,
  ADD COLUMN `edi_referencia_pedido_cliente` varchar(50) DEFAULT NULL 
      COMMENT 'Nº pedido del cliente (ORDERS) para el INVOIC'                                  AFTER `edi_referencia_albaran_edi`,
  ADD COLUMN `edi_intentos_envio`          tinyint(3)    DEFAULT 0 
      COMMENT 'Contador de intentos de envío EDI (para reintentos)'                            AFTER `edi_referencia_pedido_cliente`,
  ADD COLUMN `edi_error_descripcion`       text          DEFAULT NULL 
      COMMENT 'Descripción del último error en el envío EDI'                                   AFTER `edi_intentos_envio`,
  ADD INDEX `idx_ge_facturas_edi_estado` (`edi_estado_envio`),
  ADD INDEX `idx_ge_facturas_edi_message_id` (`edi_message_id`);

-- ========================================
-- HITO 3: Campos EDI para ge_facturas_lineas
-- ========================================

ALTER TABLE `ge_facturas_lineas`
  ADD COLUMN `edi_ean_enviado`             varchar(20)   DEFAULT NULL 
      COMMENT 'EAN del artículo enviado en el INVOIC'                                          AFTER `articulo_codigo`,
  ADD COLUMN `edi_numero_linea_pedido`     int(11)       DEFAULT NULL 
      COMMENT 'Número de línea del ORDERS al que corresponde'                                  AFTER `edi_ean_enviado`,
  ADD COLUMN `edi_numero_linea_albaran`    int(11)       DEFAULT NULL 
      COMMENT 'Número de línea del DESADV al que corresponde'                                  AFTER `edi_numero_linea_pedido`;

-- ========================================
-- HITO 3: GLN de la empresa emisora
-- (Verificar si ya existe en ge_empresas)
-- ========================================

-- Añadir GLN a ge_empresas SI NO EXISTE:
ALTER TABLE `ge_empresas`
  ADD COLUMN `gln`       varchar(20)   DEFAULT NULL 
      COMMENT 'GLN (Global Location Number EAN-13) de la empresa para EDI'                    AFTER `nif`;

-- ========================================
-- HITO 3: Código EDI en ge_formas_pago
-- ========================================

ALTER TABLE `ge_formas_pago`
  ADD COLUMN `edi_codigo` varchar(5)    DEFAULT NULL 
      COMMENT 'Código EDI de la forma de pago (10=contado, 20=transferencia, 60=pagaré)';
```

---

## 5. Proceso de Implementación en AGRIGEST (Delphi)

### 5.1 Módulo Delphi Propuesto

**Nuevo módulo**: `uEdicomFacturasClient.pas`

**Responsabilidades**:
1. `SeleccionarFacturasParaEnvio(): TDataSet` — Facturas con `edi_estado_envio = 'NO_ENVIADO'` y cliente con GLN
2. `ConstruirPayloadINVOIC(FacturaId: Integer): TJSONObject` — Mapear `ge_facturas` + `ge_facturas_lineas` al formato canónico EDICOM
3. `EnviarINVOIC(Payload: TJSONObject): string` — POST a EDICOM, retorna `messageId`
4. `MarcarFacturaEnviada(FacturaId: Integer; MessageId: string)` — UPDATE estado EDI
5. `ManejarError(FacturaId: Integer; Error: string)` — Gestión de reintentos

### 5.2 Construcción del Payload INVOIC

```pascal
// Pseudocódigo del mapeo AGRIGEST → INVOIC
function ConstruirPayloadINVOIC(FacturaId: Integer): TJSONObject;
begin
  Result := TJSONObject.Create;
  Result.AddPair('documentType', 'INVOIC');
  
  // Cabecera de la factura
  Result.AddPair('invoiceNumber', Serie + PadLeft(Numero, 8, '0'));
  Result.AddPair('invoiceDate', FormatDateTime('yyyy-mm-dd', Fecha));
  Result.AddPair('invoiceType', edi_tipo_factura); // '380' o '381'
  Result.AddPair('orderReference', edi_referencia_pedido_cliente);
  Result.AddPair('despatchReference', edi_referencia_albaran_edi);
  Result.AddPair('currency', 'EUR');
  
  // Vendedor (nuestra empresa)
  var Seller := TJSONObject.Create;
  Seller.AddPair('gln', GlnEmpresa);     // ge_empresas.gln
  Seller.AddPair('nif', NifEmpresa);     // ge_empresas.nif
  Seller.AddPair('name', NombreEmpresa); // ge_empresas.nombre_fiscal
  Result.AddPair('seller', Seller);
  
  // Comprador (cliente)
  var Buyer := TJSONObject.Create;
  Buyer.AddPair('gln', GlnCliente);      // ge_clientes.ean
  Buyer.AddPair('nif', NifCliente);      // ge_clientes.nif
  Buyer.AddPair('name', NombreCliente);  // ge_clientes.nombre_fiscal
  Result.AddPair('buyer', Buyer);
  
  // Condiciones de pago
  var Payment := TJSONObject.Create;
  Payment.AddPair('code', EdiCodigoPago); // ge_formas_pago.edi_codigo
  Result.AddPair('paymentTerms', Payment);
  
  // Líneas de factura
  var Lines := TJSONArray.Create;
  // Por cada línea en ge_facturas_lineas...
  var Line := TJSONObject.Create;
  Line.AddPair('lineNumber', Posicion);
  Line.AddPair('ean', ge_articulos.ean);
  Line.AddPair('description', Descripcion);
  Line.AddPair('invoicedQuantity', Cantidad);
  Line.AddPair('unitPrice', Precio);
  Line.AddPair('lineNetAmount', Base);
  Line.AddPair('vatRate', TipoIva);
  Line.AddPair('vatAmount', Iva);
  Line.AddPair('lineTotalAmount', Total);
  Lines.AddElement(Line);
  Result.AddPair('lines', Lines);
  
  // Totales
  var Totals := TJSONObject.Create;
  Totals.AddPair('taxableBase', BaseImponible);
  Totals.AddPair('vatAmount', IvaTotal);
  Totals.AddPair('invoiceTotal', Total);
  Result.AddPair('totals', Totals);
end;
```

### 5.3 Flujo de Proceso

```
USUARIO genera/confirma factura en AGRIGEST
  │
  ├── 1. Factura guardada con edi_estado_envio = 'NO_ENVIADO'
  │
  ├── 2. [Botón "Enviar EDI" o proceso automático post-facturación]
  │
  ├── 3. Validaciones previas:
  │   ├── Cliente tiene GLN (ge_clientes.ean no vacío)
  │   ├── Artículos tienen EAN (ge_articulos.ean no vacío)
  │   ├── Empresa tiene GLN (ge_empresas.gln no vacío)
  │   └── Forma de pago tiene código EDI
  │
  ├── 4. AutenticarEdicom() → Bearer Token
  │
  ├── 5. ConstruirPayloadINVOIC(facturaId) → JSON
  │
  ├── 6. EnviarINVOIC(JSON) → POST a EDICOM
  │   └── Respuesta: messageId
  │
  └── 7. MarcarFacturaEnviada()
      ├── UPDATE ge_facturas SET edi_message_id = messageId
      ├── UPDATE ge_facturas SET edi_estado_envio = 'ENVIADO'
      └── UPDATE ge_facturas SET edi_enviado_at = NOW()

  [Si error]
  └── 8. ManejarError()
      ├── UPDATE ge_facturas SET edi_estado_envio = 'ERROR'
      ├── UPDATE ge_facturas SET edi_error_descripcion = ErrorMsg
      └── UPDATE ge_facturas SET edi_intentos_envio = edi_intentos_envio + 1
```

### 5.4 Integración en la UI

- Columna de estado EDI en el listado de facturas con iconos de color
- Botón **"Enviar Factura EDI"** en el editor de facturas y en la lista (selección múltiple)
- Panel de **Reintentos EDI**: facturas en estado `ERROR` con botón de reintento
- Histórico de envíos EDI visible desde el detalle de factura

---

## 6. Datos a Solicitar a EDICOM

- [ ] Endpoint exacto de publicación de INVOIC
- [ ] Esquema JSON canónico del payload INVOIC completo (Canonical Format Document)
- [ ] Códigos de tipo de factura requeridos (`380`, `381`, etc.)
- [ ] Codificación de formas de pago EDI (tabla de correspondencia)
- [ ] Requisitos de IVA desglosado por tipo
- [ ] Manejo de facturas rectificativas/abonos (`381`) — ¿deben referenciar la factura original?
- [ ] GLN de nuestra empresa
- [ ] Validaciones fiscales específicas por cliente/cadena

---

## 7. Consideraciones Legales y Técnicas

- **Factura electrónica EDI ≠ Factura electrónica fiscal**: El `INVOIC` EDI es un documento B2B entre empresa y cliente. La factura fiscal puede ser el mismo documento o uno adicional (según acuerdo con Hacienda y el cliente).
- **Abonos y rectificativas**: El código `381` en `edi_tipo_factura` indica abono. Algunos clientes requieren que se referencie el número de la factura original.
- **Tolerancias de importes**: Algunos clientes EDI validan los importes con tolerancias (diferencias de redondeo). El total del INVOIC debe cuadrar con el pedido y el albarán dentro de la tolerancia acordada.
- **Conciliación 3 vías (ORDERS → DESADV → INVOIC)**: Para cadenas que la exigen, el INVOIC debe referenciar tanto el número de pedido del cliente (`orderReference`) como el número del albarán (`despatchReference`). Esto requiere tener ambos datos en `ge_facturas`.
- **Retención de copias**: Guardar el `messageId` de EDICOM permite recuperar copias del mensaje enviado desde el portal EDICOM si fuera necesario.

---

## 8. Resumen de Campos EDI Pendientes de Datos Maestros

| Entidad | Campo a Completar | Descripción | Acción |
|---|---|---|---|
| `ge_clientes` | `ean` | GLN del cliente EDI | Solicitar GLNs a cada cliente/cadena |
| `ge_clientes_destinos` | `ean` | GLN del punto de entrega | Solicitar por tienda/almacén |
| `ge_articulos` | `ean` | EAN-13 del artículo | Verificar completitud en catálogo |
| `ge_empresas` | `gln` (nuevo) | GLN de nuestra empresa | Obtener de EDICOM en onboarding |
| `ge_formas_pago` | `edi_codigo` (nuevo) | Código EDI forma de pago | Mapeo según tabla EDIFACT |
