{ "info": { "author": "Enrico Zini", "author_email": "enrico@truelite.it", "bugtrack_url": null, "classifiers": [], "description": "# Python A38\n\nLibrary to generate Italian Fattura Elettronica from Python.\n\nThis library implements a declarative data model similar to Django models, that\nis designed to describe, validate, serialize and parse Italian Fattura\nElettronica data.\n\nOnly part of the specification is implemented, with more added as needs will\narise. You are welcome to implement the missing pieces you need and send a pull\nrequest: the idea is to have a good, free (as in freedom) library to make\nbilling in Italy with Python easier for everyone.\n\nThe library can generate various kinds of fatture that pass validation, and can\nparse all the example XML files distributed by\n[fatturapa.gov.it](https://www.fatturapa.gov.it/export/fatturazione/it/normativa/f-2.htm)\n\n\n## Dependencies\n\nRequired: dateutil, pytz, asn1crypto, and the python3 standard library.\n\nOptional:\n * yapf for formatting `a38tool python` output\n * lxml for rendering to HTML\n * the wkhtmltopdf command for rendering to PDF\n * requests for downloading CA certificates for signature verification\n\n\n## `a38tool` script\n\nA simple command line wrapper to the library functions is available as `a38tool`:\n\n```text\n$ a38tool --help\nusage: a38tool [-h] [--verbose] [--debug]\n {json,xml,python,diff,validate,html,pdf,update_capath} ...\n\nHandle fattura elettronica files\n\npositional arguments:\n {json,xml,python,diff,validate,html,pdf,update_capath}\n actions\n json output a fattura in JSON\n xml output a fattura in XML\n python output a fattura as Python code\n diff show the difference between two fatture\n validate validate the contents of a fattura\n html render a Fattura as HTML using a .xslt stylesheet\n pdf render a Fattura as PDF using a .xslt stylesheet\n update_capath create/update an openssl CApath with CA certificates\n that can be used to validate digital signatures\n\noptional arguments:\n -h, --help show this help message and exit\n --verbose, -v verbose output\n --debug debug output\n```\n\nSee [a38tool.md](a38tool.md) for more details.\n\n\n\n## Example code\n\n```py\nimport a38.fattura as a38\nfrom a38.validation import Validation\nimport datetime\nimport sys\n\ncedente_prestatore = a38.CedentePrestatore(\n a38.DatiAnagraficiCedentePrestatore(\n a38.IdFiscaleIVA(\"IT\", \"01234567890\"),\n codice_fiscale=\"NTNBLN22C23A123U\",\n anagrafica=a38.Anagrafica(denominazione=\"Test User\"),\n regime_fiscale=\"RF01\",\n ),\n a38.Sede(indirizzo=\"via Monferrato\", numero_civico=\"1\", cap=\"50100\", comune=\"Firenze\", provincia=\"FI\", nazione=\"IT\"),\n iscrizione_rea=a38.IscrizioneREA(\n ufficio=\"FI\",\n numero_rea=\"123456\",\n stato_liquidazione=\"LN\",\n ),\n contatti=a38.Contatti(email=\"local_part@pec_domain.it\"),\n)\n\ncessionario_committente = a38.CessionarioCommittente(\n a38.DatiAnagraficiCessionarioCommittente(\n a38.IdFiscaleIVA(\"IT\", \"76543210987\"),\n anagrafica=a38.Anagrafica(denominazione=\"A Company SRL\"),\n ),\n a38.Sede(indirizzo=\"via Langhe\", numero_civico=\"1\", cap=\"50142\", comune=\"Firenze\", provincia=\"FI\", nazione=\"IT\"),\n)\n\nbill_number = 1\n\nf = a38.FatturaPrivati12()\nf.fattura_elettronica_header.dati_trasmissione.id_trasmittente = a38.IdTrasmittente(\"IT\", \"10293847561\")\nf.fattura_elettronica_header.dati_trasmissione.codice_destinatario = \"FUFUFUF\"\nf.fattura_elettronica_header.cedente_prestatore = cedente_prestatore\nf.fattura_elettronica_header.cessionario_committente = cessionario_committente\n\nbody = f.fattura_elettronica_body[0]\nbody.dati_generali.dati_generali_documento = a38.DatiGeneraliDocumento(\n tipo_documento=\"TD01\",\n divisa=\"EUR\",\n data=datetime.date.today(),\n numero=bill_number,\n causale=[\"Test billing\"],\n)\n\nbody.dati_beni_servizi.add_dettaglio_linee(\n descrizione=\"Test item\", quantita=2, unita_misura=\"kg\",\n prezzo_unitario=\"25.50\", aliquota_iva=\"22.00\")\n\nbody.dati_beni_servizi.add_dettaglio_linee(\n descrizione=\"Other item\", quantita=1, unita_misura=\"kg\",\n prezzo_unitario=\"15.50\", aliquota_iva=\"22.00\")\n\nbody.dati_beni_servizi.build_dati_riepilogo()\nbody.build_importo_totale_documento()\n\nres = Validation()\nf.validate(res)\nif res.warnings:\n for w in res.warnings:\n print(str(w), file=sys.stderr)\nif res.errors:\n for e in res.errors:\n print(str(e), file=sys.stderr)\n\nfilename = \"{}{}_{:05d}.xml\".format(\n f.fattura_elettronica_header.cedente_prestatore.dati_anagrafici.id_fiscale_iva.id_paese,\n f.fattura_elettronica_header.cedente_prestatore.dati_anagrafici.id_fiscale_iva.id_codice,\n bill_number)\n\ntree = f.build_etree()\nwith open(filename, \"wb\") as out:\n tree.write(out)\n```\n\n\n# Digital signatures\n\nDigital signatures on Firma Elettronica are\n[CAdES](https://en.wikipedia.org/wiki/CAdES_(computing)) signatures.\n\nopenssl cal verify the signatures, but not yet generate them. A patch to sign\nwith CAdES [has been recently merged](https://github.com/openssl/openssl/commit/e85d19c68e7fb3302410bd72d434793e5c0c23a0)\nbut not yet released as of 2019-02-26.\n\n## Downloading CA certificates\n\nCA certificates for validating digital certificates are\n[distributed by the EU in XML format](https://ec.europa.eu/cefdigital/wiki/display/cefdigital/esignature).\nSee also [the AGID page about it](https://www.agid.gov.it/it/piattaforme/firma-elettronica-qualificata/certificati).\n\nThere is a [Trusted List Browser](https://webgate.ec.europa.eu/tl-browser/) but\napparently no way of getting a simple bundle of certificates useable by\nopenssl.\n\n`a38tool` has basic features to download and parse CA certificate information,\nand maintain a CA certificate directory:\n\n```\na38tool update_capath certdir/ --remove-old\n```\n\nNo particular effort is made to validate the downloaded certificates, besides\nthe standard HTTPS checks performed by the [requests\nlibrary](http://docs.python-requests.org/en/master/).\n\n## Verifying signed `.p7m` files\n\nOnce you have a CA certificate directory, verifying signed p7m files is quite\nstraightforward:\n\n```\nopenssl cms -verify -in tests/data/test.txt.p7m -inform der -CApath certs/\n```\n\n\n# Useful links\n\nXSLT stylesheets for displaying fatture:\n\n* From fatturapa.gov.it for\n [privati](https://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/fatturaordinaria_v1.2.xsl)\n and\n [PA](https://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/fatturapa_v1.2.xsl)\n* From [AssoSoftware](http://www.assosoftware.it/allegati/assoinvoice/FoglioStileAssoSoftware.zip)\n\n\n# Copyright\n\nCopyright 2019 Truelite S.r.l.\n\nThis software is released under the Apache License 2.0\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Truelite/python-a38/", "keywords": "", "license": "https://www.apache.org/licenses/LICENSE-2.0.html", "maintainer": "", "maintainer_email": "", "name": "a38", "package_url": "https://pypi.org/project/a38/", "platform": "", "project_url": "https://pypi.org/project/a38/", "project_urls": { "Homepage": "https://github.com/Truelite/python-a38/" }, "release_url": "https://pypi.org/project/a38/0.1.2/", "requires_dist": [ "asn1crypto", "python-dateutil", "pytz", "requests ; extra == 'cacerts'", "yapf ; extra == 'formatted_python'", "lxml ; extra == 'html'" ], "requires_python": "", "summary": "parse and generate Italian Fattura Elettronica", "version": "0.1.2" }, "last_serial": 5342034, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "fc893b48d697b26fd577008255a910b9", "sha256": "2c1e510503db340d09df1017d43453f16f71403910f6b7b55330d6c45976deb1" }, "downloads": -1, "filename": "a38-0.1.1-py3-none-any.whl", "has_sig": false, "md5_digest": "fc893b48d697b26fd577008255a910b9", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32635, "upload_time": "2019-03-07T13:40:46", "url": "https://files.pythonhosted.org/packages/ad/88/da1083fb9dfa1d0cf649fb6b45f3941bbe4cdfc7ff53a14680cbf5c14256/a38-0.1.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0070e5aeda6612831c29e1d5e1a6839d", "sha256": "7677516fc1a9d8717c294c7e96a8e71b75c2326eca2a66e4ccbaa5d165fa30fc" }, "downloads": -1, "filename": "a38-0.1.1.tar.gz", "has_sig": false, "md5_digest": "0070e5aeda6612831c29e1d5e1a6839d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 40081, "upload_time": "2019-03-07T13:40:48", "url": "https://files.pythonhosted.org/packages/bd/f6/31d13e3bc1f982ff14d2bc438c5dd20907fd3e8c0411b819fa809297a51c/a38-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "3b9d83cff8b716641289bb8d336349ee", "sha256": "571f93f66fea2264ed4eaf11ea1892918fb52976324990c486d53f60ac8d3206" }, "downloads": -1, "filename": "a38-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3b9d83cff8b716641289bb8d336349ee", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32700, "upload_time": "2019-05-31T09:50:58", "url": "https://files.pythonhosted.org/packages/54/6a/cb0fc79092912c6daf93d80ac2d9108a0a47163d8a5c5f8b95cece4d57a2/a38-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e96da1e090baa64963af21cadb90315b", "sha256": "feed1023d86c5f836f326859bfaf8dfee0a71732697bc8adbdd29a1ad8aaf2ef" }, "downloads": -1, "filename": "a38-0.1.2.tar.gz", "has_sig": false, "md5_digest": "e96da1e090baa64963af21cadb90315b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43482, "upload_time": "2019-05-31T09:51:01", "url": "https://files.pythonhosted.org/packages/bc/04/698641ad32f9e8ea3454c9cedd5a92bd77081bc2c0427d70b9e0048f4d17/a38-0.1.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3b9d83cff8b716641289bb8d336349ee", "sha256": "571f93f66fea2264ed4eaf11ea1892918fb52976324990c486d53f60ac8d3206" }, "downloads": -1, "filename": "a38-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "3b9d83cff8b716641289bb8d336349ee", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32700, "upload_time": "2019-05-31T09:50:58", "url": "https://files.pythonhosted.org/packages/54/6a/cb0fc79092912c6daf93d80ac2d9108a0a47163d8a5c5f8b95cece4d57a2/a38-0.1.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e96da1e090baa64963af21cadb90315b", "sha256": "feed1023d86c5f836f326859bfaf8dfee0a71732697bc8adbdd29a1ad8aaf2ef" }, "downloads": -1, "filename": "a38-0.1.2.tar.gz", "has_sig": false, "md5_digest": "e96da1e090baa64963af21cadb90315b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43482, "upload_time": "2019-05-31T09:51:01", "url": "https://files.pythonhosted.org/packages/bc/04/698641ad32f9e8ea3454c9cedd5a92bd77081bc2c0427d70b9e0048f4d17/a38-0.1.2.tar.gz" } ] }