[REF] l10n_it_edi_sdicoop: merge l10n_it_edi_sdicoop into l10n_it_edi

At present there are two methods of sending/receiving electronic
invoices in odoo.

The first, contained within l10n_it_edi, sends and receives the xml via
a verified email known as 'PEC mail', with use of a fetchmail server. It
is no longer the prefered method.

The second, contained within l10n_it_edi_sdicoop, sends and receives the
xml via a proxy hosted by odoo. The proxy server in turncommunicates
with the 'sdicoop' system. This has become the prefered method.

The 'PEC mail' method of sending/receiving invoices will no longer be
supported. The contents of l10n_it_edi_sdicoop will be merged into
l10n_it_edi and the fields, views, methods, and dependencies
exclussively associated with the old 'PEC mail' are deleted.

There are many instances in which a function was provided in l10n_it_edi
that worked through the fetchmail edi system, and was then overriden in
l10n_it_edi_sdicoop. In this case the content  of the overriden method
is replaced with the content of the method that has overriden it.

The manifest has been updated to reflect the slight change in the file
structure (the addition of cron.xml and res_config_settings_views.xml to
the data and views folders respectively). The fetchmail dependency has
also been removed from the manifest.

The tests have been updated to match the restructuring of the module.

The i18n files have been altered to include the relevant translations
from the l10n_it_edi_sdicoop module, and the translation terms that are
made redundant are deleted.

closes odoo/odoo#82581

Related: odoo/upgrade#3828
Signed-off-by: Daniel Kosky (dako) <dako@odoo.com>
This commit is contained in:
dbkosky
2022-09-05 14:49:23 +02:00
committed by dbkosky
parent aa07576098
commit ece6371229
31 changed files with 1073 additions and 2270 deletions
+6 -1
View File
@@ -7,7 +7,10 @@
'version': '0.3',
'depends': [
'l10n_it',
'account_edi'
# Although account_edi is a dependency of account_edi_proxy_client,
# it is here because it's in the auto-install
'account_edi',
'account_edi_proxy_client',
],
'auto_install': ['l10n_it', 'account_edi'],
'author': 'Odoo',
@@ -21,6 +24,8 @@ E-invoice implementation
'data/account_edi_data.xml',
'data/invoice_it_template.xml',
'data/invoice_it_simplified_template.xml',
'data/ir_cron.xml',
'views/res_config_settings_views.xml',
'views/l10n_it_view.xml',
],
'demo': [
+242 -9
View File
@@ -10,6 +10,7 @@ msgstr ""
"PO-Revision-Date: 2022-03-29 14:31+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
@@ -89,6 +90,24 @@ msgid ""
"'Scissione dei pagamenti' is not compatible with exoneration of kind 'N6'"
msgstr "'Scissione dei pagamenti' non è compatibile con l'esenzione di tipo 'N6'"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">\n"
" Fattura Elettronica mode\n"
" </span>"
msgstr "<span class=\"o_form_label\">Modalitá Fattura Elettronica</span>"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "<span class=\"o_form_label\">Allow Odoo to process invoices</span>"
msgstr "<span class=\"o_form_label\">Permetti ad Odoo di inviare le fatture</span>"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "A Demo service is in use."
msgstr "Un servizio Demo è in uso"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_company.py:0
#, python-format
@@ -102,6 +121,17 @@ msgstr "Tutti i campi che riguardano l'Indice Economico e Amministrativo devono
msgid "Attachment from XML"
msgstr "Allegato dall'XML"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "An Official or Test service has been registered."
msgstr "É giá stato registrato un servizio Ufficiale o di Test."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Attached file is empty"
msgstr "Il file allegato è vuoto"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
@@ -109,6 +139,11 @@ msgstr "Allegato dall'XML"
msgid "Bank account not found, useful informations from XML file:"
msgstr "Conto bancario non trovato, informazioni utili dal file XML:"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "By checking this box, I accept that Odoo may process my invoices."
msgstr "Selezionando questa casella, accetto che Odoo invii le mie fatture."
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_codice_fiscale
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner__l10n_it_codice_fiscale
@@ -141,6 +176,11 @@ msgstr "L'azienda ha un rappresentante fiscale"
msgid "Company listed on the register of companies"
msgstr "L'azienda compare nel Registro delle Imprese"
#. module: l10n_it_edi
#: model:ir.model,name:l10n_it_edi.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazione configurazioni"
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_mail_pec_server_id
msgid "Configure your PEC-mail server to send electronic invoices."
@@ -189,12 +229,15 @@ msgid ""
msgstr "Consegna non riuscita, il SdI certifica che ha ricevuto la fattura "
"e che non è stato possibile consegnare il file al destinatario"
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__demo
msgid "Demo"
msgstr "Demo"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_edi_format__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_ir_mail_server__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner__display_name
@@ -237,6 +280,11 @@ msgstr "Il SdI certifica che ha ricevuto la Fattura e che non è stato possibile
msgid "Economic and Administrative Index"
msgstr "Indice Economico e Amministrativo"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "Electronic Document Invoicing"
msgstr "Interscambio di dati in formato elettronico"
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.account_invoice_form_l10n_it
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_company_form_l10n_it
@@ -308,6 +356,20 @@ msgstr "Termine massimo di decorrenza per l'accettazione o il rifiuto della comu
msgid "FatturaPA Send State"
msgstr "Stato di invio della FatturaPA"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_payment__l10n_it_edi_transaction
msgid "FatturaPA Transaction"
msgstr "Transazione FatturaPA"
#. module: l10n_it_edi
#: model:ir.actions.server,name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice_ir_actions_server
#: model:ir.cron,cron_name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice
#: model:ir.cron,name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice
msgid "FatturaPA: Receive invoices from the exchange system"
msgstr "FatturaPA: Ricezione fatture dal Sistema d'Interscambio"
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_codice_fiscale
msgid "Fiscal code of your company"
@@ -327,10 +389,9 @@ msgstr "Esente dalle tasse (Italia)"
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_edi_format__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_ir_mail_server__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__id
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner__id
msgid "ID"
msgstr "ID"
@@ -355,6 +416,20 @@ msgid ""
msgstr "Se l'Imposta risulta essere un'Esenzione, bisogna compilare il Tipo "
"di Esenzione e il suo valore dev'essere 0.0."
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid ""
"In demo mode Odoo will just simulate the sending of invoices to the government.<br/>\n"
" In test mode (experimental) Odoo will send the invoices to a non-production service.\n"
" Saving this change will direct all companies on this database to this use this configuration.\n"
" Once registered for testing or official, the mode cannot be changed."
msgstr "Nella modalitá Demo, Odoo simulerá l'invio delle fatture "
"all'Agenzia.<br/> Nella modalitá di Test (sperimentale) Odoo manderá le fatture "
"a un servizio non di produzione. Salvando quest'impostazione fará sì che "
"tutte le Aziende di questo Database usino questa configurazione. "
"Una volta registrato per la modalitá Test o Ufficiale, la modalitá non può "
"più essere cambiata."
#. module: l10n_it_edi
#: model:ir.model,name:l10n_it_edi.model_fetchmail_server
msgid "Incoming Mail Server"
@@ -386,11 +461,17 @@ msgstr "Riferimento Fattura"
msgid ""
"Invoices for PA are not managed by Odoo, you can download the document and "
"send it on your own."
msgstr "Le Fatture verso la PA non sono gestite da Odoo, puoi scaricare il "
"documento ed inviarlo manualmente."
msgstr "Le Fatture per la Pubblica Amministrazione non sono gestite da Odoo. "
"É possibile scaricare il documento e inviarlo tramite il sito dell'Agenzia delle Entrate."
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__is_edi_proxy_active
msgid "Is Edi Proxy Active"
msgstr "É attivo il Proxy EDI"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Italian invoice: %s"
msgstr "Fattura italiana: %s"
@@ -400,6 +481,21 @@ msgstr "Fattura italiana: %s"
msgid "Journal Entry"
msgstr "Movimento Contabile"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_proxy_current_state
msgid "L10N It Edi Proxy Current State"
msgstr "Stato corrente del Proxy L10n It EDI"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_sdicoop_demo_mode
msgid "L10N It Edi Sdicoop Demo Mode"
msgstr "Modalitá demo L10n It EDI Sdicoop"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_sdicoop_register
msgid "L10N It Edi Sdicoop Register"
msgstr "Registra L10n It EDI Sdicoop"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_einvoice_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_einvoice_name
@@ -421,13 +517,12 @@ msgstr "Ha rappresentante fiscale"
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_edi_format____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_ir_mail_server____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
msgstr "Data Modifica"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt__write_uid
@@ -518,6 +613,11 @@ msgstr "Numero nel Registro delle Imprese"
msgid "Numero DDT"
msgstr "Numero DDT"
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__prod
msgid "Official"
msgstr "Ufficiale"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
@@ -586,6 +686,14 @@ msgstr "Per favore, configurare l'email PEC statale \n"
msgid "Please configure Username for this Server PEC"
msgstr "Per favore, configurare il Nome Utente per questo server email PEC"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"Please fill your codice fiscale to be able to receive invoices from "
"FatturaPA"
msgstr "Per favore, inserire il Codice Fiscale per poter ricevere le fatture da FatturaPA"
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_tax_system
msgid "Please select the Tax system to which you are subjected."
@@ -823,6 +931,109 @@ msgstr "Questa fattura è consegnata e scaduta (è passato il termine "
msgid "This invoice is delivered and refused by destinatory"
msgstr "Questa fattura è consegnata e rifiutata dal destinatario"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Service momentarily unavailable"
msgstr "Servizio momentaneamente non disponibile"
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__test
msgid "Test (experimental)"
msgstr "Test (sperimentale)"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_config_settings.py:0
#, python-format
msgid ""
"The company has already registered with the service as 'Test' or 'Official',"
" it cannot change."
msgstr "L'Azienda è giá registrata al servizio con la modalitá Test o Officiale, non può essere cambiata."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Addressee has failed. "
"You will be required to send a courtesy copy of the invoice to your customer"
" through another channel, outside of the Exchange System, and promptly "
"notify him that the original is deposited in his personal area on the portal"
" \"Invoices and Fees\" of the Revenue Agency."
msgstr "La fattura è stata inviata, ma la consegna al Destinatario è fallita. "
"É necessario mandare una copia di cortesia della fattura al cliente "
"tramite un altro canale al di fuori dell'SdI, e notificarlo che l'originale "
"è depositato nella sua Area Personale all' interno del portale 'Fatture e Corrispettivi' "
"sul sito dell'Agenzia delle Entrate"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Public Administration "
"has failed. The Exchange System will contact them to report the problem and "
"request that they provide a solution. During the following 10 days, the "
"Exchange System will try to forward the FatturaPA file to the Public "
"Administration in question again. Should this also fail, the System will "
"notify Odoo of the failed delivery, and you will be required to send the "
"invoice to the Administration through another channel, outside of the "
"Exchange System."
msgstr "La fattura è stata inviata, ma la consegna alla Pubblica Amministrazione "
"è fallita. Il Sistema d'Interscambio contatterá il Destinatario per far rapporto "
"sul problema, e richiederá che venga approntata una soluzione. Durante i "
"10 giorni successivi, il Sistema d'Interscambio proverá a inoltrare il file "
"FatturaPA in questione ancora. Dovesse fallire anche questo tentativo, il "
"sistema notificherá Odoo della mancata consegna, e sará necessario inviare "
"la fattura tramite un altro canale, al di fuori dell'SdI"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "The invoice has been refused by the Exchange System"
msgstr "La fattura è stata rifiutata dal Sistema d'Interscambio"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been succesfully transmitted. The addressee has 15 days to "
"accept or reject it."
msgstr "La fattura è stata trasmessa con successo. Il Destinatario ha 15 giorni per accettarla o rifiutarla."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "The invoice was refused by the addressee."
msgstr "La fattura è stata rifiutata dal Destinatario."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation"
msgstr "La fattura è stata trasmessa con successo alla Pubblica Amministrazione "
"e siamo in attesa di conferma."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation."
msgstr "La fattura è stata inviata con successo alla Pubblica Amministrazione "
"e siamo in attesa di conferma."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"This invoice number had already been submitted to the SdI, so it is set as Sent. Please verify that the system is correctly configured, because the correct flow does not need to send the same invoice twice for any reason.\n"
" Original message from the SDI: %s"
msgstr "Una fattura con questo numero è giá stata inviata all'SdI, quindi viene impostata come inviata. "
"Per favore, verificare che il sistema sia configurato correttamente, perchè in nessun caso il sistema "
"ha motivo di inviare la stessa fattura più volte.\n"
"Messaggio originale dall'SdI: %s"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
@@ -851,6 +1062,12 @@ msgstr "Numero Documento Di Trasporto"
msgid "Transport informations from XML file:"
msgstr "Informazioni di Trasporto dal file XML:"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Unauthorized user"
msgstr "Utente non autorizzato"
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__l10n_it_vat_due_date
msgid "VAT due date"
@@ -862,6 +1079,12 @@ msgstr "Data scadenza IVA"
msgid "Vendor not found, useful informations from XML file:"
msgstr "Fornitore non trovato, informazioni dal file XML:"
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "You are not allowed to check the status of this invoice."
msgstr "Non sei autorizzato a controllare lo stato di questa fattura."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
@@ -870,6 +1093,16 @@ msgid ""
"no errors"
msgstr "Non puoi rigenerare una Fattura Elettronica se è giá stata inviata senza errori."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"You must accept the terms and conditions in the settings to use FatturaPA."
msgstr "Devi accettare i Termini e le Condizioni d'uso nelle impostazioni "
"per poter utilizzare FatturaPA."
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_company.py:0
#, python-format
+221 -216
View File
@@ -17,18 +17,11 @@ msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "%s has an amount of 0.0, you must indicate the kind of exoneration."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid ""
"%s isn't in a right state. It must be in a 'Not yet send' or 'Invalid' "
"state."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
@@ -89,6 +82,34 @@ msgid ""
"'Scissione dei pagamenti' is not compatible with exoneration of kind 'N6'"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">\n"
" Fattura Elettronica mode\n"
" </span>"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "<span class=\"o_form_label\">Allow Odoo to process invoices</span>"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "A Demo service is in use."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid ""
"A simplified invoice was created instead of an ordinary one. This is because"
" the invoice is a domestic invoice with "
"a total amount of less than or equal to 400€ and the customer's address is "
"incomplete."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_company.py:0
#, python-format
@@ -96,6 +117,23 @@ msgid ""
"All fields about the Economic and Administrative Index must be completed."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "All quantities should be positive."
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "An Official or Test service has been registered."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Attached file is empty"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
@@ -109,6 +147,19 @@ msgstr ""
msgid "Bank account not found, useful informations from XML file:"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "By checking this box, I accept that Odoo may process my invoices."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"Cannot apply Reverse Charge to a bill which contains both services and "
"goods."
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_codice_fiscale
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner__l10n_it_codice_fiscale
@@ -126,11 +177,6 @@ msgstr ""
msgid "Companies"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_address_send_fatturapa
msgid "Company PEC-mail"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_company_form_l10n_it
msgid "Company have a tax representative"
@@ -142,8 +188,8 @@ msgid "Company listed on the register of companies"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_mail_pec_server_id
msgid "Configure your PEC-mail server to send electronic invoices."
#: model:ir.model,name:l10n_it_edi.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: l10n_it_edi
@@ -182,30 +228,20 @@ msgid "Dati Bollo"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__failed_delivery
msgid ""
"Delivery impossible, ES certify that it has received the invoice and that "
"the file could not be delivered to the addressee"
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__demo
msgid "Demo"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_edi_format__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_ir_mail_server__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__display_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner__display_name
msgid "Display Name"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "E-Invoice is delivery to the destinatory:<br/>%s"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
@@ -223,16 +259,13 @@ msgid "EDI format"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid ""
"ES certify that it has received the invoice and that the file"
" could not be delivered to the addressee. <br/>%s"
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_company_form_l10n_it
msgid "Economic and Administrative Index"
msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_company_form_l10n_it
msgid "Economic and Administrative Index"
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid "Electronic Document Invoicing"
msgstr ""
#. module: l10n_it_edi
@@ -248,36 +281,6 @@ msgstr ""
msgid "Electronic invoice"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_address_recipient_fatturapa
msgid "Enter Government PEC-mail address. Ex: sdi01@pec.fatturapa.it"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_address_send_fatturapa
msgid "Enter your company PEC-mail address. Ex: yourcompany@pec.mail.it"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid "Error when sending mail with E-Invoice: %s"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid ""
"Error when sending mail with E-Invoice: Your company must have a mail PEC "
"server and must indicate the mail PEC that will send electronic invoice."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "Errors in the E-Invoice :<br/>%s"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__l10n_it_kind_exoneration
msgid "Exoneration"
@@ -289,18 +292,24 @@ msgid "Exoneration type"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid ""
"Expiration of the maximum term for communication of acceptance/refusal:"
" %s<br/>%s"
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_payment__l10n_it_edi_attachment_id
msgid "FatturaPA Attachment"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_send_state
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_send_state
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_payment__l10n_it_send_state
msgid "FatturaPA Send State"
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_payment__l10n_it_edi_transaction
msgid "FatturaPA Transaction"
msgstr ""
#. module: l10n_it_edi
#: model:ir.actions.server,name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice_ir_actions_server
#: model:ir.cron,cron_name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice
#: model:ir.cron,name:l10n_it_edi.ir_cron_receive_fattura_pa_invoice
msgid "FatturaPA: Receive invoices from the exchange system"
msgstr ""
#. module: l10n_it_edi
@@ -308,11 +317,6 @@ msgstr ""
msgid "Fiscal code of your company"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_address_recipient_fatturapa
msgid "Government PEC-mail"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__l10n_it_has_exoneration
msgid "Has exoneration of tax (Italy)"
@@ -335,12 +339,6 @@ msgstr ""
msgid "INTERNAL"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_fetchmail_server__l10n_it_is_pec
msgid ""
"If PEC Server, only mail from '...@pec.fatturapa.it' will be processed."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
@@ -350,8 +348,18 @@ msgid ""
msgstr ""
#. module: l10n_it_edi
#: model:ir.model,name:l10n_it_edi.model_fetchmail_server
msgid "Incoming Mail Server"
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.res_config_settings_view_form
msgid ""
"In demo mode Odoo will just simulate the sending of invoices to the government.<br/>\n"
" In test mode (experimental) Odoo will send the invoices to a non-production service.\n"
" Saving this change will direct all companies on this database to this use this configuration.\n"
" Once registered for testing or official, the mode cannot be changed."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "In line %s, you must select one and only one tax by line."
msgstr ""
#. module: l10n_it_edi
@@ -362,12 +370,6 @@ msgid ""
"person and '12345678901' or 'IT12345678901' for businesses."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid "Invalid configuration:"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt__invoice_id
msgid "Invoice Reference"
@@ -382,6 +384,12 @@ msgid ""
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__is_edi_proxy_active
msgid "Is Edi Proxy Active"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid "Italian invoice: %s"
@@ -392,6 +400,21 @@ msgstr ""
msgid "Journal Entry"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_proxy_current_state
msgid "L10N It Edi Proxy Current State"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_sdicoop_demo_mode
msgid "L10N It Edi Sdicoop Demo Mode"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_config_settings__l10n_it_edi_sdicoop_register
msgid "L10N It Edi Sdicoop Register"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_bank_statement_line__l10n_it_einvoice_name
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move__l10n_it_einvoice_name
@@ -413,8 +436,6 @@ msgstr ""
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_edi_format____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_ir_mail_server____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_l10n_it_ddt____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company____last_update
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_partner____last_update
@@ -431,11 +452,6 @@ msgstr ""
msgid "Last Updated on"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server__l10n_it_last_uid
msgid "Last message UID"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__l10n_it_law_reference
msgid "Law Reference"
@@ -451,17 +467,6 @@ msgstr ""
msgid "MP05"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model,name:l10n_it_edi.model_ir_mail_server
msgid "Mail Server"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid "Mail sent on %s by %s"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,help:l10n_it_edi.field_res_company__l10n_it_eco_index_share_capital
msgid ""
@@ -481,21 +486,11 @@ msgid ""
"invoice."
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__new
msgid "New"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_company__l10n_it_eco_index_sole_shareholder__no
msgid "Not a limited liability company"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__to_send
msgid "Not yet send"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_eco_index_number
msgid "Number in register of companies"
@@ -507,20 +502,8 @@ msgid "Numero DDT"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "Original E-invoice XML file"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__other
msgid "Other"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "Outcome notice: %s<br/>%s"
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__prod
msgid "Official"
msgstr ""
#. module: l10n_it_edi
@@ -536,6 +519,7 @@ msgstr ""
#. module: l10n_it_edi
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.account_invoice_it_FatturaPA_export
#: model_terms:ir.ui.view,arch_db:l10n_it_edi.account_invoice_it_simplified_FatturaPA_export
msgid "PDF"
msgstr ""
@@ -545,32 +529,17 @@ msgstr ""
msgid "PEC e-mail"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "PEC mail server must be of type IMAP."
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_fetchmail_server__l10n_it_is_pec
msgid "PEC server"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_company__l10n_it_eco_index_sole_shareholder__sm
msgid "Più soci"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Please configure Government PEC-mail\tin company settings"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid "Please configure Username for this Server PEC"
msgid ""
"Please fill your codice fiscale to be able to receive invoices from "
"FatturaPA"
msgstr ""
#. module: l10n_it_edi
@@ -589,30 +558,9 @@ msgid "SI"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Sending file: %s"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_invoice.py:0
#, python-format
msgid "Sending file: %s to ES: %s"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__invalid
msgid "Sent, but invalid"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__sent
msgid "Sent, waiting for response"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_res_company__l10n_it_mail_pec_server_id
msgid "Server PEC"
msgid "Service momentarily unavailable"
msgstr ""
#. module: l10n_it_edi
@@ -685,24 +633,24 @@ msgid "Tax representative partner %s of %s must have a tax number."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/ir_mail_server.py:0
#, python-format
msgid ""
"The E-invoice is not delivered to the addressee. The Exchange System is"
" unable to deliver the file to the Public Administration. The"
" Exchange System will contact the PA to report the problem "
"and request that they provide a solution. During the "
"following 15 days, the Exchange System will try to forward the FatturaPA"
" file to the Administration in question again. More "
"information:<br/>%s"
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__test
msgid "Test (experimental)"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The buyer, %s, or his company must have either a VAT number either a tax "
"code (Codice Fiscale)."
"The buyer, %s, or his company must have a VAT number and/or a tax code "
"(Codice Fiscale)."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_config_settings.py:0
#, python-format
msgid ""
"The company has already registered with the service as 'Test' or 'Official',"
" it cannot change."
msgstr ""
#. module: l10n_it_edi
@@ -719,13 +667,69 @@ msgstr ""
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The maximum length for VAT number is 30. %s have a VAT number too long: %s."
"The filename is duplicated. Try again (or adjust the FatturaPA Filename "
"sequence). Original message from the SDI: %s"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "The seller must have a bank account."
msgid ""
"The invoice has been issued, but the delivery to the Addressee has failed. "
"You will be required to send a courtesy copy of the invoice to your customer"
" through another channel, outside of the Exchange System, and promptly "
"notify him that the original is deposited in his personal area on the portal"
" \"Invoices and Fees\" of the Revenue Agency."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Public Administration "
"has failed. The Exchange System will contact them to report the problem and "
"request that they provide a solution. During the following 10 days, the "
"Exchange System will try to forward the FatturaPA file to the Public "
"Administration in question again. Should this also fail, the System will "
"notify Odoo of the failed delivery, and you will be required to send the "
"invoice to the Administration through another channel, outside of the "
"Exchange System."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "The invoice has been refused by the Exchange System"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been succesfully transmitted. The addressee has 15 days to "
"accept or reject it."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "The invoice was refused by the addressee."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was sent to FatturaPA, but we are still awaiting a response. "
"Click the link above to check for an update."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"The maximum length for VAT number is 30. %s have a VAT number too long: %s."
msgstr ""
#. module: l10n_it_edi
@@ -773,25 +777,11 @@ msgid ""
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__delivered
msgid "This invoice is delivered"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__delivered_accepted
msgid "This invoice is delivered and accepted by destinatory"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__delivered_expired
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"This invoice is delivered and expired (expiry of the maximum term for "
"communication of acceptance/refusal)"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields.selection,name:l10n_it_edi.selection__account_move__l10n_it_send_state__delivered_refused
msgid "This invoice is delivered and refused by destinatory"
"This invoice number had already been submitted to the SdI, so it is set as Sent. Please verify that the system is correctly configured, because the correct flow does not need to send the same invoice twice for any reason.\n"
" Original message from the SDI: %s"
msgstr ""
#. module: l10n_it_edi
@@ -822,11 +812,25 @@ msgstr ""
msgid "Transport informations from XML file:"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "Unauthorized user"
msgstr ""
#. module: l10n_it_edi
#: model:ir.model.fields,field_description:l10n_it_edi.field_account_tax__l10n_it_vat_due_date
msgid "VAT due date"
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"Vendor bills sent as self-invoices to the SdI require a valid PA Index "
"(Codice Destinatario) on the company's contact."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
@@ -836,9 +840,16 @@ msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "You are not allowed to check the status of this invoice."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid ""
"You can't regenerate an E-Invoice when the first one is sent and there are "
"no errors"
"You must accept the terms and conditions in the settings to use FatturaPA."
msgstr ""
#. module: l10n_it_edi
@@ -847,12 +858,6 @@ msgstr ""
msgid "You must select a tax representative."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/account_edi_format.py:0
#, python-format
msgid "You must select one and only one tax by line."
msgstr ""
#. module: l10n_it_edi
#: code:addons/l10n_it_edi/models/res_company.py:0
#, python-format
+1 -1
View File
@@ -3,8 +3,8 @@
from . import res_partner
from . import res_company
from . import res_config_settings
from . import account_invoice
from . import account_edi_format
from . import ir_mail_server
from . import ddt
from . import mail_template
+377 -66
View File
@@ -1,7 +1,9 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, fields, _
from odoo import api, models, fields, _, _lt
from odoo.exceptions import UserError
from odoo.addons.account_edi_proxy_client.models.account_edi_proxy_user import AccountEdiProxyError
from odoo.addons.l10n_it_edi.tools.remove_signature import remove_signature
from odoo.osv.expression import OR, AND
@@ -9,6 +11,7 @@ from lxml import etree
from datetime import datetime
import re
import logging
import base64
_logger = logging.getLogger(__name__)
@@ -264,74 +267,71 @@ class AccountEdiFormat(models.Model):
def _l10n_it_is_simplified_document_type(self, document_type):
return self._l10n_it_document_type_mapping().get(document_type, {}).get('simplified', False)
# -------------------------------------------------------------------------
# Export
# -------------------------------------------------------------------------
def _prepare_invoice_report(self, pdf_writer, edi_document):
self.ensure_one()
if self.code != 'fattura_pa':
return super()._prepare_invoice_report(pdf_writer, edi_document)
if edi_document.attachment_id:
pdf_writer.embed_odoo_attachment(edi_document.attachment_id)
def _is_compatible_with_journal(self, journal):
# OVERRIDE
self.ensure_one()
if self.code != 'fattura_pa':
return super()._is_compatible_with_journal(journal)
return journal.type in ('sale', 'purchase') and journal.country_code == 'IT'
def _l10n_it_edi_is_required_for_invoice(self, invoice):
""" Is the edi required for this invoice based on the method (here: PEC mail)
Deprecated: in future release PEC mail will be removed.
TO OVERRIDE
"""
return ((invoice.is_sale_document() or self._l10n_it_get_document_type(invoice))
and invoice.country_code == 'IT'
and invoice.l10n_it_send_state not in ('sent', 'delivered', 'delivered_accepted'))
def _is_required_for_invoice(self, invoice):
# OVERRIDE
self.ensure_one()
if self.code != 'fattura_pa':
return super()._is_required_for_invoice(invoice)
return self._l10n_it_edi_is_required_for_invoice(invoice)
def _post_fattura_pa(self, invoices):
# TO OVERRIDE
invoice = invoices # no batching ensure that we only have one invoice
invoice.l10n_it_send_state = 'other'
invoice._check_before_xml_exporting()
if invoice.l10n_it_einvoice_id and invoice.l10n_it_send_state not in ['invalid', 'to_send']:
return {'error': _("You can't regenerate an E-Invoice when the first one is sent and there are no errors")}
if invoice.l10n_it_einvoice_id:
invoice.l10n_it_einvoice_id.unlink()
res = invoice.invoice_generate_xml()
if invoice._is_commercial_partner_pa():
invoice.message_post(
body=(_("Invoices for PA are not managed by Odoo, you can download the document and send it on your own."))
)
else:
invoice.l10n_it_send_state = 'to_send'
if 'attachment' in res:
res['success'] = True
return {invoice: res}
def _post_invoice_edi(self, invoices, test_mode=False):
# OVERRIDE
self.ensure_one()
edi_result = super()._post_invoice_edi(invoices)
if self.code != 'fattura_pa':
return edi_result
return self._post_fattura_pa(invoices)
# -------------------------------------------------------------------------
# Import
# -------------------------------------------------------------------------
def _cron_receive_fattura_pa(self):
''' Check the proxy for incoming invoices.
'''
proxy_users = self.env['account_edi_proxy_client.user'].search([('edi_format_id', '=', self.env.ref('l10n_it_edi.edi_fatturaPA').id)])
if proxy_users._get_demo_state() == 'demo':
return
for proxy_user in proxy_users:
company = proxy_user.company_id
try:
res = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/in/RicezioneInvoice',
params={'recipient_codice_fiscale': company.l10n_it_codice_fiscale})
except AccountEdiProxyError as e:
_logger.error('Error while receiving file from SdiCoop: %s', e)
proxy_acks = []
for id_transaction, fattura in res.items():
if self.env['ir.attachment'].search([('name', '=', fattura['filename']), ('res_model', '=', 'account.move')], limit=1):
# name should be unique, the invoice already exists
_logger.info('E-invoice already exists: %s', fattura['filename'])
proxy_acks.append(id_transaction)
continue
file = proxy_user._decrypt_data(fattura['file'], fattura['key'])
try:
tree = etree.fromstring(file)
except Exception:
# should not happen as the file has been checked by SdiCoop
_logger.info('Received file badly formatted, skipping: \n %s', file)
continue
invoice = self.env['account.move'].with_company(company).create({'move_type': 'in_invoice'})
attachment = self.env['ir.attachment'].create({
'name': fattura['filename'],
'raw': file,
'type': 'binary',
'res_model': 'account.move',
'res_id': invoice.id
})
if not self.env.context.get('test_skip_commit'):
self.env.cr.commit() # In case something fails after, we still have the attachment
# So that we don't delete the attachment when deleting the invoice
attachment.res_id = False
attachment.res_model = False
invoice.unlink()
invoice = self.env.ref('l10n_it_edi.edi_fatturaPA')._create_invoice_from_xml_tree(fattura['filename'], tree)
attachment.write({'res_model': 'account.move',
'res_id': invoice.id})
proxy_acks.append(id_transaction)
if not self.env.context.get('test_skip_commit'):
self.env.cr.commit()
if proxy_acks:
try:
proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/ack',
params={'transaction_ids': proxy_acks})
except AccountEdiProxyError as e:
_logger.error('Error while receiving file from SdiCoop: %s', e)
def _check_filename_is_fattura_pa(self, filename):
return re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", filename)
@@ -747,7 +747,6 @@ class AccountEdiFormat(models.Model):
invoice_line_global_discount.price_unit = general_discount
new_invoice = invoice_form
new_invoice.l10n_it_send_state = "other"
elements = body_tree.xpath('.//Allegati')
if elements:
@@ -775,3 +774,315 @@ class AccountEdiFormat(models.Model):
invoices += new_invoice
return invoices
# -------------------------------------------------------------------------
# Export
# -------------------------------------------------------------------------
def _prepare_invoice_report(self, pdf_writer, edi_document):
self.ensure_one()
if self.code != 'fattura_pa':
return super()._prepare_invoice_report(pdf_writer, edi_document)
if edi_document.attachment_id:
pdf_writer.embed_odoo_attachment(edi_document.attachment_id)
def _is_compatible_with_journal(self, journal):
# OVERRIDE
self.ensure_one()
if self.code != 'fattura_pa':
return super()._is_compatible_with_journal(journal)
return journal.type in ('sale', 'purchase') and journal.country_code == 'IT'
def _is_required_for_invoice(self, invoice):
# OVERRIDE
self.ensure_one()
if self.code != 'fattura_pa':
return super()._is_required_for_invoice(invoice)
is_self_invoice = self._l10n_it_edi_is_self_invoice(invoice)
return (
(invoice.is_sale_document() or (is_self_invoice and invoice.is_purchase_document()))
and invoice.country_code == 'IT'
)
def _export_as_xml(self, invoice):
''' Create the xml file content.
:return: The XML content as str.
'''
template_values = invoice._prepare_fatturapa_export_values()
if not self._l10n_it_is_simplified_document_type(template_values['document_type']):
content = self.env['ir.qweb']._render('l10n_it_edi.account_invoice_it_FatturaPA_export', template_values)
else:
content = self.env['ir.qweb']._render('l10n_it_edi.account_invoice_it_simplified_FatturaPA_export', template_values)
invoice.message_post(body=_(
"A simplified invoice was created instead of an ordinary one. This is because the invoice \
is a domestic invoice with a total amount of less than or equal to 400€ and the customer's address is incomplete."
))
return content
def _get_invoice_edi_content(self, move):
#OVERRIDE
if self.code != 'fattura_pa':
return super()._get_invoice_edi_content(move)
return self._export_as_xml(move)
def _check_move_configuration(self, move):
# OVERRIDE
res = super()._check_move_configuration(move)
if self.code != 'fattura_pa':
return res
res.extend(self._l10n_it_edi_check_invoice_configuration(move))
if not self._get_proxy_user(move.company_id):
res.append(_("You must accept the terms and conditions in the settings to use FatturaPA."))
return res
def _needs_web_services(self):
self.ensure_one()
return self.code == 'fattura_pa' or super()._needs_web_services()
def _support_batching(self, move=None, state=None, company=None):
# OVERRIDE
if self.code == 'fattura_pa':
return state == 'to_send' and move.is_invoice()
return super()._support_batching(move=move, state=state, company=company)
def _get_batch_key(self, move, state):
# OVERRIDE
if self.code != 'fattura_pa':
return super()._get_batch_key(move, state)
return move.move_type, bool(move.l10n_it_edi_transaction)
def _l10n_it_post_invoices_step_1(self, invoices):
''' Send the invoices to the proxy.
'''
to_return = {}
to_send = {}
for invoice in invoices:
xml = "<?xml version='1.0' encoding='UTF-8'?>" + str(self._export_as_xml(invoice))
filename = self._l10n_it_edi_generate_electronic_invoice_filename(invoice)
attachment = self.env['ir.attachment'].create({
'name': filename,
'res_id': invoice.id,
'res_model': invoice._name,
'raw': xml.encode(),
'description': _('Italian invoice: %s', invoice.move_type),
'type': 'binary',
})
invoice.l10n_it_edi_attachment_id = attachment
if invoice._is_commercial_partner_pa():
invoice.message_post(
body=(_("Invoices for PA are not managed by Odoo, you can download the document and send it on your own."))
)
to_return[invoice] = {'attachment': attachment, 'success': True}
else:
to_send[filename] = {
'invoice': invoice,
'data': {'filename': filename, 'xml': base64.b64encode(xml.encode()).decode()}}
company = invoices.company_id
proxy_user = self._get_proxy_user(company)
if not proxy_user: # proxy user should exist, because there is a check in _check_move_configuration
return {invoice: {
'error': _("You must accept the terms and conditions in the settings to use FatturaPA."),
'blocking_level': 'error'} for invoice in invoices}
responses = {}
if proxy_user._get_demo_state() == 'demo':
responses = {i['data']['filename']: {'id_transaction': 'demo'} for i in to_send.values()}
else:
try:
responses = self._l10n_it_edi_upload([i['data'] for i in to_send.values()], proxy_user)
except AccountEdiProxyError as e:
return {invoice: {'error': e.message, 'blocking_level': 'error'} for invoice in invoices}
for filename, response in responses.items():
invoice = to_send[filename]['invoice']
to_return[invoice] = response
if 'id_transaction' in response:
invoice.l10n_it_edi_transaction = response['id_transaction']
to_return[invoice].update({
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
})
return to_return
def _l10n_it_post_invoices_step_2(self, invoices):
''' Check if the sent invoices have been processed by FatturaPA.
'''
to_check = {i.l10n_it_edi_transaction: i for i in invoices}
to_return = {}
company = invoices.company_id
proxy_user = self._get_proxy_user(company)
if not proxy_user: # proxy user should exist, because there is a check in _check_move_configuration
return {invoice: {
'error': _("You must accept the terms and conditions in the settings to use FatturaPA."),
'blocking_level': 'error'} for invoice in invoices}
if proxy_user._get_demo_state() == 'demo':
# simulate success and bypass ack
return {invoice: {'attachment': invoice.l10n_it_edi_attachment_id} for invoice in invoices}
else:
try:
responses = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/in/TrasmissioneFatture',
params={'ids_transaction': list(to_check.keys())})
except AccountEdiProxyError as e:
return {invoice: {'error': e.message, 'blocking_level': 'error'} for invoice in invoices}
proxy_acks = []
for id_transaction, response in responses.items():
invoice = to_check[id_transaction]
if 'error' in response:
to_return[invoice] = response
continue
state = response['state']
if state == 'awaiting_outcome':
to_return[invoice] = {
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
}
continue
elif state == 'not_found':
# Invoice does not exist on proxy. Either it does not belong to this proxy_user or it was not created correctly when
# it was sent to the proxy.
to_return[invoice] = {'error': _('You are not allowed to check the status of this invoice.'), 'blocking_level': 'error'}
continue
if not response.get('file'): # It means there is no status update, so we can skip it
document = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code == 'fattura_pa')
to_return[invoice] = {'error': document.error, 'blocking_level': document.blocking_level}
continue
xml = proxy_user._decrypt_data(response['file'], response['key'])
response_tree = etree.fromstring(xml)
if state == 'ricevutaConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _('The invoice has been succesfully transmitted. The addressee has 15 days to accept or reject it.')}
else:
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
elif state == 'notificaScarto':
elements = response_tree.xpath('//Errore')
error_codes = [element.find('Codice').text for element in elements]
errors = [element.find('Descrizione').text for element in elements]
# Duplicated invoice
if '00404' in error_codes:
idx = error_codes.index('00404')
invoice.message_post(body=_(
'This invoice number had already been submitted to the SdI, so it is'
' set as Sent. Please verify that the system is correctly configured,'
' because the correct flow does not need to send the same invoice'
' twice for any reason.\n'
' Original message from the SDI: %s', errors[idx]))
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else:
# Add helpful text if duplicated filename error
if '00002' in error_codes:
idx = error_codes.index('00002')
errors[idx] = _(
'The filename is duplicated. Try again (or adjust the FatturaPA Filename sequence).'
' Original message from the SDI: %s', [errors[idx]]
)
to_return[invoice] = {'error': self._format_error_message(_('The invoice has been refused by the Exchange System'), errors), 'blocking_level': 'error'}
invoice.l10n_it_edi_transaction = False
elif state == 'notificaMancataConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _(
'The invoice has been issued, but the delivery to the Public Administration'
' has failed. The Exchange System will contact them to report the problem'
' and request that they provide a solution.'
' During the following 10 days, the Exchange System will try to forward the'
' FatturaPA file to the Public Administration in question again.'
' Should this also fail, the System will notify Odoo of the failed delivery,'
' and you will be required to send the invoice to the Administration'
' through another channel, outside of the Exchange System.')}
else:
to_return[invoice] = {'success': True, 'attachment': invoice.l10n_it_edi_attachment_id}
invoice._message_log(body=_(
'The invoice has been issued, but the delivery to the Addressee has'
' failed. You will be required to send a courtesy copy of the invoice'
' to your customer through another channel, outside of the Exchange'
' System, and promptly notify him that the original is deposited'
' in his personal area on the portal "Invoices and Fees" of the'
' Revenue Agency.'))
elif state == 'notificaEsito':
outcome = response_tree.find('Esito').text
if outcome == 'EC01':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else: # ECO2
to_return[invoice] = {'error': _('The invoice was refused by the addressee.'), 'blocking_level': 'error'}
elif state == 'NotificaDecorrenzaTermini':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
proxy_acks.append(id_transaction)
if proxy_acks:
try:
proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/ack',
params={'transaction_ids': proxy_acks})
except AccountEdiProxyError as e:
# Will be ignored and acked again next time.
_logger.error('Error while acking file to SdiCoop: %s', e)
return to_return
def _post_fattura_pa(self, invoices):
# OVERRIDE
if not invoices[0].l10n_it_edi_transaction:
return self._l10n_it_post_invoices_step_1(invoices)
else:
return self._l10n_it_post_invoices_step_2(invoices)
def _post_invoice_edi(self, invoices, test_mode=False):
# OVERRIDE
self.ensure_one()
edi_result = super()._post_invoice_edi(invoices)
if self.code != 'fattura_pa':
return edi_result
return self._post_fattura_pa(invoices)
# -------------------------------------------------------------------------
# Proxy methods
# -------------------------------------------------------------------------
def _get_proxy_identification(self, company):
if self.code != 'fattura_pa':
return super()._get_proxy_identification()
if not company.l10n_it_codice_fiscale:
raise UserError(_('Please fill your codice fiscale to be able to receive invoices from FatturaPA'))
return self.env['res.partner']._l10n_it_normalize_codice_fiscale(company.l10n_it_codice_fiscale)
def _l10n_it_edi_upload(self, files, proxy_user):
'''Upload files to fatturapa.
:param files: A list of dictionary {filename, base64_xml}.
:returns: A dictionary.
* message: Message from fatturapa.
* transactionId: The fatturapa ID of this request.
* error: An eventual error.
* error_level: Info, warning, error.
'''
ERRORS = {
'EI01': {'error': _lt('Attached file is empty'), 'blocking_level': 'error'},
'EI02': {'error': _lt('Service momentarily unavailable'), 'blocking_level': 'warning'},
'EI03': {'error': _lt('Unauthorized user'), 'blocking_level': 'error'},
}
if not files:
return {}
result = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/out/SdiRiceviFile', params={'files': files})
# Translate the errors.
for filename in result.keys():
if 'error' in result[filename]:
result[filename] = ERRORS.get(result[filename]['error'], {'error': result[filename]['error'], 'blocking_level': 'error'})
return result
+5 -82
View File
@@ -10,7 +10,9 @@ from datetime import datetime
from odoo import api, fields, models, _
from odoo.tools import float_repr, float_compare
from odoo.exceptions import UserError, ValidationError
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
_logger = logging.getLogger(__name__)
DEFAULT_FACTUR_ITALIAN_DATE_FORMAT = '%Y-%m-%d'
@@ -18,19 +20,8 @@ DEFAULT_FACTUR_ITALIAN_DATE_FORMAT = '%Y-%m-%d'
class AccountMove(models.Model):
_inherit = 'account.move'
l10n_it_send_state = fields.Selection([
('new', 'New'),
('other', 'Other'),
('to_send', 'Not yet send'),
('sent', 'Sent, waiting for response'),
('invalid', 'Sent, but invalid'),
('delivered', 'This invoice is delivered'),
('delivered_accepted', 'This invoice is delivered and accepted by destinatory'),
('delivered_refused', 'This invoice is delivered and refused by destinatory'),
('delivered_expired', 'This invoice is delivered and expired (expiry of the maximum term for communication of acceptance/refusal)'),
('failed_delivery', 'Delivery impossible, ES certify that it has received the invoice and that the file \
could not be delivered to the addressee') # ok we must do nothing
], default='to_send', copy=False, string="FatturaPA Send State")
l10n_it_edi_transaction = fields.Char(copy=False, string="FatturaPA Transaction")
l10n_it_edi_attachment_id = fields.Many2one('ir.attachment', copy=False, string="FatturaPA Attachment")
l10n_it_stamp_duty = fields.Float(default=0, string="Dati Bollo", readonly=True, states={'draft': [('readonly', False)]})
@@ -48,12 +39,6 @@ class AccountMove(models.Model):
invoice.l10n_it_einvoice_id = einvoice.attachment_id
invoice.l10n_it_einvoice_name = einvoice.attachment_id.name
def _check_before_xml_exporting(self):
# DEPRECATED use AccountEdiFormat._l10n_it_edi_check_invoice_configuration instead
errors = self.env['account.edi.format']._l10n_it_edi_check_invoice_configuration(self)
if errors:
raise UserError(self.env['account.edi.format']._format_error_message(_("Invalid configuration:"), errors))
def invoice_generate_xml(self):
self.ensure_one()
report_name = self.env['account.edi.format']._l10n_it_edi_generate_electronic_invoice_filename(self)
@@ -298,73 +283,11 @@ class AccountMove(models.Model):
}
return template_values
def _export_as_xml(self):
'''DEPRECATED : this will be moved to AccountEdiFormat in a future version.
Create the xml file content.
:return: The XML content as str.
'''
template_values = self._prepare_fatturapa_export_values()
if not self.env['account.edi.format']._l10n_it_is_simplified_document_type(template_values['document_type']):
content = self.env['ir.qweb']._render('l10n_it_edi.account_invoice_it_FatturaPA_export', template_values)
else:
content = self.env['ir.qweb']._render('l10n_it_edi.account_invoice_it_simplified_FatturaPA_export', template_values)
self.message_post(body=_("A simplified invoice was created instead of an ordinary one. This is because the invoice \
is a domestic invoice with a total amount of less than or equal to 400€ and the customer's address is incomplete."))
return content
def _post(self, soft=True):
# OVERRIDE
posted = super()._post(soft=soft)
for move in posted.filtered(lambda m: m.l10n_it_send_state == 'to_send' and m.move_type in ['out_invoice', 'out_refund'] and m.company_id.country_id.code == 'IT'):
move.send_pec_mail()
return posted
def send_pec_mail(self):
self.ensure_one()
allowed_state = ['to_send', 'invalid']
if (
not self.company_id.l10n_it_mail_pec_server_id
or not self.company_id.l10n_it_mail_pec_server_id.active
or not self.company_id.l10n_it_address_send_fatturapa
):
self.message_post(
body=(_("Error when sending mail with E-Invoice: Your company must have a mail PEC server and must indicate the mail PEC that will send electronic invoice."))
)
self.l10n_it_send_state = 'invalid'
return
if self.l10n_it_send_state not in allowed_state:
raise UserError(_("%s isn't in a right state. It must be in a 'Not yet send' or 'Invalid' state.") % (self.display_name))
message = self.env['mail.message'].create({
'subject': _('Sending file: %s') % (self.l10n_it_einvoice_name),
'body': _('Sending file: %s to ES: %s') % (self.l10n_it_einvoice_name, self.env.company.l10n_it_address_recipient_fatturapa),
'author_id': self.env.user.partner_id.id,
'email_from': self.env.company.l10n_it_address_send_fatturapa,
'reply_to': self.env.company.l10n_it_address_send_fatturapa,
'mail_server_id': self.env.company.l10n_it_mail_pec_server_id.id,
'attachment_ids': [(6, 0, self.l10n_it_einvoice_id.ids)],
})
mail_fattura = self.env['mail.mail'].sudo().with_context(wo_bounce_return_path=True).create({
'mail_message_id': message.id,
'email_to': self.env.company.l10n_it_address_recipient_fatturapa,
})
try:
mail_fattura.send(raise_exception=True)
self.message_post(
body=(_("Mail sent on %s by %s") % (fields.Datetime.now(), self.env.user.display_name))
)
self.l10n_it_send_state = 'sent'
except MailDeliveryException as error:
self.message_post(
body=(_("Error when sending mail with E-Invoice: %s") % (error.args[0]))
)
self.l10n_it_send_state = 'invalid'
def _compose_info_message(self, tree, element_tags):
output_str = ""
elements = tree.xpath(element_tags)
-449
View File
@@ -1,449 +0,0 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import zipfile
import io
import re
import logging
import email
import email.policy
import dateutil
import pytz
from lxml import etree
from datetime import datetime
from xmlrpc import client as xmlrpclib
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError, UserError
from odoo.addons.l10n_it_edi.tools.remove_signature import remove_signature
_logger = logging.getLogger(__name__)
class FetchmailServer(models.Model):
_name = 'fetchmail.server'
_inherit = 'fetchmail.server'
l10n_it_is_pec = fields.Boolean('PEC server', help="If PEC Server, only mail from '...@pec.fatturapa.it' will be processed.")
l10n_it_last_uid = fields.Integer(string='Last message UID IT', default=1)
def _search_edi_invoice(self, att_name, send_state=False):
""" Search sent l10n_it_edi fatturaPA invoices """
conditions = [
('move_id', "!=", False),
('edi_format_id.code', '=', 'fattura_pa'),
('attachment_id.name', '=', att_name),
]
if send_state:
conditions.append(('move_id.l10n_it_send_state', '=', send_state))
return self.env['account.edi.document'].search(conditions, limit=1).move_id
@api.constrains('l10n_it_is_pec', 'server_type')
def _check_pec(self):
for record in self:
if record.l10n_it_is_pec and record.server_type != 'imap':
raise ValidationError(_("PEC mail server must be of type IMAP."))
def fetch_mail(self):
""" WARNING: meant for cron usage only - will commit() after each email! """
MailThread = self.env['mail.thread']
for server in self.filtered(lambda s: s.l10n_it_is_pec):
_logger.info('start checking for new emails on %s PEC server %s', server.server_type, server.name)
count, failed = 0, 0
imap_server = None
try:
imap_server = server.connect()
imap_server.select()
# Only download new emails
email_filter = ['(UID %s:*)' % (server.l10n_it_last_uid)]
# The l10n_it_edi.fatturapa_bypass_incoming_address_filter prevents the sender address check on incoming email.
bypass_incoming_address_filter = self.env['ir.config_parameter'].get_param('l10n_it_edi.bypass_incoming_address_filter', False)
if not bypass_incoming_address_filter:
email_filter.append('(FROM "@pec.fatturapa.it")')
data = imap_server.uid('search', None, *email_filter)[1]
new_max_uid = server.l10n_it_last_uid
for uid in data[0].split():
if int(uid) <= server.l10n_it_last_uid:
# We get always minimum 1 message. If no new message, we receive the newest already managed.
continue
result, data = imap_server.uid('fetch', uid, '(RFC822)')
if not data[0]:
continue
message = data[0][1]
# To leave the mail in the state in which they were.
if "Seen" not in data[1].decode("utf-8"):
imap_server.uid('STORE', uid, '+FLAGS', '(\\Seen)')
else:
imap_server.uid('STORE', uid, '-FLAGS', '(\\Seen)')
# See details in message_process() in mail_thread.py
if isinstance(message, xmlrpclib.Binary):
message = bytes(message.data)
if isinstance(message, str):
message = message.encode('utf-8')
msg_txt = email.message_from_bytes(message, policy=email.policy.SMTP)
try:
self._attachment_invoice(msg_txt)
new_max_uid = max(new_max_uid, int(uid))
except Exception:
_logger.info('Failed to process mail from %s server %s.', server.server_type, server.name, exc_info=True)
failed += 1
self._cr.commit()
count += 1
server.write({'l10n_it_last_uid': new_max_uid})
_logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", count, server.server_type, server.name, (count - failed), failed)
except Exception:
_logger.info("General failure when trying to fetch mail from %s server %s.", server.server_type, server.name, exc_info=True)
finally:
if imap_server:
imap_server.close()
imap_server.logout()
server.write({'date': fields.Datetime.now()})
return super(FetchmailServer, self.filtered(lambda s: not s.l10n_it_is_pec)).fetch_mail()
def _attachment_invoice(self, msg_txt):
parsed_values = self.env['mail.thread']._message_parse_extract_payload(msg_txt)
body, attachments = parsed_values['body'], parsed_values['attachments']
from_address = msg_txt.get('from')
for attachment in attachments:
split_attachment = attachment.fname.rpartition('.')
if len(split_attachment) < 3:
_logger.info('E-invoice filename not compliant: %s', attachment.fname)
continue
attachment_name = split_attachment[0]
attachment_ext = split_attachment[2]
split_underscore = attachment_name.rsplit('_', 2)
if len(split_underscore) < 2:
_logger.info('E-invoice filename not compliant: %s', attachment.fname)
continue
if attachment_ext != 'zip':
if split_underscore[1] in ['RC', 'NS', 'MC', 'MT', 'EC', 'SE', 'NE', 'DT']:
# we have a receipt
self._message_receipt_invoice(split_underscore[1], attachment)
else:
att_filename = attachment.fname
match = re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", att_filename)
# If match, we have an invoice.
if match:
# If it's signed, the content has a bytes type and we just remove the signature's envelope
if match.groups()[1] == 'xml.p7m':
att_content_data = remove_signature(attachment.content)
# If the envelope cannot be removed, the remove_signature returns None, so we skip
if not att_content_data:
_logger.warning("E-invoice couldn't be read: %s", att_filename)
continue
att_filename = att_filename.replace('.xml.p7m', '.xml')
else:
# Otherwise, it should be an utf-8 encoded XML string
att_content_data = attachment.content.encode()
self._create_invoice_from_mail(att_content_data, att_filename, from_address)
else:
if split_underscore[1] == 'AT':
# Attestazione di avvenuta trasmissione della fattura con impossibilità di recapito
self._message_AT_invoice(attachment)
else:
_logger.info('New E-invoice in zip file: %s', attachment.fname)
self._create_invoice_from_mail_with_zip(attachment, from_address)
def _create_invoice_from_mail(self, att_content_data, att_name, from_address):
""" Creates an invoice from the content of an email present in ir.attachments
:param att_content_data: The 'utf-8' encoded bytes string representing the content of the attachment.
:param att_name: The attachment's file name.
:param from_address: The sender address of the email.
"""
invoices = self.env['account.move']
# Check if we already imported the email as an attachment
existing = self.env['ir.attachment'].search([('name', '=', att_name), ('res_model', '=', 'account.move')])
if existing:
_logger.info('E-invoice already exist: %s', att_name)
return invoices
# Create the new attachment for the file
attachment = self.env['ir.attachment'].create({
'name': att_name,
'raw': att_content_data,
'res_model': 'account.move',
'type': 'binary'})
# Decode the file.
try:
tree = etree.fromstring(att_content_data)
except Exception:
_logger.info('The xml file is badly formatted: %s', att_name)
return invoices
invoices = self.env.ref('l10n_it_edi.edi_fatturaPA')._create_invoice_from_xml_tree(att_name, tree)
if not invoices:
_logger.info('E-invoice not found in file: %s', att_name)
return invoices
invoices.l10n_it_send_state = "new"
invoices.invoice_source_email = from_address
for invoice in invoices:
invoice.with_context(no_new_invoice=True, default_res_id=invoice.id) \
.message_post(body=(_("Original E-invoice XML file")), attachment_ids=[attachment.id])
self._cr.commit()
_logger.info('New E-invoices (%s), ids: %s', att_name, [x.id for x in invoices])
return invoices
def _create_invoice_from_mail_with_zip(self, attachment_zip, from_address):
with zipfile.ZipFile(io.BytesIO(attachment_zip.content)) as z:
for att_name in z.namelist():
existing = self.env['ir.attachment'].search([('name', '=', att_name), ('res_model', '=', 'account.move')])
if existing:
# invoice already exist
_logger.info('E-invoice in zip file (%s) already exist: %s', attachment_zip.fname, att_name)
continue
att_content = z.open(att_name).read()
self._create_invoice_from_mail(att_content, att_name, from_address)
def _message_AT_invoice(self, attachment_zip):
with zipfile.ZipFile(io.BytesIO(attachment_zip.content)) as z:
for attachment_name in z.namelist():
split_name_attachment = attachment_name.rpartition('.')
if len(split_name_attachment) < 3:
continue
split_underscore = split_name_attachment[0].rsplit('_', 2)
if len(split_underscore) < 2:
continue
if split_underscore[1] == 'AT':
attachment = z.open(attachment_name).read()
_logger.info('New AT receipt for: %s', split_underscore[0])
try:
tree = etree.fromstring(attachment)
except:
_logger.info('Error in decoding new receipt file: %s', attachment_name)
return
elements = tree.xpath('//NomeFile')
if elements and elements[0].text:
filename = elements[0].text
else:
return
related_invoice = self._search_edi_invoice(filename)
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', filename)
return
related_invoice.l10n_it_send_state = 'failed_delivery'
info = self._return_multi_line_xml(tree, ['//IdentificativoSdI', '//DataOraRicezione', '//MessageId', '//PecMessageId', '//Note'])
related_invoice.message_post(
body=(_("ES certify that it has received the invoice and that the file \
could not be delivered to the addressee. <br/>%s") % (info))
)
def _message_receipt_invoice(self, receipt_type, attachment):
try:
tree = etree.fromstring(attachment.content.encode())
except:
_logger.info('Error in decoding new receipt file: %s', attachment.fname)
return {}
elements = tree.xpath('//NomeFile')
if elements and elements[0].text:
filename = elements[0].text
else:
return {}
if receipt_type == 'RC':
# Delivery receipt
# This is the receipt sent by the ES to the transmitting subject to communicate
# delivery of the file to the addressee
related_invoice = self._search_edi_invoice(filename, 'sent')
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', attachment.fname)
return
related_invoice.l10n_it_send_state = 'delivered'
info = self._return_multi_line_xml(tree, ['//IdentificativoSdI', '//DataOraRicezione', '//DataOraConsegna', '//Note'])
related_invoice.message_post(
body=(_("E-Invoice is delivery to the destinatory:<br/>%s") % (info))
)
elif receipt_type == 'NS':
# Rejection notice
# This is the receipt sent by the ES to the transmitting subject if one or more of
# the checks carried out by the ES on the file received do not have a successful result.
related_invoice = self._search_edi_invoice(filename, 'sent')
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', attachment.fname)
return
related_invoice.l10n_it_send_state = 'invalid'
error = self._return_error_xml(tree)
related_invoice.message_post(
body=(_("Errors in the E-Invoice :<br/>%s") % (error))
)
related_invoice.activity_schedule(
'mail.mail_activity_data_todo',
summary='Rejection notice',
user_id=related_invoice.invoice_user_id.id if related_invoice.invoice_user_id else self.env.user.id)
elif receipt_type == 'MC':
# Failed delivery notice
# This is the receipt sent by the ES to the transmitting subject if the file is not
# delivered to the addressee.
related_invoice = self._search_edi_invoice(filename, 'sent')
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', attachment.fname)
return
info = self._return_multi_line_xml(tree, [
'//IdentificativoSdI',
'//DataOraRicezione',
'//Descrizione',
'//MessageId',
'//Note'])
related_invoice.message_post(
body=(_("The E-invoice is not delivered to the addressee. The Exchange System is\
unable to deliver the file to the Public Administration. The Exchange System will\
contact the PA to report the problem and request that they provide a solution. \
During the following 15 days, the Exchange System will try to forward the FatturaPA\
file to the Administration in question again. More information:<br/>%s") % (info))
)
elif receipt_type == 'NE':
# Outcome notice
# This is the receipt sent by the ES to the invoice sender to communicate the result
# (acceptance or refusal of the invoice) of the checks carried out on the document by
# the addressee.
related_invoice = self._search_edi_invoice(filename, 'delivered')
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', attachment.fname)
return
elements = tree.xpath('//Esito')
if elements and elements[0].text:
if elements[0].text == 'EC01':
related_invoice.l10n_it_send_state = 'delivered_accepted'
elif elements[0].text == 'EC02':
related_invoice.l10n_it_send_state = 'delivered_refused'
info = self._return_multi_line_xml(tree,
['//Esito',
'//Descrizione',
'//IdentificativoSdI',
'//DataOraRicezione',
'//DataOraConsegna',
'//Note'
])
related_invoice.message_post(
body=(_("Outcome notice: %s<br/>%s") % (related_invoice.l10n_it_send_state, info))
)
if related_invoice.l10n_it_send_state == 'delivered_refused':
related_invoice.activity_schedule(
'mail.mail_activity_todo',
user_id=related_invoice.invoice_user_id.id if related_invoice.invoice_user_id else self.env.user.id,
summary='Outcome notice: Refused')
# elif receipt_type == 'MT':
# Metadata file
# This is the file sent by the ES to the addressee together with the invoice file,
# containing the main reference data of the file useful for processing, including
# the IdentificativoSDI.
# Useless for Odoo
elif receipt_type == 'DT':
# Deadline passed notice
# This is the receipt sent by the ES to both the invoice sender and the invoice
# addressee to communicate the expiry of the maximum term for communication of
# acceptance/refusal.
related_invoice = self._search_edi_invoice(filename, 'delivered')
if not related_invoice:
_logger.info('Error: invoice not found for receipt file: %s', attachment.fname)
return
related_invoice.l10n_it_send_state = 'delivered_expired'
info = self._return_multi_line_xml(tree, [
'//Descrizione',
'//IdentificativoSdI',
'//Note'])
related_invoice.message_post(
body=(_("Expiration of the maximum term for communication of acceptance/refusal:\
%s<br/>%s") % (filename, info))
)
def _return_multi_line_xml(self, tree, element_tags):
output_str = "<ul>"
for element_tag in element_tags:
elements = tree.xpath(element_tag)
if not elements:
continue
for element in elements:
if element.text:
text = " ".join(element.text.split())
output_str += "<li>%s: %s</li>" % (element.tag, text)
return output_str + "</ul>"
def _return_error_xml(self, tree):
output_str = "<ul>"
elements = tree.xpath('//Errore')
if not elements:
return
for element in elements:
descrizione = " ".join(element[1].text.split())
if descrizione:
output_str += "<li>Errore %s: %s</li>" % (element[0].text, descrizione)
return output_str + "</ul>"
class IrMailServer(models.Model):
_name = "ir.mail_server"
_inherit = "ir.mail_server"
res_company_l10n_it_mail_pec_server_ids = fields.One2many(
comodel_name='res.company',
inverse_name='l10n_it_mail_pec_server_id',
string='PEC-mail server active usage',
readonly=True)
def _get_test_email_addresses(self):
self.ensure_one()
company = self.env["res.company"].search([("l10n_it_mail_pec_server_id", "=", self.id)], limit=1)
if not company:
# it's not a PEC server
return super()._get_test_email_addresses()
email_from = self.smtp_user
if not email_from:
raise UserError(_('Please configure Username for this Server PEC'))
email_to = company.l10n_it_address_recipient_fatturapa
if not email_to:
raise UserError(_('Please configure Government PEC-mail in company settings'))
return email_from, email_to
def _active_usages_compute(self):
usages_super = super()._active_usages_compute()
for record in self.filtered('res_company_l10n_it_mail_pec_server_ids'):
usages_super.setdefault(record.id, []).extend(
map(lambda c: _('%s (Company E-Invoicing PEC-mail Server)', c.display_name),
record.res_company_l10n_it_mail_pec_server_ids)
)
return usages_super
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False,
attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None,
body_alternative=None, subtype_alternative='plain'):
if self.env.context.get('wo_bounce_return_path') and headers:
headers['Return-Path'] = email_from
return super(IrMailServer, self).build_email(email_from, email_to, subject, body, email_cc=email_cc, email_bcc=email_bcc, reply_to=reply_to,
attachments=attachments, message_id=message_id, references=references, object_id=object_id, subtype=subtype, headers=headers,
body_alternative=body_alternative, subtype_alternative=subtype_alternative)
-9
View File
@@ -34,15 +34,6 @@ class ResCompany(models.Model):
l10n_it_tax_system = fields.Selection(selection=TAX_SYSTEM, string="Tax System",
help="Please select the Tax system to which you are subjected.")
# PEC server
l10n_it_mail_pec_server_id = fields.Many2one('ir.mail_server', string="Server PEC",
help="Configure your PEC-mail server to send electronic invoices.")
l10n_it_address_recipient_fatturapa = fields.Char(string="Government PEC-mail",
help="Enter Government PEC-mail address. Ex: sdi01@pec.fatturapa.it")
l10n_it_address_send_fatturapa = fields.Char(string="Company PEC-mail",
help="Enter your company PEC-mail address. Ex: yourcompany@pec.mail.it")
# Economic and Administrative Index
l10n_it_has_eco_index = fields.Boolean(default=False,
help="The seller/provider is a company listed on the register of companies and as\
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, models, fields, _
from odoo.exceptions import UserError
+4 -3
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_ir_mail_server
from . import test_res_partner
from . import common
from . import test_edi_export
from . import test_edi_import
from . import test_edi_reverse_charge
+64
View File
@@ -0,0 +1,64 @@
from unittest.mock import patch, MagicMock
from odoo import tools
from odoo.tests import tagged
from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
from odoo.addons.account_edi_proxy_client.models.account_edi_proxy_user import AccountEdiProxyClientUser
def patch_proxy_user(func):
@patch.object(AccountEdiProxyClientUser, '_make_request', MagicMock(spec=AccountEdiProxyClientUser._make_request))
@patch.object(AccountEdiProxyClientUser, '_decrypt_data', MagicMock(spec=AccountEdiProxyClientUser._decrypt_data))
@patch.object(AccountEdiProxyClientUser, '_get_demo_state', MagicMock(spec=AccountEdiProxyClientUser._get_demo_state))
def patched(self, *args, **kwargs):
return func(self, *args, **kwargs)
return patched
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdi(AccountEdiTestCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_it.l10n_it_chart_template_generic', edi_format_ref="l10n_it_edi.edi_fatturaPA"):
super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref)
# Company data ------
cls.company = cls.company_data_2['company']
cls.company.l10n_it_codice_fiscale = '01234560157'
cls.company.partner_id.l10n_it_pa_index = "0803HR0"
cls.company.vat = 'IT01234560157'
cls.test_bank = cls.env['res.partner.bank'].with_company(cls.company).create({
'partner_id': cls.company.partner_id.id,
'acc_number': 'IT1212341234123412341234123',
'bank_name': 'BIG BANK',
'bank_bic': 'BIGGBANQ',
})
cls.company.l10n_it_tax_system = "RF01"
cls.company.street = "1234 Test Street"
cls.company.zip = "12345"
cls.company.city = "Prova"
cls.company.country_id = cls.env.ref('base.it')
# We create this because we are unable to post without a proxy user existing
cls.proxy_user = cls.env['account_edi_proxy_client.user'].create({
'id_client': 'l10n_it_edi_test',
'company_id': cls.company.id,
'edi_format_id': cls.edi_format.id,
'edi_identification': 'l10n_it_edi_test',
'private_key': 'l10n_it_edi_test',
})
cls.standard_line = {
'name': 'standard_line',
'quantity': 1,
'price_unit': 800.40,
'tax_ids': [(6, 0, [cls.company.account_sale_tax_id.id])]
}
@classmethod
def _get_test_file_content(cls, filename):
""" Get the content of a test file inside this module """
path = 'l10n_it_edi/tests/expected_xmls/' + filename
with tools.file_open(path, mode='rb') as test_file:
return test_file.read()
@@ -8,35 +8,19 @@ from freezegun import freeze_time
from odoo import tools
from odoo.tests import tagged
from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class TestItEdiCommon(AccountEdiTestCommon):
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiExport(TestItEdi):
@classmethod
def setUpClass(cls):
super().setUpClass(chart_template_ref='l10n_it.l10n_it_chart_template_generic',
edi_format_ref='l10n_it_edi.edi_fatturaPA')
# Use the company_data_2 to test that the e-invoice is imported for the right company
cls.company = cls.company_data_2['company']
cls.company.l10n_it_codice_fiscale = '01234560157'
cls.company.vat = 'IT01234560157'
cls.test_bank = cls.env['res.partner.bank'].with_company(cls.company).create({
'partner_id': cls.company.partner_id.id,
'acc_number': 'IT1212341234123412341234123',
'bank_name': 'BIG BANK',
'bank_bic': 'BIGGBANQ',
})
cls.company.l10n_it_tax_system = "RF01"
cls.company.street = "1234 Test Street"
cls.company.zip = "12345"
cls.company.city = "Prova"
cls.company.country_id = cls.env.ref('base.it')
cls.price_included_tax = cls.env['account.tax'].create({
'name': '22% price included tax',
'amount': 22.0,
@@ -119,13 +103,6 @@ class TestItEdiCommon(AccountEdiTestCommon):
'is_company': True,
})
cls.standard_line = {
'name': 'standard_line',
'quantity': 1,
'price_unit': 800.40,
'tax_ids': [(6, 0, [cls.company.account_sale_tax_id.id])]
}
cls.standard_line_below_400 = {
'name': 'cheap_line',
'quantity': 1,
@@ -290,15 +267,6 @@ class TestItEdiCommon(AccountEdiTestCommon):
],
})
# We create this because we are unable to post without a proxy user existing
cls.proxy_user = cls.env['account_edi_proxy_client.user'].create({
'id_client': 'l10n_it_edi_sdicoop_test',
'company_id': cls.company.id,
'edi_format_id': cls.edi_format.id,
'edi_identification': 'l10n_it_edi_sdicoop_test',
'private_key': 'l10n_it_edi_sdicoop_test',
})
cls.zero_tax_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
@@ -335,13 +303,10 @@ class TestItEdiCommon(AccountEdiTestCommon):
@classmethod
def _get_test_file_content(cls, filename):
""" Get the content of a test file inside this module """
path = 'l10n_it_edi_sdicoop/tests/expected_xmls/' + filename
path = 'l10n_it_edi/tests/expected_xmls/' + filename
with tools.file_open(path, mode='rb') as test_file:
return test_file.read()
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdi(TestItEdiCommon):
def test_price_included_taxes(self):
""" When the tax is price included, there should be a rounding value added to the xml, if the sum(subtotals) * tax_rate is not
equal to taxable base * tax rate (there is a constraint in the edi where taxable base * tax rate = tax amount, but also
@@ -415,7 +380,7 @@ class TestItEdi(TestItEdiCommon):
2577.29
</xpath>
''')
invoice_etree = etree.fromstring(self.price_included_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.price_included_invoice))
# Remove the attachment and its details
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
@@ -424,7 +389,7 @@ class TestItEdi(TestItEdiCommon):
# The EDI can account for discounts, but a line with, for example, a 100% discount should still have
# a corresponding tax with a base amount of 0
invoice_etree = etree.fromstring(self.partial_discount_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.partial_discount_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
@@ -481,7 +446,7 @@ class TestItEdi(TestItEdiCommon):
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_fully_discounted_inovice(self):
invoice_etree = etree.fromstring(self.full_discount_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.full_discount_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
@@ -518,7 +483,7 @@ class TestItEdi(TestItEdiCommon):
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_non_latin_and_latin_invoice(self):
invoice_etree = etree.fromstring(self.non_latin_and_latin_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.non_latin_and_latin_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
@@ -567,7 +532,7 @@ class TestItEdi(TestItEdiCommon):
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_below_400_codice_simplified_invoice(self):
invoice_etree = etree.fromstring(self.below_400_codice_simplified_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.below_400_codice_simplified_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_simplified_basis_xml),
'''
@@ -597,7 +562,7 @@ class TestItEdi(TestItEdiCommon):
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_total_400_VAT_simplified_invoice(self):
invoice_etree = etree.fromstring(self.total_400_VAT_simplified_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.total_400_VAT_simplified_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_simplified_basis_xml),
'''
@@ -635,7 +600,7 @@ class TestItEdi(TestItEdiCommon):
self.assertEqual(res[self.pa_partner_invoice], {'attachment': self.pa_partner_invoice.l10n_it_edi_attachment_id, 'success': True})
def test_zero_percent_taxes(self):
invoice_etree = etree.fromstring(self.zero_tax_invoice._export_as_xml())
invoice_etree = etree.fromstring(self.edi_format._export_as_xml(self.zero_tax_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
+136
View File
@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import logging
from freezegun import freeze_time
from lxml import etree
from odoo import tools
from odoo.tests import tagged
from odoo.addons.l10n_it_edi.tests.common import TestItEdi, patch_proxy_user
from odoo.addons.l10n_it_edi.tools.remove_signature import remove_signature
_logger = logging.getLogger(__name__)
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiImport(TestItEdi):
""" Main test class for the l10n_it_edi vendor bills XML import"""
fake_test_content = """<?xml version="1.0" encoding="UTF-8"?>
<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"
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>
<ProgressivoInvio>TWICE_TEST</ProgressivoInvio>
</DatiTrasmissione>
<CessionarioCommittente>
<DatiAnagrafici>
<CodiceFiscale>01234560157</CodiceFiscale>
</DatiAnagrafici>
</CessionarioCommittente>
</FatturaElettronicaHeader>
<FatturaElettronicaBody>
<DatiGenerali>
<DatiGeneraliDocumento>
<TipoDocumento>TD02</TipoDocumento>
</DatiGeneraliDocumento>
</DatiGenerali>
</FatturaElettronicaBody>
</p:FatturaElettronica>"""
@classmethod
def setUpClass(cls):
super().setUpClass(chart_template_ref='l10n_it.l10n_it_chart_template_generic',
edi_format_ref='l10n_it_edi.edi_fatturaPA')
# Build test data.
# invoice_filename1 is used for vendor bill receipts tests
# invoice_filename2 is used for vendor bill tests
cls.invoice_filename1 = 'IT01234567890_FPR01.xml'
cls.invoice_filename2 = 'IT01234567890_FPR02.xml'
cls.signed_invoice_filename = 'IT01234567890_FPR01.xml.p7m'
cls.invoice_content = cls._get_test_file_content(cls.invoice_filename1)
cls.signed_invoice_content = cls._get_test_file_content(cls.signed_invoice_filename)
cls.invoice = cls.env['account.move'].create({
'move_type': 'in_invoice',
'ref': '01234567890'
})
cls.attachment = cls.env['ir.attachment'].create({
'name': cls.invoice_filename1,
'raw': cls.invoice_content,
'res_id': cls.invoice.id,
'res_model': 'account.move',
})
cls.edi_document = cls.env['account.edi.document'].create({
'edi_format_id': cls.edi_format.id,
'move_id': cls.invoice.id,
'attachment_id': cls.attachment.id,
'state': 'sent'
})
cls.test_invoice_xmls = {k: cls._get_test_file_content(v) for k, v in [
('normal_1', 'IT01234567890_FPR01.xml'),
('signed', 'IT01234567890_FPR01.xml.p7m'),
]}
@classmethod
def _get_test_file_content(cls, filename):
""" Get the content of a test file inside this module """
path = 'l10n_it_edi/tests/expected_xmls/' + filename
with tools.file_open(path, mode='rb') as test_file:
return test_file.read()
# -----------------------------
#
# Vendor bills
#
# -----------------------------
def test_receive_vendor_bill(self):
""" Test a sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
content = etree.fromstring(self.invoice_content)
invoices = self.edi_format._create_invoice_from_xml_tree(self.invoice_filename2, content)
self.assertTrue(bool(invoices))
def test_receive_signed_vendor_bill(self):
""" Test a signed (P7M) sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
with freeze_time('2020-04-06'):
content = etree.fromstring(remove_signature(self.signed_invoice_content))
invoices = self.edi_format._create_invoice_from_xml_tree(self.signed_invoice_filename, content)
self.assertRecordValues(invoices, [{
'company_id': self.company.id,
'name': 'BILL/2014/12/0001',
'invoice_date': datetime.date(2014, 12, 18),
'ref': '01234567890',
}])
@patch_proxy_user
def test_receive_same_vendor_bill_twice(self):
""" Test that the second time we are receiving an SdiCoop invoice, the second is discarded """
# The make_request function is called twice when running the _cron_receive_fattura_pa
# first to the /in/RicezioneInvoice endpoint (to find new incoming invoices)
# second to the /api/l10n_it_edi/1/ack to acknowledge the invoices have been recieved
content = self.fake_test_content.encode()
fake_responses = [
# Response of the format id_transaction: fattura dict
{'9999999999': {'filename': self.invoice_filename2, 'key': '123', 'file': content}},
# The response from the _make_request for the ack can be None
None,
] * 2 # Since the cron is run twice, and we want the fake results both times
self.proxy_user._make_request.side_effect = fake_responses
# When calling the decrypt function, the file we're accessing is already decrypted, just return the file
self.proxy_user._decrypt_data.side_effect = lambda file, _key: file
# In order for the cron function to progress to the point that it imports, we cannot be in demo mode
self.proxy_user._get_demo_state.return_value = 'unit_test'
self.edi_format.with_context({'test_skip_commit': True})._cron_receive_fattura_pa()
# There should be one attachement with this filename
attachment = self.env['ir.attachment'].search([('name', '=', self.invoice_filename2)])
self.assertEqual(len(attachment), 1)
invoice = self.env['account.move'].search([('payment_reference', '=', 'TWICE_TEST')])
self.assertEqual(len(invoice), 1)
@@ -6,15 +6,16 @@ from lxml import etree
from odoo import fields
from odoo.tests import tagged
from odoo.addons.l10n_it_edi_sdicoop.tests.test_edi_xml import TestItEdiCommon
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiReverseCharge(TestItEdiCommon):
class TestItEdiReverseCharge(TestItEdi):
@classmethod
def setUpClass(cls):
super().setUpClass()
super().setUpClass(chart_template_ref='l10n_it.l10n_it_chart_template_generic',
edi_format_ref='l10n_it_edi.edi_fatturaPA')
# Helper functions -----------
def get_tag_ids(tag_codes):
@@ -180,7 +181,7 @@ class TestItEdiReverseCharge(TestItEdiCommon):
)
def _test_invoice_with_sample_file(self, invoice, filename, xpaths_file=None, xpaths_result=None):
result = self._cleanup_etree(invoice._export_as_xml(), xpaths_result)
result = self._cleanup_etree(self.edi_format._export_as_xml(invoice), xpaths_result)
expected = self._cleanup_etree(self._get_test_file_content(filename), xpaths_file)
self.assertXmlTreeEqual(result, expected)
@@ -1,185 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
import logging
from collections import namedtuple
from unittest.mock import patch
from freezegun import freeze_time
from odoo import tools
from odoo.exceptions import UserError
from odoo.tests import tagged
from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
from odoo.addons.l10n_it_edi.tools.remove_signature import remove_signature
_logger = logging.getLogger(__name__)
@tagged('post_install_l10n', 'post_install', '-at_install')
class PecMailServerTests(AccountEdiTestCommon):
""" Main test class for the l10n_it_edi vendor bills XML import from a PEC mail account"""
fake_test_content = """<?xml version="1.0" encoding="UTF-8"?>
<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"
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>
<CessionarioCommittente>
<DatiAnagrafici>
<CodiceFiscale>01234560157</CodiceFiscale>
</DatiAnagrafici>
</CessionarioCommittente>
</FatturaElettronicaHeader>
<FatturaElettronicaBody>
<DatiGenerali>
<DatiGeneraliDocumento>
<TipoDocumento>TD02</TipoDocumento>
</DatiGeneraliDocumento>
</DatiGenerali>
</FatturaElettronicaBody>
</p:FatturaElettronica>"""
@classmethod
def setUpClass(cls):
""" Setup the test class with a PEC mail server and a fake fatturaPA content """
super().setUpClass(chart_template_ref='l10n_it.l10n_it_chart_template_generic',
edi_format_ref='l10n_it_edi.edi_fatturaPA')
# Use the company_data_2 to test that the e-invoice is imported for the right company
cls.company = cls.company_data_2['company']
# Initialize the company's codice fiscale
cls.company.l10n_it_codice_fiscale = '01234560157'
# Build test data.
# invoice_filename1 is used for vendor bill receipts tests
# invoice_filename2 is used for vendor bill tests
cls.invoice_filename1 = 'IT01234567890_FPR01.xml'
cls.invoice_filename2 = 'IT01234567890_FPR02.xml'
cls.signed_invoice_filename = 'IT01234567890_FPR01.xml.p7m'
cls.invoice_content = cls._get_test_file_content(cls.invoice_filename1)
cls.signed_invoice_content = cls._get_test_file_content(cls.signed_invoice_filename)
cls.invoice = cls.env['account.move'].create({
'move_type': 'in_invoice',
'ref': '01234567890'
})
cls.attachment = cls.env['ir.attachment'].create({
'name': cls.invoice_filename1,
'raw': cls.invoice_content,
'res_id': cls.invoice.id,
'res_model': 'account.move',
})
cls.edi_document = cls.env['account.edi.document'].create({
'edi_format_id': cls.edi_format.id,
'move_id': cls.invoice.id,
'attachment_id': cls.attachment.id,
'state': 'sent'
})
# Initialize the fetchmail server that has to be tested
cls.server = cls.env['fetchmail.server'].sudo().create({
'name': 'test_server',
'server_type': 'imap',
'l10n_it_is_pec': True})
cls.test_invoice_xmls = {k: cls._get_test_file_content(v) for k, v in [
('normal_1', 'IT01234567890_FPR01.xml'),
('signed', 'IT01234567890_FPR01.xml.p7m'),
]}
@classmethod
def _get_test_file_content(cls, filename):
""" Get the content of a test file inside this module """
path = 'l10n_it_edi/tests/expected_xmls/' + filename
with tools.file_open(path, mode='rb') as test_file:
return test_file.read()
def _create_invoice(self, content, filename):
""" Create an invoice from given attachment content """
with patch.object(self.server._cr, 'commit', return_value=None):
if filename.endswith(".p7m"):
content = remove_signature(content)
return self.server._create_invoice_from_mail(content, filename, 'fake@address.be')
# -----------------------------
#
# Vendor bills
#
# -----------------------------
def test_receive_vendor_bill(self):
""" Test a sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
invoices = self._create_invoice(self.invoice_content, self.invoice_filename2)
self.assertTrue(bool(invoices))
def test_receive_signed_vendor_bill(self):
""" Test a signed (P7M) sample e-invoice file from https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2/IT01234567890_FPR01.xml """
with freeze_time('2020-04-06'):
invoices = self._create_invoice(self.signed_invoice_content, self.signed_invoice_filename)
self.assertRecordValues(invoices, [{
'company_id': self.company.id,
'name': 'BILL/2014/12/0001',
'invoice_date': datetime.date(2014, 12, 18),
'ref': '01234567890',
}])
def test_receive_same_vendor_bill_twice(self):
""" Test that the second time we are receiving a PEC mail with the same attachment, the second is discarded """
content = self.fake_test_content.encode()
for result in [True, False]:
invoice = self._create_invoice(content, self.invoice_filename2)
self.assertEqual(result, bool(invoice))
# -----------------------------
#
# Receipts
#
# -----------------------------
def _test_receipt(self, receipt_type, source_state, destination_state):
""" Test a receipt from the ones in the module's test files """
# Simulate the 'sent' state of the move, even if we didn't actually send an email in this test
self.invoice.l10n_it_send_state = source_state
# Create a fake receipt from the test file
receipt_filename = 'IT01234567890_FPR01_%s_001.xml' % receipt_type
receipt_content = self._get_test_file_content(receipt_filename).decode()
create_mail_attachment = namedtuple('Attachment', ('fname', 'content', 'info'))
receipt_mail_attachment = create_mail_attachment(receipt_filename, receipt_content, {})
# Simulate the arrival of the receipt
with patch.object(self.server._cr, 'commit', return_value=None):
self.server._message_receipt_invoice(receipt_type, receipt_mail_attachment)
# Check the Destination state of the edi_document
self.assertTrue(destination_state, self.edi_document.state)
def test_ricevuta_consegna(self):
""" Test a receipt adapted from https://www.fatturapa.gov.it/export/documenti/messaggi/v1.0/IT01234567890_11111_RC_001.xml """
self._test_receipt('RC', 'sent', 'delivered')
def test_decorrenza_termini(self):
""" Test a receipt adapted from https://www.fatturapa.gov.it/export/documenti/messaggi/v1.0/IT01234567890_11111_DT_001.xml """
self._test_receipt('DT', 'delivered', 'delivered_expired')
# -----------------------------
# Mail server
# -----------------------------
def test_server_archived_usage_protection(self):
""" Test the protection against using archived server (servers used cannot be archived) """
server = self.env['ir.mail_server'].create({
'name': 'Server',
'smtp_host': 'archive-test.smtp.local',
})
self.company.l10n_it_mail_pec_server_id = server.id
with self.assertRaises(UserError, msg='Server cannot be archived because it is used'):
server.action_archive()
self.assertTrue(server.active)
self.company.l10n_it_mail_pec_server_id = False
server.action_archive() # No more usage -> can be archived
self.assertFalse(server.active)
-91
View File
@@ -1,19 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="fetchmail_server_form_l10n_it" model="ir.ui.view">
<field name="name">fetchmail.server.form.l10n.it</field>
<field name="model">fetchmail.server</field>
<field name="priority">20</field>
<field name="inherit_id" ref="mail.view_email_server_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='date']" position="after">
<field name="l10n_it_is_pec"/>
</xpath>
</data>
</field>
</record>
<record id="account_tax_form_l10n_it" model="ir.ui.view">
<field name="name">account.tax.form.l10n.it</field>
<field name="model">account.tax</field>
@@ -58,11 +44,6 @@
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//div[hasclass('o_address_format')]" position="after">
<field name="l10n_it_mail_pec_server_id" attrs="{'invisible': [('country_code', '!=', 'IT')]}"/>
<field name="l10n_it_address_send_fatturapa" attrs="{'invisible': [('country_code', '!=', 'IT')]}"/>
<field name="l10n_it_address_recipient_fatturapa" attrs="{'invisible': [('country_code', '!=', 'IT')]}"/>
</xpath>
<xpath expr="//field[@name='vat']" position="after">
<field name="l10n_it_codice_fiscale" attrs="{'invisible': [('country_code', '!=', 'IT')]}"/>
<field name="l10n_it_tax_system" attrs="{'invisible': [('country_code', '!=', 'IT')]}"/>
@@ -102,46 +83,6 @@
</field>
</record>
<record id="invoice_supplier_tree_l10n_it" model="ir.ui.view">
<field name="name">account.invoice.supplier.tree.l10n.it</field>
<field name="model">account.move</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_move_tree"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='currency_id']" position="after">
<field name="l10n_it_send_state" invisible="1" widget="label_selection" options="{'classes': {'to_send': 'default', 'invalid': 'danger', 'sent': 'warning',
'delivered': 'success', 'delivered_accepted': 'success', 'delivered_refused': 'success', 'delivered_expired': 'success', 'failed_delivery': 'success'}}"/>
<button icon="fa-paper-plane-o" class="btn-outline-warning disabled" aria-label="Sent" title="Sent" attrs="{'invisible': [('l10n_it_send_state', '!=', 'sent')]}"/>
<button icon="fa-exclamation-triangle" class="btn-outline-danger disabled" aria-label="Error" title="Error" attrs="{'invisible': [('l10n_it_send_state', '!=', 'invalid')]}"/>
<button icon="fa-check" class="btn-outline-success disabled" aria-label="Delivered" title="Delivered" attrs="{'invisible': [('l10n_it_send_state', 'not in', ['delivered', 'delivered_accepted', 'delivered_refused', 'delivered_expired', 'failed_delivery'])]}"/>
</xpath>
</data>
</field>
</record>
<record id="invoice_kanban_l10n_it" model="ir.ui.view">
<field name="name">account.invoice.kanban.l10n.it</field>
<field name="model">account.move</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_account_move_kanban"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='currency_id']" position="after">
<field name="l10n_it_send_state"/>
</xpath>
<xpath expr="//div[hasclass('o_kanban_record_headings')]" position="inside">
<i class="text-success fa fa-plus-circle" aria-label="New" t-if="record.l10n_it_send_state.raw_value == 'new'"/>
<i class="text-warning fa fa-paper-plane-o" aria-label="Sent, waiting for response" t-if="record.l10n_it_send_state.raw_value == 'sent'"/>
<i class="text-danger fa fa-exclamation-triangle" aria-label="Sent, but invalid" t-if="record.l10n_it_send_state.raw_value == 'invalid'"/>
<i class="text-success fa fa-check" aria-label="Delivered Invoice" t-if="['delivered', 'delivered_accepted', 'delivered_refused', 'delivered_expired', 'failed_delivery'].indexOf(record.l10n_it_send_state.raw_value) >= 0" />
</xpath>
</data>
</field>
</record>
<record id="account_invoice_form_l10n_it" model="ir.ui.view">
<field name="name">account.move.form.l10n.it</field>
<field name="model">account.move</field>
@@ -149,20 +90,6 @@
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='move_type']" position="before">
<div class="alert alert-success" role="alert"
attrs="{'invisible': ['|', ('move_type', 'not in', ('out_invoice', 'out_refund')), ('l10n_it_send_state', 'not in', ['delivered', 'delivered_accepted', 'delivered_refused', 'delivered_expired', 'failed_delivery'])]}">
<i class="fa fa-check" aria-label="Delivered" title="Delivered"></i> <field name="l10n_it_send_state" readonly="1"/>
</div>
<div class="alert alert-warning" role="alert"
attrs="{'invisible': ['|', ('move_type', 'not in', ('out_invoice', 'out_refund')), ('l10n_it_send_state', '!=', 'sent')]}">
<i class="fa fa-paper-plane-o"/> E-Invoice sent, waiting for a response
</div>
<div class="alert alert-danger" role="alert"
attrs="{'invisible': ['|', ('move_type', 'not in', ('out_invoice', 'out_refund')), ('l10n_it_send_state', '!=', 'invalid')]}">
<i class="fa fa-exclamation-triangle"/> E-Invoice check failed. You can modify the invoice, and resend it.
</div>
</xpath>
<xpath expr="//page[@name='other_info']" position="after">
<page string="Electronic Invoicing"
name="electronic_invoicing"
@@ -180,24 +107,6 @@
</field>
</record>
<record id="view_account_invoice_filter_l10n_it" model="ir.ui.view">
<field name="name">account.invoice.select.l10n.it</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='late']" position="after">
<separator/>
<filter name="error" string="E-invoice error" domain="[('l10n_it_send_state', '=', 'invalid')]"/>
<filter name="sent" string="E-invoice sent" domain="[('l10n_it_send_state', '=', 'sent')]"/>
<filter name="no_sent" string="E-invoice to send" domain="[('l10n_it_send_state', 'in', ['to_send',False])]"/>
<filter name="delivered" string="E-invoice delivered" domain="[('l10n_it_send_state', 'in', ['delivered', 'delivered_accepted', 'delivered_refused', 'delivered_expired', 'failed_delivery'])]"/>
</xpath>
<xpath expr="//filter[@name='status']" position="after">
<filter name="send_status" string="Send status" context="{'group_by':'l10n_it_send_state'}"/>
</xpath>
</field>
</record>
<record id="l10n_it_ddt" model="ir.ui.view">
<field name="name">ddt.form.l10n.it</field>
<field name="model">l10n_it.ddt</field>
-14
View File
@@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from odoo import api, SUPERUSER_ID
def _disable_pec_mail_post_init(cr, registry):
''' Pec mail cannot be used in conjunction with SdiCoop, so disable the Pec fetchmail servers.
'''
env = api.Environment(cr, SUPERUSER_ID, {})
env['fetchmail.server'].search([('l10n_it_is_pec', '=', True)]).l10n_it_is_pec = False
env['res.company'].search([('l10n_it_mail_pec_server_id', '!=', None)]).l10n_it_mail_pec_server_id = None
@@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Italy - E-invoicing (SdiCoop)',
'version': '0.3',
'depends': [
'l10n_it_edi',
'account_edi',
'account_edi_proxy_client',
],
'auto_install': ['l10n_it_edi'],
'author': 'Odoo',
'description': """
E-invoice implementation for Italy with the web-service. Ability to send and receive document from SdiCoop. Files sent by SdiCoop are first stored on the proxy
and then fetched by this module.
""",
'category': 'Accounting/Localizations/EDI',
'website': 'http://www.odoo.com/',
'data': [
'data/cron.xml',
'views/l10n_it_view.xml',
'views/res_config_settings_views.xml',
],
'post_init_hook': '_disable_pec_mail_post_init',
'license': 'LGPL-3',
}
-305
View File
@@ -1,305 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_it_edi_sdicoop
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-03-30 10:14+0000\n"
"PO-Revision-Date: 2022-03-30 10:14+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">\n"
" Fattura Elettronica mode\n"
" </span>"
msgstr "<span class=\"o_form_label\">Modalitá Fattura Elettronica</span>"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "<span class=\"o_form_label\">Allow Odoo to process invoices</span>"
msgstr "<span class=\"o_form_label\">Permetti ad Odoo di inviare le fatture</span>"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "A Demo service is in use."
msgstr "Un servizio Demo è in uso"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "An Official or Test service has been registered."
msgstr "É giá stato registrato un servizio Ufficiale o di Test."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Attached file is empty"
msgstr "Il file allegato è vuoto"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "By checking this box, I accept that Odoo may process my invoices."
msgstr "Selezionando questa casella, accetto che Odoo invii le mie fatture."
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_res_config_settings
msgid "Config Settings"
msgstr "Impostazione configurazioni"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__demo
msgid "Demo"
msgstr "Demo"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format__display_name
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_account_edi_format
msgid "EDI format"
msgstr "Formato EDI"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "Electronic Document Invoicing"
msgstr "Interscambio di dati in formato elettronico"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_bank_statement_line__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_payment__l10n_it_edi_attachment_id
msgid "FatturaPA Attachment"
msgstr "Allegato FatturaPA"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_bank_statement_line__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_payment__l10n_it_edi_transaction
msgid "FatturaPA Transaction"
msgstr "Transazione FatturaPA"
#. module: l10n_it_edi_sdicoop
#: model:ir.actions.server,name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice_ir_actions_server
#: model:ir.cron,cron_name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice
#: model:ir.cron,name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice
msgid "FatturaPA: Receive invoices from the exchange system"
msgstr "FatturaPA: Ricezione fatture dal Sistema d'Interscambio"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format__id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__id
msgid "ID"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid ""
"In demo mode Odoo will just simulate the sending of invoices to the government.<br/>\n"
" In test mode (experimental) Odoo will send the invoices to a non-production service.\n"
" Saving this change will direct all companies on this database to this use this configuration.\n"
" Once registered for testing or official, the mode cannot be changed."
msgstr "Nella modalitá Demo, Odoo simulerá l'invio delle fatture "
"all'Agenzia.<br/> Nella modalitá di Test (sperimentale) Odoo manderá le fatture "
"a un servizio non di produzione. Salvando quest'impostazione fará sì che "
"tutte le Aziende di questo Database usino questa configurazione. "
"Una volta registrato per la modalitá Test o Ufficiale, la modalitá non può "
"più essere cambiata."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"Invoices for PA are not managed by Odoo, you can download the document and "
"send it on your own."
msgstr "Le Fatture per la Pubblica Amministrazione non sono gestite da Odoo. "
"É possibile scaricare il documento e inviarlo tramite il sito dell'Agenzia delle Entrate."
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__is_edi_proxy_active
msgid "Is Edi Proxy Active"
msgstr "É attivo il Proxy EDI"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Italian invoice: %s"
msgstr "Fattura italiana: %s"
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_proxy_current_state
msgid "L10N It Edi Proxy Current State"
msgstr "Stato corrente del Proxy L10n It EDI"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_sdicoop_demo_mode
msgid "L10N It Edi Sdicoop Demo Mode"
msgstr "Modalitá demo L10n It EDI Sdicoop"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_sdicoop_register
msgid "L10N It Edi Sdicoop Register"
msgstr "Registra L10n It EDI Sdicoop"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format____last_update
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings____last_update
msgid "Last Modified on"
msgstr "Data Modifica"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__prod
msgid "Official"
msgstr "Ufficiale"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"Please fill your codice fiscale to be able to receive invoices from "
"FatturaPA"
msgstr "Per favore, inserire il Codice Fiscale per poter ricevere le fatture da FatturaPA"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Service momentarily unavailable"
msgstr "Servizio momentaneamente non disponibile"
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__test
msgid "Test (experimental)"
msgstr "Test (sperimentale)"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/res_config_settings.py:0
#, python-format
msgid ""
"The company has already registered with the service as 'Test' or 'Official',"
" it cannot change."
msgstr "L'Azienda è giá registrata al servizio con la modalitá Test o Officiale, non può essere cambiata."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Addressee has failed. "
"You will be required to send a courtesy copy of the invoice to your customer"
" through another channel, outside of the Exchange System, and promptly "
"notify him that the original is deposited in his personal area on the portal"
" \"Invoices and Fees\" of the Revenue Agency."
msgstr "La fattura è stata inviata, ma la consegna al Destinatario è fallita. "
"É necessario mandare una copia di cortesia della fattura al cliente "
"tramite un altro canale al di fuori dell'SdI, e notificarlo che l'originale "
"è depositato nella sua Area Personale all' interno del portale 'Fatture e Corrispettivi' "
"sul sito dell'Agenzia delle Entrate"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Public Administration "
"has failed. The Exchange System will contact them to report the problem and "
"request that they provide a solution. During the following 10 days, the "
"Exchange System will try to forward the FatturaPA file to the Public "
"Administration in question again. Should this also fail, the System will "
"notify Odoo of the failed delivery, and you will be required to send the "
"invoice to the Administration through another channel, outside of the "
"Exchange System."
msgstr "La fattura è stata inviata, ma la consegna alla Pubblica Amministrazione "
"è fallita. Il Sistema d'Interscambio contatterá il Destinatario per far rapporto "
"sul problema, e richiederá che venga approntata una soluzione. Durante i "
"10 giorni successivi, il Sistema d'Interscambio proverá a inoltrare il file "
"FatturaPA in questione ancora. Dovesse fallire anche questo tentativo, il "
"sistema notificherá Odoo della mancata consegna, e sará necessario inviare "
"la fattura tramite un altro canale, al di fuori dell'SdI"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "The invoice has been refused by the Exchange System"
msgstr "La fattura è stata rifiutata dal Sistema d'Interscambio"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been succesfully transmitted. The addressee has 15 days to "
"accept or reject it."
msgstr "La fattura è stata trasmessa con successo. Il Destinatario ha 15 giorni per accettarla o rifiutarla."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "The invoice was refused by the addressee."
msgstr "La fattura è stata rifiutata dal Destinatario."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation"
msgstr "La fattura è stata trasmessa con successo alla Pubblica Amministrazione "
"e siamo in attesa di conferma."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation."
msgstr "La fattura è stata inviata con successo alla Pubblica Amministrazione "
"e siamo in attesa di conferma."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"This invoice number had already been submitted to the SdI, so it is set as Sent. Please verify that the system is correctly configured, because the correct flow does not need to send the same invoice twice for any reason.\n"
" Original message from the SDI: %s"
msgstr "Una fattura con questo numero è giá stata inviata all'SdI, quindi viene impostata come inviata. "
"Per favore, verificare che il sistema sia configurato correttamente, perchè in nessun caso il sistema "
"ha motivo di inviare la stessa fattura più volte.\n"
"Messaggio originale dall'SdI: %s"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Unauthorized user"
msgstr "Utente non autorizzato"
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "You are not allowed to check the status of this invoice."
msgstr "Non sei autorizzato a controllare lo stato di questa fattura."
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"You must accept the terms and conditions in the settings to use FatturaPA."
msgstr "Devi accettare i Termini e le Condizioni d'uso nelle impostazioni "
"per poter utilizzare FatturaPA."
@@ -1,299 +0,0 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_it_edi_sdicoop
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-03-30 10:14+0000\n"
"PO-Revision-Date: 2022-03-30 10:14+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid ""
"<span class=\"o_form_label\">\n"
" Fattura Elettronica mode\n"
" </span>"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "<span class=\"o_form_label\">Allow Odoo to process invoices</span>"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "A Demo service is in use."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "An Official or Test service has been registered."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Attached file is empty"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "By checking this box, I accept that Odoo may process my invoices."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__demo
msgid "Demo"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format__display_name
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__display_name
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__display_name
msgid "Display Name"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_account_edi_format
msgid "EDI format"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid "Electronic Document Invoicing"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_bank_statement_line__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__l10n_it_edi_attachment_id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_payment__l10n_it_edi_attachment_id
msgid "FatturaPA Attachment"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_bank_statement_line__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__l10n_it_edi_transaction
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_payment__l10n_it_edi_transaction
msgid "FatturaPA Transaction"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.actions.server,name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice_ir_actions_server
#: model:ir.cron,cron_name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice
#: model:ir.cron,name:l10n_it_edi_sdicoop.ir_cron_receive_fattura_pa_invoice
msgid "FatturaPA: Receive invoices from the exchange system"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format__id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move__id
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__id
msgid "ID"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model_terms:ir.ui.view,arch_db:l10n_it_edi_sdicoop.res_config_settings_view_form
msgid ""
"In demo mode Odoo will just simulate the sending of invoices to the government.<br/>\n"
" In test mode (experimental) Odoo will send the invoices to a non-production service.\n"
" Saving this change will direct all companies on this database to this use this configuration.\n"
" Once registered for testing or official, the mode cannot be changed."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"Invoices for PA are not managed by Odoo, you can download the document and "
"send it on your own."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__is_edi_proxy_active
msgid "Is Edi Proxy Active"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Italian invoice: %s"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model,name:l10n_it_edi_sdicoop.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_proxy_current_state
msgid "L10N It Edi Proxy Current State"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_sdicoop_demo_mode
msgid "L10N It Edi Sdicoop Demo Mode"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings__l10n_it_edi_sdicoop_register
msgid "L10N It Edi Sdicoop Register"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_edi_format____last_update
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_account_move____last_update
#: model:ir.model.fields,field_description:l10n_it_edi_sdicoop.field_res_config_settings____last_update
msgid "Last Modified on"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__prod
msgid "Official"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"Please fill your codice fiscale to be able to receive invoices from "
"FatturaPA"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Service momentarily unavailable"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: model:ir.model.fields.selection,name:l10n_it_edi_sdicoop.selection__res_config_settings__l10n_it_edi_sdicoop_demo_mode__test
msgid "Test (experimental)"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/res_config_settings.py:0
#, python-format
msgid ""
"The company has already registered with the service as 'Test' or 'Official',"
" it cannot change."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The filename is duplicated. Try again (or adjust the FatturaPA Filename "
"sequence). Original message from the SDI: %s"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Addressee has failed. "
"You will be required to send a courtesy copy of the invoice to your customer"
" through another channel, outside of the Exchange System, and promptly "
"notify him that the original is deposited in his personal area on the portal"
" \"Invoices and Fees\" of the Revenue Agency."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been issued, but the delivery to the Public Administration "
"has failed. The Exchange System will contact them to report the problem and "
"request that they provide a solution. During the following 10 days, the "
"Exchange System will try to forward the FatturaPA file to the Public "
"Administration in question again. Should this also fail, the System will "
"notify Odoo of the failed delivery, and you will be required to send the "
"invoice to the Administration through another channel, outside of the "
"Exchange System."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "The invoice has been refused by the Exchange System"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice has been succesfully transmitted. The addressee has 15 days to "
"accept or reject it."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "The invoice was refused by the addressee."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was sent to FatturaPA, but we are still awaiting a response. "
"Click the link above to check for an update."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"The invoice was successfully transmitted to the Public Administration and we"
" are waiting for confirmation."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"This invoice number had already been submitted to the SdI, so it is set as Sent. Please verify that the system is correctly configured, because the correct flow does not need to send the same invoice twice for any reason.\n"
" Original message from the SDI: %s"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "Unauthorized user"
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid "You are not allowed to check the status of this invoice."
msgstr ""
#. module: l10n_it_edi_sdicoop
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#: code:addons/l10n_it_edi_sdicoop/models/account_edi_format.py:0
#, python-format
msgid ""
"You must accept the terms and conditions in the settings to use FatturaPA."
msgstr ""
@@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import account_invoice
from . import account_edi_format
from . import res_config_settings
@@ -1,353 +0,0 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, _, _lt
from odoo.exceptions import UserError
from odoo.addons.account_edi_proxy_client.models.account_edi_proxy_user import AccountEdiProxyError
from lxml import etree
import base64
import logging
_logger = logging.getLogger(__name__)
class AccountEdiFormat(models.Model):
_inherit = 'account.edi.format'
# -------------------------------------------------------------------------
# Import
# -------------------------------------------------------------------------
def _cron_receive_fattura_pa(self):
''' Check the proxy for incoming invoices.
'''
proxy_users = self.env['account_edi_proxy_client.user'].search([('edi_format_id', '=', self.env.ref('l10n_it_edi.edi_fatturaPA').id)])
if proxy_users._get_demo_state() == 'demo':
return
for proxy_user in proxy_users:
company = proxy_user.company_id
try:
res = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/in/RicezioneInvoice',
params={'recipient_codice_fiscale': company.l10n_it_codice_fiscale})
except AccountEdiProxyError as e:
_logger.error('Error while receiving file from SdiCoop: %s', e)
proxy_acks = []
for id_transaction, fattura in res.items():
if self.env['ir.attachment'].search([('name', '=', fattura['filename']), ('res_model', '=', 'account.move')], limit=1):
# name should be unique, the invoice already exists
_logger.info('E-invoice already exist: %s', fattura['filename'])
proxy_acks.append(id_transaction)
continue
file = proxy_user._decrypt_data(fattura['file'], fattura['key'])
try:
tree = etree.fromstring(file)
except Exception:
# should not happen as the file has been checked by SdiCoop
_logger.info('Received file badly formatted, skipping: \n %s', file)
continue
invoice = self.env['account.move'].with_company(company).create({'move_type': 'in_invoice'})
attachment = self.env['ir.attachment'].create({
'name': fattura['filename'],
'raw': file,
'type': 'binary',
'res_model': 'account.move',
'res_id': invoice.id
})
if not self.env.context.get('test_skip_commit'):
self.env.cr.commit() #In case something fails after, we still have the attachment
# So that we don't delete the attachment when deleting the invoice
attachment.res_id = False
attachment.res_model = False
invoice.unlink()
invoice = self.env.ref('l10n_it_edi.edi_fatturaPA')._create_invoice_from_xml_tree(fattura['filename'], tree)
attachment.write({'res_model': 'account.move',
'res_id': invoice.id})
proxy_acks.append(id_transaction)
if not self.env.context.get('test_skip_commit'):
self.env.cr.commit()
if proxy_acks:
try:
proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/ack',
params={'transaction_ids': proxy_acks})
except AccountEdiProxyError as e:
_logger.error('Error while receiving file from SdiCoop: %s', e)
# -------------------------------------------------------------------------
# Export
# -------------------------------------------------------------------------
def _get_invoice_edi_content(self, move):
#OVERRIDE
if self.code != 'fattura_pa':
return super()._get_invoice_edi_content(move)
return move._export_as_xml()
def _check_move_configuration(self, move):
# OVERRIDE
res = super()._check_move_configuration(move)
if self.code != 'fattura_pa':
return res
res.extend(self._l10n_it_edi_check_invoice_configuration(move))
if not self._get_proxy_user(move.company_id):
res.append(_("You must accept the terms and conditions in the settings to use FatturaPA."))
return res
def _needs_web_services(self):
self.ensure_one()
return self.code == 'fattura_pa' or super()._needs_web_services()
def _l10n_it_edi_is_required_for_invoice(self, invoice):
""" _is_required_for_invoice for SdiCoop.
OVERRIDE
"""
is_self_invoice = self._l10n_it_edi_is_self_invoice(invoice)
return (
(invoice.is_sale_document() or (is_self_invoice and invoice.is_purchase_document()))
and invoice.l10n_it_send_state not in ('sent', 'delivered', 'delivered_accepted')
and invoice.country_code == 'IT'
)
def _support_batching(self, move=None, state=None, company=None):
# OVERRIDE
if self.code == 'fattura_pa':
return state == 'to_send' and move.is_invoice()
return super()._support_batching(move=move, state=state, company=company)
def _get_batch_key(self, move, state):
# OVERRIDE
if self.code != 'fattura_pa':
return super()._get_batch_key(move, state)
return move.move_type, bool(move.l10n_it_edi_transaction)
def _l10n_it_post_invoices_step_1(self, invoices):
''' Send the invoices to the proxy.
'''
to_return = {}
to_send = {}
for invoice in invoices:
xml = "<?xml version='1.0' encoding='UTF-8'?>" + str(invoice._export_as_xml())
filename = self._l10n_it_edi_generate_electronic_invoice_filename(invoice)
attachment = self.env['ir.attachment'].create({
'name': filename,
'res_id': invoice.id,
'res_model': invoice._name,
'raw': xml.encode(),
'description': _('Italian invoice: %s', invoice.move_type),
'type': 'binary',
})
invoice.l10n_it_edi_attachment_id = attachment
if invoice._is_commercial_partner_pa():
invoice.message_post(
body=(_("Invoices for PA are not managed by Odoo, you can download the document and send it on your own."))
)
to_return[invoice] = {'attachment': attachment, 'success': True}
else:
to_send[filename] = {
'invoice': invoice,
'data': {'filename': filename, 'xml': base64.b64encode(xml.encode()).decode()}}
company = invoices.company_id
proxy_user = self._get_proxy_user(company)
if not proxy_user: # proxy user should exist, because there is a check in _check_move_configuration
return {invoice: {
'error': _("You must accept the terms and conditions in the settings to use FatturaPA."),
'blocking_level': 'error'} for invoice in invoices}
responses = {}
if proxy_user._get_demo_state() == 'demo':
responses = {i['data']['filename']: {'id_transaction': 'demo'} for i in to_send.values()}
else:
try:
responses = self._l10n_it_edi_upload([i['data'] for i in to_send.values()], proxy_user)
except AccountEdiProxyError as e:
return {invoice: {'error': e.message, 'blocking_level': 'error'} for invoice in invoices}
for filename, response in responses.items():
invoice = to_send[filename]['invoice']
to_return[invoice] = response
if 'id_transaction' in response:
invoice.l10n_it_edi_transaction = response['id_transaction']
to_return[invoice].update({
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
})
return to_return
def _l10n_it_post_invoices_step_2(self, invoices):
''' Check if the sent invoices have been processed by FatturaPA.
'''
to_check = {i.l10n_it_edi_transaction: i for i in invoices}
to_return = {}
company = invoices.company_id
proxy_user = self._get_proxy_user(company)
if not proxy_user: # proxy user should exist, because there is a check in _check_move_configuration
return {invoice: {
'error': _("You must accept the terms and conditions in the settings to use FatturaPA."),
'blocking_level': 'error'} for invoice in invoices}
if proxy_user._get_demo_state() == 'demo':
# simulate success and bypass ack
return {invoice: {'attachment': invoice.l10n_it_edi_attachment_id} for invoice in invoices}
else:
try:
responses = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/in/TrasmissioneFatture',
params={'ids_transaction': list(to_check.keys())})
except AccountEdiProxyError as e:
return {invoice: {'error': e.message, 'blocking_level': 'error'} for invoice in invoices}
proxy_acks = []
for id_transaction, response in responses.items():
invoice = to_check[id_transaction]
if 'error' in response:
to_return[invoice] = response
continue
state = response['state']
if state == 'awaiting_outcome':
to_return[invoice] = {
'error': _('The invoice was sent to FatturaPA, but we are still awaiting a response. Click the link above to check for an update.'),
'blocking_level': 'info',
}
continue
elif state == 'not_found':
# Invoice does not exist on proxy. Either it does not belong to this proxy_user or it was not created correctly when
# it was sent to the proxy.
to_return[invoice] = {'error': _('You are not allowed to check the status of this invoice.'), 'blocking_level': 'error'}
continue
if not response.get('file'): # It means there is no status update, so we can skip it
document = invoice.edi_document_ids.filtered(lambda d: d.edi_format_id.code == 'fattura_pa')
to_return[invoice] = {'error': document.error, 'blocking_level': document.blocking_level}
continue
xml = proxy_user._decrypt_data(response['file'], response['key'])
response_tree = etree.fromstring(xml)
if state == 'ricevutaConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _('The invoice has been succesfully transmitted. The addressee has 15 days to accept or reject it.')}
else:
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
elif state == 'notificaScarto':
elements = response_tree.xpath('//Errore')
error_codes = [element.find('Codice').text for element in elements]
errors = [element.find('Descrizione').text for element in elements]
# Duplicated invoice
if '00404' in error_codes:
idx = error_codes.index('00404')
invoice.message_post(body=_(
'This invoice number had already been submitted to the SdI, so it is'
' set as Sent. Please verify that the system is correctly configured,'
' because the correct flow does not need to send the same invoice'
' twice for any reason.\n'
' Original message from the SDI: %s', errors[idx]))
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else:
# Add helpful text if duplicated filename error
if '00002' in error_codes:
idx = error_codes.index('00002')
errors[idx] = _(
'The filename is duplicated. Try again (or adjust the FatturaPA Filename sequence).'
' Original message from the SDI: %s', [errors[idx]]
)
to_return[invoice] = {'error': self._format_error_message(_('The invoice has been refused by the Exchange System'), errors), 'blocking_level': 'error'}
invoice.l10n_it_edi_transaction = False
elif state == 'notificaMancataConsegna':
if invoice._is_commercial_partner_pa():
to_return[invoice] = {'error': _(
'The invoice has been issued, but the delivery to the Public Administration'
' has failed. The Exchange System will contact them to report the problem'
' and request that they provide a solution.'
' During the following 10 days, the Exchange System will try to forward the'
' FatturaPA file to the Public Administration in question again.'
' Should this also fail, the System will notify Odoo of the failed delivery,'
' and you will be required to send the invoice to the Administration'
' through another channel, outside of the Exchange System.')}
else:
to_return[invoice] = {'success': True, 'attachment': invoice.l10n_it_edi_attachment_id}
invoice._message_log(body=_(
'The invoice has been issued, but the delivery to the Addressee has'
' failed. You will be required to send a courtesy copy of the invoice'
' to your customer through another channel, outside of the Exchange'
' System, and promptly notify him that the original is deposited'
' in his personal area on the portal "Invoices and Fees" of the'
' Revenue Agency.'))
elif state == 'notificaEsito':
outcome = response_tree.find('Esito').text
if outcome == 'EC01':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
else: # ECO2
to_return[invoice] = {'error': _('The invoice was refused by the addressee.'), 'blocking_level': 'error'}
elif state == 'NotificaDecorrenzaTermini':
to_return[invoice] = {'attachment': invoice.l10n_it_edi_attachment_id, 'success': True}
proxy_acks.append(id_transaction)
if proxy_acks:
try:
proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/ack',
params={'transaction_ids': proxy_acks})
except AccountEdiProxyError as e:
# Will be ignored and acked again next time.
_logger.error('Error while acking file to SdiCoop: %s', e)
return to_return
def _post_fattura_pa(self, invoices):
# OVERRIDE
if not invoices[0].l10n_it_edi_transaction:
return self._l10n_it_post_invoices_step_1(invoices)
else:
return self._l10n_it_post_invoices_step_2(invoices)
# -------------------------------------------------------------------------
# Proxy methods
# -------------------------------------------------------------------------
def _get_proxy_identification(self, company):
if self.code != 'fattura_pa':
return super()._get_proxy_identification()
if not company.l10n_it_codice_fiscale:
raise UserError(_('Please fill your codice fiscale to be able to receive invoices from FatturaPA'))
return self.env['res.partner']._l10n_it_normalize_codice_fiscale(company.l10n_it_codice_fiscale)
def _l10n_it_edi_upload(self, files, proxy_user):
'''Upload files to fatturapa.
:param files: A list of dictionary {filename, base64_xml}.
:returns: A dictionary.
* message: Message from fatturapa.
* transactionId: The fatturapa ID of this request.
* error: An eventual error.
* error_level: Info, warning, error.
'''
ERRORS = {
'EI01': {'error': _lt('Attached file is empty'), 'blocking_level': 'error'},
'EI02': {'error': _lt('Service momentarily unavailable'), 'blocking_level': 'warning'},
'EI03': {'error': _lt('Unauthorized user'), 'blocking_level': 'error'},
}
if not files:
return {}
result = proxy_user._make_request(proxy_user._get_server_url() + '/api/l10n_it_edi/1/out/SdiRiceviFile', params={'files': files})
# Translate the errors.
for filename in result.keys():
if 'error' in result[filename]:
result[filename] = ERRORS.get(result[filename]['error'], {'error': result[filename]['error'], 'blocking_level': 'error'})
return result
@@ -1,24 +0,0 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import fields, models
_logger = logging.getLogger(__name__)
DEFAULT_FACTUR_ITALIAN_DATE_FORMAT = '%Y-%m-%d'
class AccountMove(models.Model):
_inherit = 'account.move'
l10n_it_edi_transaction = fields.Char(copy=False, string="FatturaPA Transaction")
l10n_it_edi_attachment_id = fields.Many2one('ir.attachment', copy=False, string="FatturaPA Attachment")
def send_pec_mail(self):
self.ensure_one()
# OVERRIDE
# With SdiCoop web-service, no need to send PEC mail.
# Set the state to 'other' because the invoice should not be managed par l10n_it_edi.
self.l10n_it_send_state = 'other'
@@ -1,5 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_edi_xml
from . import test_edi_reverse_charge_xml
@@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="fetchmail_server_form_l10n_it_inherit" model="ir.ui.view">
<field name="name">fetchmail.server.form.l10n.it.inherit</field>
<field name="model">fetchmail.server</field>
<field name="priority">30</field>
<field name="inherit_id" ref="l10n_it_edi.fetchmail_server_form_l10n_it"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='l10n_it_is_pec']" position="replace" />
</data>
</field>
</record>
<record id="res_company_form_l10n_it_inherit" model="ir.ui.view">
<field name="name">res.company.form.l10n.it.inherit</field>
<field name="model">res.company</field>
<field name="priority">30</field>
<field name="inherit_id" ref="l10n_it_edi.res_company_form_l10n_it"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='l10n_it_mail_pec_server_id']" position="replace"/>
<xpath expr="//field[@name='l10n_it_address_send_fatturapa']" position="replace"/>
<xpath expr="//field[@name='l10n_it_address_recipient_fatturapa']" position="replace"/>
</data>
</field>
</record>
<record id="l10n_it_edi.invoice_supplier_tree_l10n_it" model="ir.ui.view">
<field name="arch" type="xml">
<!-- Remove the l10n_state_it field. Doing so
with xpath might replace other view elements that we shouldn't remove.-->
<data/>
</field>
</record>
<record id="l10n_it_edi.invoice_kanban_l10n_it" model="ir.ui.view">
<field name="arch" type="xml">
<!-- Remove the l10n_state_it field. Doing so
with xpath might replace other view elements that we shouldn't remove.-->
<data/>
</field>
</record>
<record id="l10n_it_edi.account_invoice_form_l10n_it" model="ir.ui.view">
<field name="arch" type="xml">
<!-- Remove the l10n_state_it field but keep other info tab.
Doing so with xpath might repace other view elements that we shouldn't remove. -->
<data>
<xpath expr="//page[@name='other_info']" position="after">
<page string="Electronic Invoicing"
name="electronic_invoicing"
attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund'))]}">
<group>
<group>
<field name="l10n_it_stamp_duty"/>
<field name="l10n_it_ddt_id"
attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}"/>
</group>
</group>
</page>
</xpath>
</data>
</field>
</record>
<record id="l10n_it_edi.view_account_invoice_filter_l10n_it" model="ir.ui.view">
<field name="arch" type="xml">
<!-- Remove the filters.-->
<data />
</field>
</record>
</odoo>