# FatturaPA Elettronica per VTE CRM — Documento di Reimplementazione su Linux / PHP 8

> Documento tecnico per ricreare il plugin di Fatturazione Elettronica italiana (FtPA) — attualmente desktop VB.NET integrato con il gestionale **Mercurio** (Firebird) — come **modulo PHP 8 per il CRM VTE / vtenext** su server Linux.

---

## Indice

1. [Sintesi e obiettivo](#1-sintesi-e-obiettivo)
2. [Architettura attuale (.NET) vs target (PHP 8/VTE)](#2-architettura-attuale-net-vs-target-php-8vte)
3. [Stack tecnologico consigliato](#3-stack-tecnologico-consigliato)
4. [Modello dati (schema MySQL/MariaDB)](#4-modello-dati-schema-mysqlmariadb)
5. [Generazione XML FatturaPA 1.2.1](#5-generazione-xml-fatturapa-121)
6. [Validazione XSD e regole di business](#6-validazione-xsd-e-regole-di-business)
7. [Naming file, progressivi e archiviazione](#7-naming-file-progressivi-e-archiviazione)
8. [Client SdI / provider (ftpa.it REST)](#8-client-sdi--provider-ftpait-rest)
9. [Ciclo di vita e notifiche SdI](#9-ciclo-di-vita-e-notifiche-sdi)
10. [Sistema a plugin (export e import)](#10-sistema-a-plugin-export-e-import)
11. [Integrazione nel CRM VTE/vtenext](#11-integrazione-nel-crm-vtevtenext)
12. [Sicurezza](#12-sicurezza)
13. [Struttura del progetto PHP](#13-struttura-del-progetto-php)
14. [Roadmap di implementazione](#14-roadmap-di-implementazione)
15. [Appendice — riferimenti file sorgente](#15-appendice--riferimenti-file-sorgente)

---

## 1. Sintesi e obiettivo

Il software originale è un'applicazione **Windows Forms in VB.NET / C#** (`FtPA.exe`) che:

- legge documenti dal gestionale **Mercurio** (DB **Firebird**, tabelle con prefisso `F*`);
- genera l'**XML FatturaPA 1.2.1** (formato AdE, namespace `http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2`);
- lo **valida** con lo schema XSD ufficiale;
- lo **invia** allo SdI (Sistema di Interscambio) tramite il provider **Pantarei / ftpa.it** via **REST**;
- gestisce **fatture passive** (ricezione), **autofatture/reverse charge**, **notifiche SdI** e una **cache locale** (LiteDB);
- applica trasformazioni cliente-specifiche tramite un **sistema a plugin** (DLL `IFtpaPluginable`).

**Obiettivo del porting:** riprodurre queste funzionalità come **modulo PHP 8** all'interno del CRM **VTE (vtenext)**, su Linux, con DB **MySQL/MariaDB**, sostituendo:

| Componente .NET | Equivalente PHP 8 / VTE |
|---|---|
| Firebird (Mercurio) | MySQL/MariaDB (DB di VTE) |
| LiteDB (cache locale) | Tabelle MySQL dedicate |
| `FatturaPA12.Utilities.WriteXMLPA` | Servizio `FatturaPaBuilder` (DOMDocument) |
| `FatturaPA12Validate` | `DOMDocument::schemaValidate()` + libxml |
| `WS/Client.vb` (REST) | Client HTTP (Guzzle / cURL) |
| Plugin DLL `IFtpaPluginable` | Pipeline di plugin PHP configurabili |
| WinForms UI | Moduli/azioni VTE (Smarty + controller) |

> **Nota su VTE:** VTE (vtenext) è un CRM open source in PHP/MySQL, derivato da vTiger. Il modulo va realizzato come **modulo custom vtenext** (cartella `modules/FatturaElettronica/`), riusando ORM/CRMEntity, i web services e il layer di permessi del CRM.

---

## 2. Architettura attuale (.NET) vs target (PHP 8/VTE)

### Componenti del software originale

| Progetto | Tipo | Ruolo |
|---|---|---|
| `FtPA` | WinExe .NET 4.6.2 | UI, orchestrazione, client REST |
| `Pantarei.Mercurio.FatturaPA` | netstandard2.0 | **Motore XML** + validazione + plugin loader |
| `Plugins.*` (16 DLL VB) | Class library | Trasformazioni cliente-specifiche (export) |
| `Plugins.Import.*` (C#) | Class library | Trasformazioni in importazione fatture attive |
| `FtPAChecker` | Console | Genera XML da CLI per test |
| `FtPAutil` / `FtPAutilManager` | WCF + SQL Server | Acquisto pacchetti (commerciale, **non SdI**) |
| `FatturaElettronicaViewer` | WinForms C# | Viewer XML standalone |

### Flusso logico target (PHP/VTE)

```
[VTE CRM]
   │  (Fattura/Inventory module)
   ▼
[Service FatturaElettronica]
   ├── MercurioRepository  →  InvoiceRepository (MySQL VTE)
   ├── FatturaPaBuilder    →  costruisce DOMDocument FatturaPA 1.2.1
   ├── PluginPipeline      →  hook IFtpaPluginable (mutazione DOM in-memory)
   ├── FatturaPaValidator  →  XSD 1.2.1 + regole business
   ├── FileNamingService   →  IT{piva}_{hex5}.xml
   └── SdiClient           →  REST provider (ftpa.it) → SdI
   ▼
[Storage XML + tabelle stato/notifiche]
```

---

## 3. Stack tecnologico consigliato

- **PHP 8.1+** (preferibile 8.2/8.3) con estensioni: `ext-dom`, `ext-libxml`, `ext-simplexml`, `ext-curl`, `ext-openssl`, `ext-mbstring`, `ext-pdo_mysql`, `ext-json`, `ext-fileinfo`, `ext-zip` (per `.p7m`/firma).
- **MySQL/MariaDB** (quello già usato da VTE).
- **Composer** per le dipendenze:
  - `guzzlehttp/guzzle` — client HTTP verso il provider;
  - `robrichards/xmlseclibs` o `phpseclib/phpseclib` — firma XAdES/CAdES (se richiesta);
  - `psr/log` + `monolog/monolog` — logging.
- **XSD ufficiali** AdE: `Schema_VFPR12.xsd` / `Schema_del_file_xml_FatturaPA_v1.2.1.xsd` (da depositare in `resources/xsd/`).
- **XSL ufficiali** AdE per anteprima HTML (`fatturaPA_v1.2.1.xsl`, `fatturaordinaria_v1.2.2.xsl`).

> Importante: la formattazione numerica deve usare **sempre il punto** come separatore decimale (locale invariante). Non affidarsi al locale di sistema.

---

## 4. Modello dati (schema MySQL/MariaDB)

Lo schema seguente normalizza il modello Firebird/LiteDB originale. Tutte le tabelle operative includono `company_id` e `fiscal_year` (in Mercurio il "DB anno" implicava già il contesto).

### 4.1 Configurazione (da `FTPASETTING`)

```sql
CREATE TABLE ftpa_settings (
  id              INT PRIMARY KEY AUTO_INCREMENT,
  company_id      INT NOT NULL,
  endpoint_url    VARCHAR(255) NOT NULL DEFAULT 'https://www.ftpa.it/fattura_elettronica/rest',
  ep_username     VARCHAR(100),
  ep_password     VARBINARY(512),         -- cifrata (vedi sez. Sicurezza)
  progressivo_invio CHAR(5) DEFAULT '00001', -- contatore esadecimale per nome file
  esigibilita_default CHAR(1) DEFAULT 'I',  -- I=immediata, D=differita, S=split payment
  flag_view_esteri  TINYINT(1) DEFAULT 0,
  flag_view_inviate TINYINT(1) DEFAULT 0,
  tipo_conai        TINYINT DEFAULT 0,      -- 0,1,2
  flag_solo_dest    TINYINT(1) DEFAULT 0,
  flag_exc_doa      TINYINT(1) DEFAULT 0,   -- escludi DatiOrdineAcquisto
  flag_auto_bollo_virt TINYINT(1) DEFAULT 0,
  lettera_esenzione_prefix VARCHAR(255),
  UNIQUE KEY uq_company (company_id)
);
```

### 4.2 Dati emittente (CedentePrestatore) — template

In Mercurio è un template XML (`config\config{PIVA}.12.xml`). In VTE conviene una tabella:

```sql
CREATE TABLE ftpa_cedente (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  company_id    INT NOT NULL,
  id_paese      CHAR(2) DEFAULT 'IT',
  id_codice     VARCHAR(28) NOT NULL,    -- partita IVA trasmittente
  codice_fiscale VARCHAR(16),
  denominazione VARCHAR(80),
  nome          VARCHAR(60),
  cognome       VARCHAR(60),
  regime_fiscale VARCHAR(4) NOT NULL,    -- RF01..RF19
  indirizzo     VARCHAR(60), numero_civico VARCHAR(8),
  cap CHAR(5), comune VARCHAR(60), provincia CHAR(2), nazione CHAR(2) DEFAULT 'IT',
  rea_ufficio CHAR(2), rea_numero VARCHAR(20), rea_capitale DECIMAL(15,2),
  rea_socio_unico VARCHAR(2), rea_stato_liquidazione VARCHAR(2),
  contatti_telefono VARCHAR(12), contatti_email VARCHAR(256),
  -- IdTrasmittente / eventuale terzo intermediario
  trasmittente_id_paese CHAR(2) DEFAULT 'IT',
  trasmittente_id_codice VARCHAR(28),
  UNIQUE KEY uq_company (company_id)
);
```

### 4.3 Anagrafica cliente/fornitore (da `FCLIFOR`)

```sql
CREATE TABLE ftpa_clifor (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  crm_account_id INT,                    -- collegamento all'account VTE
  cod_clifor    VARCHAR(10),
  denoma        VARCHAR(255),
  nome VARCHAR(60), cognome VARCHAR(60),
  indirizzo VARCHAR(255), cap VARCHAR(10), citta VARCHAR(100), prov CHAR(2),
  piva VARCHAR(28), cod_fisc VARCHAR(20),
  cod_nazione CHAR(2) DEFAULT 'IT',
  cod_destinatario_pa VARCHAR(7) DEFAULT '0000000',  -- codice SdI (6 PA / 7 privati)
  email_pec VARCHAR(255),
  tipo_fatt_elettr VARCHAR(10),          -- es. 'UB' per UBL
  cod_pagamento INT,
  KEY idx_account (crm_account_id)
);
```

### 4.4 Codici IVA (da `FCODIVA`)

```sql
CREATE TABLE ftpa_cod_iva (
  id            INT PRIMARY KEY,
  descr         VARCHAR(100),
  descr_docu    VARCHAR(100),
  perc_aliquota DECIMAL(5,2),
  natura        VARCHAR(10),             -- N1, N2.1, N3.1, N6.x ...
  rif_norma     VARCHAR(255),
  tipo_com_autofatt CHAR(1),             -- N/T comportamento natura in autofattura
  flag_fe       CHAR(1) DEFAULT 'S',     -- 'N' blocca invio SdI
  split_payment TINYINT(1) DEFAULT 0,
  perc_indet    DECIMAL(5,2)
);
```

### 4.5 Fattura attiva (testata + righe + riepiloghi)

```sql
CREATE TABLE ftpa_invoice (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  company_id    INT NOT NULL,
  crm_invoice_id INT,                    -- collegamento Invoice di VTE
  cod_clifor    VARCHAR(10),
  tipo_documento VARCHAR(4) DEFAULT 'TD01', -- TD01..TD28
  numero        VARCHAR(20),
  data_doc      DATE,
  divisa        CHAR(3) DEFAULT 'EUR',
  importo_totale DECIMAL(15,2),
  arrotondamento DECIMAL(15,2),
  causale       TEXT,
  formato_trasmissione CHAR(5),          -- FPA12 / FPR12
  codice_destinatario VARCHAR(7),
  pec_destinatario VARCHAR(255),
  progressivo_invio_xml VARCHAR(11),     -- campo XML ProgressivoInvio
  file_name     VARCHAR(120),            -- IT{piva}_{hex5}.xml
  xml_path      VARCHAR(500),
  ftpa_serial   INT DEFAULT 0,           -- 0=non inviata, >0=seriale provider, -1=manuale
  id_sdi        VARCHAR(50),
  stato         VARCHAR(50),             -- vedi ciclo di vita
  blocco_invio_sdi TINYINT(1) DEFAULT 0,
  created_at    DATETIME DEFAULT CURRENT_TIMESTAMP,
  KEY idx_clifor (cod_clifor),
  KEY idx_sdi (id_sdi)
);

CREATE TABLE ftpa_invoice_line (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  invoice_id    INT NOT NULL,
  numero_linea  INT NOT NULL,
  descrizione   VARCHAR(1000),
  codice_articolo_tipo VARCHAR(35),      -- es. AswArtCli, EAN
  codice_articolo_valore VARCHAR(35),
  quantita      DECIMAL(21,8),
  unita_misura  VARCHAR(10),
  prezzo_unitario DECIMAL(21,8),
  prezzo_totale DECIMAL(21,8),
  aliquota_iva  DECIMAL(5,2),
  natura        VARCHAR(10),
  sconto_tipo   CHAR(2),                 -- SC / MG
  sconto_perc   DECIMAL(5,2),
  rif_amministrazione VARCHAR(20),
  FOREIGN KEY (invoice_id) REFERENCES ftpa_invoice(id) ON DELETE CASCADE
);

CREATE TABLE ftpa_invoice_riepilogo (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  invoice_id    INT NOT NULL,
  aliquota_iva  DECIMAL(5,2),
  natura        VARCHAR(10),
  imponibile    DECIMAL(15,2),
  imposta       DECIMAL(15,2),
  esigibilita_iva CHAR(1),               -- I/D/S
  rif_normativo VARCHAR(100),
  FOREIGN KEY (invoice_id) REFERENCES ftpa_invoice(id) ON DELETE CASCADE
);

CREATE TABLE ftpa_invoice_pagamento (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  invoice_id    INT NOT NULL,
  condizioni_pagamento CHAR(4),          -- TP01/TP02/TP03
  modalita_pagamento CHAR(4),            -- MP01..MP23
  data_scadenza DATE,
  importo       DECIMAL(15,2),
  iban VARCHAR(34), bic VARCHAR(11),
  FOREIGN KEY (invoice_id) REFERENCES ftpa_invoice(id) ON DELETE CASCADE
);

CREATE TABLE ftpa_invoice_attachment (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  invoice_id    INT NOT NULL,
  nome          VARCHAR(255),
  formato       VARCHAR(20),             -- estensione upper, es. PDF
  descrizione   TEXT,
  file_path     VARCHAR(500),
  FOREIGN KEY (invoice_id) REFERENCES ftpa_invoice(id) ON DELETE CASCADE
);

-- Documenti correlati (da FTDOCFPADOA): ordine/DDT/fattura collegata/contratto
CREATE TABLE ftpa_invoice_related_doc (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  invoice_id    INT NOT NULL,
  tipo          ENUM('OrdineAcquisto','Contratto','Convenzione','Ricezione','FtCollegata'),
  rif_num_linea VARCHAR(255),
  id_documento  VARCHAR(50),
  data_doc      DATE,
  num_item      VARCHAR(50),
  cod_commessa  VARCHAR(50),
  cod_cup       VARCHAR(50),
  cod_cig       VARCHAR(50),
  FOREIGN KEY (invoice_id) REFERENCES ftpa_invoice(id) ON DELETE CASCADE
);
```

### 4.6 Fatture passive e import (da LiteDB `FTIMPORTATE`)

```sql
CREATE TABLE ftpa_invoice_passive (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  company_id    INT NOT NULL,
  seriale       VARCHAR(20),             -- per leggi_documento_passivo
  id_sdi        VARCHAR(50) UNIQUE,
  cod_clifor    VARCHAR(10),
  mittente      VARCHAR(255),
  nome_file     VARCHAR(120),
  numero_fattura VARCHAR(50),
  data_fattura  DATE,
  data_sdi      DATE,
  tipo_documento VARCHAR(4),
  totale_documento DECIMAL(15,2),
  codice_destinatario VARCHAR(7),
  esito         VARCHAR(50),
  is_importata  TINYINT(1) DEFAULT 0,
  data_importazione DATETIME,
  id_registrazione INT,                  -- entry contabile/Invoice VTE
  xml_path VARCHAR(500), pdf_path VARCHAR(500),
  raw_json JSON
);
```

### 4.7 Documenti inviati e notifiche SdI (da API provider)

```sql
CREATE TABLE ftpa_sent_document (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  id_sdi        VARCHAR(50),
  seriale       VARCHAR(20),
  progressivo   VARCHAR(50),
  data_documento DATE,
  denominazione VARCHAR(255),
  stato_archiviazione VARCHAR(50),
  descrizione_stato VARCHAR(100),
  data_aggiornamento_stato DATETIME,
  data_ricezione VARCHAR(50),
  xml_path VARCHAR(500),
  raw_json JSON,
  UNIQUE KEY uq_sdi (id_sdi)
);

CREATE TABLE ftpa_notification (
  id            INT PRIMARY KEY AUTO_INCREMENT,
  id_sdi        VARCHAR(50),
  seriale       VARCHAR(20),
  stato         VARCHAR(100),            -- codice breve: RC, NS, MC, ...
  lab           VARCHAR(100),            -- etichetta leggibile
  data_inserimento DATETIME,
  esito         ENUM('sconosciuto','scarto','mancata_consegna','consegna','accettata','rifiutata','decorrenza_termini'),
  ricevuta_xml  LONGTEXT,
  esito_xml_path VARCHAR(500),
  KEY idx_sdi (id_sdi)
);
```

### 4.8 Reverse charge / autofatture e alias import (da LiteDB)

```sql
CREATE TABLE ftpa_reverse_charge_settings (
  id INT PRIMARY KEY AUTO_INCREMENT,
  company_id INT NOT NULL,
  cod_sezionale_iva VARCHAR(10),
  cod_cli_autofattura VARCHAR(10),
  tipo_doc VARCHAR(10)
);

CREATE TABLE ftpa_import_alias (
  id INT PRIMARY KEY AUTO_INCREMENT,
  tipo ENUM('blocchettario','cod_iva','pagam','regime_iva'),
  descr VARCHAR(255),
  xml_value VARCHAR(255),
  mercurio_value VARCHAR(255)
);
```

---

## 5. Generazione XML FatturaPA 1.2.1

L'XML è composto da due blocchi principali sotto la radice `p:FatturaElettronica`:
**`FatturaElettronicaHeader`** (uno) e uno o più **`FatturaElettronicaBody`**.

### 5.1 Scheletro XML target

```xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="template.xsl"?>
<p:FatturaElettronica versione="FPR12"
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
    xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <FatturaElettronicaHeader>
    <DatiTrasmissione>
      <IdTrasmittente><IdPaese>IT</IdPaese><IdCodice>01234567890</IdCodice></IdTrasmittente>
      <ProgressivoInvio>2601A</ProgressivoInvio>
      <FormatoTrasmissione>FPR12</FormatoTrasmissione>
      <CodiceDestinatario>0000000</CodiceDestinatario>
      <PECDestinatario>cliente@pec.it</PECDestinatario>
    </DatiTrasmissione>
    <CedentePrestatore> ... </CedentePrestatore>
    <CessionarioCommittente> ... </CessionarioCommittente>
  </FatturaElettronicaHeader>
  <FatturaElettronicaBody>
    <DatiGenerali> ... </DatiGenerali>
    <DatiBeniServizi>
      <DettaglioLinee> ... </DettaglioLinee>
      <DatiRiepilogo> ... </DatiRiepilogo>
    </DatiBeniServizi>
    <DatiPagamento> ... </DatiPagamento>
    <Allegati> ... </Allegati>
  </FatturaElettronicaBody>
</p:FatturaElettronica>
```

**Regole di serializzazione fondamentali (dal sorgente VB):**

- Encoding **UTF-8 senza BOM**.
- La radice usa prefisso `p:` (e dichiara `ds`, `p`, `xsi`); **tutti gli elementi figli sono unqualified** (senza prefisso namespace).
- Attributo `versione` = `FPA12` (PA, codice destinatario a 6 caratteri) oppure `FPR12` (privati B2B/B2C, 7 caratteri).
- Processing instruction `xml-stylesheet` per anteprima.

In PHP usare `DOMDocument` con `formatOutput = true` e `saveXML()`; impostare manualmente i namespace sull'elemento radice e creare i figli con `createElement` (senza namespace).

### 5.2 Sezioni e campi obbligatori

**DatiTrasmissione** (obbligatorio):
- `IdTrasmittente` (`IdPaese` + `IdCodice`)
- `ProgressivoInvio`
- `FormatoTrasmissione`
- `CodiceDestinatario`
- opzionali: `ContattiTrasmittente`, `PECDestinatario`

**CedentePrestatore** (emittente, dalla tabella `ftpa_cedente`):
- `DatiAnagrafici`: `IdFiscaleIVA` (`IdPaese`+`IdCodice`), `CodiceFiscale`, `Anagrafica` (Denominazione **oppure** Nome+Cognome), `RegimeFiscale` (RF01..RF19)
- `Sede`: `Indirizzo`, `CAP`, `Comune`, `Provincia`, `Nazione`
- opzionali: `IscrizioneREA`, `Contatti`

**CessionarioCommittente** (cliente):
- `DatiAnagrafici`: `Anagrafica` (obbligatoria); `IdFiscaleIVA` e/o `CodiceFiscale` (almeno uno in pratica)
- `Sede`

**DatiGeneraliDocumento** (obbligatorio):
- `TipoDocumento` (TD01..TD28), `Divisa`, `Data`, `Numero`
- opzionali: `ImportoTotaleDocumento`, `Arrotondamento`, `Causale[]`, `DatiBollo`, `DatiRitenuta`, `DatiCassaPrevidenziale`

**DatiBeniServizi** (obbligatorio):
- almeno una **`DettaglioLinee`**: `NumeroLinea`, `Descrizione`, `PrezzoUnitario`, `PrezzoTotale`, `AliquotaIVA` (+ opz. `Quantita`, `UnitaMisura`, `CodiceArticolo`, `Natura`, `ScontoMaggiorazione`, `AltriDatiGestionali`, `RiferimentoAmministrazione`)
- almeno un **`DatiRiepilogo`**: `AliquotaIVA`, `ImponibileImporto`, `Imposta` (+ `Natura`, `EsigibilitaIVA`, `RiferimentoNormativo`)

**DatiPagamento** (se presente): `CondizioniPagamento` (TP01/02/03) + almeno un `DettaglioPagamento` (`ModalitaPagamento`, `ImportoPagamento`, eventuale `DataScadenzaPagamento`, `IBAN`, `BIC`).

**Allegati** (se presenti): `NomeAttachment`, `FormatoAttachment`, `Attachment` (base64).

### 5.3 Mappatura dei dati (logica core `WriteXMLPA`)

| Campo XML | Origine (Mercurio) | Logica/Note |
|---|---|---|
| `ProgressivoInvio` (XML) | calcolato | `RIGHT(anno,2) + LPAD(blocco,2) + LPAD(progFattura,5)` |
| `FormatoTrasmissione` | lunghezza `CODDESTINATARIOPA` | 6 char → `FPA12`, altrimenti `FPR12` |
| `CodiceDestinatario` | `FCLIFOR.CODDESTINATARIOPA` | default `0000000`; estero → `XXXXXXX` |
| `PECDestinatario` | `FCLIFOR.EMAILPEC` | se valorizzato |
| `TipoDocumento` | `FTDOCFPA.TIPODOC`/cliente/blocchetto | default `TD01`; se totale < 0 → `TD04` (NC) |
| `ImportoTotaleDocumento` | `FTDOC.TOTFATT` × segno | NC = −1 |
| Righe `DettaglioLinee` | `FRDOC` (`TIPORIGAFTPA=0`) | descrizione+figlie (max 1000), quantità abs, prezzo×cambio×segno |
| `AliquotaIVA` riga | `FCODIVA.PERCALIQUOTA` | |
| `Natura` riga | `FCODIVA.NFATTURAPA` | obbligatoria se aliquota = 0 |
| `DatiRiepilogo` | **castelletto `FCONTABRIVA`** | NON somma righe: imponibile/imposta dalla contabilità |
| `EsigibilitaIVA` | `FCODIVA.SPLITPAYMENT` / default | `S` se split; altrimenti `ftpa_settings.esigibilita_default` (solo se imposta ≠ 0) |
| `DatiPagamento` | `FSCADENZ` + `FPAGAM` + `FBANCHE` | `ModalitaPagamento` default `MP01` |
| Documenti correlati | `FTDOCFPADOA` (ordine→DDT→fattura) | con CUP/CIG/commessa |

> **Punto cruciale:** i **riepiloghi IVA** non si ricalcolano sommando le righe ma si leggono dal **castelletto contabile**. Nel porting VTE l'equivalente è la tabella `ftpa_invoice_riepilogo`, da popolare dai totali IVA dell'`Invoice` del CRM. Verificare che la somma righe ≈ riepilogo (tolleranza arrotondamento).

### 5.4 Formati numerici (decimali)

| Campo | Decimali | Esempio |
|---|---|---|
| `Quantita` | 4 (anche 2..8) | `1.0000` |
| `PrezzoUnitario` | 2..8 (8 se scorporo IVA) | `12.34000000` |
| `PrezzoTotale` (linea) | 2..8 | `123.45` |
| `AliquotaIVA` | 2 | `22.00` |
| `ImponibileImporto`, `Imposta` | 2 | `100.00` |
| `ImportoTotaleDocumento` | 2 | `122.00` |

```php
// Helper formattazione invariante
function fmt(float $v, int $dec = 2): string {
    return number_format($v, $dec, '.', '');
}
```

### 5.5 Formule di calcolo

```
segnoDoc = (TipoDocumento == 'TD04') ? -1 : +1     // nota di credito

// Riga
PrezzoUnitario = segnoDoc * sign(Qta) * PREZZO * COEFFCAMBIO
                 [se scorporo IVA: PREZZO /= (1 + aliquota/100), formato 8 dec]
PrezzoTotale   = segnoDoc * IMPORTOSCONTO * COEFFCAMBIO

// Riepilogo (dal castelletto)
ImponibileRiepilogo = segnoDoc * IMPONIBILE
ImpostaRiepilogo    = segnoDoc * IMPOSTA

// Sconto "a cascata" tipo "10+5"
percTot = 100
foreach (p in split('+')) percTot *= (100 - p)/100
scontoEquivalente = 100 - percTot      // 10+5 => 14.5%

// Split payment: nelle scadenze l'ImportoPagamento = solo imponibile (esigibilità S)
```

### 5.6 Autofatture / reverse charge (`WriteXMLAutoFattura`)

- Tipi documento `TD16`..`TD27` (es. `TD17` acquisti da estero).
- Cedente e cessionario **invertiti**; `SoggettoEmittente = CC`.
- Una riga di riepilogo per ciascuna aliquota nel castelletto.
- `ProgressivoInvio` (XML) = `{annoReg:yy}{idContab:000000}`.
- **Le autofatture NON passano dai plugin export** (nel sorgente la chiamata è commentata): valutare se mantenere questa scelta.

---

## 6. Validazione XSD e regole di business

### 6.1 Validazione XSD

Depositare gli XSD ufficiali in `resources/xsd/`. Validare prima dell'invio:

```php
$dom = new DOMDocument();
$dom->loadXML($xmlString);
libxml_use_internal_errors(true);
if (!$dom->schemaValidate(__DIR__.'/resources/xsd/Schema_VFPR12.xsd')) {
    $errors = libxml_get_errors();
    // raccogliere e mostrare gli errori (numero riga + messaggio)
}
```

> Nota: l'XSD AdE include `ds:Signature` (xmldsig) per fatture firmate. Per la sola generazione non firmata si valida con lo schema "non firmato" oppure si rende opzionale il blocco signature. Nel sorgente originale la firma **non** è implementata: la validazione usa `Versione_1.2.1.xsd` con il blocco `ds:Signature` rimosso/commentato.

### 6.2 Regole di business aggiuntive (oltre XSD)

Da implementare nel `FatturaPaValidator` (riprese da `WriteXMLPA`):

- Le **Nature N2, N3, N6** (senza sotto-codice) sono **vietate dal 2021** → usare i sotto-codici (N2.1, N2.2, N3.1…, N6.1…).
- Se `Natura` valorizzata **e** `AliquotaIVA > 0` → **errore**.
- Se `AliquotaIVA = 0` **senza** `Natura` → **errore**.
- Se non esiste alcun `DatiRiepilogo` → "documento non contabilizzato" → **errore**.
- Almeno una riga deve avere cliente con P.IVA o Codice Fiscale.
- `CodiceDestinatario` coerente con `FormatoTrasmissione` (6 char ⇒ FPA12, 7 char ⇒ FPR12).

---

## 7. Naming file, progressivi e archiviazione

### 7.1 Nome file (per SdI)

```
IT{partitaIVA}_{progressivo}.xml      // es. IT01234567890_2601A.xml
```

- Il **progressivo del nome file** è un **contatore esadecimale a 5 caratteri** (campo `ftpa_settings.progressivo_invio`), **indipendente** dal campo XML `ProgressivoInvio`.
- Incremento atomico:

```php
$next = str_pad(strtoupper(dechex(hexdec($current) + 1)), 5, '0', STR_PAD_LEFT);
```

- Variante UBL: prefisso `UB` invece di `IT` (solo per clienti con `tipo_fatt_elettr='UB'`; opzionale nel porting).

### 7.2 Archiviazione file (filesystem Linux)

Replicare la struttura cartelle in una base configurabile (es. `storage/ftpa/{company}/`):

| Cartella originale | Equivalente Linux | Contenuto |
|---|---|---|
| `ProgramData/FtAttive/` | `storage/ftpa/{c}/attive/` | XML generati; dopo invio → `attive/storage/{yyMM}/` |
| `ProgramData/FtPassive/` | `storage/ftpa/{c}/passive/` | XML passive |
| `ProgramData/FtPassiveFiles/` | `storage/ftpa/{c}/passive_files/` | PDF `{idSdi}_stft.pdf` |
| `ProgramData/v121/` | `resources/xsd/` + `resources/xsl/` | XSD/XSL |

Notifiche: `{idSdi}.esito.xml` in `attive/`.

> Su Linux attenzione a permessi (`www-data`), percorsi case-sensitive e pulizia nome file: rimuovere `\ / : * ? " < > | \r \n`.

---

## 8. Client SdI / provider (ftpa.it REST)

Il software **non parla direttamente con lo SdI**: usa il provider **Pantarei / ftpa.it** come hub. Base URL di default:

```
https://www.ftpa.it/fattura_elettronica/rest
```

### 8.1 Autenticazione

**HTTP Basic Auth** (nessun OAuth/JWT). Attenzione: l'encoding originale usa **ISO-8859-1**.

```php
$auth = 'Basic ' . base64_encode($username . ':' . $password);
// header: Authorization: $auth
```

Formato date nelle richieste: `dd/MM/yyyy`.

### 8.2 Endpoint

| # | Metodo | Path | Content-Type | Parametri |
|---|---|---|---|---|
| 1 | GET | `/` | — | health check (HTTP 200) |
| 2 | POST | `/lista_documenti_attivi_inviati` | form-urlencoded | `data` |
| 3 | POST | `/lista_documenti_conservazione` | form-urlencoded | `data` |
| 4 | POST | `/lista_documenti_passivi` | form-urlencoded | `data_inizio?`, `ricerca?`, `nuovi?` |
| 5 | POST | `/responso_documento_passivo` | form-urlencoded | `nome_file`, `numero_fattura`, `nuovi` (A/R), `contenuto_esito` |
| 6 | POST | `/invia_documento` | **multipart/form-data** | `file` (XML in base64) |
| 7 | POST | `/leggi_documento_attivo` | form-urlencoded | `idsdi` |
| 8 | POST | `/leggi_documento_passivo` | form-urlencoded | `seriale`, `pdf_cortesia?` |
| 9 | POST | `/spazio_utente` | form-urlencoded | `pi` |

### 8.3 Pattern risposta

Tutte le risposte contengono almeno:

```json
{ "codice": "OK", "errore": { "numero": 0, "msg": "" } }
```

Controllare **sempre** `codice === "OK"` **e** HTTP 200.

### 8.4 Invio fattura attiva

```php
$xmlB64 = base64_encode(file_get_contents($xmlPath));
$resp = $client->post('/invia_documento', [
    'multipart' => [
        ['name' => 'file', 'contents' => $xmlB64],
    ],
]);
```

> **Avvertenza:** la risposta di `/invia_documento` può essere **JSON non perfettamente valido**. Il client originale estrae il seriale (`idf`) via regex. Implementare un parser tollerante con fallback regex. Il seriale (`idf`) va salvato in `ftpa_invoice.ftpa_serial`.

### 8.5 Lettura documenti

- `/leggi_documento_attivo` → campo `file` è **base64** dell'XML (decodificare UTF-8).
- `/leggi_documento_passivo` → `file_xml` è **testo in chiaro**; `pdf_cortesia`/`pdf_documento` sono **base64**.

### 8.6 Esempio client Guzzle (scheletro)

```php
final class SdiClient {
    public function __construct(
        private \GuzzleHttp\Client $http,
        private string $username,
        private string $password
    ) {}

    private function authHeader(): array {
        return ['Authorization' => 'Basic ' . base64_encode("{$this->username}:{$this->password}")];
    }

    public function inviaDocumento(string $xmlPath): array {
        $res = $this->http->post('/invia_documento', [
            'headers'   => $this->authHeader(),
            'multipart' => [['name' => 'file', 'contents' => base64_encode(file_get_contents($xmlPath))]],
        ]);
        return $this->parseInviaResponse((string) $res->getBody());
    }

    public function listaDocumentiPassivi(?string $dataInizio, ?string $ricerca, ?bool $nuovi): array {
        $form = array_filter([
            'data_inizio' => $dataInizio,
            'ricerca'     => $ricerca,
            'nuovi'       => $nuovi === null ? null : ($nuovi ? '1' : '0'),
        ], fn($v) => $v !== null);
        $res = $this->http->post('/lista_documenti_passivi', [
            'headers'     => $this->authHeader(),
            'form_params' => $form,
        ]);
        return json_decode((string) $res->getBody(), true);
    }
    // ... altri endpoint
}
```

### 8.7 Servizio commerciale SOAP (opzionale, NON SdI)

Esiste un servizio WCF `FtPAUtilSvc` (`http://www.mypantarei.net/ftpautil/FtPAUtilSvc.svc`) per **acquisto pacchetti fatture** (`GetListino`, `AcquistaPacchetto`, key statica). **Non riguarda la fatturazione SdI** e può essere omesso o sostituito da una pagina/contatto commerciale.

---

## 9. Ciclo di vita e notifiche SdI

### 9.1 Stato locale fattura attiva (`ftpa_invoice.ftpa_serial`)

| Valore | Significato |
|---|---|
| `0` | Non inviata |
| `> 0` | Inviata (seriale del provider) |
| `-1` | Segnata manualmente come inviata (senza provider) |

### 9.2 Notifiche SdI riconosciute

Dal campo `stati[].lab` (case-insensitive) il software mappa:

| `lab` | Esito | XSL |
|---|---|---|
| "ricevuta di consegna" | `RicevutaConsegna` | `RC_v1.1.xsl` |
| "notifica di mancata consegna" | `MancataConsegna` | `MC_v1.1.xsl` |
| "notifica di scarto" | `NotificaScarto` | `NS_v1.1.xsl` |
| altro | `Sconosciuto` | — |

> Stati ulteriori (**accettata, rifiutata, decorrenza termini** lato PA/B2B) arrivano nel testo `lab` o nell'XML `ricevuta_xml` ma **non** sono mappati esplicitamente nel sorgente. Nel porting conviene gestirli (enum esteso in `ftpa_notification.esito`) parsando `ricevuta_xml`.

### 9.3 Flusso completo invio

```
1. Genera XML (FatturaPaBuilder) → file IT{piva}_{hex5}.xml
2. (opz.) Conferma/edit manuale XML
3. POST /invia_documento (base64)
4. Salva seriale in ftpa_invoice.ftpa_serial; stato = "inviata"
5. Sposta XML in attive/storage/{yyMM}/
6. Polling periodico POST /lista_documenti_attivi_inviati → aggiorna ftpa_sent_document + ftpa_notification
7. (opz.) POST /leggi_documento_attivo per riscaricare l'XML
```

Un **cron job** PHP dovrebbe eseguire il polling (passo 6) e aggiornare gli stati.

---

## 10. Sistema a plugin (export e import)

Esistono **due sistemi distinti** di plugin: da mantenere separati.

### 10.1 Plugin di EXPORT (`IFtpaPluginable`)

Hook unico, invocato **dopo** la costruzione dell'oggetto XML in memoria e **prima** della serializzazione. Il documento viene **mutato in-place**.

Interfaccia originale (VB):

```vb
Sub Process(idDoc As Integer, doc As FatturaElettronicaType,
            fbConnString As String, rowAssociation As Dictionary(Of Integer, Integer))
```

Equivalente PHP:

```php
interface IFtpaPlugin {
    /**
     * @param int    $idDoc         id fattura
     * @param object $doc           DOM/oggetto fattura (mutabile in-place)
     * @param array  $rowAssociation  map: riga sorgente => NumeroLinea XML
     */
    public function isProcessable(int $idDoc, object $doc, array $ctx): bool;
    public function process(int $idDoc, object $doc, array $rowAssociation, array $ctx): void;
}
```

> Il parametro `rowAssociation` (mappa riga sorgente → `NumeroLinea` XML) è **essenziale** per i plugin che agiscono riga-per-riga: va preservato.

**Differenze rispetto all'originale da migliorare nel porting:**

- L'originale carica DLL da `Plugins\` con reflection, **ordine implicito** (filesystem), **nessuna priorità**, **nessun isolamento errori**, plugin attivato per **P.IVA hardcoded**.
- In VTE: registrare i plugin in **configurazione/tabella** con `priorità` esplicita, abilitazione **per azienda/account**, e gestione errori `fail-fast` configurabile.

**Elenco plugin originali (regole cliente) — riferimento funzionale:**

| Plugin | Attivazione | Effetto |
|---|---|---|
| BolloVirtuale | categoria cliente `BA` | `DatiBollo` (2€) + causale assolvimento |
| CupCig | se `CUPCIG` valorizzato | sostituisce `DatiOrdineAcquisto` con CUP/CIG |
| ExtraUE | cliente non UE | `IdCodice = OO99999999999` |
| Maklaus.Esteri | cliente estero | rimuove `PECDestinatario` |
| IbanDefault | sempre | causale + IBAN su scadenze bonifico |
| RifOrdClifor | se esistono ordini | `IdDocumento` = `RIFORDCLIFOR` |
| RifOrdCliforDDT | se esistono DDT | crea `DatiOrdineAcquisto` dal DDT |
| GianiGrazzi.Default | sempre | come RifOrdClifor, eccezione se manca rif |
| Davini.Default / Sidal | sempre / P.IVA specifica | causali destinazione, ADG, codici articolo |
| Etruria.Default | sempre | `UnitaMisura` da articolo |
| AresYSL | P.IVA + causale "STH" | codici articolo cliente, fasi, DOA |
| SorCarrefour / SorMetro / SorCoopNO | P.IVA specifiche GDO | causali, `DatiRicezione`, codici articolo/UM |
| IlGiglio.LeoFrance | P.IVA specifica | normalizza codici articolo |

> Nota: `FillCupCigFromFattura` è **logica core** (non plugin) e va sempre eseguita; è distinta dal plugin `CupCig` che ha comportamento diverso.

### 10.2 Plugin di IMPORT (`IFtpaImportPlugin`)

Per importare fatture **attive da XML esterno** verso il documento gestionale. Due hook:

```php
interface IFtpaImportPlugin {
    public function processFatturaElettronica(object $fattura, array $ctx): void;        // prima del mapping
    public function processFatturaMercurio(object $fattura, object $documento, array $ctx): void; // dopo
}
```

Esempio originale (AllPrint): righe "etic*" con qty>10 → dividere qty per 1000 e moltiplicare il prezzo ×1000.

### 10.3 Pipeline PHP consigliata

```php
final class PluginPipeline {
    /** @var IFtpaPlugin[] ordinati per priorità */
    private array $plugins = [];

    public function register(IFtpaPlugin $p, int $priority = 100): void { /* ... */ }

    public function processAll(int $idDoc, object $doc, array $rowAssoc, array $ctx): void {
        foreach ($this->sorted() as $p) {
            if ($p->isProcessable($idDoc, $doc, $ctx)) {
                $p->process($idDoc, $doc, $rowAssoc, $ctx);
            }
        }
    }
}
```

---

## 11. Integrazione nel CRM VTE/vtenext

### 11.1 Modulo custom

Creare un modulo `FatturaElettronica` sotto `modules/`:

```
modules/FatturaElettronica/
├── FatturaElettronica.php          # CRMEntity (entità modulo, se serve persistere fatture FE)
├── actions/                        # azioni (GenerateXml, Send, Sync, ViewPassive)
├── views/                          # viste (lista, dettaglio)
├── models/                         # Builder, Validator, SdiClient, Repository
├── resources/
│   ├── xsd/                        # XSD AdE 1.2.1
│   └── xsl/                        # XSL anteprima + notifiche
├── cron/                           # SyncSdiCron.php (polling stati)
└── settings/                       # pannello configurazione (credenziali, cedente)
```

### 11.2 Collegamento alle entità VTE

- Usare il modulo **Invoice** (Fatture) esistente di vtenext come sorgente dati.
- `ftpa_invoice.crm_invoice_id` → `vtiger_invoice.invoiceid`.
- `ftpa_clifor.crm_account_id` → `vtiger_account.accountid` (i campi P.IVA/CF/PEC/codice SdI vanno aggiunti come campi custom sull'Account o su un blocco dedicato).
- Sfruttare il sistema di **permessi** e **profili** di vtenext per limitare chi può inviare/visualizzare.

### 11.3 Configurazione

Pannello impostazioni del modulo (mappato su `ftpa_settings` + `ftpa_cedente`):
- endpoint, username, password provider;
- dati anagrafici emittente (CedentePrestatore);
- regime fiscale, esigibilità default, flag (CONAI, bollo automatico, esteri, esclusione DOA).

### 11.4 Cron / scheduler

vtenext ha un sistema di **CronTasks**: registrare un task per:
1. polling `/lista_documenti_attivi_inviati` → aggiorna stati/notifiche;
2. polling `/lista_documenti_passivi` → scarica nuove fatture passive.

---

## 12. Sicurezza

- **Credenziali provider**: cifrare `ep_password` (es. `openssl` con chiave applicativa fuori dal DB) — mai in chiaro.
- **Trasporto**: solo HTTPS verso il provider; validare certificati.
- **File XML/PDF**: storage fuori dalla webroot, accesso mediato da controller con permessi.
- **Input XML passive**: parsing con `libxml_disable_entity_loader` / `LIBXML_NONET` per prevenire **XXE**.
- **Firma digitale** (se necessaria, formato `CAdES .p7m` o `XAdES`): usare `phpseclib`/`xmlseclibs` o un servizio HSM esterno. Nel sorgente originale la firma **non** è implementata (gestita a valle dal provider/SdI); replicare solo se il flusso lo richiede.
- **Multi-tenant**: isolare i dati per `company_id` su tutte le query.

---

## 13. Struttura del progetto PHP

Se realizzato come libreria autonoma (riusabile dal modulo VTE):

```
src/
├── Config/            Settings, CedenteTemplate
├── Domain/            Invoice, InvoiceLine, Riepilogo, Pagamento, Allegato, RelatedDoc
├── Repository/        InvoiceRepository (PDO MySQL)
├── Xml/
│   ├── FatturaPaBuilder.php      # DOMDocument FatturaPA 1.2.1
│   ├── AutofatturaBuilder.php
│   └── NumberFormatter.php       # formattazione invariante
├── Validation/        FatturaPaValidator (XSD + regole business)
├── Naming/            FileNamingService (IT{piva}_{hex5})
├── Sdi/               SdiClient (Guzzle), DTO risposte, ResponseParser
├── Plugin/            IFtpaPlugin, IFtpaImportPlugin, PluginPipeline, plugin concreti
├── Notification/      NotificationMapper (RC/MC/NS/...)
└── Storage/           FileStorage (filesystem)
resources/
├── xsd/   xsl/
tests/
composer.json
```

`composer.json` (estratto):

```json
{
  "require": {
    "php": ">=8.1",
    "ext-dom": "*", "ext-libxml": "*", "ext-curl": "*",
    "ext-openssl": "*", "ext-mbstring": "*", "ext-pdo": "*",
    "guzzlehttp/guzzle": "^7.8",
    "monolog/monolog": "^3.0"
  },
  "require-dev": { "phpunit/phpunit": "^10" }
}
```

---

## 14. Roadmap di implementazione

| Fase | Attività | Output |
|---|---|---|
| **1. Fondamenta** | Schema MySQL (sez. 4), entità Domain, repository | DB + DTO |
| **2. Builder XML** | `FatturaPaBuilder` (header/body), `NumberFormatter` | XML 1.2.1 valido |
| **3. Validazione** | XSD + regole business (sez. 6) | Validator |
| **4. Naming/Storage** | Progressivo hex, cartelle storage | File su disco |
| **5. Client SdI** | `SdiClient` Guzzle, DTO risposte, parser tollerante | Invio/lettura |
| **6. Stati/Notifiche** | Mapping notifiche, cron polling | Aggiornamento stati |
| **7. Plugin** | `PluginPipeline` + plugin essenziali (Bollo, CupCig, Iban, Esteri) | Estendibilità |
| **8. Passive** | Lista/lettura/import passive | Ciclo passivo |
| **9. Autofatture** | `AutofatturaBuilder` TD16–TD27 | Reverse charge |
| **10. Integrazione VTE** | Modulo, viste, settings, permessi, cron | Modulo vtenext |
| **11. Test** | Unit + fatture reali in ambiente **test** SdI | Collaudo |

**Suggerimento:** iniziare replicando `WriteXMLPA` (la logica core di generazione) con una fattura di esempio, validarla contro l'XSD, poi aggiungere client e plugin.

---

## 15. Appendice — riferimenti file sorgente

Mappa tra logica e file originali (per consultazione durante il porting):

| Logica | File sorgente | Righe chiave |
|---|---|---|
| Generazione XML attiva | `Pantarei.Mercurio.FatturaPA/FatturaPA12/Utilities.vb` | `WriteXMLPA` 924–1941 |
| Orchestrazione | `.../Utilities.vb` | `CreaXMLFatturaElettronica` 47–102 |
| Autofattura | `.../Utilities.vb` | `WriteXMLAutoFattura` 2025–2332 |
| Modello XSD (classi) | `.../FatturaPA12/FatturaPA12.vb` | tipi/serializzazione |
| Validazione XSD | `.../FatturaPA12/FatturaPA12Validate.vb` | — |
| Helper globali | `.../InternalGlobal.vb` | CUP/CIG 94–113 |
| Funzioni globali / path / DB | `FtPA/Globale.vb` | 1–372 |
| Client REST | `FtPA/WS/Client.vb` | Basic auth 219–226; invio 234–286 |
| DTO risposte JSON | `FtPA/WS/ClassiJson/*.vb` | — |
| Config/credenziali/progressivo | `FtPA/Repositories/FtpaSettings.vb` | progressivo 206–237 |
| Notifiche SdI | `FtPA/Repositories/NotificaInvio.vb` | mapping 20–33 |
| Flusso UI invio | `FtPA/Forms/Tab/tabFattAtt.vb` | 222–490 |
| Monitoraggio inviate | `FtPA/Forms/Tab/tabFattAttInviate.vb` | 100–136 |
| Fatture passive | `FtPA/Forms/Tab/tabFattPass.vb` | 130–336 |
| Plugin export (interfaccia) | `Pantarei.Mercurio.FatturaPA/Plugin/IFtpaPluginable.vb` | 3–13 |
| Plugin export (loader) | `.../Plugin/PluginLoader.vb` | 13–117 |
| Plugin import (interfaccia) | `Plugins.Import/Ftpa.Plugins.Import.Core/IFtpaImportPlugin.cs` | 9–14 |
| Plugin import (loader) | `.../ImportPluginLoader.cs` | 14–149 |
| XSD ufficiali | `FtPA/XSD/Versione_1.2.1.xsd` | — |
| XSL anteprima | `FtPA/XSL/fatturaPA_v1.2.1.xsl`, `fatturaPA_AS.xsl` | — |

### Tabelle Firebird Mercurio → tabelle MySQL VTE (sintesi)

| Firebird (origine) | MySQL (target) |
|---|---|
| `FTDOC` / `FRDOC` | `ftpa_invoice` / `ftpa_invoice_line` |
| `FCONTABRIVA` | `ftpa_invoice_riepilogo` |
| `FSCADENZ`+`FPAGAM`+`FBANCHE` | `ftpa_invoice_pagamento` |
| `FCLIFOR` | `ftpa_clifor` |
| `FCODIVA` | `ftpa_cod_iva` |
| `FTDOCFPADOA` | `ftpa_invoice_related_doc` |
| `FTPASETTING` | `ftpa_settings` |
| `config{PIVA}.12.xml` (template) | `ftpa_cedente` |
| `FCONTAB`/`FCONTABRIVA` (autofatt) | `ftpa_invoice` (TD16–27) + riepiloghi |
| LiteDB `FTIMPORTATE` | `ftpa_invoice_passive` |
| API provider (stati/notifiche) | `ftpa_sent_document` / `ftpa_notification` |

---

> **Documento generato dall'analisi del codice sorgente VB.NET/C# del plugin FtPA per Mercurio.** Le costanti (endpoint, formati, formule, naming) sono estratte direttamente dal sorgente. Verificare con il provider (Pantarei/ftpa.it) eventuali aggiornamenti di API e con l'Agenzia delle Entrate la versione corrente dello schema FatturaPA prima del go-live.



