Modalità di emergenza
Cosa fare quando il PEM non può emettere documenti commerciali: annotare le vendite sul registro di emergenza e trasmettere a fine giornata la chiusura di emergenza, con i riepiloghi IVA.
Quando si usa
Se il PEM entra nello stato di Fuori servizio, questo non può emettere documenti commerciali. Finché il PEM è fuori servizio, le chiamate …/fiscale/invio/* (vendita, annullo, reso, chiusura) rispondono 409. L'esercente annota i documenti commerciali sul registro di emergenza dei corrispettivi fuori dal sistema. A fine giornata ne trasmette gli aggregati con la chiusura di emergenza.
La modalità di emergenza si applica anche quando il tuo gestionale non può connettersi alle API per mancata connessione internet. In questo caso, la comunicazione di Fuori servizio può avvenire successivamente al ripristino della connessione.
Entrare in emergenza
Il tuo software porta il PEM fuori servizio con PUT …/fiscale/stato, indicando stato 01, una motivazione e le note, fino a 255 caratteri. Stato e motivazione sono stringhe di due cifre: "01", non 1.
Note sempre obbligatorie
note con ogni Fuori servizio, qualunque sia la motivazione, e con la dismissione; senza, risponde 422.curl -X PUT \
https://pem-staging.billyssw.it/api/pem/ASVS-241232/fiscale/stato \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{ "stato": "01", "motivazione": "01", "note": "Guasto alla rete del punto vendita" }'| Motivazione | Significato |
|---|---|
| 01 | Problemi hardware. |
| 02 | Problemi software. |
| 03 | Problemi di comunicazione tra PEM e PEL. |
| 08 | Mancata integrità. |
| 09 | Messa in magazzino: periodo di inattività. |
| 10 | Altro. |
Gli altri codici non si inviano: 04 e 05 riguardano la dismissione, che ha un endpoint dedicato; 06 e 07 li attribuisce il sistema al passaggio In servizio. Al rientro si invia solo { "stato": "03" }, senza motivazione.
Il PEM può anche finire fuori servizio da solo:
- se un documento non raggiunge il PEL entro 12 ore, con motivazione
03. Prima di cambiare stato il PEM chiude la giornata e conserva il Journal firmato, che trasmette al rientro in servizio; - se la catena degli hash risulta compromessa, con motivazione
08.
Chiusura di emergenza
A fine giornata l'esercente riporta i totali del registro di emergenza: un riepilogo per aliquota, natura o ventilazione, più i totali dei pagamenti. È ammessa solo con il PEM Fuori servizio ed è riservata all'esercente titolare: un operatore riceve 403.
curl -X POST \
https://pem-staging.billyssw.it/api/pem/ASVS-241232/fiscale/emergenza/chiusura \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "3f1c2a9e-8b7d-4c1e-9a2f-5d6e7f8a9b0c",
"n_doc_commerciali": 12,
"riepiloghi": [
{ "iva": "22", "importo_parziale": "100.00", "imposta": "22.00" },
{ "iva": "10", "importo_parziale": "50.50", "imposta": "5.05" }
],
"totali": {
"pagato_contanti": "100.00",
"pagato_elettronico": "77.55",
"sconto_a_pagare": "0.00"
}
}'{
"success": true,
"dati": {
"id_presa_in_carico": "12345",
"n_doc_commerciali": 12
}
}Regole principali
| Campo | Regola |
|---|---|
| idempotency_key | UUID, obbligatorio, uno per chiusura. Vedi sotto, Idempotenza. |
| n_doc_commerciali | Numero di documenti annotati sul registro nella giornata, almeno 1. |
| riepiloghi | Da 1 a 40 righe. Ognuna indica esattamente uno tra iva (4, 5, 10, 22), natura (N1–N6) e ventilazione_iva (SI). La stessa aliquota o natura non si ripete, salvo con codice_attivita diversi. |
| riepiloghi[].importo_parziale | Imponibile, al netto dell'IVA (al lordo con ventilazione_iva). È già al netto di resi, annulli, beni in sospeso e non riscossi, ma comprende gli omaggi. Può essere negativo. |
| riepiloghi[].imposta | Obbligatoria se e solo se la riga ha iva. Deve valere importo_parziale × aliquota, con una tolleranza di ±0,01. |
| riepiloghi[].totale_ammontare_resi, totale_ammontare_annulli, beni_in_sospeso, non_riscosso_* | Facoltativi, mai negativi. Li si indica a parte, senza toglierli una seconda volta dall'imponibile. |
| riepiloghi[].codice_attivita | Facoltativo, deve essere uno dei codici Ateco configurati sul PEM. |
| totali | pagato_contanti, pagato_elettronico e sconto_a_pagare sono obbligatori. ticket (pagato_ticket, numero_ticket) è facoltativo: senza opzione buoni pasto è un normale pagamento; con l'opzione attiva nella giornata richiede un riepilogo al 10% il cui lordo copra i ticket, e il riferimento normativo TICKET lo attribuisce il sistema (rif_normativo non si invia). |
Gli importi sono stringhe decimali con al massimo due decimali ("100.00"), mai numeri in virgola mobile. Se il registro riporta importi IVA inclusa, scorpora l'IVA prima dell'invio: imponibile = lordo × 100 / (100 + aliquota), arrotondato al centesimo, e l'imposta per differenza. Così imponibile più imposta danno il lordo esatto: ad esempio 122,00 € al 22% diventano 100.00 + 22.00. Le righe con natura o ventilazione non hanno imposta. L'elenco completo dei campi è nel riferimento REST.
Lo stesso calcolo in codice, a partire dalle vendite del registro con aliquota IVA, importi IVA inclusa in centesimi. Lavorare in centesimi interi evita gli errori della virgola mobile.
// $vendite: [['iva' => '22', 'lordo' => 12200], ...] — lordo in centesimi
$lordi = [];
foreach ($vendite as $v) {
$lordi[$v['iva']] = ($lordi[$v['iva']] ?? 0) + $v['lordo'];
}
$euro = fn (int $c): string =>
($c < 0 ? '-' : '') . sprintf('%d.%02d', intdiv(abs($c), 100), abs($c) % 100);
$riepiloghi = [];
foreach ($lordi as $iva => $lordo) {
$imponibile = (int) round($lordo * 100 / (100 + (int) $iva)); // al centesimo
$riepiloghi[] = [
'iva' => (string) $iva,
'importo_parziale' => $euro($imponibile),
'imposta' => $euro($lordo - $imponibile), // per differenza
];
}// vendite: [{ iva: '22', lordo: 12200 }, ...] — lordo in centesimi
const lordi = {};
for (const v of vendite) lordi[v.iva] = (lordi[v.iva] ?? 0) + v.lordo;
const euro = (c) =>
(c < 0 ? '-' : '') + `${Math.trunc(Math.abs(c) / 100)}.${String(Math.abs(c) % 100).padStart(2, '0')}`;
// arrotondamento al centesimo, metà lontano dallo zero anche per i negativi
const arrotonda = (x) => Math.sign(x) * Math.round(Math.abs(x));
const riepiloghi = Object.entries(lordi).map(([iva, lordo]) => {
const imponibile = arrotonda((lordo * 100) / (100 + Number(iva)));
return {
iva,
importo_parziale: euro(imponibile),
imposta: euro(lordo - imponibile), // per differenza
};
});Le righe con natura (N1–N6) e quelle con ventilazione si costruiscono a parte: importo_parziale è il lordo e non c'è imposta.
Giornata e idempotenza
- La chiusura vale sempre per la giornata in cui viene inviata, e non c'è un campo data. Inviala lo stesso giorno delle vendite che riepiloga: inviata dopo la mezzanotte, viene registrata sulla giornata nuova e non c'è modo di attribuirla a quella precedente.
- Una giornata ammette più chiusure di emergenza: ogni
idempotency_keynuova è una chiusura e una trasmissione distinte, anche con dati identici. - Ripetere la chiamata con la stessa chiave è sicuro. Se la chiusura è già trasmessa ricevi lo stesso
id_presa_in_caricoe non parte un nuovo invio. Se l'invio era fallito, il PEM lo ritenta con i dati salvati, compresa la giornata originale. - La stessa chiave con dati diversi viene rifiutata con
422sul campoidempotency_key. Per dati nuovi, genera una chiave nuova.
Seguire la trasmissione
Ogni chiusura di emergenza trasmessa diventa una richiesta di tipo CORRISPETTIVI, come quella ordinaria. Si segue con lo scope fiscale:read:
GET …/fiscale/richieste?tipo=CORRISPETTIVIelenca le trasmissioni all'AdE;- con l'
iddi quell'elenco, non con l'id_presa_in_carico,richieste/stato/{id}ne aggiorna lo stato erichieste/esito/{id}scarica l'esito XML.