mod(regional,italy): Updates to Italian Localization

fixes:
removed cleanup_files from italy/utils
removed extract_doc_number from italy/utils
added country filter to italian localization patch
replaced dict assignment with dot operator in prepare_invoice
correcions in e-invoice xml following changes in prepare_invoice
added setup_report in patch
added section for e-invoicing custom fields in company
added Italy in test_company
diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py
index d7dbddc..22bf84e 100644
--- a/erpnext/regional/italy/__init__.py
+++ b/erpnext/regional/italy/__init__.py
@@ -30,3 +30,34 @@
     "N6-Inversione Contabile",
     "N7-IVA assolta in altro stato UE"
 ]
+
+mode_of_payment_codes = [
+    "MP01-Contanti",
+    "MP02-Assegno",
+    "MP03-Assegno circolare",
+    "MP04-Contanti presso Tesoreria",
+    "MP05-Bonifico",
+    "MP06-Vaglia cambiario",
+    "MP07-Bollettino bancario",
+    "MP08-Carta di pagamento",
+    "MP09-RID",
+    "MP10-RID utenze",
+    "MP11-RID veloce",
+    "MP12-RIBA",
+    "MP13-MAV",
+    "MP14-Quietanza erario",
+    "MP15-Giroconto su conti di contabilità speciale",
+    "MP16-Domiciliazione bancaria",
+    "MP17-Domiciliazione postale",
+    "MP18-Bollettino di c/c postale",
+    "MP19-SEPA Direct Debit",
+    "MP20-SEPA Direct Debit CORE",
+    "MP21-SEPA Direct Debit B2B",
+    "MP22-Trattenuta su somme già riscosse"
+]
+
+vat_collectability_options = [
+    "I-Immediata",
+    "D-Differita",
+    "S-Scissione dei Pagamenti"
+]
diff --git a/erpnext/regional/italy/e-invoice.xml b/erpnext/regional/italy/e-invoice.xml
new file mode 100644
index 0000000..84b7fff
--- /dev/null
+++ b/erpnext/regional/italy/e-invoice.xml
@@ -0,0 +1,208 @@
+{%- macro format_float(value) -%}
+{{ "%.2f" % value|abs }}
+{%- endmacro -%}
+
+{%- macro render_address(address) %}
+<Indirizzo>{{ address.address_line1 }}</Indirizzo>
+<CAP>{{ address.pincode }}</CAP>
+<Comune>{{ address.city }}</Comune>
+{%- if address.state %}
+<Provincia>{{ address.state }}</Provincia>
+{%- endif %}
+<Nazione>{{ address.country_code|upper }}</Nazione>
+{%- endmacro %}
+
+{%- macro render_discount_or_margin(item) -%}
+{%- if item.discount_percentage > 0.0 or item.margin_type %}
+<ScontoMaggiorazione>
+  {%- if item.discount_percentage > 0.0 %}
+  <Tipo>SC</Tipo>
+  <Percentuale>{{ format_float(item.discount_percentage) }}</Percentuale>
+  {%- endif %}
+  {%- if item.margin_rate_or_amount > 0.0 -%}
+    <Tipo>MG</Tipo>
+    {%- if item.margin_type == "Percentage" -%}
+      <Percentuale>{{ format_float(item.margin_rate_or_amount) }}</Percentuale>
+    {%- elif item.margin_type == "Amount" -%}
+      <Importo>{{ format_float(item.margin_rate_or_amount) }}</Importo>
+    {%- endif -%}
+  {%- endif %}
+</ScontoMaggiorazione>
+{%- endif -%}
+{%- endmacro -%}
+
+<?xml version='1.0' encoding='UTF-8'?>
+<p:FatturaElettronica 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" 
+  versione="{{ doc.transmission_format_code }}" 
+  xsi:schemaLocation="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2 http://www.fatturapa.gov.it/export/fatturazione/sdi/fatturapa/v1.2/Schema_del_file_xml_FatturaPA_versione_1.2.xsd">
+  <FatturaElettronicaHeader>
+    <DatiTrasmissione>
+      <IdTrasmittente>
+        <IdPaese>{{ doc.company_address_data.country_code|upper or "IT" }}</IdPaese>
+        <IdCodice>{{ doc.company_fiscal_code or doc.company_tax_id | replace("IT","") }}</IdCodice>
+      </IdTrasmittente>
+      <ProgressivoInvio>{{ doc.progressive_number }}</ProgressivoInvio>
+      <FormatoTrasmissione>{{ doc.transmission_format_code }}</FormatoTrasmissione>
+      <CodiceDestinatario>{{ doc.customer_data.recipient_code }}</CodiceDestinatario>
+      {% if doc.company_data.phone or doc.company_data.email -%}
+      <ContattiTrasmittente>
+        {% if doc.company_data.phone -%}<Telefono>{{ doc.company_data.phone }}</Telefono>{%- endif %}
+        {% if doc.company_data.email -%}<Email>{{ doc.company_data.email }}</Email>{%- endif %}
+      </ContattiTrasmittente>
+      {% endif -%}
+    </DatiTrasmissione>
+    <CedentePrestatore>
+      <DatiAnagrafici>
+        <IdFiscaleIVA>
+          <IdPaese>{{ doc.company_address_data.country_code|upper or "IT"}}</IdPaese>
+          <IdCodice>{{ doc.company_tax_id | replace("IT","") }}</IdCodice>
+        </IdFiscaleIVA>
+        {%- if doc.company_fiscal_code %}
+        <CodiceFiscale>{{ doc.company_fiscal_code }}</CodiceFiscale>
+        {%- endif %}
+        <Anagrafica>
+          <Denominazione>{{ doc.company }}</Denominazione>
+        </Anagrafica>
+        <RegimeFiscale>{{ doc.company_fiscal_regime.split("-")[0] }}</RegimeFiscale>
+      </DatiAnagrafici>
+      <Sede>
+      {{ render_address(doc.company_address_data) }}
+      </Sede>
+      {%- if doc.company_data.registration_number %}
+      <IscrizioneREA>
+        <Ufficio>{{ doc.company_data.registrar_office_province }}</Ufficio>
+        <NumeroREA>{{ doc.company_data.registration_number }}</NumeroREA>
+        {%- if doc.company_data.share_capital_amount %}
+        <CapitaleSociale>{{ format_float(doc.company_data.share_capital_amount) }}</CapitaleSociale>
+        {%- endif %}
+        {%- if doc.company_data.no_of_members %}
+        <SocioUnico>{{ doc.company_data.no_of_members.split("-")[0] }}</SocioUnico>
+        {%- endif %}
+        {%- if doc.company_data.liquidation_state %}
+        <StatoLiquidazione>{{ doc.company_data.liquidation_state.split("-")[0] }}</StatoLiquidazione>
+        {%- endif %}
+      </IscrizioneREA>
+      {%- endif %}
+    </CedentePrestatore>
+    <CessionarioCommittente>
+      <DatiAnagrafici>
+        {%- if doc.customer_data.customer_type == "Individual" %}
+          <CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
+          <Anagrafica>
+            <Nome>{{ doc.customer_data.first_name }}</Nome>
+            <Cognome>{{ doc.customer_data.last_name }}</Cognome>
+          </Anagrafica>
+        {%- else %}
+          {%- if doc.customer_data.is_public_administration %}
+          <CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
+          {%- else %}
+          <IdFiscaleIVA>
+            <IdPaese>{{ doc.customer_address_data.country_code|upper or "IT" }}</IdPaese>
+            <IdCodice>{{ doc.tax_id | replace("IT","") }}</IdCodice>
+          </IdFiscaleIVA>
+          {%- endif %}
+          <Anagrafica>
+            <Denominazione>{{ doc.customer_name }}</Denominazione>
+          </Anagrafica>
+        {%- endif %}
+      </DatiAnagrafici>
+      {%- if doc.customer_address_data %}
+      <Sede>
+      {{ render_address(doc.customer_address_data) }}
+      </Sede>
+      {%- endif %}
+    </CessionarioCommittente>
+  </FatturaElettronicaHeader>
+  <FatturaElettronicaBody>
+    <DatiGenerali>
+      <DatiGeneraliDocumento>
+        <TipoDocumento>{{ doc.type_of_document }}</TipoDocumento>
+        <Divisa>EUR</Divisa>
+        <Data>{{ doc.posting_date }}</Data>
+        <Numero>{{ doc.unamended_name }}</Numero>
+        {%- if doc.stamp_duty %}
+        <DatiBollo>
+          <BolloVirtuale>SI</BolloVirtuale>
+          <ImportoBollo>{{ format_float(doc.stamp_duty) }}</ImportoBollo>
+        </DatiBollo>
+        {%- endif %}
+        <ImportoTotaleDocumento>{{ format_float(doc.grand_total) }}</ImportoTotaleDocumento>
+        <Causale>VENDITA</Causale>
+      </DatiGeneraliDocumento>
+      {%- if doc.po_no %}
+      <DatiOrdineAcquisto>
+        <IdDocumento>{{ doc.po_no }}</IdDocumento>
+        {%- if doc.po_date %}
+        <Data>{{ doc.po_date }}</Data>
+        {%- endif %}
+      </DatiOrdineAcquisto>
+      {%- endif %}
+      {%- if doc.is_return and doc.return_against_unamended %}
+      <DatiFattureCollegate>
+        <IdDocumento>{{ doc.return_against_unamended }}</IdDocumento>
+      </DatiFattureCollegate>
+      {%- endif %}
+      {%- if doc.shipping_address_data %}
+      <DatiTrasporto>
+      <IndirizzoResa>
+        {{ render_address(doc.shipping_address_data) }}
+      </IndirizzoResa>
+      </DatiTrasporto>
+      {%- endif %}
+    </DatiGenerali>
+    <DatiBeniServizi>
+      {%- for item in doc.items %}
+      <DettaglioLinee>
+        <NumeroLinea>{{ item.idx }}</NumeroLinea>
+        <CodiceArticolo>
+          <CodiceTipo>CODICE</CodiceTipo>
+          <CodiceValore>{{ item.item_code }}</CodiceValore>
+        </CodiceArticolo>
+        <Descrizione>{{ item.description or item.item_name }}</Descrizione>
+        <Quantita>{{ format_float(item.qty) }}</Quantita>
+        <UnitaMisura>{{ item.stock_uom }}</UnitaMisura>
+        <PrezzoUnitario>{{ format_float(item.price_list_rate or item.rate) }}</PrezzoUnitario>
+        {{ render_discount_or_margin(item) }}
+        <PrezzoTotale>{{ format_float(item.amount) }}</PrezzoTotale>
+        <AliquotaIVA>{{ format_float(item.tax_rate) }}</AliquotaIVA>
+        {%- if item.tax_exemption_reason %}
+        <Natura>{{ item.tax_exemption_reason.split("-")[0] }}</Natura>
+        {%- endif %}
+      </DettaglioLinee>
+      {%- endfor %}
+      {%- for tax, data in doc.tax_data.items() %}
+      <DatiRiepilogo>
+        <AliquotaIVA>{{ format_float(tax|flt) }}</AliquotaIVA>
+        {%- if data.tax_exemption_reason %}
+        <Natura>{{ data.tax_exemption_reason.split("-")[0] }}</Natura>
+        {%- endif %}
+        <ImponibileImporto>{{ format_float(data.taxable_amount) }}</ImponibileImporto>
+        <Imposta>{{ format_float(data.tax_amount) }}</Imposta>
+        <EsigibilitaIVA>{{ doc.vat_collectability.split("-")[0] }}</EsigibilitaIVA>
+        {%- if data.tax_exemption_law %}
+        <RiferimentoNormativo>{{ data.tax_exemption_law }}</RiferimentoNormativo>
+        {%- endif %}
+      </DatiRiepilogo>
+      {%- endfor %}
+    </DatiBeniServizi>
+    {%- if doc.payment_schedule %}
+    <DatiPagamento>
+      {%- if payment_schedule|length > 1 %}
+      <CondizioniPagamento>TP01</CondizioniPagamento>
+      {%- else %}
+      <CondizioniPagamento>TP02</CondizioniPagamento>
+      {%- endif %}
+      {%- for payment_term in doc.payment_schedule %}
+      <DettaglioPagamento>
+        <ModalitaPagamento>{{ payment_term.mode_of_payment_code.split("-")[0] }}</ModalitaPagamento>
+        <DataScadenzaPagamento>{{ payment_term.due_date }}</DataScadenzaPagamento>
+        <ImportoPagamento>{{ format_float(payment_term.payment_amount) }}</ImportoPagamento>
+        {%- if payment_term.bank_account_iban %}<IBAN>{{ payment_term.bank_account_iban }}</IBAN>{%- endif %}
+      </DettaglioPagamento>
+      {%- endfor %}
+    </DatiPagamento>
+    {%- endif %}
+  </FatturaElettronicaBody>
+</p:FatturaElettronica>
diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py
index 1931fdc..b4ab26f 100644
--- a/erpnext/regional/italy/setup.py
+++ b/erpnext/regional/italy/setup.py
@@ -5,33 +5,152 @@
 from __future__ import unicode_literals
 
 import frappe
+from frappe import _
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons
+from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options
 
 def setup(company=None, patch=True):
-	make_custom_fields()
+    make_custom_fields()
+    setup_report()
 
 def make_custom_fields(update=True):
-    fiscal_code_field = dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='tax_id', print_hide=1)
+    invoice_item_fields = [
+        dict(fieldname='tax_rate', label='Tax Rate',
+			fieldtype='Float', insert_after='description',
+			print_hide=1, hidden=1, read_only=1),
+		dict(fieldname='tax_amount', label='Tax Amount',
+			fieldtype='Currency', insert_after='tax_rate',
+			print_hide=1, hidden=1, read_only=1, options="currency"),
+		dict(fieldname='total_amount', label='Total Amount',
+			fieldtype='Currency', insert_after='tax_amount',
+			print_hide=1, hidden=1, read_only=1, options="currency")
+    ]
+
     custom_fields = {
         'Company': [
-            fiscal_code_field,
+            dict(fieldname='sb_e_invoicing', label='E-Invoicing',
+			    fieldtype='Section Break', insert_after='date_of_establishment', print_hide=1),
             dict(fieldname='fiscal_regime', label='Fiscal Regime',
-			    fieldtype='Select', insert_after='fiscal_code', print_hide=1,                
-                options="\n".join(map(lambda x: x.decode('utf-8'), fiscal_regimes)))
-        ],
-        'Customer': [
-            fiscal_code_field,
-            dict(fieldname='recipient_code', label='Recipient Code',
-                fieldtype='Data', insert_after='fiscal_code', print_hide=1, default="0000000"),
-            dict(fieldname='pec', label='Recipient PEC',
-                fieldtype='Data', insert_after='fiscal_code', print_hide=1)
+			    fieldtype='Select', insert_after='sb_e_invoicing', print_hide=1,
+                options="\n".join(map(lambda x: x.decode('utf-8'), fiscal_regimes))),
+            dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='fiscal_regime', print_hide=1,
+                description=_("Applicable if the company is an Individual or a Proprietorship")),
+            dict(fieldname='vat_collectability', label='VAT Collectability',
+			    fieldtype='Select', insert_after='fiscal_code', print_hide=1,
+                options="\n".join(map(lambda x: x.decode('utf-8'), vat_collectability_options))),
+            dict(fieldname='cb_e_invoicing1', fieldtype='Column Break', insert_after='vat_collectability', print_hide=1),
+            dict(fieldname='registrar_office_province', label='Province of the Registrar Office',
+			    fieldtype='Data', insert_after='cb_e_invoicing1', print_hide=1, length=2),
+            dict(fieldname='registration_number', label='Registration Number',
+			    fieldtype='Data', insert_after='registrar_office_province', print_hide=1, length=20),
+            dict(fieldname='share_capital_amount', label='Share Capital',
+			    fieldtype='Currency', insert_after='registration_number', print_hide=1,
+                description=_('Applicable if the company is SpA, SApA or SRL')),
+            dict(fieldname='no_of_members', label='No of Members',
+			    fieldtype='Select', insert_after='share_capital_amount', print_hide=1,
+                options="\nSU-Socio Unico\nSM-Piu Soci", description=_("Applicable if the company is a limited liability company")),
+            dict(fieldname='liquidation_state', label='Liquidation State',
+			    fieldtype='Select', insert_after='no_of_members', print_hide=1,
+                options="\nLS-In Liquidazione\nLN-Non in Liquidazione")
         ],
         'Sales Taxes and Charges': [
             dict(fieldname='tax_exemption_reason', label='Tax Exemption Reason',
+                fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
+                depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0',
+                options="\n" + "\n".join(map(lambda x: x.decode('utf-8'), tax_exemption_reasons))),
+            dict(fieldname='tax_exemption_law', label='Tax Exempt Under',
+                fieldtype='Text', insert_after='tax_exemption_reason', print_hide=1,
+                depends_on='eval:doc.charge_type!="Actual" && doc.rate==0.0')
+        ],
+        'Customer': [
+            dict(fieldname='fiscal_code', label='Fiscal Code', fieldtype='Data', insert_after='tax_id', print_hide=1),
+            dict(fieldname='recipient_code', label='Recipient Code',
+                fieldtype='Data', insert_after='fiscal_code', print_hide=1, default="0000000"),
+            dict(fieldname='pec', label='Recipient PEC',
+                fieldtype='Data', insert_after='fiscal_code', print_hide=1),
+            dict(fieldname='is_public_administration', label='Is Public Administration',
+			    fieldtype='Check', insert_after='is_internal_customer', print_hide=1,
+                description=_("Set this if the customer is a Public Administration company."),
+                depends_on='eval:doc.customer_type=="Company"'),
+            dict(fieldname='first_name', label='First Name', fieldtype='Data',
+                insert_after='salutation', print_hide=1, depends_on='eval:doc.customer_type!="Company"'),
+            dict(fieldname='last_name', label='Last Name', fieldtype='Data',
+                insert_after='first_name', print_hide=1, depends_on='eval:doc.customer_type!="Company"')
+        ],
+        'Mode of Payment': [
+            dict(fieldname='mode_of_payment_code', label='Code',
 		    fieldtype='Select', insert_after='included_in_print_rate', print_hide=1,
-            options="\n".join(map(lambda x: x.decode('utf-8'), tax_exemption_reasons)))
+            options="\n".join(map(lambda x: x.decode('utf-8'), mode_of_payment_codes)))
+        ],
+        'Payment Schedule': [
+            dict(fieldname='mode_of_payment_code', label='Code',
+                fieldtype='Select', insert_after='mode_of_payment', print_hide=1,
+                options="\n".join(map(lambda x: x.decode('utf-8'), mode_of_payment_codes)),
+                fetch_from="mode_of_payment.mode_of_payment_code", read_only=1),
+            dict(fieldname='bank_account', label='Bank Account',
+                fieldtype='Link', insert_after='mode_of_payment_code', print_hide=1,
+                options="Bank Account"),
+            dict(fieldname='bank_account_name', label='Bank Account Name',
+                fieldtype='Data', insert_after='bank_account', print_hide=1,
+                fetch_from="bank_account.account_name", read_only=1),
+            dict(fieldname='bank_account_no', label='Bank Account No',
+                fieldtype='Data', insert_after='bank_account_name', print_hide=1,
+                fetch_from="bank_account.bank_account_no", read_only=1),
+            dict(fieldname='bank_account_iban', label='IBAN',
+                fieldtype='Data', insert_after='bank_account_name', print_hide=1,
+                fetch_from="bank_account.iban", read_only=1),
+        ],
+        "Sales Invoice": [
+            dict(fieldname='vat_collectability', label='VAT Collectability',
+			    fieldtype='Select', insert_after='taxes_and_charges', print_hide=1,
+                options="\n".join(map(lambda x: x.decode('utf-8'), vat_collectability_options)),
+                fetch_from="company.vat_collectability"),
+            dict(fieldname='sb_e_invoicing_reference', label='E-Invoicing',
+			    fieldtype='Section Break', insert_after='pos_total_qty', print_hide=1),
+            dict(fieldname='company_tax_id', label='Company Tax ID',
+			    fieldtype='Data', insert_after='sb_e_invoicing_reference', print_hide=1, read_only=1,
+                fetch_from="company.tax_id"),
+            dict(fieldname='company_fiscal_code', label='Company Fiscal Code',
+			    fieldtype='Data', insert_after='company_tax_id', print_hide=1, read_only=1,
+                fetch_from="company.fiscal_code"),
+            dict(fieldname='company_fiscal_regime', label='Company Fiscal Regime',
+			    fieldtype='Data', insert_after='company_fiscal_code', print_hide=1, read_only=1,
+                fetch_from="company.fiscal_regime"),
+            dict(fieldname='cb_e_invoicing_reference', fieldtype='Column Break',
+                insert_after='company_fiscal_regime', print_hide=1),
+            dict(fieldname='customer_fiscal_code', label='Customer Fiscal Code',
+                fieldtype='Data', insert_after='cb_e_invoicing_reference', read_only=1,
+                fetch_from="customer.fiscal_code"),
+        ],
+        'Purchase Invoice Item': invoice_item_fields,
+		'Sales Order Item': invoice_item_fields,
+		'Delivery Note Item': invoice_item_fields,
+        'Sales Invoice Item': invoice_item_fields,
+		'Quotation Item': invoice_item_fields,
+		'Purchase Order Item': invoice_item_fields,
+		'Purchase Receipt Item': invoice_item_fields,
+		'Supplier Quotation Item': invoice_item_fields,
+        'Address': [
+            dict(fieldname='country_code', label='Country Code',
+			    fieldtype='Data', insert_after='country', print_hide=1, read_only=1,
+                fetch_from="country.code")
         ]
     }
 
     create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=update)
+
+def setup_report():
+    report_name = 'Electronic Invoice Register'
+
+    frappe.db.sql(""" update `tabReport` set disabled = 0 where
+        name = %s """, report_name)
+
+    if not frappe.db.get_value('Custom Role', dict(report=report_name)):
+        frappe.get_doc(dict(
+            doctype='Custom Role',
+            report=report_name,
+            roles= [
+                dict(role='Accounts User'),
+                dict(role='Accounts Manager')
+            ]
+        )).insert()
diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py
new file mode 100644
index 0000000..69f7b46
--- /dev/null
+++ b/erpnext/regional/italy/utils.py
@@ -0,0 +1,251 @@
+import frappe, json, os
+from frappe.utils import flt
+from erpnext.controllers.taxes_and_totals import get_itemised_tax
+from frappe import _
+from frappe.utils.file_manager import save_file, remove_file
+from frappe.desk.form.load import get_attachments
+
+
+def update_itemised_tax_data(doc):
+	if not doc.taxes: return
+
+	itemised_tax = get_itemised_tax(doc.taxes)
+
+	for row in doc.items:
+		tax_rate = 0.0
+		if itemised_tax.get(row.item_code):
+			tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
+
+		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+		row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
+		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+
+@frappe.whitelist()
+def export_invoices(filters=None):
+	saved_xmls = []
+
+	invoices = frappe.get_all("Sales Invoice", filters=get_conditions(filters), fields=["*"])
+
+	for invoice in invoices:
+		attachments = get_e_invoice_attachments(invoice)
+		saved_xmls += [attachment.file_name for attachment in attachments]
+
+	zip_filename = "{0}-einvoices.zip".format(frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
+
+	download_zip(saved_xmls, zip_filename)
+
+
+@frappe.whitelist()
+def prepare_invoice(invoice, progressive_number):
+	#set company information
+	company = frappe.get_doc("Company", invoice.company)
+
+	invoice.progressive_number = progressive_number
+	invoice.unamended_name = get_unamended_name(invoice)
+	invoice.company_data = company
+	company_address = frappe.get_doc("Address", invoice.company_address)
+	invoice.company_address_data = company_address
+
+	#Set invoice type
+	if invoice.is_return and invoice.return_against:
+		invoice.type_of_document = "TD04" #Credit Note (Nota di Credito)
+		invoice.return_against_unamended =  get_unamended_name(frappe.get_doc("Sales Invoice", invoice.return_against))
+	else:
+		invoice.type_of_document = "TD01" #Sales Invoice (Fattura)
+
+	#set customer information
+	invoice.customer_data = frappe.get_doc("Customer", invoice.customer)
+	customer_address = frappe.get_doc("Address", invoice.customer_address)
+	invoice.customer_address_data = customer_address
+
+	if invoice.shipping_address_name:
+		invoice.shipping_address_data = frappe.get_doc("Address", invoice.shipping_address_name)
+
+	if invoice.customer_data.is_public_administration:
+		invoice.transmission_format_code = "FPA12"
+	else:
+		invoice.transmission_format_code = "FPR12"
+
+	tax_data = get_invoice_summary(invoice.items, invoice.taxes)
+	invoice.tax_data = tax_data
+
+	#Check if stamp duty (Bollo) of 2 EUR exists.
+	stamp_duty_charge_row = next((tax for tax in invoice.taxes if tax.charge_type == _("Actual") and tax.tax_amount == 2.0 ), None)
+	if stamp_duty_charge_row:
+		invoice.stamp_duty = stamp_duty_charge_row.tax_amount
+
+	for item in invoice.items:
+		if item.tax_rate == 0.0:
+			item.tax_exemption_reason = tax_data["0.0"]["tax_exemption_reason"]
+
+	return invoice
+
+def get_conditions(filters):
+	filters = json.loads(filters)
+
+	conditions = {"docstatus": 1}
+
+	if filters.get("company"): conditions["company"] = filters["company"]
+	if filters.get("customer"): conditions["customer"] =  filters["customer"]
+
+	if filters.get("from_date"): conditions["posting_date"] = (">=", filters["from_date"])
+	if filters.get("to_date"): conditions["posting_date"] = ("<=", filters["to_date"])
+
+	if filters.get("from_date") and filters.get("to_date"):
+		conditions["posting_date"] = ("between", [filters.get("from_date"), filters.get("to_date")])
+
+	return conditions
+
+#TODO: Use function from frappe once PR #6853 is merged.
+def download_zip(files, output_filename):
+	from zipfile import ZipFile
+
+	input_files = [frappe.get_site_path('private', 'files', filename) for filename in files]
+	output_path = frappe.get_site_path('private', 'files', output_filename)
+
+	with ZipFile(output_path, 'w') as output_zip:
+		for input_file in input_files:
+			output_zip.write(input_file, arcname=os.path.basename(input_file))
+
+	with open(output_path, 'rb') as fileobj:
+		filedata = fileobj.read()
+
+	frappe.local.response.filename = output_filename
+	frappe.local.response.filecontent = filedata
+	frappe.local.response.type = "download"
+
+def get_invoice_summary(items, taxes):
+	summary_data = frappe._dict()
+	for tax in taxes:
+		#Include only VAT charges.
+		if tax.charge_type == "Actual":
+			continue
+
+		#Check item tax rates if tax rate is zero.
+		if tax.rate == 0:
+			for item in items:
+				item_tax_rate = json.loads(item.item_tax_rate)
+				if tax.account_head in item_tax_rate:
+					key = str(item_tax_rate[tax.account_head])
+					summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0, "tax_exemption_reason": "", "tax_exemption_law": ""})
+					summary_data[key]["tax_amount"] += item.tax_amount
+					summary_data[key]["taxable_amount"] += item.net_amount
+					if key == "0.0":
+						summary_data[key]["tax_exemption_reason"] = tax.tax_exemption_reason
+						summary_data[key]["tax_exemption_law"] = tax.tax_exemption_law
+
+			if summary_data == {}: #Implies that Zero VAT has not been set on any item.
+				summary_data.setdefault("0.0", {"tax_amount": 0.0, "taxable_amount": tax.total,
+					"tax_exemption_reason": tax.tax_exemption_reason, "tax_exemption_law": tax.tax_exemption_law})
+
+		else:
+			item_wise_tax_detail = json.loads(tax.item_wise_tax_detail)
+			for rate_item in [tax_item for tax_item in item_wise_tax_detail.items() if tax_item[1][0] == tax.rate]:
+				key = str(tax.rate)
+				if not summary_data.get(key): summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0})
+				summary_data[key]["tax_amount"] += rate_item[1][1]
+				summary_data[key]["taxable_amount"] += sum([item.net_amount for item in items if item.item_code == rate_item[0]])
+
+	return summary_data
+
+#Preflight for successful e-invoice export.
+def sales_invoice_validate(doc):
+	#Validate company
+	if not doc.company_address:
+		frappe.throw(_("Please set an Address on the Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+	else:
+		validate_address(doc.company_address, "Company")
+
+	if not doc.company_tax_id and not doc.company_fiscal_code:
+		frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
+
+	#Validate customer details
+	customer_type, is_public_administration = frappe.db.get_value("Customer", doc.customer, ["customer_type", "is_public_administration"])
+	if customer_type == _("Individual"):
+		if not doc.customer_fiscal_code:
+			frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+	else:
+		if is_public_administration:
+			if not doc.customer_fiscal_code:
+				frappe.throw(_("Please set Fiscal Code for the public administration '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+		else:
+			if not doc.tax_id:
+				frappe.throw(_("Please set Tax ID for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
+
+	if not doc.customer_address:
+	 	frappe.throw(_("Please set the Customer Address"), title=_("E-Invoicing Information Missing"))
+	else:
+		validate_address(doc.customer_address, "Customer")
+
+	if not len(doc.taxes):
+		frappe.throw(_("Please set at least one row in the Taxes and Charges Table"), title=_("E-Invoicing Information Missing"))
+	else:
+		for row in doc.taxes:
+			if row.rate == 0 and row.tax_amount == 0 and not row.tax_exemption_reason:
+				frappe.throw(_("Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges".format(row.idx)),
+					title=_("E-Invoicing Information Missing"))
+
+
+#Ensure payment details are valid for e-invoice.
+def sales_invoice_on_submit(doc):
+	#Validate payment details
+	if not len(doc.payment_schedule):
+		frappe.throw(_("Please set the Payment Schedule"), title=_("E-Invoicing Information Missing"))
+	else:
+		for schedule in doc.payment_schedule:
+			if not schedule.mode_of_payment:
+				frappe.throw(_("Row {0}: Please set the Mode of Payment in Payment Schedule".format(schedule.idx)),
+					title=_("E-Invoicing Information Missing"))
+
+	prepare_and_attach_invoice(doc)
+
+def prepare_and_attach_invoice(doc):
+	progressive_name, progressive_number = get_progressive_name_and_number(doc)
+
+	invoice = prepare_invoice(doc, progressive_number)
+	invoice_xml = frappe.render_template('erpnext/regional/italy/e-invoice.xml', context={"doc": invoice}, is_path=True)
+
+	xml_filename = progressive_name + ".xml"
+	save_file(xml_filename, invoice_xml, dt=doc.doctype, dn=doc.name, is_private=True)
+
+#Delete e-invoice attachment on cancel.
+def sales_invoice_on_cancel(doc):
+	for attachment in get_e_invoice_attachments(doc):
+		remove_file(attachment.name, attached_to_doctype=doc.doctype, attached_to_name=doc.name)
+
+def get_e_invoice_attachments(invoice):
+	out = []
+	attachments = get_attachments(invoice.doctype, invoice.name)
+	company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id
+
+	for attachment in attachments:
+		if attachment.file_name.startswith(company_tax_id) and attachment.file_name.endswith(".xml"):
+			out.append(attachment)
+
+	return out
+
+def validate_address(address_name, address_context):
+	pincode, city = frappe.db.get_value("Address", address_name, ["pincode", "city"])
+	if not pincode:
+		frappe.throw(_("Please set pin code on %s Address" % address_context), title=_("E-Invoicing Information Missing"))
+	if not city:
+		frappe.throw(_("Please set city on %s Address" % address_context), title=_("E-Invoicing Information Missing"))
+
+
+def get_unamended_name(doc):
+	attributes = ["naming_series", "amended_from"]
+	for attribute in attributes:
+		if not hasattr(doc, attribute):
+			return doc.name
+
+	if doc.amended_from:
+		return "-".join(doc.name.split("-")[:-1])
+	else:
+		return doc.name
+
+def get_progressive_name_and_number(doc):
+	company_tax_id = doc.company_tax_id if doc.company_tax_id.startswith("IT") else "IT" + doc.company_tax_id
+	progressive_name = frappe.model.naming.make_autoname(company_tax_id + "_.#####")
+	progressive_number = progressive_name.split("_")[1]
+
+	return progressive_name, progressive_number
\ No newline at end of file