Skip to content

Commit

Permalink
New fields, and basic support for fattura elettronica semplificata
Browse files Browse the repository at this point in the history
  • Loading branch information
spanezz committed Mar 7, 2019
1 parent ed6a555 commit 7ba5854
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 11 deletions.
40 changes: 29 additions & 11 deletions a38/fattura.py
Expand Up @@ -16,6 +16,7 @@


NS = "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2"
NS10 = "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.0"
NS_SIG = "http://www.w3.org/2000/09/xmldsig#"


Expand Down Expand Up @@ -579,10 +580,15 @@ class Allegati(models.Model):
attachment = fields.Base64BinaryField()


class DatiVeicoli(models.Model):
data = fields.DateField()
totale_percorso = fields.StringField(max_length=15)


class FatturaElettronicaBody(models.Model):
dati_generali = DatiGenerali
dati_beni_servizi = DatiBeniServizi
# dati_veicoli =
dati_veicoli = models.ModelField(DatiVeicoli, null=True)
dati_pagamento = fields.ModelListField(DatiPagamento, null=True)
allegati = fields.ModelListField(Allegati, null=True)

Expand Down Expand Up @@ -707,19 +713,31 @@ def get_versione(self):


def auto_from_etree(root):
expected_tag = "{{{}}}FatturaElettronica".format(NS)
if root.tag != expected_tag:
raise RuntimeError("Root element {} is not {}".format(root.tag, expected_tag))
from .fattura_semplificata import NS10, FatturaElettronicaSemplificata
tagname_ordinaria = "{{{}}}FatturaElettronica".format(NS)
tagname_semplificata = "{{{}}}FatturaElettronicaSemplificata".format(NS10)

versione = root.attrib.get("versione", None)
if versione is None:
raise RuntimeError("root element {} misses attribute 'versione'".format(root.tag))

if versione == "FPR12":
res = FatturaPrivati12()
elif versione == "FPA12":
res = FatturaPA12()
if root.tag == tagname_ordinaria:
if versione is None:
raise RuntimeError("root element {} misses attribute 'versione'".format(root.tag))
if versione == "FPR12":
res = FatturaPrivati12()
elif versione == "FPA12":
res = FatturaPA12()
else:
raise RuntimeError("unsupported versione {}".format(versione))
elif root.tag == tagname_semplificata:
if versione is None:
raise RuntimeError("root element {} misses attribute 'versione'".format(root.tag))
if versione == "FSM10":
res = FatturaElettronicaSemplificata()
else:
raise RuntimeError("unsupported versione {}".format(versione))
else:
raise RuntimeError("unsupported versione {}".format(versione))
raise RuntimeError("Root element {} is neither {} nor {}".format(
root.tag, tagname_ordinaria, tagname_semplificata))

res.from_etree(root)
return res
160 changes: 160 additions & 0 deletions a38/fattura_semplificata.py
@@ -0,0 +1,160 @@
from .fattura import (
IdTrasmittente, IdFiscaleIVA, Sede, StabileOrganizzazione, IscrizioneREA,
FullNameMixin, Allegati
)
from . import models
from . import fields

NS10 = "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.0"


class DatiTrasmissione(models.Model):
id_trasmittente = IdTrasmittente
progressivo_invio = fields.ProgressivoInvioField()
formato_trasmissione = fields.StringField(length=5, choices=("FSM10",))
codice_destinatario = fields.StringField(null=True, min_length=6, max_length=7, default="0000000")
pec_destinatario = fields.StringField(null=True, min_length=8, max_length=256, xmltag="PECDestinatario")


class RappresentanteFiscale(FullNameMixin, models.Model):
id_fiscale_iva = IdFiscaleIVA
denominazione = fields.StringField(max_length=80, null=True)
nome = fields.StringField(max_length=60, null=True)
cognome = fields.StringField(max_length=60, null=True)


class CedentePrestatore(FullNameMixin, models.Model):
id_fiscale_iva = IdFiscaleIVA
codice_fiscale = fields.StringField(min_length=11, max_length=16, null=True)
denominazione = fields.StringField(max_length=80, null=True)
nome = fields.StringField(max_length=60, null=True)
cognome = fields.StringField(max_length=60, null=True)
sede = Sede
stabile_organizzazione = fields.ModelField(StabileOrganizzazione, null=True)
rappresentante_fiscale = models.ModelField(RappresentanteFiscale, null=True)
iscrizione_rea = fields.ModelField(IscrizioneREA, null=True)
regime_fiscale = fields.StringField(
length=4, choices=("RF01", "RF02", "RF04", "RF05", "RF06", "RF07",
"RF08", "RF09", "RF10", "RF11", "RF12", "RF13",
"RF14", "RF15", "RF16", "RF17", "RF18", "RF19"))


class IdentificativiFiscali(models.Model):
id_fiscale_iva = IdFiscaleIVA
codice_fiscale = fields.StringField(min_length=11, max_length=16, null=True)


class AltriDatiIdentificativi(FullNameMixin, models.Model):
denominazione = fields.StringField(max_length=80, null=True)
nome = fields.StringField(max_length=60, null=True)
cognome = fields.StringField(max_length=60, null=True)
sede = Sede
stabile_organizzazione = fields.ModelField(StabileOrganizzazione, null=True)
rappresentante_fiscale = models.ModelField(RappresentanteFiscale, null=True)


class CessionarioCommittente(models.Model):
identificativi_fiscali = IdentificativiFiscali
altri_dati_identificativi = AltriDatiIdentificativi


class FatturaElettronicaHeader(models.Model):
dati_trasmissione = DatiTrasmissione
cedente_prestatore = CedentePrestatore
cessionario_committente = CessionarioCommittente
soggetto_emittente = fields.StringField(length=2, choices=("CC", "TZ"), null=True)


class DatiGeneraliDocumento(models.Model):
tipo_documento = fields.StringField(length=4, choices=("TD07", "TD08", "TD09"))
divisa = fields.StringField()
data = fields.DateField()
numero = fields.StringField(max_length=20)


class DatiFatturaRettificata(models.Model):
numero_fr = fields.StringField(max_length=20, xmltag="NumeroFR")
data_fr = fields.DateField(xmltag="DataFR")
elementi_rettificati = fields.StringField(max_length=1000)


class DatiGenerali(models.Model):
dati_generali_documento = DatiGeneraliDocumento
dati_fattura_rettificata = fields.ModelField(DatiFatturaRettificata, null=True)


class DatiIVA(models.Model):
imposta = fields.DecimalField(max_length=15)
aliquota = fields.DecimalField(max_length=6)


class DatiBeniServizi(models.Model):
descrizione = fields.StringField(max_length=1000)
importo = fields.DecimalField(max_length=15)
dati_iva = DatiIVA
natura = fields.StringField(length=2, null=True, choices=("N1", "N2", "N3", "N4", "N5", "N6", "N7"))
riferimento_normativo = fields.StringField(max_length=100, null=True)


class FatturaElettronicaBody(models.Model):
dati_generali = DatiGenerali
dati_beni_servizi = fields.ModelListField(DatiBeniServizi)
allegati = fields.ModelListField(Allegati, null=True)


class FatturaElettronicaSemplificata(models.Model):
"""
Fattura elettronica semplificata
"""
__xmlns__ = NS10
fattura_elettronica_header = FatturaElettronicaHeader
fattura_elettronica_body = fields.ModelListField(FatturaElettronicaBody, min_num=1)

def __init__(self, *args, **kw):
super().__init__(*args, **kw)
self.fattura_elettronica_header.dati_trasmissione.formato_trasmissione = self.get_versione()

def get_versione(self):
return "FSM10"

def get_xmlattrs(self):
return {"versione": self.get_versione()}

def validate_model(self, validation):
super().validate_model(validation)
if self.get_versione() != self.fattura_elettronica_header.dati_trasmissione.formato_trasmissione:
validation.add_error(
self.fattura_elettronica_header.dati_trasmissione._meta["formato_trasmissione"],
"formato_trasmissione should be {}".format(self.get_versione()),
code="00428")

def to_xml(self, builder):
with builder.element(self.get_xmltag(), **self.get_xmlattrs()) as b:
with b.override_default_namespace(None) as b1:
for name, field in self._meta.items():
field.to_xml(b1, getattr(self, name))

def build_etree(self, lxml=False):
"""
Build and return an ElementTree with the fattura in XML format
"""
self.fattura_elettronica_header.dati_trasmissione.formato_trasmissione = self.get_versione()
if lxml:
from a38.builder import LXMLBuilder
builder = LXMLBuilder()
else:
from a38.builder import Builder
builder = Builder()
builder.default_namespace = NS10
self.to_xml(builder)
return builder.get_tree()

def from_etree(self, el):
versione = el.attrib.get("versione", None)
if versione is None:
raise RuntimeError("root element {} misses attribute 'versione'".format(el.tag))

if versione != self.get_versione():
raise RuntimeError("root element versione is {} instead of {}".format(versione, self.get_versione()))

return super().from_etree(el)
5 changes: 5 additions & 0 deletions doc/README.md
Expand Up @@ -7,3 +7,8 @@ Normativa: <https://www.fatturapa.gov.it/export/fatturazione/it/normativa/f-2.ht
Assocons validator: <https://fatturazione-elettronica-pa.assocons.it/validazione-fattura-elettronica.html>

Agenzia delle Entrate validator: <https://www.fatturapa.gov.it/export/fatturazione/it/sperimentazione.htm>

# TODO

Get documentation from <https://www.agenziaentrate.gov.it/wps/content/nsilib/nsi/schede/comunicazioni/fatture+e+corrispettivi/fatture+e+corrispettivi+st/st+invio+di+fatturazione+elettronica>
(it covers Fattura Elettronica Semplificata)

0 comments on commit 7ba5854

Please sign in to comment.