diff --git a/account_invoice_import/README.rst b/account_invoice_import/README.rst new file mode 100644 index 0000000000..e3f23b45b2 --- /dev/null +++ b/account_invoice_import/README.rst @@ -0,0 +1,120 @@ +====================== +Account Invoice Import +====================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github + :target: https://github.com/OCA/edi/tree/12.0/account_invoice_import + :alt: OCA/edi +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/edi-12-0/edi-12-0-account_invoice_import + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/226/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module has been started by lazy accounting users who hate enter they supplier invoices manually in Odoo. Almost all companies have several supplier invoices to enter regularly in the system from the same suppliers: phone bill, electricity bill, Internet access, train tickets, etc. Most of these invoices are available as PDF. We dream that we would be able to automatically extract from the PDF the required information to enter the invoice as supplier invoice in Odoo. To know the full story behind the development of this module, read this `blog post `_. + +In the future, we believe we will have structured information embedded inside the metadata of PDF invoices. There are 2 main standards for electronic invoicing: + +* `CII `_ (Cross-Industry Invoice) developped by `UN/CEFACT `_ (United Nations Centre for Trade Facilitation and Electronic Business), +* `UBL `_ (Universal Business Language) which is an ISO standard (`ISO/IEC 19845 `_) developped by `OASIS `_ (Organization for the Advancement of Structured Information Standards). + +For example, there is already a standard in Germany called `ZUGFeRD `_ which is based on CII. + +This module doesn't do anything useful by itself ; it requires other modules to work: each modules adds a specific invoice format. + +Here is how the module works: + +* the user starts a wizard and uploads the PDF or XML invoice, +* if it is an XML file, Odoo will parse it to create the invoice (requires additional modules for specific XML formats, such as the module *account_invoice_import_ubl* for the UBL format), +* if it is a PDF file with an embedded XML file in ZUGFeRD/CII format, Odoo will extract the embedded XML file and parse it to create the invoice (requires the module *account_invoice_import_zugferd*), +* otherwise, Odoo will use the *invoice2data* Python library to try to interpret the text of the PDF (requires the module *account_invoice_import_invoice2data*), +* if there is already some draft supplier invoice for this supplier, Odoo will propose to select one to update or create a new draft invoice, +* otherwise, Odoo will directly create a new draft supplier invoice and attach the PDF to it. + +This module also works with supplier refunds. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Go to the form view of the suppliers and configure it with the following parameters: + +* *is a Company ?* is True +* *Supplier* is True +* the *TIN* (i.e. VAT number) is set (the VAT number is used by default when searching the supplier in the Odoo partner database) +* in the *Accounting* tab, create one or several *Invoice Import Configurations*. + +You can configure a mail gateway to import invoices from an email: + +* Go to the menu *Settings > Technical > Email > Incoming Mail Servers* and setup the access (POP or IMAP) to the mailbox that will be used to received the invoices, +* In the section *Actions to perform on incoming mails*, set the field *Create a new record* to *Wizard to import supplier invoices/refunds* (model *account.invoice.import*). The field *Server Action* should be left empty. +* If you are in a multi-company setup, you also have to go to the menu *Accounting > Configuration > Settings*: in the section *Invoice Import*, enter the email of the mailbox used to import invoices in the field *Mail Gateway: Destination E-mail* (it will be used to select the right company to import the invoice in). + +Known issues / Roadmap +====================== + +* Remove dependency on *base_iban* and develop a separate glue module between this module and *base_iban* + +* Enhance the update of an existing invoice by analysing the lines (lines are only available when the invoice has an embedded XML file) + +* Add a mail gateway to be able to forward the emails that we receive with PDF invoices to a dedicated address ; the gateway would detach the PDF invoice from the email and create the draft supplier invoice in Odoo. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion + +Contributors +~~~~~~~~~~~~ + +* Alexis de Lattre +* Andrea Stirpe +* Nicolas JEUDY + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/edi `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_invoice_import/__init__.py b/account_invoice_import/__init__.py new file mode 100644 index 0000000000..9b4296142f --- /dev/null +++ b/account_invoice_import/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/account_invoice_import/__manifest__.py b/account_invoice_import/__manifest__.py new file mode 100644 index 0000000000..2407887c2c --- /dev/null +++ b/account_invoice_import/__manifest__.py @@ -0,0 +1,32 @@ +# Copyright 2015-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Account Invoice Import", + "version": "14.0.1.0.0", + "category": "Accounting & Finance", + "license": "AGPL-3", + "summary": "Import supplier invoices/refunds as PDF or XML files", + "author": "Akretion,Odoo Community Association (OCA)", + "maintainers": ["alexis-via"], + "website": "https://github.com/OCA/edi", + "depends": [ + "account", + "base_iban", + "base_business_document_import", + "onchange_helper", + ], + "data": [ + "security/ir.model.access.csv", + "security/rule.xml", + "views/account_invoice_import_config.xml", + "views/res_config_settings.xml", + "wizard/account_invoice_import_view.xml", + "views/account_invoice.xml", + "views/account_journal_dashboard.xml", + "views/res_partner.xml", + ], + "images": ["images/sshot-wizard1.png"], + "installable": True, +} diff --git a/account_invoice_import/i18n/account_invoice_import.pot b/account_invoice_import/i18n/account_invoice_import.pot new file mode 100644 index 0000000000..9675a82c7b --- /dev/null +++ b/account_invoice_import/i18n/account_invoice_import.pot @@ -0,0 +1,601 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_import +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \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: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice.py:21 +#, python-format +msgid " Amount w/o tax: %s %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:781 +#, python-format +msgid "%d invoice line(s) deleted: %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:796 +#, python-format +msgid "%d new invoice line(s) created: %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default Taxes\n" +" " +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:290 +#, python-format +msgid "Account missing on product '%s' or on it's related category '%s'." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Accounting Parameters" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__active +msgid "Active" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:620 +#, python-format +msgid "Adjustment" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_credit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_credit_account_id +msgid "Adjustment Credit Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_debit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_debit_account_id +msgid "Adjustment Debit Account" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:682 +#, python-format +msgid "Adjustment on %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_analytic_id +msgid "Analytic Account" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Archived" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_create_bank_account +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_create_bank_account +msgid "Auto-create Bank Account of Supplier" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Cancel" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_company +msgid "Companies" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__company_id +msgid "Company" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import_config +msgid "Configuration for the import of Supplier Invoices" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_partner +msgid "Contact" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Create New" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_uid +msgid "Created by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_date +msgid "Created on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__currency_id +msgid "Currency" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default taxes applied to local transactions" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__display_name +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__display_name +msgid "Display Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_id +msgid "Draft Supplier Invoice to Update" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_id +msgid "Expense Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_filename +msgid "Filename" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__label +msgid "Force Description" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__label +msgid "Force supplier invoice line description" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Group By" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__id +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__id +msgid "ID" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "If the format you need is not listed above, you should install an additionnal Odoo module that adds support for that format (e.g. account_invoice_import_factur-x, account_invoice_import_ubl, account_invoice_import_invoice2data, etc)." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "If there are several invoice import configurations for the supplier of the invoice, Odoo will ask you to choose one of them. If there is an existing draft invoice for that supplier, Odoo will propose you to update that draft invoice or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_config_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_menu +msgid "Import Bills" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.invoice_supplier_form +msgid "Import Invoice File" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import Supplier Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_menu +msgid "Import Vendor Bill" +msgstr "" + +#. module: account_invoice_import +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_root +msgid "Import Vendor Bills" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +#: model:ir.model,name:account_invoice_import.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Invoice Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__import_config_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_ids +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_ids +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_tree +#: model_terms:ir.ui.view,arch_db:account_invoice_import.view_partner_property_form +msgid "Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_type +msgid "Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:1040 +#, python-format +msgid "Invoice successfully imported from email sent by %s on %s with subject %s." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import____last_update +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:253 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:297 +#, python-format +msgid "MISSING DESCRIPTION" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "Mail Gateway: Destination E-mail" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__invoice_line_method +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Method for Invoice Line" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:225 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:534 +#, python-format +msgid "Missing Invoice Import Configuration on partner '%s'." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Auto-selected Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__name +msgid "Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_count +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_count +msgid "Number of Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:932 +#, python-format +msgid "Only the date format 102 is supported " +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_file +msgid "PDF or XML Invoice" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__partner_id +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Partner" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +msgid "Refund" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Search Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Select Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__sequence +msgid "Sequence" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Some draft supplier invoices/refunds have been found for the supplier of the invoice you are importing; one of them may correspond to the invoice you are importing. You can either select an existing draft supplier invoice to update or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__state +msgid "State" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__static_product_id +msgid "Static Product" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:56 +#, python-format +msgid "Static Product must be set on the invoice import configuration of supplier '%s' that has a Method for Invoice Line set to 'Single Line, Static Product' or 'Multi Line, Static Product'." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__partner_id +msgid "Supplier" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__tax_ids +msgid "Taxes" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:65 +#, python-format +msgid "The Expense Account must be set on the invoice import configuration of supplier '%s' that has a Method for Invoice Line set to 'Single Line, No Product' or 'Multi Line, No Product'." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:872 +#, python-format +msgid "The currency of the imported invoice (%s) is different from the currency of the existing invoice (%s)" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__invoice_line_method +msgid "The multi-line methods will not work for PDF invoices that don't have an embedded XML file. The 'Multi Line, Auto-selected Product' method will only work with ZUGFeRD invoices at Comfort or Extended level, not at Basic level." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:759 +#, python-format +msgid "The quantity has been updated on the invoice line with product '%s' from %s to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "The supplier has several invoice import configurations: please select the one you want to use for this import." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:860 +#, python-format +msgid "The supplier of the imported invoice (%s) is different from the supplier of the invoice to update (%s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:719 +#, python-format +msgid "The total amount is different from the untaxed amount, but no tax has been configured !" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:727 +#, python-format +msgid "The total tax amount has been forced to %s %s (amount computed by Odoo was: %s %s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:767 +#, python-format +msgid "The unit price has been updated on the invoice line with product '%s' from %s to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "These taxes are set in any new product created." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:375 +#, python-format +msgid "This XML file is not XML-compliant. Error: %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,help:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "This field is used in multi-company setups to import the invoices received by the mail gateway in the appropriate company" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:520 +#, python-format +msgid "This invoice already exists in Odoo. It's Supplier Invoice Number is '%s' and it's Odoo number is '%s'" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:590 +#, python-format +msgid "This invoice has been created automatically via file import" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:891 +#, python-format +msgid "This invoice has been updated automatically via the import of file %s" +msgstr "" + +#. module: account_invoice_import +#: sql_constraint:res.company:0 +msgid "This invoice import email already exists!" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:86 +#, python-format +msgid "This type of PDF invoice is not supported. Did you install the module to support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:384 +#, python-format +msgid "This type of XML invoice is not supported. Did you install the module to support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_total +msgid "Total" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_untaxed +msgid "Total Untaxed" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Existing" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update From Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Upload below the supplier invoice (or refund) as PDF or XML file: Odoo will create a draft supplier invoice (or refund). Supported formats:" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import +msgid "Wizard to import supplier invoices/refunds" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:443 +#, python-format +msgid "You are importing an invoice in a company that cannot deduct VAT and the imported invoice has several VAT taxes on the same line (%s). We do not support this scenario for the moment." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:259 +#, python-format +msgid "You have selected a Multi Line method for this import but Odoo could not extract/read any XML file inside the PDF invoice." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:643 +#, python-format +msgid "You must configure the 'Adjustment Credit Account' on the Accounting Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:637 +#, python-format +msgid "You must configure the 'Adjustment Debit Account' on the Accounting Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:848 +#, python-format +msgid "You must select a supplier invoice or refund to update" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:866 +#, python-format +msgid "You must select an Invoice Import Configuration." +msgstr "" + diff --git a/account_invoice_import/i18n/de.po b/account_invoice_import/i18n/de.po new file mode 100644 index 0000000000..e0d49b7e3a --- /dev/null +++ b/account_invoice_import/i18n/de.po @@ -0,0 +1,728 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_import +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2020-05-27 09:19+0000\n" +"Last-Translator: Maria Sparenberg \n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.10\n" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice.py:21 +#, python-format +msgid " Amount w/o tax: %s %s" +msgstr " Nettobetrag: %s %s" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:781 +#, python-format +msgid "%d invoice line(s) deleted: %s" +msgstr "%d Rechnungszeilen entfernt: %s" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:796 +#, python-format +msgid "%d new invoice line(s) created: %s" +msgstr "%d neue Rechnungszeilen erstellt: %s" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "" +"Default Taxes\n" +" " +msgstr "" +"Standard Steuern\n" +" " + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:290 +#, python-format +msgid "Account missing on product '%s' or on it's related category '%s'." +msgstr "Das Konto fehlt beim Produkt '%s' oder bei dessen Kategorie '%s'." + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Accounting Parameters" +msgstr "Buchhaltungsparameter" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__active +msgid "Active" +msgstr "Aktiv" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:620 +#, python-format +msgid "Adjustment" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_credit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_credit_account_id +msgid "Adjustment Credit Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_debit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_debit_account_id +msgid "Adjustment Debit Account" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:682 +#, python-format +msgid "Adjustment on %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_analytic_id +msgid "Analytic Account" +msgstr "Kostenstelle" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Archived" +msgstr "Archiviert" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_create_bank_account +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_create_bank_account +msgid "Auto-create Bank Account of Supplier" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Cancel" +msgstr "Abbrechen" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_company +msgid "Companies" +msgstr "Unternehmen" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__company_id +msgid "Company" +msgstr "Unternehmen" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_config_settings +msgid "Config Settings" +msgstr "Konfigurationseinstellungen" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import_config +msgid "Configuration for the import of Supplier Invoices" +msgstr "Konfiguration für den Import von Lieferantenrechnungen" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_partner +msgid "Contact" +msgstr "Kontakt" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Create New" +msgstr "Erstellen" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_uid +msgid "Created by" +msgstr "Erstellt von" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_date +msgid "Created on" +msgstr "Erstellt am" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__currency_id +msgid "Currency" +msgstr "Währung" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default taxes applied to local transactions" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__display_name +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__display_name +msgid "Display Name" +msgstr "Anzeigename" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_id +msgid "Draft Supplier Invoice to Update" +msgstr "Zu bearbeitender Rechnungsentwurf" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_id +msgid "Expense Account" +msgstr "Aufwandskonto" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_filename +msgid "Filename" +msgstr "Dateiname" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__label +msgid "Force Description" +msgstr "Beschreibung erzwingen" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__label +msgid "Force supplier invoice line description" +msgstr "Beschreibung einer Rechnungszeile erzwingen" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Group By" +msgstr "Gruppiere nach" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__id +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__id +msgid "ID" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If the format you need is not listed above, you should install an " +"additionnal Odoo module that adds support for that format (e.g. " +"account_invoice_import_factur-x, account_invoice_import_ubl, " +"account_invoice_import_invoice2data, etc)." +msgstr "" +"Sollte das erforderliche Format fehlen, muss es gegebenenfalls durch ein " +"zusätzliches Odoo Modul installiert werden (z.B. " +"account_invoice_import_factur-x, account_invoice_import_ubl, " +"account_invoice_import_invoice2data, etc)." + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If there are several invoice import configurations for the supplier of the " +"invoice, Odoo will ask you to choose one of them. If there is an existing " +"draft invoice for that supplier, Odoo will propose you to update that draft " +"invoice or create a new draft invoice." +msgstr "" +"Sollte es mehrere Importkonfigurationen für einen Lieferanten geben, erfragt " +"Odoo welche Konfiguration anzuwenden ist. Sollte bereits ein " +"Rechnungsentwurf für diesen Lieferanten existieren, schlägt Odoo vor " +"entweder diesen Rechnungsentwurf zu aktualisieren oder eine neue Rechnung zu " +"erstellen." + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import" +msgstr "Import" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_config_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_menu +msgid "Import Bills" +msgstr "Rechnungen importieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.invoice_supplier_form +msgid "Import Invoice File" +msgstr "Rechnungsdatei importieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import Supplier Invoice or Refund" +msgstr "Lieferantenrechnung oder Gutschrift importieren" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_menu +msgid "Import Vendor Bill" +msgstr "Rechnung importieren" + +#. module: account_invoice_import +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_root +msgid "Import Vendor Bills" +msgstr "Rechnungen importieren" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +#: model:ir.model,name:account_invoice_import.model_account_invoice +msgid "Invoice" +msgstr "Rechnung" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Invoice Import" +msgstr "Rechnungsimport" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__import_config_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_ids +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_ids +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Invoice Import Configuration" +msgstr "Rechnungsimport konfigurieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_tree +#: model_terms:ir.ui.view,arch_db:account_invoice_import.view_partner_property_form +msgid "Invoice Import Configurations" +msgstr "Rechnungsimportkonfigurationen" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_type +msgid "Invoice or Refund" +msgstr "Rechnung oder Gutschrift" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:1040 +#, python-format +msgid "" +"Invoice successfully imported from email sent by %s on %s with " +"subject %s." +msgstr "" +"Per E-Mail gesendete Rechnung von %s mit Datum %s und Betreff %s wurde erfolgreich importiert ." + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import____last_update +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config____last_update +msgid "Last Modified on" +msgstr "Aktualisiert am" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_uid +msgid "Last Updated by" +msgstr "Aktualisiert von" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_date +msgid "Last Updated on" +msgstr "Aktualisiert am" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:253 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:297 +#, python-format +msgid "MISSING DESCRIPTION" +msgstr "FEHLENDE BESCHREIBUNG" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "Mail Gateway: Destination E-mail" +msgstr "Mail Gateway: Ziel-E-Mail" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__invoice_line_method +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Method for Invoice Line" +msgstr "Methode für Rechnungszeile" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:225 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:534 +#, python-format +msgid "Missing Invoice Import Configuration on partner '%s'." +msgstr "Rechnungsimport Konfiguration für Partner '%s' fehlt." + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Auto-selected Product" +msgstr "Multiple Zeilen,Auto-Selektion Produkt" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, No Product" +msgstr "Multiple Zeilen, kein Produkt" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Static Product" +msgstr "Multiple Zeilen, statisches Produkt" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__name +msgid "Name" +msgstr "Name" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_count +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_count +msgid "Number of Invoice Import Configurations" +msgstr "Anzahl der Rechnungsimport Konfigurationen" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:932 +#, python-format +msgid "Only the date format 102 is supported " +msgstr "Es wird nur das Datumsformat 102 unterstützt " + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_file +msgid "PDF or XML Invoice" +msgstr "PDF oder XML Rechnung" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__partner_id +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Partner" +msgstr "Partner" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +msgid "Refund" +msgstr "Gutschrift" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Search Invoice Import Configurations" +msgstr "Suche nach Rechnungsimportkonfigurationen" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Select Invoice Import Configuration" +msgstr "Rechnungsimportkonfiguration auswählen" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__sequence +msgid "Sequence" +msgstr "Nummernfolge" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, No Product" +msgstr "Eine Zeile, kein Produkt" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, Static Product" +msgstr "Eine Zeile, statisches Produkt" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Some draft supplier invoices/refunds have been found for the supplier of the " +"invoice you are importing; one of them may correspond to the invoice you are " +"importing. You can either select an existing draft supplier invoice to " +"update or create a new draft invoice." +msgstr "" +"Für den Lieferanten der Rechnung, die Sie importieren, wurden einige " +"Entwürfe von Lieferantenrechnungen / Rückerstattungen gefunden. Eine davon " +"entspricht möglicherweise der Rechnung, die Sie importieren. Sie können " +"entweder einen vorhandenen Entwurf einer Lieferantenrechnung zum " +"Aktualisieren auswählen oder einen neuen Entwurf einer Rechnung erstellen." + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__state +msgid "State" +msgstr "Status" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__static_product_id +msgid "Static Product" +msgstr "Statisches Produkt" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:56 +#, python-format +msgid "" +"Static Product must be set on the invoice import configuration of supplier " +"'%s' that has a Method for Invoice Line set to 'Single Line, Static Product' " +"or 'Multi Line, Static Product'." +msgstr "" +"Das statische Produkt muss in der Rechnungsimportkonfiguration des " +"Lieferanten '%s' festgelegt werden, für die eine Methode für die " +"Rechnungsposition auf 'Eine Zeile, statisches Produkt' oder 'Multiple " +"Zeilen, statisches Produkt' festgelegt ist." + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__partner_id +msgid "Supplier" +msgstr "Lieferant" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__tax_ids +msgid "Taxes" +msgstr "Steuern" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:65 +#, python-format +msgid "" +"The Expense Account must be set on the invoice import configuration of " +"supplier '%s' that has a Method for Invoice Line set to 'Single Line, No " +"Product' or 'Multi Line, No Product'." +msgstr "" +"Das Aufwandskonto muss in der Rechnungsimportkonfiguration des Lieferanten " +"'%s' festgelegt werden, für die eine Methode für die Rechnungsposition auf " +"'Eine Zeile, kein Produkt' oder 'Multiple Zeile, kein Produkt' festgelegt " +"ist." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:872 +#, python-format +msgid "" +"The currency of the imported invoice (%s) is different from the currency of " +"the existing invoice (%s)" +msgstr "" +"Die Währung der importierten Rechnung (%s) unterscheidet sich von der " +"Währung der vorhandenen Rechnung (%s)" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__invoice_line_method +msgid "" +"The multi-line methods will not work for PDF invoices that don't have an " +"embedded XML file. The 'Multi Line, Auto-selected Product' method will only " +"work with ZUGFeRD invoices at Comfort or Extended level, not at Basic level." +msgstr "" +"Die mehrzeiligen Methoden funktionieren nicht für PDF-Rechnungen ohne " +"eingebettete XML-Datei. Die Methode \"Multiple Zeile, Auto-Selektion Produkt" +"\" funktioniert nur mit ZUGFeRD-Rechnungen auf Komfort- oder erweiterter " +"Ebene, nicht auf Basisebene." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:759 +#, python-format +msgid "" +"The quantity has been updated on the invoice line with product '%s' from %s " +"to %s %s" +msgstr "" +"Die Menge wurde in der Rechnungszeile mit dem Produkt '%s' von %s auf %s %s " +"aktualisiert" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"The supplier has several invoice import configurations: please select the " +"one you want to use for this import." +msgstr "" +"Der Lieferant verfügt über mehrere Konfigurationen für den Rechnungsimport: " +"Wählen Sie die Konfiguration aus, die Sie für diesen Import verwenden " +"möchten." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:860 +#, python-format +msgid "" +"The supplier of the imported invoice (%s) is different from the supplier of " +"the invoice to update (%s)." +msgstr "" +"Der Lieferant der importierten Rechnung (%s) unterscheidet sich vom " +"Lieferanten der zu aktualisierenden Rechnung (%s)." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:719 +#, python-format +msgid "" +"The total amount is different from the untaxed amount, but no tax has been " +"configured !" +msgstr "" +"Der Gesamtbetrag unterscheidet sich vom Nettobetrag, es wurde jedoch keine " +"Steuer konfiguriert!" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:727 +#, python-format +msgid "" +"The total tax amount has been forced to %s %s (amount computed by Odoo was: " +"%s %s)." +msgstr "" +"Der Gesamtsteuerbetrag wurde auf %s %s abgeändert (von Odoo berechneter " +"Betrag war: %s %s)." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:767 +#, python-format +msgid "" +"The unit price has been updated on the invoice line with product '%s' from " +"%s to %s %s" +msgstr "" +"Der Stückpreis wurde in der Rechnungszeile mit dem Produkt '%s' von %s auf " +"%s %s aktualisiert" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "These taxes are set in any new product created." +msgstr "Diese Steuern werden in jedem neu erstellten Produkt festgelegt." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:375 +#, python-format +msgid "This XML file is not XML-compliant. Error: %s" +msgstr "Diese XML-Datei ist nicht XML-kompatibel. Fehler: %s" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,help:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "" +"This field is used in multi-company setups to import the invoices received " +"by the mail gateway in the appropriate company" +msgstr "" +"Dieses Feld wird in unternehmensübergreifenden Setups verwendet, um die vom " +"Mail-Gateway empfangenen Rechnungen in das entsprechende Unternehmen zu " +"importieren" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:520 +#, python-format +msgid "" +"This invoice already exists in Odoo. It's Supplier Invoice Number is '%s' " +"and it's Odoo number is '%s'" +msgstr "" +"Diese Rechnung existiert bereits in Odoo. Die Lieferantenrechnungsnummer " +"lautet '%s' und die Odoo-Nummer lautet '%s'" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:590 +#, python-format +msgid "This invoice has been created automatically via file import" +msgstr "Diese Rechnung wurde automatisch per Dateiimport erstellt" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:891 +#, python-format +msgid "This invoice has been updated automatically via the import of file %s" +msgstr "" +"Diese Rechnung wurde automatisch über den Import der Datei %s aktualisiert" + +#. module: account_invoice_import +#: sql_constraint:res.company:0 +msgid "This invoice import email already exists!" +msgstr "Diese Rechnungsimport-E-Mail existiert bereits!" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:86 +#, python-format +msgid "" +"This type of PDF invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" +"Diese Art der PDF-Rechnung wird nicht unterstützt. Haben Sie das Modul " +"installiert, um diesen Dateityp zu unterstützen?" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:384 +#, python-format +msgid "" +"This type of XML invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" +"Diese Art der XML-Rechnung wird nicht unterstützt. Haben Sie das Modul " +"installiert, um diesen Dateityp zu unterstützen?" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_total +msgid "Total" +msgstr "Gesamt" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_untaxed +msgid "Total Untaxed" +msgstr "Netto" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update" +msgstr "Aktualisieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Existing" +msgstr "Vorhandene aktualisieren" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update From Invoice" +msgstr "Von Rechnung aktualisieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Invoice" +msgstr "Rechnung aktualisieren" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Upload below the supplier invoice (or refund) as PDF or XML file: Odoo will " +"create a draft supplier invoice (or refund). Supported formats:" +msgstr "" +"Laden Sie unten die Lieferantenrechnung (oder Gutschrift) als PDF- oder XML-" +"Datei hoch: Odoo erstellt einen Entwurf einer Lieferantenrechnung (oder " +"Gutschrift). Unterstützte Formate:" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import +msgid "Wizard to import supplier invoices/refunds" +msgstr "Assistent für den Import von Rechnungen/-gutschriften" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:443 +#, python-format +msgid "" +"You are importing an invoice in a company that cannot deduct VAT and the " +"imported invoice has several VAT taxes on the same line (%s). We do not " +"support this scenario for the moment." +msgstr "" +"Sie importieren eine Rechnung in ein Unternehmen, das keine Mehrwertsteuer " +"abziehen kann, und die importierte Rechnung enthält mehrere " +"Mehrwertsteuersteuern in derselben Zeile (%s). Wir unterstützen dieses " +"Szenario derzeit nicht." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:259 +#, python-format +msgid "" +"You have selected a Multi Line method for this import but Odoo could not " +"extract/read any XML file inside the PDF invoice." +msgstr "" +"Sie haben eine mehrzeilige Methode für diesen Import ausgewählt, aber Odoo " +"konnte keine XML-Datei in der PDF-Rechnung extrahieren / lesen." + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:643 +#, python-format +msgid "" +"You must configure the 'Adjustment Credit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:637 +#, python-format +msgid "" +"You must configure the 'Adjustment Debit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:848 +#, python-format +msgid "You must select a supplier invoice or refund to update" +msgstr "" +"Sie müssen eine Lieferantenrechnung oder eine Gutschrift auswählen, um sie " +"zu aktualisieren" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:866 +#, python-format +msgid "You must select an Invoice Import Configuration." +msgstr "Sie müssen eine Rechnungsimportkonfiguration auswählen." diff --git a/account_invoice_import/i18n/es.po b/account_invoice_import/i18n/es.po new file mode 100644 index 0000000000..ffa0c38b07 --- /dev/null +++ b/account_invoice_import/i18n/es.po @@ -0,0 +1,678 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_import +# +# Translators: +# enjolras , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-03-12 01:43+0000\n" +"PO-Revision-Date: 2018-03-12 01:43+0000\n" +"Last-Translator: enjolras , 2018\n" +"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice.py:21 +#, python-format +msgid " Amount w/o tax: %s %s" +msgstr "Base imposible: %s %s" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:781 +#, python-format +msgid "%d invoice line(s) deleted: %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:796 +#, python-format +msgid "%d new invoice line(s) created: %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "" +"Default Taxes\n" +" " +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:290 +#, python-format +msgid "Account missing on product '%s' or on it's related category '%s'." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Accounting Parameters" +msgstr "Parámetros de contabilidad" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__active +msgid "Active" +msgstr "Activo" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:620 +#, python-format +msgid "Adjustment" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_credit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_credit_account_id +msgid "Adjustment Credit Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_debit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_debit_account_id +msgid "Adjustment Debit Account" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:682 +#, python-format +msgid "Adjustment on %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_analytic_id +msgid "Analytic Account" +msgstr "Cuenta analítica" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Archived" +msgstr "Archivado" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_create_bank_account +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_create_bank_account +msgid "Auto-create Bank Account of Supplier" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_company +msgid "Companies" +msgstr "Compañías" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__company_id +msgid "Company" +msgstr "Compañía" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_config_settings +#, fuzzy +msgid "Config Settings" +msgstr "account.config.settings" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import_config +msgid "Configuration for the import of Supplier Invoices" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_partner +msgid "Contact" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Create New" +msgstr "Crear nuevo" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__currency_id +msgid "Currency" +msgstr "Moneda" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default taxes applied to local transactions" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__display_name +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_id +msgid "Draft Supplier Invoice to Update" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_id +msgid "Expense Account" +msgstr "Cuenta de gastos" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_filename +msgid "Filename" +msgstr "Archivo" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__label +msgid "Force Description" +msgstr "Descripción obligatoria" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__label +msgid "Force supplier invoice line description" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Group By" +msgstr "Agrupar por" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__id +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__id +msgid "ID" +msgstr "ID" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If the format you need is not listed above, you should install an " +"additionnal Odoo module that adds support for that format (e.g. " +"account_invoice_import_factur-x, account_invoice_import_ubl, " +"account_invoice_import_invoice2data, etc)." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If there are several invoice import configurations for the supplier of the " +"invoice, Odoo will ask you to choose one of them. If there is an existing " +"draft invoice for that supplier, Odoo will propose you to update that draft " +"invoice or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import" +msgstr "Importar" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_config_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_menu +msgid "Import Bills" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.invoice_supplier_form +msgid "Import Invoice File" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import Supplier Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_menu +msgid "Import Vendor Bill" +msgstr "" + +#. module: account_invoice_import +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_root +msgid "Import Vendor Bills" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +#: model:ir.model,name:account_invoice_import.model_account_invoice +msgid "Invoice" +msgstr "Factura" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Invoice Import" +msgstr "Importación de facturas" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__import_config_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_ids +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_ids +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Invoice Import Configuration" +msgstr "Configuración de importación de facturas" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_tree +#: model_terms:ir.ui.view,arch_db:account_invoice_import.view_partner_property_form +msgid "Invoice Import Configurations" +msgstr "Configuraciones de importación de facturas" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_type +msgid "Invoice or Refund" +msgstr "Factura o reembolso" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:1040 +#, python-format +msgid "" +"Invoice successfully imported from email sent by %s on %s with " +"subject %s." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import____last_update +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:253 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:297 +#, python-format +msgid "MISSING DESCRIPTION" +msgstr "FALTA DESCRIPCIÓN" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "Mail Gateway: Destination E-mail" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__invoice_line_method +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Method for Invoice Line" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:225 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:534 +#, python-format +msgid "Missing Invoice Import Configuration on partner '%s'." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Auto-selected Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__name +msgid "Name" +msgstr "Nombre" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_count +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_count +#, fuzzy +msgid "Number of Invoice Import Configurations" +msgstr "Configuraciones de importación de facturas" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:932 +#, python-format +msgid "Only the date format 102 is supported " +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_file +msgid "PDF or XML Invoice" +msgstr "Factura PDF o XML" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__partner_id +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Partner" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +msgid "Refund" +msgstr "Reembolso" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Search Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#, fuzzy +msgid "Select Invoice Import Configuration" +msgstr "Configuración de importación de facturas" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__sequence +msgid "Sequence" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Some draft supplier invoices/refunds have been found for the supplier of the " +"invoice you are importing; one of them may correspond to the invoice you are " +"importing. You can either select an existing draft supplier invoice to " +"update or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__state +msgid "State" +msgstr "Estado" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__static_product_id +msgid "Static Product" +msgstr "Producto estático" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:56 +#, python-format +msgid "" +"Static Product must be set on the invoice import configuration of supplier " +"'%s' that has a Method for Invoice Line set to 'Single Line, Static Product' " +"or 'Multi Line, Static Product'." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__partner_id +msgid "Supplier" +msgstr "Proveedor" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__tax_ids +msgid "Taxes" +msgstr "Impuestos" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:65 +#, python-format +msgid "" +"The Expense Account must be set on the invoice import configuration of " +"supplier '%s' that has a Method for Invoice Line set to 'Single Line, No " +"Product' or 'Multi Line, No Product'." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:872 +#, python-format +msgid "" +"The currency of the imported invoice (%s) is different from the currency of " +"the existing invoice (%s)" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__invoice_line_method +msgid "" +"The multi-line methods will not work for PDF invoices that don't have an " +"embedded XML file. The 'Multi Line, Auto-selected Product' method will only " +"work with ZUGFeRD invoices at Comfort or Extended level, not at Basic level." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:759 +#, python-format +msgid "" +"The quantity has been updated on the invoice line with product '%s' from %s " +"to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"The supplier has several invoice import configurations: please select the " +"one you want to use for this import." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:860 +#, python-format +msgid "" +"The supplier of the imported invoice (%s) is different from the supplier of " +"the invoice to update (%s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:719 +#, python-format +msgid "" +"The total amount is different from the untaxed amount, but no tax has been " +"configured !" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:727 +#, python-format +msgid "" +"The total tax amount has been forced to %s %s (amount computed by Odoo was: " +"%s %s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:767 +#, python-format +msgid "" +"The unit price has been updated on the invoice line with product '%s' from " +"%s to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "These taxes are set in any new product created." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:375 +#, python-format +msgid "This XML file is not XML-compliant. Error: %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,help:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "" +"This field is used in multi-company setups to import the invoices received " +"by the mail gateway in the appropriate company" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:520 +#, python-format +msgid "" +"This invoice already exists in Odoo. It's Supplier Invoice Number is '%s' " +"and it's Odoo number is '%s'" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:590 +#, python-format +msgid "This invoice has been created automatically via file import" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:891 +#, python-format +msgid "This invoice has been updated automatically via the import of file %s" +msgstr "" + +#. module: account_invoice_import +#: sql_constraint:res.company:0 +msgid "This invoice import email already exists!" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:86 +#, python-format +msgid "" +"This type of PDF invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:384 +#, python-format +msgid "" +"This type of XML invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_total +msgid "Total" +msgstr "Total" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_untaxed +msgid "Total Untaxed" +msgstr "Base imponible" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update" +msgstr "Actualizar" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Existing" +msgstr "Actualizar existente" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update From Invoice" +msgstr "Actualizar desde factura" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Invoice" +msgstr "Actualizar factura" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Upload below the supplier invoice (or refund) as PDF or XML file: Odoo will " +"create a draft supplier invoice (or refund). Supported formats:" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import +msgid "Wizard to import supplier invoices/refunds" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:443 +#, python-format +msgid "" +"You are importing an invoice in a company that cannot deduct VAT and the " +"imported invoice has several VAT taxes on the same line (%s). We do not " +"support this scenario for the moment." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:259 +#, python-format +msgid "" +"You have selected a Multi Line method for this import but Odoo could not " +"extract/read any XML file inside the PDF invoice." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:643 +#, python-format +msgid "" +"You must configure the 'Adjustment Credit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:637 +#, python-format +msgid "" +"You must configure the 'Adjustment Debit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:848 +#, python-format +msgid "You must select a supplier invoice or refund to update" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:866 +#, fuzzy, python-format +msgid "You must select an Invoice Import Configuration." +msgstr "Configuración de importación de facturas" + +#~ msgid "ISO/IEC 19845" +#~ msgstr "ISO/IEC 19845" + +#~ msgid "Universal Business Language" +#~ msgstr "Universal Business Language" + +#~ msgid "ZUGFeRD" +#~ msgstr "ZUGFeRD" + +#~ msgid "invoice2data" +#~ msgstr "invoice2data" diff --git a/account_invoice_import/i18n/fr.po b/account_invoice_import/i18n/fr.po new file mode 100644 index 0000000000..f53baf2d3e --- /dev/null +++ b/account_invoice_import/i18n/fr.po @@ -0,0 +1,664 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_import +# +# Translators: +# Quentin THEURET , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-29 11:54+0000\n" +"PO-Revision-Date: 2018-01-29 11:54+0000\n" +"Last-Translator: Quentin THEURET , 2018\n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice.py:21 +#, python-format +msgid " Amount w/o tax: %s %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:781 +#, python-format +msgid "%d invoice line(s) deleted: %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:796 +#, python-format +msgid "%d new invoice line(s) created: %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "" +"Default Taxes\n" +" " +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:290 +#, python-format +msgid "Account missing on product '%s' or on it's related category '%s'." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Accounting Parameters" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__active +msgid "Active" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:620 +#, python-format +msgid "Adjustment" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_credit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_credit_account_id +msgid "Adjustment Credit Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_debit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_debit_account_id +msgid "Adjustment Debit Account" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:682 +#, python-format +msgid "Adjustment on %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_analytic_id +msgid "Analytic Account" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Archived" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_create_bank_account +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_create_bank_account +msgid "Auto-create Bank Account of Supplier" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Cancel" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_company +msgid "Companies" +msgstr "Sociétés" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__company_id +msgid "Company" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_config_settings +#, fuzzy +msgid "Config Settings" +msgstr "account.config.settings" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import_config +msgid "Configuration for the import of Supplier Invoices" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_partner +msgid "Contact" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Create New" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_uid +msgid "Created by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_date +msgid "Created on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__currency_id +msgid "Currency" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default taxes applied to local transactions" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__display_name +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__display_name +msgid "Display Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_id +msgid "Draft Supplier Invoice to Update" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_id +msgid "Expense Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_filename +msgid "Filename" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__label +msgid "Force Description" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__label +msgid "Force supplier invoice line description" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Group By" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__id +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__id +msgid "ID" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If the format you need is not listed above, you should install an " +"additionnal Odoo module that adds support for that format (e.g. " +"account_invoice_import_factur-x, account_invoice_import_ubl, " +"account_invoice_import_invoice2data, etc)." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If there are several invoice import configurations for the supplier of the " +"invoice, Odoo will ask you to choose one of them. If there is an existing " +"draft invoice for that supplier, Odoo will propose you to update that draft " +"invoice or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_config_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_menu +msgid "Import Bills" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.invoice_supplier_form +msgid "Import Invoice File" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import Supplier Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_menu +msgid "Import Vendor Bill" +msgstr "" + +#. module: account_invoice_import +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_root +msgid "Import Vendor Bills" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +#: model:ir.model,name:account_invoice_import.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Invoice Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__import_config_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_ids +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_ids +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_tree +#: model_terms:ir.ui.view,arch_db:account_invoice_import.view_partner_property_form +msgid "Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_type +msgid "Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:1040 +#, python-format +msgid "" +"Invoice successfully imported from email sent by %s on %s with " +"subject %s." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import____last_update +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:253 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:297 +#, python-format +msgid "MISSING DESCRIPTION" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "Mail Gateway: Destination E-mail" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__invoice_line_method +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Method for Invoice Line" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:225 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:534 +#, python-format +msgid "Missing Invoice Import Configuration on partner '%s'." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Auto-selected Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__name +msgid "Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_count +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_count +msgid "Number of Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:932 +#, python-format +msgid "Only the date format 102 is supported " +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_file +msgid "PDF or XML Invoice" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__partner_id +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Partner" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +msgid "Refund" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Search Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Select Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__sequence +msgid "Sequence" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Some draft supplier invoices/refunds have been found for the supplier of the " +"invoice you are importing; one of them may correspond to the invoice you are " +"importing. You can either select an existing draft supplier invoice to " +"update or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__state +msgid "State" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__static_product_id +msgid "Static Product" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:56 +#, python-format +msgid "" +"Static Product must be set on the invoice import configuration of supplier " +"'%s' that has a Method for Invoice Line set to 'Single Line, Static Product' " +"or 'Multi Line, Static Product'." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__partner_id +msgid "Supplier" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__tax_ids +msgid "Taxes" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:65 +#, python-format +msgid "" +"The Expense Account must be set on the invoice import configuration of " +"supplier '%s' that has a Method for Invoice Line set to 'Single Line, No " +"Product' or 'Multi Line, No Product'." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:872 +#, python-format +msgid "" +"The currency of the imported invoice (%s) is different from the currency of " +"the existing invoice (%s)" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__invoice_line_method +msgid "" +"The multi-line methods will not work for PDF invoices that don't have an " +"embedded XML file. The 'Multi Line, Auto-selected Product' method will only " +"work with ZUGFeRD invoices at Comfort or Extended level, not at Basic level." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:759 +#, python-format +msgid "" +"The quantity has been updated on the invoice line with product '%s' from %s " +"to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"The supplier has several invoice import configurations: please select the " +"one you want to use for this import." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:860 +#, python-format +msgid "" +"The supplier of the imported invoice (%s) is different from the supplier of " +"the invoice to update (%s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:719 +#, python-format +msgid "" +"The total amount is different from the untaxed amount, but no tax has been " +"configured !" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:727 +#, python-format +msgid "" +"The total tax amount has been forced to %s %s (amount computed by Odoo was: " +"%s %s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:767 +#, python-format +msgid "" +"The unit price has been updated on the invoice line with product '%s' from " +"%s to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "These taxes are set in any new product created." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:375 +#, python-format +msgid "This XML file is not XML-compliant. Error: %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,help:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "" +"This field is used in multi-company setups to import the invoices received " +"by the mail gateway in the appropriate company" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:520 +#, python-format +msgid "" +"This invoice already exists in Odoo. It's Supplier Invoice Number is '%s' " +"and it's Odoo number is '%s'" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:590 +#, python-format +msgid "This invoice has been created automatically via file import" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:891 +#, python-format +msgid "This invoice has been updated automatically via the import of file %s" +msgstr "" + +#. module: account_invoice_import +#: sql_constraint:res.company:0 +msgid "This invoice import email already exists!" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:86 +#, python-format +msgid "" +"This type of PDF invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:384 +#, python-format +msgid "" +"This type of XML invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_total +msgid "Total" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_untaxed +msgid "Total Untaxed" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Existing" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update From Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Upload below the supplier invoice (or refund) as PDF or XML file: Odoo will " +"create a draft supplier invoice (or refund). Supported formats:" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import +msgid "Wizard to import supplier invoices/refunds" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:443 +#, python-format +msgid "" +"You are importing an invoice in a company that cannot deduct VAT and the " +"imported invoice has several VAT taxes on the same line (%s). We do not " +"support this scenario for the moment." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:259 +#, python-format +msgid "" +"You have selected a Multi Line method for this import but Odoo could not " +"extract/read any XML file inside the PDF invoice." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:643 +#, python-format +msgid "" +"You must configure the 'Adjustment Credit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:637 +#, python-format +msgid "" +"You must configure the 'Adjustment Debit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:848 +#, python-format +msgid "You must select a supplier invoice or refund to update" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:866 +#, python-format +msgid "You must select an Invoice Import Configuration." +msgstr "" diff --git a/account_invoice_import/i18n/sl.po b/account_invoice_import/i18n/sl.po new file mode 100644 index 0000000000..77c3390e3d --- /dev/null +++ b/account_invoice_import/i18n/sl.po @@ -0,0 +1,664 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_import +# +# Translators: +# Matjaž Mozetič , 2016 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2016-11-12 13:37+0000\n" +"PO-Revision-Date: 2016-11-12 13:37+0000\n" +"Last-Translator: Matjaž Mozetič , 2016\n" +"Language-Team: Slovenian (https://www.transifex.com/oca/teams/23907/sl/)\n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" +"%100==4 ? 2 : 3);\n" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice.py:21 +#, python-format +msgid " Amount w/o tax: %s %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:781 +#, python-format +msgid "%d invoice line(s) deleted: %s" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:796 +#, python-format +msgid "%d new invoice line(s) created: %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "" +"Default Taxes\n" +" " +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:290 +#, python-format +msgid "Account missing on product '%s' or on it's related category '%s'." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Accounting Parameters" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__active +msgid "Active" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:620 +#, python-format +msgid "Adjustment" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_credit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_credit_account_id +msgid "Adjustment Credit Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__adjustment_debit_account_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__adjustment_debit_account_id +msgid "Adjustment Debit Account" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:682 +#, python-format +msgid "Adjustment on %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_analytic_id +msgid "Analytic Account" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Archived" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_create_bank_account +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_create_bank_account +msgid "Auto-create Bank Account of Supplier" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Cancel" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_company +msgid "Companies" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__company_id +msgid "Company" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import_config +msgid "Configuration for the import of Supplier Invoices" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_res_partner +msgid "Contact" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Create New" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_uid +msgid "Created by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__create_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__create_date +msgid "Created on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__currency_id +msgid "Currency" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Default taxes applied to local transactions" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__display_name +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__display_name +msgid "Display Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_id +msgid "Draft Supplier Invoice to Update" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__account_id +msgid "Expense Account" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_filename +msgid "Filename" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__label +msgid "Force Description" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__label +msgid "Force supplier invoice line description" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Group By" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__id +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__id +msgid "ID" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If the format you need is not listed above, you should install an " +"additionnal Odoo module that adds support for that format (e.g. " +"account_invoice_import_factur-x, account_invoice_import_ubl, " +"account_invoice_import_invoice2data, etc)." +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"If there are several invoice import configurations for the supplier of the " +"invoice, Odoo will ask you to choose one of them. If there is an existing " +"draft invoice for that supplier, Odoo will propose you to update that draft " +"invoice or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_config_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_menu +msgid "Import Bills" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.invoice_supplier_form +msgid "Import Invoice File" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Import Supplier Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: model:ir.actions.act_window,name:account_invoice_import.account_invoice_import_action +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_menu +msgid "Import Vendor Bill" +msgstr "" + +#. module: account_invoice_import +#: model:ir.ui.menu,name:account_invoice_import.account_invoice_import_config_root +msgid "Import Vendor Bills" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +#: model:ir.model,name:account_invoice_import.model_account_invoice +msgid "Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "Invoice Import" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__import_config_id +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_ids +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_ids +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_form +msgid "Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_tree +#: model_terms:ir.ui.view,arch_db:account_invoice_import.view_partner_property_form +msgid "Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_type +msgid "Invoice or Refund" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:1040 +#, python-format +msgid "" +"Invoice successfully imported from email sent by %s on %s with " +"subject %s." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import____last_update +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_uid +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__write_date +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__write_date +msgid "Last Updated on" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:253 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:297 +#, python-format +msgid "MISSING DESCRIPTION" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,field_description:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "Mail Gateway: Destination E-mail" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__invoice_line_method +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Method for Invoice Line" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:225 +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:534 +#, python-format +msgid "Missing Invoice Import Configuration on partner '%s'." +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Auto-selected Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Multi Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__name +msgid "Name" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_res_partner__invoice_import_count +#: model:ir.model.fields,field_description:account_invoice_import.field_res_users__invoice_import_count +msgid "Number of Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:932 +#, python-format +msgid "Only the date format 102 is supported " +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__invoice_file +msgid "PDF or XML Invoice" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__partner_id +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Partner" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,invoice_type:0 +msgid "Refund" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_config_search +msgid "Search Invoice Import Configurations" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Select Invoice Import Configuration" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__sequence +msgid "Sequence" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, No Product" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import.config,invoice_line_method:0 +msgid "Single Line, Static Product" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Some draft supplier invoices/refunds have been found for the supplier of the " +"invoice you are importing; one of them may correspond to the invoice you are " +"importing. You can either select an existing draft supplier invoice to " +"update or create a new draft invoice." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__state +msgid "State" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__static_product_id +msgid "Static Product" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:56 +#, python-format +msgid "" +"Static Product must be set on the invoice import configuration of supplier " +"'%s' that has a Method for Invoice Line set to 'Single Line, Static Product' " +"or 'Multi Line, Static Product'." +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__partner_id +msgid "Supplier" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import_config__tax_ids +msgid "Taxes" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/models/account_invoice_import_config.py:65 +#, python-format +msgid "" +"The Expense Account must be set on the invoice import configuration of " +"supplier '%s' that has a Method for Invoice Line set to 'Single Line, No " +"Product' or 'Multi Line, No Product'." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:872 +#, python-format +msgid "" +"The currency of the imported invoice (%s) is different from the currency of " +"the existing invoice (%s)" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_account_invoice_import_config__invoice_line_method +msgid "" +"The multi-line methods will not work for PDF invoices that don't have an " +"embedded XML file. The 'Multi Line, Auto-selected Product' method will only " +"work with ZUGFeRD invoices at Comfort or Extended level, not at Basic level." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:759 +#, python-format +msgid "" +"The quantity has been updated on the invoice line with product '%s' from %s " +"to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"The supplier has several invoice import configurations: please select the " +"one you want to use for this import." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:860 +#, python-format +msgid "" +"The supplier of the imported invoice (%s) is different from the supplier of " +"the invoice to update (%s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:719 +#, python-format +msgid "" +"The total amount is different from the untaxed amount, but no tax has been " +"configured !" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:727 +#, python-format +msgid "" +"The total tax amount has been forced to %s %s (amount computed by Odoo was: " +"%s %s)." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:767 +#, python-format +msgid "" +"The unit price has been updated on the invoice line with product '%s' from " +"%s to %s %s" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.res_config_settings_view_form +msgid "These taxes are set in any new product created." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:375 +#, python-format +msgid "This XML file is not XML-compliant. Error: %s" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,help:account_invoice_import.field_res_company__invoice_import_email +#: model:ir.model.fields,help:account_invoice_import.field_res_config_settings__invoice_import_email +msgid "" +"This field is used in multi-company setups to import the invoices received " +"by the mail gateway in the appropriate company" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:520 +#, python-format +msgid "" +"This invoice already exists in Odoo. It's Supplier Invoice Number is '%s' " +"and it's Odoo number is '%s'" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:590 +#, python-format +msgid "This invoice has been created automatically via file import" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:891 +#, python-format +msgid "This invoice has been updated automatically via the import of file %s" +msgstr "" + +#. module: account_invoice_import +#: sql_constraint:res.company:0 +msgid "This invoice import email already exists!" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:86 +#, python-format +msgid "" +"This type of PDF invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:384 +#, python-format +msgid "" +"This type of XML invoice is not supported. Did you install the module to " +"support this type of file?" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_total +msgid "Total" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model.fields,field_description:account_invoice_import.field_account_invoice_import__amount_untaxed +msgid "Total Untaxed" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Existing" +msgstr "" + +#. module: account_invoice_import +#: selection:account.invoice.import,state:0 +msgid "Update From Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "Update Invoice" +msgstr "" + +#. module: account_invoice_import +#: model_terms:ir.ui.view,arch_db:account_invoice_import.account_invoice_import_form +msgid "" +"Upload below the supplier invoice (or refund) as PDF or XML file: Odoo will " +"create a draft supplier invoice (or refund). Supported formats:" +msgstr "" + +#. module: account_invoice_import +#: model:ir.model,name:account_invoice_import.model_account_invoice_import +msgid "Wizard to import supplier invoices/refunds" +msgstr "Čarovnik za uvoz prejetih računov/dobropisov" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:443 +#, python-format +msgid "" +"You are importing an invoice in a company that cannot deduct VAT and the " +"imported invoice has several VAT taxes on the same line (%s). We do not " +"support this scenario for the moment." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:259 +#, python-format +msgid "" +"You have selected a Multi Line method for this import but Odoo could not " +"extract/read any XML file inside the PDF invoice." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:643 +#, python-format +msgid "" +"You must configure the 'Adjustment Credit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:637 +#, python-format +msgid "" +"You must configure the 'Adjustment Debit Account' on the Accounting " +"Configuration page." +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:848 +#, python-format +msgid "You must select a supplier invoice or refund to update" +msgstr "" + +#. module: account_invoice_import +#: code:addons/account_invoice_import/wizard/account_invoice_import.py:866 +#, python-format +msgid "You must select an Invoice Import Configuration." +msgstr "" diff --git a/account_invoice_import/images/sshot-wizard1.png b/account_invoice_import/images/sshot-wizard1.png new file mode 100644 index 0000000000..cd58af51a7 Binary files /dev/null and b/account_invoice_import/images/sshot-wizard1.png differ diff --git a/account_invoice_import/models/__init__.py b/account_invoice_import/models/__init__.py new file mode 100644 index 0000000000..14d2b527d1 --- /dev/null +++ b/account_invoice_import/models/__init__.py @@ -0,0 +1,4 @@ +from . import res_partner +from . import res_company +from . import account_invoice_import_config +from . import account_move diff --git a/account_invoice_import/models/account_invoice_import_config.py b/account_invoice_import/models/account_invoice_import_config.py new file mode 100644 index 0000000000..a13acc1988 --- /dev/null +++ b/account_invoice_import/models/account_invoice_import_config.py @@ -0,0 +1,119 @@ +# Copyright 2015-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class AccountInvoiceImportConfig(models.Model): + _name = "account.invoice.import.config" + _description = "Configuration for the import of Supplier Invoices" + _order = "sequence" + _check_company_auto = True + + name = fields.Char(required=True) + partner_id = fields.Many2one( + "res.partner", + ondelete="cascade", + domain=[("parent_id", "=", False)], + ) + active = fields.Boolean(default=True) + sequence = fields.Integer() + invoice_line_method = fields.Selection( + [ + ("1line_no_product", "Single Line, No Product"), + ("1line_static_product", "Single Line, Static Product"), + ("nline_no_product", "Multi Line, No Product"), + ("nline_static_product", "Multi Line, Static Product"), + ("nline_auto_product", "Multi Line, Auto-selected Product"), + ], + string="Method for Invoice Line", + required=True, + default="1line_no_product", + help="The multi-line methods will not work for PDF invoices " + "that don't have an embedded XML file which has structured information " + "on each line.", + ) + company_id = fields.Many2one( + "res.company", + ondelete="cascade", + required=True, + default=lambda self: self.env.company, + ) + account_id = fields.Many2one( + "account.account", + string="Expense Account", + domain="[('deprecated', '=', False), ('company_id', '=', company_id)]", + check_company=True, + ) + account_analytic_id = fields.Many2one( + "account.analytic.account", string="Analytic Account", check_company=True + ) + label = fields.Char( + string="Force Description", help="Force supplier invoice line description" + ) + tax_ids = fields.Many2many( + "account.tax", + string="Taxes", + domain="[('type_tax_use', '=', 'purchase'), ('company_id', '=', company_id)]", + check_company=True, + ) + static_product_id = fields.Many2one( + "product.product", + check_company=True, + domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", + ) + + @api.constrains("invoice_line_method", "account_id", "static_product_id") + def _check_import_config(self): + for config in self: + if ( + "static_product" in config.invoice_line_method + and not config.static_product_id + ): + raise ValidationError( + _( + "Static Product must be set on the invoice import " + "configuration of supplier '%s' that has a Method " + "for Invoice Line set to 'Single Line, Static Product' " + "or 'Multi Line, Static Product'." + ) + % config.partner_id.name + ) + if "no_product" in config.invoice_line_method and not config.account_id: + raise ValidationError( + _( + "The Expense Account must be set on the invoice " + "import configuration of supplier '%s' that has a " + "Method for Invoice Line set to 'Single Line, No Product' " + "or 'Multi Line, No Product'." + ) + % config.partner_id.name + ) + + @api.onchange("invoice_line_method", "account_id") + def invoice_line_method_change(self): + if self.invoice_line_method == "1line_no_product" and self.account_id: + self.tax_ids = [(6, 0, self.account_id.tax_ids.ids)] + elif self.invoice_line_method != "1line_no_product": + self.tax_ids = [(6, 0, [])] + + def convert_to_import_config(self): + self.ensure_one() + vals = { + "invoice_line_method": self.invoice_line_method, + "account_analytic": self.account_analytic_id or False, + } + if self.invoice_line_method == "1line_no_product": + vals["account"] = self.account_id + vals["taxes"] = self.tax_ids + vals["label"] = self.label or False + elif self.invoice_line_method == "1line_static_product": + vals["product"] = self.static_product_id + vals["label"] = self.label or False + elif self.invoice_line_method == "nline_no_product": + vals["account"] = self.account_id + elif self.invoice_line_method == "nline_static_product": + vals["product"] = self.static_product_id + return vals diff --git a/account_invoice_import/models/account_move.py b/account_invoice_import/models/account_move.py new file mode 100644 index 0000000000..2c48ccec05 --- /dev/null +++ b/account_invoice_import/models/account_move.py @@ -0,0 +1,19 @@ +# Copyright 2015-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, models +from odoo.tools.misc import format_amount + + +class AccountMove(models.Model): + _inherit = "account.move" + + def _get_move_display_name(self, show_ref=False): + """Add amount_untaxed in name_get of invoices""" + name = super()._get_move_display_name(show_ref=show_ref) + if self.env.context.get("invoice_show_amount"): + name += _(" Amount w/o tax: %s") % format_amount( + self.env, self.amount_untaxed, self.currency_id + ) + return name diff --git a/account_invoice_import/models/res_company.py b/account_invoice_import/models/res_company.py new file mode 100644 index 0000000000..acdae51d45 --- /dev/null +++ b/account_invoice_import/models/res_company.py @@ -0,0 +1,34 @@ +# Copyright 2017-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + adjustment_credit_account_id = fields.Many2one( + "account.account", + domain="[('deprecated', '=', False), ('company_id', '=', company_id)]", + ) + adjustment_debit_account_id = fields.Many2one( + "account.account", + domain="[('deprecated', '=', False), ('company_id', '=', company_id)]", + ) + invoice_import_email = fields.Char( + "Mail Gateway: Destination E-mail", + help="This field is used in multi-company setups to import the " + "invoices received by the mail gateway in the appropriate company", + ) + invoice_import_create_bank_account = fields.Boolean( + string="Auto-create Bank Account of Supplier" + ) + + _sql_constraints = [ + ( + "invoice_import_email_uniq", + "unique(invoice_import_email)", + "This invoice import email already exists!", + ) + ] diff --git a/account_invoice_import/models/res_partner.py b/account_invoice_import/models/res_partner.py new file mode 100644 index 0000000000..fa54d56635 --- /dev/null +++ b/account_invoice_import/models/res_partner.py @@ -0,0 +1,33 @@ +# Copyright 2015-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + invoice_import_ids = fields.One2many( + "account.invoice.import.config", + "partner_id", + string="Invoice Import Configuration", + ) + invoice_import_count = fields.Integer( + compute="_compute_invoice_import_count", + string="Number of Invoice Import Configurations", + readonly=True, + ) + + def _compute_invoice_import_count(self): + config_data = self.env["account.invoice.import.config"].read_group( + [("partner_id", "in", self.ids), ("company_id", "=", self.env.company.id)], + ["partner_id"], + ["partner_id"], + ) + mapped_data = { + config["partner_id"][0]: config["partner_id_count"] + for config in config_data + } + for partner in self: + partner.invoice_import_count = mapped_data.get(partner.id, 0) diff --git a/account_invoice_import/readme/CONFIGURE.rst b/account_invoice_import/readme/CONFIGURE.rst new file mode 100644 index 0000000000..d743ea8324 --- /dev/null +++ b/account_invoice_import/readme/CONFIGURE.rst @@ -0,0 +1,11 @@ +Go to the form view of the suppliers and configure it with the following parameters: + +* Individual/Company: *Company* +* the *TIN* (i.e. VAT number) is set (the VAT number is used by default when searching the supplier in the Odoo partner database) +* in the *Accounting* tab, create one or several *Invoice Import Configurations*. + +You can configure a mail gateway to import invoices from an email: + +* Go to the menu *Settings > Technical > Email > Incoming Mail Servers* and setup the access (POP or IMAP) to the mailbox that will be used to receive the invoices, +* In the section *Actions to perform on incoming mails*, set the field *Create a new record* to *Wizard to import supplier invoices/refunds* (model *account.invoice.import*). +* If you are in a multi-company setup, you also have to go to the menu *Invoicing > Configuration > Settings*: in the section *Invoice Import*, enter the email of the mailbox used to import invoices in the field *Mail Gateway: Destination E-mail* (it will be used to import the invoice in the proper company). diff --git a/account_invoice_import/readme/CONTRIBUTORS.rst b/account_invoice_import/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..872aca6be6 --- /dev/null +++ b/account_invoice_import/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* Alexis de Lattre +* Andrea Stirpe +* Nicolas JEUDY +* Yannick Vaucher +* Ronald Portier diff --git a/account_invoice_import/readme/DESCRIPTION.rst b/account_invoice_import/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..b3ef24503b --- /dev/null +++ b/account_invoice_import/readme/DESCRIPTION.rst @@ -0,0 +1,21 @@ +This module has been started by lazy accounting users who hate enter they vendor bills manually in Odoo. Almost all companies have several vendor bills to enter regularly in the system from the same vendors: phone bill, electricity bill, Internet access, train tickets, etc. Most of these invoices are available as PDF. If we are able to automatically extract from the PDF the required information to enter the invoice as vendor bill in Odoo, then this module will create it automatically. To know the full story behind the development of this module, read this `blog post `_. + +In order to reliably extract the required information from the invoice, two international standards exists to describe an Invoice in XML: + +* `CII `_ (Cross-Industry Invoice) developped by `UN/CEFACT `_ (United Nations Centre for Trade Facilitation and Electronic Business), +* `UBL `_ (Universal Business Language) which is an ISO standard (`ISO/IEC 19845 `_) developped by `OASIS `_ (Organization for the Advancement of Structured Information Standards). + +Some e-invoice standards such as `Factur-X `_ propose to embed the XML description of the invoice inside the PDF invoice. Other people think that the futur is pure-XML invoices: a European initiative called `PEPPOL `_ aims at setting up an open network to exchange e-invoices as UBL XML. We don't know yet which standard and which practice will prevail on electronic invoicing in the future, but we hope that lazy accountants won't have to manually encode their vendor bills in the near future. This module is here to help achieve this goal! + +This module doesn't do anything useful by itself ; it requires other modules to work: each modules adds a specific invoice format. + +Here is how the module works: + +* the user starts a wizard and uploads the PDF or XML invoice, +* if it is an XML file, Odoo will parse it to create the invoice (requires additional modules for specific XML formats, such as the module *account_invoice_import_ubl* for the UBL format), +* if it is a PDF file with an embedded XML file in Factur-X/CII format, Odoo will extract the embedded XML file and parse it to create the invoice (requires the module *account_invoice_import_facturx*), +* otherwise, Odoo will use the *invoice2data* Python library to try to interpret the text of the PDF (requires the module *account_invoice_import_invoice2data*), +* if there is already some draft supplier invoice for this supplier, Odoo will propose to select one to update or create a new draft invoice, +* otherwise, Odoo will directly create a new draft supplier invoice and attach the PDF to it. + +This module also works with supplier refunds. diff --git a/account_invoice_import/readme/ROADMAP.rst b/account_invoice_import/readme/ROADMAP.rst new file mode 100644 index 0000000000..f933df4a1d --- /dev/null +++ b/account_invoice_import/readme/ROADMAP.rst @@ -0,0 +1 @@ +* Remove dependency on *base_iban* and develop a separate glue module between this module and *base_iban* diff --git a/account_invoice_import/readme/USAGE.rst b/account_invoice_import/readme/USAGE.rst new file mode 100644 index 0000000000..468cd24512 --- /dev/null +++ b/account_invoice_import/readme/USAGE.rst @@ -0,0 +1,13 @@ +Go to the menu *Invoicing > Vendors > Import Vendor Bill* and follow the instructions of the wizard. You can also start the wizard from the *Accounting Dashboard*: on the purchase journal, click on the *Upload* button. + +This module also supports the scenario where you have a draft vendor bill (generated from a purchase order for instance) and you have to update it to comply with the real invoice sent by the vendor: on the form view of the draft vendor bill, click on the button *Import Invoice File* and follow the instructions of the wizard. + +If you have a large volume of invoices to import, you may be interested by the script **mass_invoice_import.py** which is available in the *scripts* subdirectory of this module. If you run: + +.. code:: + + ./mass_invoice_import.py --help + +you will have detailed instructions on how to use the script. + +A particular use case of this script is to have a directory where all the invoices saved are automatically uploaded in Odoo. For that, have a look at the sample script **inotify-sample.sh** available in the same subdirectory. Edit this sample script to adapt it to your needs. diff --git a/account_invoice_import/scripts/inotify-sample.sh b/account_invoice_import/scripts/inotify-sample.sh new file mode 100755 index 0000000000..283ec4d1f1 --- /dev/null +++ b/account_invoice_import/scripts/inotify-sample.sh @@ -0,0 +1,29 @@ +#! /bin/sh +# -*- coding: utf-8 -*- +# Copyright 2019-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# +# This script is designed to run under Linux +# To install the required tools, run: +# sudo apt install inotify-tools + +# CUSTOMIZE the variables below +INVOICE_DIR="/home/alexis/invoices2odoo" +MASS_INVOICE_IMPORT_SCRIPT="/usr/local/bin/mass_invoice_import.py" +ODOO_SERVER="localhost" +ODOO_PORT=8069 +ODOO_DB="o4_test1" +ODOO_LOGIN="admin" +ODOO_PASSWORD="admin" + +echo "Start to monitor $INVOICE_DIR for new files" + +inotifywait -m -e create -e moved_to --format "%e %w%f" "$INVOICE_DIR" | + while read event filepath; do + if [ -f "$filepath" ]; then + echo "File '$filepath' appeared via $event" + # do something with the file + "$MASS_INVOICE_IMPORT_SCRIPT" -s "$ODOO_SERVER" -p $ODOO_PORT -x -d "$ODOO_DB" -u "$ODOO_LOGIN" -w "$ODOO_PASSWORD" "$filepath" + fi + done diff --git a/account_invoice_import/scripts/mass_invoice_import.py b/account_invoice_import/scripts/mass_invoice_import.py new file mode 100755 index 0000000000..b37ecbcdb3 --- /dev/null +++ b/account_invoice_import/scripts/mass_invoice_import.py @@ -0,0 +1,282 @@ +#! /usr/bin/python3 +# Copyright 2017-2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +""" +Mass import of PDF/XML invoice. +The module OCA/edi/account_invoice_import must be installed on Odoo. +""" +import argparse +import base64 +import getpass +import logging +import mimetypes +import os +import sys + +import odoorpc + +__author__ = "Alexis de Lattre " +__date__ = "March 2021" +__version__ = "0.2" + +FORMAT = "%(asctime)s [%(levelname)s] %(message)s" +logging.basicConfig(format=FORMAT) +logger = logging.getLogger("add_xivo_user") + +fail_subdir_ok = {} # key = directory, value: failsubdir or False +invoice_ids = [] +fail_files = [] + + +def send_file(odoo, file_path): + filename = os.path.basename(file_path) + filetype = mimetypes.guess_type(filename) + logger.debug("filetype of file %s=%s", filename, filetype) + inv_mime = ["application/xml", "text/xml", "application/pdf"] + if filetype and filetype[0] in inv_mime: + logger.info("Starting to upload file %s to Odoo", filename) + if not os.access(file_path, os.R_OK): + logger.error("No read access on file %s. Skipping.", filename) + return False + with open(file_path, "rb") as f: + f.seek(0) + invoice = f.read() + inv_b64 = base64.encodebytes(invoice) + aiio = odoo.env["account.invoice.import"] + try: + invoice_id = aiio.create_invoice_webservice( + inv_b64.decode("utf8"), filename, "mass import script" + ) + if invoice_id: + logger.info("Invoice ID %d successfully created in Odoo", invoice_id) + invoice_ids.append(invoice_id) + return "success" + else: + logger.warning("Invoice import failed") + fail_files.append(filename) + return "failure" + except Exception as e: + logger.warning("Odoo failed to import file %s. Reason: %s", filename, e) + fail_files.append(filename) + return "failure" + else: + logger.warning("Filetype of file %s is %s. Skipping.", filename, filetype) + return False + + +def update_fail_subdir(directory, fail_subdir): + fail_subdir_ok[directory] = False + # We need write access on directory not only to create a sub-dir but also + # to move files out of it + if not os.access(directory, os.W_OK): + logger.warning( + "No permission to create a sub-directory nor remove " + "files from %s. Disabling the move-to-faildir feature " + "for that directory.", + directory, + ) + return False + fail_dir_path = os.path.join(directory, fail_subdir) + if not os.path.exists(fail_dir_path): + logger.info("Creating sub-directory %s", fail_subdir) + os.makedirs(fail_dir_path) + if not os.access(fail_dir_path, os.W_OK): + logger.warning( + "No permission to move files to %s. Disabling the move-to-faildir" + "feature for directory %s", + fail_dir_path, + directory, + ) + return False + fail_subdir_ok[directory] = fail_dir_path + return True + + +def handle_failure(directory, entry, file_path): + if not args.no_move_failed: + if directory not in fail_subdir_ok: + update_fail_subdir(directory, args.fail_subdir) + fail_dir_path = fail_subdir_ok[directory] + if fail_dir_path: + logger.info( + "Moving file %s to sub-directory %s", + entry, + args.fail_subdir, + ) + os.rename(file_path, os.path.join(fail_dir_path, entry)) + + +def browse_directory(odoo, directory): + if os.path.isdir(directory): + logger.info("Start working on directory %s", directory) + for entry in os.listdir(directory): + file_path = os.path.join(directory, entry) + logger.debug("file_path=%s", entry) + if not os.path.isfile(file_path): + continue + res = send_file(odoo, file_path) + if res == "failure": + handle_failure(directory, entry, file_path) + + elif os.path.isfile(directory): + res = send_file(odoo, directory) + else: + logger.warning("%s is not a directory nor a file. Skipped." % directory) + + +def main(args): + if args.log_level: + log_level = args.log_level.lower() + log_map = { + "debug": logging.DEBUG, + "info": logging.INFO, + "warn": logging.WARN, + "error": logging.ERROR, + } + if log_level in log_map: + logger.setLevel(log_map[log_level]) + else: + logger.error( + "Wrong value for log level (%s). Possible values: " + "debug, info, warn, error.", + log_level, + ) + sys.exit(1) + pwd = args.password + first_login = True + while not pwd: + # prompt for password + pwd = getpass.getpass() + if first_login: + logger.error("Cannot connect with an empty password. Re-enter a password.") + first_login = False + if not args: + logger.error( + "Missing directory argument. You should pass to the " + "script at least one directory as argument." + ) + sys.exit(1) + proto = args.no_ssl and "jsonrpc" or "jsonrpc+ssl" + logger.info( + "Connecting to Odoo %s:%s in %s database %s username %s", + args.server, + args.port, + proto, + args.database, + args.username, + ) + try: + odoo = odoorpc.ODOO(args.server, proto, args.port) + odoo.login(args.database, args.username, pwd) + logger.info("Successfully connected to Odoo") + except Exception as e: + logger.error("Failed to connect to Odoo. Error: %s", e) + sys.exit(1) + + for directory in args.dir_list: + browse_directory(odoo, directory) + logger.info( + "RESULT: %d invoice%s created in Odoo, %d invoice import failure%s.", + len(invoice_ids), + len(invoice_ids) > 1 and "s" or "", + len(fail_files), + len(fail_files) > 1 and "s" or "", + ) + logger.debug("IDs of created invoices: %s", invoice_ids) + logger.debug("Fail invoice imports: %s", fail_files) + + +if __name__ == "__main__": + usage = ( + "Usage: mass_invoice_import.py [options] directory1 directory2 " + "directory3 ..." + ) + epilog = ( + "Script written by Alexis de Lattre. " "Published under the GNU AGPL licence." + ) + description = ( + "This script is designed for mass import of " + "PDF or XML invoices in Odoo. The OCA module account_invoice_import " + "must be installed on Odoo together with the module(s) that add " + "support for the specific invoice format." + ) + parser = argparse.ArgumentParser( + usage=usage, epilog=epilog, description=description + ) + parser.add_argument( + "-s", + "--server", + dest="server", + type=str, + required=True, + help="DNS or IP address of the Odoo server.", + ) + parser.add_argument( + "-p", + "--port", + dest="port", + type=int, + default=443, + help="Port of Odoo's HTTP(S) interface. Default: 443.", + ) + parser.add_argument( + "-x", + "--no-ssl", + dest="no_ssl", + action="store_true", + help="Use un-encrypted HTTP connection instead of HTTPS.", + ) + parser.add_argument( + "-d", + "--database", + dest="database", + type=str, + required=True, + help="Odoo database name.", + ) + parser.add_argument( + "-u", + "--username", + dest="username", + type=str, + required=True, + help="Username to use when connecting to Odoo.", + ) + parser.add_argument( + "-w", + "--password", + dest="password", + type=str, + required=True, + help="Password of the Odoo user. If you don't use this option, " + "the script will prompt you for a password.", + ) + parser.add_argument( + "-m", + "--no-move-fail", + dest="no_move_failed", + action="store_true", + help="Don't move failed invoices to a fail sub-directory.", + ) + parser.add_argument( + "-k", + "--fail-subdir-name", + dest="fail_subdir", + type=str, + default="odoo-import_fail", + help="Fail sub-directory name. Default value: 'odoo-import_fail'.", + ) + parser.add_argument( + "-l", + "--log-level", + dest="log_level", + default="info", + type=str, + help="Set log level. Possible values: debug, info, warn, error. " + "Default value: info.", + ) + parser.add_argument("dir_list", help="List of directories", type=str, nargs="+") + args = parser.parse_args() + main(args) diff --git a/account_invoice_import/security/ir.model.access.csv b/account_invoice_import/security/ir.model.access.csv new file mode 100644 index 0000000000..b082640a84 --- /dev/null +++ b/account_invoice_import/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_invoice_import_config_read,Read access on account.invoice.import.config to Invoicing and Payment,model_account_invoice_import_config,account.group_account_invoice,1,0,0,0 +access_account_invoice_import_config_readonly_grp,Read access on account.invoice.import.config to Account Readonly grp,model_account_invoice_import_config,account.group_account_readonly,1,0,0,0 +access_account_invoice_import_config_full,Full access on account.invoice.import.config to Account Manager,model_account_invoice_import_config,account.group_account_manager,1,1,1,1 +access_account_invoice_import,Access to account.invoice.import wizard,model_account_invoice_import,account.group_account_invoice,1,1,1,1 diff --git a/account_invoice_import/security/rule.xml b/account_invoice_import/security/rule.xml new file mode 100644 index 0000000000..c144978f6c --- /dev/null +++ b/account_invoice_import/security/rule.xml @@ -0,0 +1,15 @@ + + + + + Import Supplier Invoices Configuration multi-company + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + diff --git a/account_invoice_import/static/description/icon.png b/account_invoice_import/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/account_invoice_import/static/description/icon.png differ diff --git a/account_invoice_import/static/description/index.html b/account_invoice_import/static/description/index.html new file mode 100644 index 0000000000..40c0519594 --- /dev/null +++ b/account_invoice_import/static/description/index.html @@ -0,0 +1,464 @@ + + + + + + +Account Invoice Import + + + +
+

Account Invoice Import

+ + +

Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runbot

+

This module has been started by lazy accounting users who hate enter they supplier invoices manually in Odoo. Almost all companies have several supplier invoices to enter regularly in the system from the same suppliers: phone bill, electricity bill, Internet access, train tickets, etc. Most of these invoices are available as PDF. We dream that we would be able to automatically extract from the PDF the required information to enter the invoice as supplier invoice in Odoo. To know the full story behind the development of this module, read this blog post.

+

In the future, we believe we will have structured information embedded inside the metadata of PDF invoices. There are 2 main standards for electronic invoicing:

+
    +
  • CII (Cross-Industry Invoice) developped by UN/CEFACT (United Nations Centre for Trade Facilitation and Electronic Business),
  • +
  • UBL (Universal Business Language) which is an ISO standard (ISO/IEC 19845) developped by OASIS (Organization for the Advancement of Structured Information Standards).
  • +
+

For example, there is already a standard in Germany called ZUGFeRD which is based on CII.

+

This module doesn’t do anything useful by itself ; it requires other modules to work: each modules adds a specific invoice format.

+

Here is how the module works:

+
    +
  • the user starts a wizard and uploads the PDF or XML invoice,
  • +
  • if it is an XML file, Odoo will parse it to create the invoice (requires additional modules for specific XML formats, such as the module account_invoice_import_ubl for the UBL format),
  • +
  • if it is a PDF file with an embedded XML file in ZUGFeRD/CII format, Odoo will extract the embedded XML file and parse it to create the invoice (requires the module account_invoice_import_zugferd),
  • +
  • otherwise, Odoo will use the invoice2data Python library to try to interpret the text of the PDF (requires the module account_invoice_import_invoice2data),
  • +
  • if there is already some draft supplier invoice for this supplier, Odoo will propose to select one to update or create a new draft invoice,
  • +
  • otherwise, Odoo will directly create a new draft supplier invoice and attach the PDF to it.
  • +
+

This module also works with supplier refunds.

+

Table of contents

+ +
+

Configuration

+

Go to the form view of the suppliers and configure it with the following parameters:

+
    +
  • is a Company ? is True
  • +
  • Supplier is True
  • +
  • the TIN (i.e. VAT number) is set (the VAT number is used by default when searching the supplier in the Odoo partner database)
  • +
  • in the Accounting tab, create one or several Invoice Import Configurations.
  • +
+

You can configure a mail gateway to import invoices from an email:

+
    +
  • Go to the menu Settings > Technical > Email > Incoming Mail Servers and setup the access (POP or IMAP) to the mailbox that will be used to received the invoices,
  • +
  • In the section Actions to perform on incoming mails, set the field Create a new record to Wizard to import supplier invoices/refunds (model account.invoice.import). The field Server Action should be left empty.
  • +
  • If you are in a multi-company setup, you also have to go to the menu Accounting > Configuration > Settings: in the section Invoice Import, enter the email of the mailbox used to import invoices in the field Mail Gateway: Destination E-mail (it will be used to select the right company to import the invoice in).
  • +
+
+
+

Known issues / Roadmap

+
    +
  • Remove dependency on base_iban and develop a separate glue module between this module and base_iban
  • +
  • Enhance the update of an existing invoice by analysing the lines (lines are only available when the invoice has an embedded XML file)
  • +
  • Add a mail gateway to be able to forward the emails that we receive with PDF invoices to a dedicated address ; the gateway would detach the PDF invoice from the email and create the draft supplier invoice in Odoo.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/edi project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/account_invoice_import/tests/__init__.py b/account_invoice_import/tests/__init__.py new file mode 100644 index 0000000000..d0a138175d --- /dev/null +++ b/account_invoice_import/tests/__init__.py @@ -0,0 +1 @@ +from . import test_invoice_import diff --git a/account_invoice_import/tests/test_invoice_import.py b/account_invoice_import/tests/test_invoice_import.py new file mode 100644 index 0000000000..6cc76ed92f --- /dev/null +++ b/account_invoice_import/tests/test_invoice_import.py @@ -0,0 +1,161 @@ +# Copyright 2017 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase +from odoo.tools import float_compare + + +class TestInvoiceImport(TransactionCase): + def setUp(self): + super().setUp() + self.expense_account = self.env["account.account"].create( + { + "code": "612AII", + "name": "expense account invoice import", + "user_type_id": self.env.ref("account.data_account_type_expenses").id, + } + ) + self.income_account = self.env["account.account"].create( + { + "code": "707AII", + "name": "revenue account invoice import", + "user_type_id": self.env.ref("account.data_account_type_revenue").id, + } + ) + purchase_tax_vals = { + "name": "Test 1% VAT", + "description": "ZZ-VAT-buy-1.0", + "type_tax_use": "purchase", + "amount": 1, + "amount_type": "percent", + "unece_type_id": self.env.ref("account_tax_unece.tax_type_vat").id, + "unece_categ_id": self.env.ref("account_tax_unece.tax_categ_s").id, + # TODO tax armageddon + # "account_id": self.expense_account.id, + # "refund_account_id": self.expense_account.id, + } + self.purchase_tax = self.env["account.tax"].create(purchase_tax_vals) + sale_tax_vals = purchase_tax_vals.copy() + sale_tax_vals.update({"description": "ZZ-VAT-sale-1.0", "type_tax_use": "sale"}) + self.sale_tax = self.env["account.tax"].create(sale_tax_vals) + self.product = self.env["product.product"].create( + { + "name": "Expense product", + "default_code": "AII-TEST-PRODUCT", + "taxes_id": [(6, 0, [self.sale_tax.id])], + "supplier_taxes_id": [(6, 0, [self.purchase_tax.id])], + "property_account_income_id": self.income_account.id, + "property_account_expense_id": self.expense_account.id, + } + ) + self.all_import_config = [ + { + "invoice_line_method": "1line_no_product", + "account": self.expense_account, + "taxes": self.purchase_tax, + }, + {"invoice_line_method": "1line_static_product", "product": self.product}, + { + "invoice_line_method": "nline_no_product", + "account": self.expense_account, + }, + {"invoice_line_method": "nline_static_product", "product": self.product}, + {"invoice_line_method": "nline_auto_product"}, + ] + + # Define partners as supplier and customer + # Wood Corner + self.env.ref("base.res_partner_1").supplier_rank = 1 + # Deco Addict + self.env.ref("base.res_partner_2").customer_rank = 1 + + def test_import_in_invoice(self): + parsed_inv = { + "type": "in_invoice", + "amount_untaxed": 100.0, + "amount_total": 101.0, + "date_invoice": "2017-08-16", + "date_due": "2017-08-31", + "date_start": "2017-08-01", + "date_end": "2017-08-31", + "partner": {"name": "Wood Corner"}, + "description": "New hi-tech gadget", + "lines": [ + { + "product": {"code": "AII-TEST-PRODUCT"}, + "name": "Super test product", + "qty": 2, + "price_unit": 50, + "taxes": [ + { + "amount_type": "percent", + "amount": 1.0, + "unece_type_code": "VAT", + "unece_categ_code": "S", + } + ], + } + ], + } + for import_c in self.all_import_config: + # hack to have a unique vendor inv ref + parsed_inv["invoice_number"] = "INV-%s" % import_c["invoice_line_method"] + self.env["account.invoice.import"].create_invoice(parsed_inv, import_c) + + def test_import_out_invoice(self): + parsed_inv = { + "type": "out_invoice", + "date_invoice": "2017-08-16", + "partner": {"name": "Deco Addict"}, + "lines": [ + { + "product": {"code": "AII-TEST-PRODUCT"}, + "name": "Super product", + "qty": 3, + "price_unit": 10.22, + "date_start": "2017-08-01", + "date_end": "2017-08-31", + "taxes": [ + { # only needed for method 'nline_no_product' + "amount_type": "percent", + "amount": 1.0, + "unece_type_code": "VAT", + "unece_categ_code": "S", + } + ], + } + ], + } + for import_config in self.all_import_config: + if not import_config["invoice_line_method"].startswith("nline"): + continue + inv = self.env["account.invoice.import"].create_invoice( + parsed_inv, import_config + ) + prec = inv.currency_id.rounding + self.assertFalse( + float_compare(inv.amount_untaxed, 30.66, precision_rounding=prec) + ) + self.assertFalse( + float_compare(inv.amount_total, 30.97, precision_rounding=prec) + ) + + def test_email_gateway(self): + """No exception occurs on incoming email""" + mail = """\ +Received: from someone@example.com +Message-Id: +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii" +Date: Thursday, 4 Jun 1998 09:43:14 -0800 +To: project-discussion@example.com +From: Nina Marton +Subject: Happy Birthday + +Happy Birthday! +See you this evening, +Nina +""" + self.env["mail.thread"].with_context( + mail_channel_noautofollow=True + ).message_process("account.invoice.import", mail) diff --git a/account_invoice_import/views/account_invoice.xml b/account_invoice_import/views/account_invoice.xml new file mode 100644 index 0000000000..0ef997087c --- /dev/null +++ b/account_invoice_import/views/account_invoice.xml @@ -0,0 +1,32 @@ + + + + + account.move + + + + + + diff --git a/account_invoice_import/views/account_invoice_import_config.xml b/account_invoice_import/views/account_invoice_import_config.xml new file mode 100644 index 0000000000..21c5c1faee --- /dev/null +++ b/account_invoice_import/views/account_invoice_import_config.xml @@ -0,0 +1,110 @@ + + + + + account.invoice.import.config + +
+ + + + + + + + + + + + + + + + + +
+
+
+ + account.invoice.import.config + + + + + + + + + + + + account.invoice.import.config + + + + + + + + + + + + + + + Import Bills + account.invoice.import.config + tree,form + {'invoice_import_config_main_view': True} + + + +
diff --git a/account_invoice_import/views/account_journal_dashboard.xml b/account_invoice_import/views/account_journal_dashboard.xml new file mode 100644 index 0000000000..9a695d8fd0 --- /dev/null +++ b/account_invoice_import/views/account_journal_dashboard.xml @@ -0,0 +1,23 @@ + + + + + account.journal + + + + + + diff --git a/account_invoice_import/views/res_config_settings.xml b/account_invoice_import/views/res_config_settings.xml new file mode 100644 index 0000000000..1955866c93 --- /dev/null +++ b/account_invoice_import/views/res_config_settings.xml @@ -0,0 +1,54 @@ + + + + + res.config.settings + + + +

Electronic Invoices Import

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + diff --git a/account_invoice_import/views/res_partner.xml b/account_invoice_import/views/res_partner.xml new file mode 100644 index 0000000000..075a5163d4 --- /dev/null +++ b/account_invoice_import/views/res_partner.xml @@ -0,0 +1,37 @@ + + + + + res.partner + + + +
+ +
+
+
diff --git a/account_invoice_import/wizard/__init__.py b/account_invoice_import/wizard/__init__.py new file mode 100644 index 0000000000..dd8275d27a --- /dev/null +++ b/account_invoice_import/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import account_invoice_import +from . import res_config_settings diff --git a/account_invoice_import/wizard/account_invoice_import.py b/account_invoice_import/wizard/account_invoice_import.py new file mode 100644 index 0000000000..2d89f15160 --- /dev/null +++ b/account_invoice_import/wizard/account_invoice_import.py @@ -0,0 +1,1439 @@ +# Copyright 2015-2021 Akretion France (http://www.akretion.com/) +# Copyright 2020-2021 Therp BV (https://therp.nl) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import logging +import mimetypes +from datetime import datetime + +from lxml import etree + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools import config, float_compare, float_is_zero, float_round +from odoo.tools.misc import format_amount + +logger = logging.getLogger(__name__) + + +class AccountInvoiceImport(models.TransientModel): + _name = "account.invoice.import" + _inherit = ["business.document.import", "mail.thread"] + _description = "Wizard to import supplier invoices/refunds" + + invoice_file = fields.Binary(string="PDF or XML Invoice") + invoice_filename = fields.Char(string="Filename") + state = fields.Selection( + [ + ("import", "Import"), + ("config", "Select Invoice Import Configuration"), + ("update", "Update"), + ("update-from-invoice", "Update From Invoice"), + ("partner-not-found", "Partner not found"), + ], + default="import", + ) + partner_id = fields.Many2one("res.partner", string="Vendor", readonly=True) + # The following partner_* fields are used for partner-not-found state + partner_vat = fields.Char(readonly=True) + partner_country_id = fields.Many2one("res.country", readonly=True) + import_config_id = fields.Many2one( + "account.invoice.import.config", string="Invoice Import Configuration" + ) + currency_id = fields.Many2one("res.currency", readonly=True) + invoice_type = fields.Selection( + [("in_invoice", "Invoice"), ("in_refund", "Refund")], + string="Invoice or Refund", + readonly=True, + ) + amount_untaxed = fields.Float( + string="Total Untaxed", digits="Account", readonly=True + ) + amount_total = fields.Float(string="Total", digits="Account", readonly=True) + invoice_id = fields.Many2one( + "account.move", string="Draft Supplier Invoice to Update" + ) + message = fields.Text(readonly=True) + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + # I can't put 'default_state' in context because then it is transfered + # to the code and it causes problems when we create invoice lines + if self.env.context.get("wizard_default_state"): + res["state"] = self.env.context["wizard_default_state"] + if self.env.context.get("default_partner_id") and not self.env.context.get( + "default_import_config_id" + ): + configs = self.env["account.invoice.import.config"].search( + [ + ("partner_id", "=", self.env.context["default_partner_id"]), + ("company_id", "=", self.env.company.id), + ] + ) + if len(configs) == 1: + res["import_config_id"] = configs.id + return res + + @api.model + def parse_xml_invoice(self, xml_root): + return False + + @api.model + def parse_pdf_invoice(self, file_data): + """This method must be inherited by additional modules with + the same kind of logic as the account_statement_import_* + modules""" + xml_files_dict = self.get_xml_files_from_pdf(file_data) + for xml_filename, xml_root in xml_files_dict.items(): + logger.info("Trying to parse XML file %s", xml_filename) + parsed_inv = self.parse_xml_invoice(xml_root) + if parsed_inv: + return parsed_inv + parsed_inv = self.fallback_parse_pdf_invoice(file_data) + if not parsed_inv: + raise UserError( + _( + "This type of PDF invoice is not supported. Did you install " + "the module to support this type of file?" + ) + ) + return parsed_inv + + def fallback_parse_pdf_invoice(self, file_data): + """Designed to be inherited by the module + account_invoice_import_invoice2data, to be sure the invoice2data + technique is used after the electronic invoice modules such as + account_invoice_import_facturx and account_invoice_import_ubl + """ + return False + + # INVOICE PIVOT format ('parsed_inv' without pre-processing) + # For refunds, we support 2 possibilities: + # a) type = 'in_invoice' with negative amounts and qty + # b) type = 'in_refund' with positive amounts and qty ("Odoo way") + # That way, it simplifies the code in the format-specific import + # modules, which is what we want! + # { + # 'type': 'in_invoice' or 'in_refund' # 'in_invoice' by default + # 'currency': { + # 'iso': 'EUR', + # 'currency_symbol': u'€', # The one or the other + # }, + # 'date': '2015-10-08', # Must be a string + # 'date_due': '2015-11-07', + # 'date_start': '2015-10-01', # for services over a period of time + # 'date_end': '2015-10-31', + # 'amount_untaxed': 10.0, + # 'amount_tax': 2.0, # provide amount_untaxed OR amount_tax + # 'amount_total': 12.0, # Total with taxes, must always be provided + # 'partner': { + # 'vat': 'FR25499247138', + # 'email': 'support@browserstack.com', + # 'name': 'Capitaine Train', + # 'street': '27 rue Henri Rolland', + # 'street2': 'ZAC des cactus', + # 'city': 'Villeurbanne', + # 'zip': '69100', + # 'country_code': 'FR', + # 'state_code': False, + # 'phone': '+33 4 72 42 24 42', + # }, + # 'company': {'vat': 'FR12123456789'}, # Rarely set in invoices + # # Only used to check we are not + # # importing the invoice in the + # # wrong company by mistake + # 'invoice_number': 'I1501243', + # 'description': 'TGV Paris-Lyon', + # 'attachments': {'file1.pdf': base64data1, 'file2.pdf': base64data2}, + # 'chatter_msg': ['Notes added in chatter of the invoice'], + # 'note': 'Note embedded in the document', + # 'origin': 'Origin note', + # 'lines': [{ + # 'product': { + # 'barcode': '4123456000021', + # 'code': 'GZ250', + # }, + # 'name': 'Gelierzucker Extra 250g', + # 'price_unit': 1.45, # price_unit without taxes + # 'qty': 2.0, + # 'price_subtotal': 2.90, # not required, but needed + # to be able to generate adjustment lines when decimal + # precision is not high enough in Odoo + # 'uom': {'unece_code': 'C62'}, + # 'taxes': [{ + # 'amount_type': 'percent', + # 'amount': 20.0, + # 'unece_type_code': 'VAT', + # 'unece_categ_code': 'S', + # 'unece_due_date_code': '432', + # }], + # 'date_start': '2015-10-01', + # 'date_end': '2015-10-31', + # # date_start and date_end on lines override the global value + # }], + # } + + # IMPORT CONFIG + # { + # 'invoice_line_method': '1line_no_product', + # 'account_analytic': Analytic account recordset, + # 'account': Account recordset, + # 'taxes': taxes multi-recordset, + # 'label': 'Force invoice line description', + # 'product': product recordset, + # } + # + # Note: we also support importing customer invoices via + # create_invoice() but only with 'nline_*' invoice import methods. + + @api.model + def _prepare_create_invoice_vals(self, parsed_inv, import_config): + assert parsed_inv.get("pre-processed"), "pre-processing not done" + assert import_config + amo = self.env["account.move"] + company = ( + self.env["res.company"].browse(self.env.context.get("force_company")) + or self.env.company + ) + if parsed_inv["type"] in ("out_invoice", "out_refund"): + partner_type = "customer" + else: + partner_type = "supplier" + partner = self._match_partner( + parsed_inv["partner"], parsed_inv["chatter_msg"], partner_type=partner_type + ) + partner = partner.commercial_partner_id + currency = self._match_currency( + parsed_inv.get("currency"), parsed_inv["chatter_msg"] + ) + journal_id = ( + amo.with_context( + default_move_type=parsed_inv["type"], company_id=company.id + ) + ._get_default_journal() + .id + ) + vals = { + "partner_id": partner.id, + "currency_id": currency.id, + "move_type": parsed_inv["type"], + "company_id": company.id, + "invoice_origin": parsed_inv.get("origin"), + "ref": parsed_inv.get("invoice_number"), + "invoice_date": parsed_inv.get("date"), + "journal_id": journal_id, + "invoice_line_ids": [], + } + vals = amo.play_onchanges(vals, ["partner_id"]) + # Force due date of the invoice + if parsed_inv.get("date_due"): + vals["invoice_date_due"] = parsed_inv.get("date_due") + # Bank info + if parsed_inv.get("iban") and vals["move_type"] == "in_invoice": + partner_bank = self._match_partner_bank( + partner, + parsed_inv["iban"], + parsed_inv.get("bic"), + parsed_inv["chatter_msg"], + create_if_not_found=company.invoice_import_create_bank_account, + ) + if partner_bank: + vals["partner_bank_id"] = partner_bank.id + # get invoice line vals + vals["invoice_line_ids"] = [] + if import_config["invoice_line_method"].startswith("1line"): + self._prepare_line_vals_1line(partner, vals, parsed_inv, import_config) + elif import_config["invoice_line_method"].startswith("nline"): + self._prepare_line_vals_nline(partner, vals, parsed_inv, import_config) + # Write analytic account + fix syntax for taxes + analytic_account = import_config.get("account_analytic", False) + if analytic_account: + for line in vals["invoice_line_ids"]: + line[2]["account_analytic_id"] = analytic_account.id + return vals + + @api.model + def _prepare_line_vals_1line(self, partner, vals, parsed_inv, import_config): + line_model = self.env["account.move.line"] + if import_config["invoice_line_method"] == "1line_no_product": + if import_config["taxes"]: + il_tax_ids = [(6, 0, import_config["taxes"].ids)] + else: + il_tax_ids = False + il_vals = { + "account_id": import_config["account"].id, + "tax_ids": il_tax_ids, + "price_unit": parsed_inv.get("amount_untaxed"), + } + elif import_config["invoice_line_method"] == "1line_static_product": + product = import_config["product"] + il_vals = {"product_id": product.id, "move_id": vals} + il_vals = line_model.play_onchanges(il_vals, ["product_id"]) + il_vals.pop("move_id") + if import_config.get("label"): + il_vals["name"] = import_config["label"] + elif parsed_inv.get("description"): + il_vals["name"] = parsed_inv["description"] + self.set_1line_price_unit_and_quantity(il_vals, parsed_inv) + self.set_1line_start_end_dates(il_vals, parsed_inv) + vals["invoice_line_ids"].append((0, 0, il_vals)) + + @api.model + def _prepare_line_vals_nline(self, partner, vals, parsed_inv, import_config): + line_model = self.env["account.move.line"] + start_end_dates_installed = hasattr(line_model, "start_date") and hasattr( + line_model, "end_date" + ) + if not parsed_inv.get("lines"): + raise UserError( + _( + "You have selected a Multi Line method for this import " + "but Odoo could not extract/read any XML file inside " + "the PDF invoice." + ) + ) + if import_config["invoice_line_method"] == "nline_no_product": + static_vals = {"account_id": import_config["account"].id} + elif import_config["invoice_line_method"] == "nline_static_product": + sproduct = import_config["product"] + static_vals = {"product_id": sproduct.id, "move_id": vals} + static_vals = line_model.play_onchanges(static_vals, ["product_id"]) + static_vals.pop("move_id") + else: + static_vals = {} + for line in parsed_inv["lines"]: + il_vals = static_vals.copy() + if import_config["invoice_line_method"] == "nline_auto_product": + product = self._match_product( + line["product"], parsed_inv["chatter_msg"], seller=partner + ) + il_vals = {"product_id": product.id, "move_id": vals} + il_vals = line_model.play_onchanges(il_vals, ["product_id"]) + il_vals.pop("move_id") + elif import_config["invoice_line_method"] == "nline_no_product": + taxes = self._match_taxes(line.get("taxes"), parsed_inv["chatter_msg"]) + il_vals["tax_ids"] = [(6, 0, taxes.ids)] + if not il_vals.get("account_id") and il_vals.get("product_id"): + product = self.env["product.product"].browse(il_vals["product_id"]) + raise UserError( + _( + "Account missing on product '%s' or on it's related " + "category '%s'." + ) + % (product.display_name, product.categ_id.display_name) + ) + if line.get("name"): + il_vals["name"] = line["name"] + if start_end_dates_installed: + il_vals["start_date"] = line.get("date_start") or parsed_inv.get( + "date_start" + ) + il_vals["end_date"] = line.get("date_end") or parsed_inv.get("date_end") + uom = self._match_uom(line.get("uom"), parsed_inv["chatter_msg"]) + il_vals["product_uom_id"] = uom.id + il_vals.update( + { + "quantity": line["qty"], + "price_unit": line["price_unit"], # TODO fix for tax incl + } + ) + vals["invoice_line_ids"].append((0, 0, il_vals)) + + @api.model + def set_1line_price_unit_and_quantity(self, il_vals, parsed_inv): + """For the moment, we only take into account the 'price_include' + option of the first tax""" + il_vals["quantity"] = 1 + il_vals["price_unit"] = parsed_inv.get("amount_total") + if il_vals.get("tax_ids"): + for tax_entry in il_vals["tax_ids"]: + if tax_entry: + tax_id = False + if tax_entry[0] == 4: + tax_id = tax_entry[1] + elif tax_entry[0] == 6: + tax_id = tax_entry[2][0] + if tax_id: + first_tax = self.env["account.tax"].browse(tax_id) + if not first_tax.price_include: + il_vals["price_unit"] = parsed_inv.get("amount_untaxed") + break + + @api.model + def set_1line_start_end_dates(self, il_vals, parsed_inv): + """Only useful if you have installed the module account_cutoff_prepaid + from https://github.com/OCA/account-closing""" + amlo = self.env["account.move.line"] + if ( + parsed_inv.get("date_start") + and parsed_inv.get("date_end") + and hasattr(amlo, "start_date") + and hasattr(amlo, "end_date") + ): + il_vals["start_date"] = parsed_inv.get("date_start") + il_vals["end_date"] = parsed_inv.get("date_end") + + def company_cannot_refund_vat(self): + company_id = self.env.context.get("force_company") or self.env.company.id + vat_purchase_taxes = self.env["account.tax"].search( + [ + ("company_id", "=", company_id), + ("amount_type", "=", "percent"), + ("type_tax_use", "=", "purchase"), + ] + ) + if not vat_purchase_taxes: + return True + return False + + @api.model + def parse_invoice(self, invoice_file_b64, invoice_filename): + assert invoice_file_b64, "No invoice file" + assert isinstance(invoice_file_b64, bytes) + logger.info("Starting to import invoice %s", invoice_filename) + file_data = base64.b64decode(invoice_file_b64) + filetype = mimetypes.guess_type(invoice_filename) + logger.debug("Invoice mimetype: %s", filetype) + if filetype and filetype[0] in ["application/xml", "text/xml"]: + try: + xml_root = etree.fromstring(file_data) + except Exception as e: + raise UserError(_("This XML file is not XML-compliant. Error: %s") % e) + pretty_xml_bytes = etree.tostring( + xml_root, pretty_print=True, encoding="UTF-8", xml_declaration=True + ) + logger.debug("Starting to import the following XML file:") + logger.debug(pretty_xml_bytes.decode("utf-8")) + parsed_inv = self.parse_xml_invoice(xml_root) + if parsed_inv is False: + raise UserError( + _( + "This type of XML invoice is not supported. " + "Did you install the module to support this type " + "of file?" + ) + ) + # Fallback on PDF + else: + parsed_inv = self.parse_pdf_invoice(file_data) + if "attachments" not in parsed_inv: + parsed_inv["attachments"] = {} + parsed_inv["attachments"][invoice_filename] = invoice_file_b64 + # pre_process_parsed_inv() will be called again a second time, + # but it's OK + pp_parsed_inv = self.pre_process_parsed_inv(parsed_inv) + return pp_parsed_inv + + @api.model + def pre_process_parsed_inv(self, parsed_inv): + if parsed_inv.get("pre-processed"): + return parsed_inv + parsed_inv["pre-processed"] = True + if "chatter_msg" not in parsed_inv: + parsed_inv["chatter_msg"] = [] + if parsed_inv.get("type") in ("out_invoice", "out_refund"): + return parsed_inv + if not parsed_inv.get("currency_rounding"): + self.get_precision_rounding_from_currency_helper(parsed_inv) + prec_pp = self.env["decimal.precision"].precision_get("Product Price") + prec_uom = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + if "amount_tax" in parsed_inv and "amount_untaxed" not in parsed_inv: + parsed_inv["amount_untaxed"] = ( + parsed_inv["amount_total"] - parsed_inv["amount_tax"] + ) + elif "amount_untaxed" not in parsed_inv and "amount_tax" not in parsed_inv: + # For invoices that never have taxes + parsed_inv["amount_untaxed"] = parsed_inv["amount_total"] + # Support the 2 refund methods; if method a) is used, we convert to + # method b) + if not parsed_inv.get("type"): + parsed_inv["type"] = "in_invoice" # default value + if ( + parsed_inv["type"] == "in_invoice" + and float_compare( + parsed_inv["amount_total"], + 0, + precision_rounding=parsed_inv["currency_rounding"], + ) + < 0 + ): + parsed_inv["type"] = "in_refund" + for entry in ["amount_untaxed", "amount_total"]: + parsed_inv[entry] *= -1 + for line in parsed_inv.get("lines", []): + line["qty"] *= -1 + if "price_subtotal" in line: + line["price_subtotal"] *= -1 + # Handle taxes: + self._pre_process_parsed_inv_taxes(parsed_inv) + # Handle rounding: + for line in parsed_inv.get("lines", []): + line["qty"] = float_round(line["qty"], precision_digits=prec_uom) + line["price_unit"] = float_round( + line["price_unit"], precision_digits=prec_pp + ) + parsed_inv_for_log = dict(parsed_inv) + if "attachments" in parsed_inv_for_log: + parsed_inv_for_log.pop("attachments") + logger.debug("Result of invoice parsing parsed_inv=%s", parsed_inv_for_log) + # the 'company' dict in parsed_inv is NOT used to auto-detect + # the company, but to check that we are not importing an + # invoice for another company by mistake + # The advantage of doing the check here is that it will be run + # in all scenarios (create/update/...), but it's not related + # to invoice parsing... + if ( + parsed_inv.get("company") + and not config["test_enable"] + and not self.env.context.get("edi_skip_company_check") + ): + self._check_company(parsed_inv["company"], parsed_inv["chatter_msg"]) + return parsed_inv + + @api.model + def _pre_process_parsed_inv_taxes(self, parsed_inv): + """Handle taxes in pre_processing parsed invoice.""" + # Handle the case where we import an invoice with VAT in a company that + # cannot deduct VAT + if self.company_cannot_refund_vat(): + parsed_inv["amount_tax"] = 0 + parsed_inv["amount_untaxed"] = parsed_inv["amount_total"] + for line in parsed_inv.get("lines", []): + if line.get("taxes"): + if len(line["taxes"]) > 1: + raise UserError( + _( + "You are importing an invoice in a company that " + "cannot deduct VAT and the imported invoice has " + "several VAT taxes on the same line (%s). We do " + "not support this scenario for the moment." + ) + % line.get("name") + ) + vat_rate = line["taxes"][0].get("amount") + if not float_is_zero(vat_rate, precision_digits=2): + line["price_unit"] = line["price_unit"] * (1 + vat_rate / 100.0) + line.pop("price_subtotal") + line["taxes"] = [] + # Rounding work + for entry in ["amount_untaxed", "amount_total"]: + parsed_inv[entry] = float_round( + parsed_inv[entry], precision_rounding=parsed_inv["currency_rounding"] + ) + + @api.model + def invoice_already_exists(self, commercial_partner, parsed_inv): + company_id = self.env.context.get("force_company") or self.env.company.id + existing_inv = self.env["account.move"].search( + [ + ("company_id", "=", company_id), + ("commercial_partner_id", "=", commercial_partner.id), + ("move_type", "=", parsed_inv["type"]), + ("ref", "=ilike", parsed_inv.get("invoice_number")), + ], + limit=1, + ) + return existing_inv + + def get_parsed_invoice(self): + """Hook to change the method of retrieval for the invoice data""" + return self.parse_invoice(self.invoice_file, self.invoice_filename) + + def goto_partner_not_found(self, parsed_inv, error_message): + """Hook designed to add an action when no partner is found + For instance to propose to create the partner based on the partner_dict. + """ + partner_dict = parsed_inv["partner"] + vals = { + "message": error_message, + "state": "partner-not-found", + "partner_vat": partner_dict.get("vat"), + } + if parsed_inv["partner"].get("country_code"): + country = self.env["res.country"].search( + [("code", "=", partner_dict["country_code"].upper().strip())], limit=1 + ) + if country: + vals["partner_country_id"] = country.id + self.write(vals) + action = ( + self.env.ref("account_invoice_import.account_invoice_import_action") + .sudo() + .read()[0] + ) + action["res_id"] = self.id + return action + + def _prepare_partner_update(self): + assert self.partner_vat + assert not self.partner_id.parent_id + vals = {} + if self.partner_id.vat: + if self.partner_id.vat != self.partner_vat: + raise UserError( + _( + "The vendor to update '%s' already has a VAT number (%s) " + "which is different from the vendor VAT number " + "of the invoice (%s)." + ) + % ( + self.partner_id.display_name, + self.partner_id.vat, + self.partner_vat, + ) + ) + + else: + vals["vat"] = self.partner_vat + if self.partner_country_id: + if self.partner_id.country_id: + if self.partner_id.country_id != self.partner_country_id: + raise UserError( + _( + "The vendor to update '%s' already has a country (%s) " + "which is different from the country of the vendor " + "of the invoice (%s)." + ) + % ( + self.partner_id.display_name, + self.partner_id.country_id.display_name, + self.partner_country_id.display_name, + ) + ) + else: + vals["country_id"] = self.partner_country_id.id + return vals + + def update_partner_vat(self): + """In the update process, we only take care of VAT and country code""" + if not self.partner_id: + raise UserError(_("You must select a vendor to update.")) + self.partner_id.write(self._prepare_partner_update()) + + def update_partner_vat_show(self): + self.update_partner_vat() + action = { + "name": self.partner_id.display_name, + "type": "ir.actions.act_window", + "res_model": "res.partner", + "res_id": self.partner_id.id, + "view_mode": "form", + } + return action + + def update_partner_vat_continue(self): + self.update_partner_vat() + return self.import_invoice() + + def _prepare_new_partner_context(self, parsed_inv): + partner_dict = parsed_inv["partner"] + context = { + "default_is_company": True, + "default_supplier_rank": 1, + "default_name": partner_dict.get("name"), + "default_street": partner_dict.get("street"), + "default_street2": partner_dict.get("street2"), + "default_street3": partner_dict.get("street3"), + "default_zip": partner_dict.get("zip"), + "default_city": partner_dict.get("city"), + "default_website": partner_dict.get("website"), + "default_vat": self.partner_vat, + "default_country_id": self.partner_country_id.id or False, + } + if ( + self.partner_country_id + and partner_dict.get("state_code") + and isinstance(partner_dict["state_code"], str) + ): + country_state = self.env["res.country.state"].search( + [ + ("code", "=", partner_dict["state_code"].upper().strip()), + ("country_id", "=", self.partner_country_id.id), + ], + limit=1, + ) + if country_state: + context["default_state_id"] = country_state.id + return context + + def new_partner(self): + parsed_inv = self.get_parsed_invoice() + # we don't create a new partner, we just show a pre-filled partner form + context = self._prepare_new_partner_context(parsed_inv) + action = { + "name": self.partner_id.display_name, + "type": "ir.actions.act_window", + "res_model": "res.partner", + "target": "current", + "view_mode": "form", + "context": context, + } + # After this, when you save the partner, the active_id field in the + # URL is still the ID of the wizard. It will trigger an error if + # you click on "0 invoice import configuration" right after: + # Record does not exist or has been deleted. + # (Record: res.partner(,), User: 2) + # If you have an idea on how to fix this problem, please tell me! + return action + + def import_invoice(self): + """Method called by the button of the wizard + (import step AND config step)""" + self.ensure_one() + amo = self.env["account.move"] + aiico = self.env["account.invoice.import.config"] + company_id = self.env.context.get("force_company") or self.env.company.id + parsed_inv = self.get_parsed_invoice() + if not self.partner_id: + try: + partner = self._match_partner( + parsed_inv["partner"], parsed_inv["chatter_msg"] + ) + except UserError as e: + return self.goto_partner_not_found(parsed_inv, e) + else: + partner = self.partner_id + partner = partner.commercial_partner_id + currency = self._match_currency( + parsed_inv.get("currency"), parsed_inv["chatter_msg"] + ) + parsed_inv["partner"]["recordset"] = partner + parsed_inv["currency"]["recordset"] = currency + wiz_vals = { + "partner_id": partner.id, + "invoice_type": parsed_inv["type"], + "currency_id": currency.id, + "amount_untaxed": parsed_inv["amount_untaxed"], + "amount_total": parsed_inv["amount_total"], + } + + existing_inv = self.invoice_already_exists(partner, parsed_inv) + if existing_inv: + raise UserError( + _( + "This invoice already exists in Odoo. It's " + "Supplier Invoice Number is '%s' and it's Odoo number " + "is '%s'" + ) + % (parsed_inv.get("invoice_number"), existing_inv.name) + ) + + if self.import_config_id: # button called from 'config' step + wiz_vals["import_config_id"] = self.import_config_id.id + import_config = self.import_config_id.convert_to_import_config() + else: # button called from 'import' step + import_configs = aiico.search( + [("partner_id", "=", partner.id), ("company_id", "=", company_id)] + ) + if not import_configs: + raise UserError( + _("Missing Invoice Import Configuration on partner '%s'.") + % partner.display_name + ) + elif len(import_configs) == 1: + wiz_vals["import_config_id"] = import_configs.id + import_config = import_configs.convert_to_import_config() + else: + logger.info( + "There are %d invoice import configs for partner %s", + len(import_configs), + partner.display_name, + ) + + if not wiz_vals.get("import_config_id"): + wiz_vals["state"] = "config" + action = ( + self.env.ref("account_invoice_import.account_invoice_import_action") + .sudo() + .read()[0] + ) + action["res_id"] = self.id + else: + draft_same_supplier_invs = amo.search( + [ + ("commercial_partner_id", "=", partner.id), + ("move_type", "=", parsed_inv["type"]), + ("state", "=", "draft"), + ] + ) + logger.debug("draft_same_supplier_invs=%s", draft_same_supplier_invs) + if draft_same_supplier_invs: + wiz_vals["state"] = "update" + if len(draft_same_supplier_invs) == 1: + wiz_vals["invoice_id"] = draft_same_supplier_invs[0].id + action = ( + self.env.ref("account_invoice_import.account_invoice_import_action") + .sudo() + .read()[0] + ) + action["res_id"] = self.id + else: + action = self.create_invoice_action( + parsed_inv, import_config, _("Import Vendor Bill wizard") + ) + self.write(wiz_vals) + return action + + def create_invoice_action_button(self): + """If I call create_invoice_action() + directly from the button, I get the context in parsed_inv""" + return self.create_invoice_action(origin=_("Import Vendor Bill wizard")) + + def create_invoice_action(self, parsed_inv=None, import_config=None, origin=None): + """parsed_inv is not a required argument""" + self.ensure_one() + if parsed_inv is None: + parsed_inv = self.get_parsed_invoice() + if import_config is None: + assert self.import_config_id + import_config = self.import_config_id.convert_to_import_config() + invoice = self.create_invoice(parsed_inv, import_config, origin) + action = self.env.ref("account.action_move_in_invoice_type").sudo().read()[0] + action.update( + { + "view_mode": "form,tree,kanban", + "view_id": False, + "views": False, + "res_id": invoice.id, + } + ) + return action + + @api.model + def create_invoice(self, parsed_inv, import_config=False, origin=None): + amo = self.env["account.move"] + parsed_inv = self.pre_process_parsed_inv(parsed_inv) + vals = self._prepare_create_invoice_vals(parsed_inv, import_config) + logger.debug("Invoice vals for creation: %s", vals) + invoice = amo.create(vals) + self.post_process_invoice(parsed_inv, invoice, import_config) + logger.info("Invoice ID %d created", invoice.id) + self.post_create_or_update(parsed_inv, invoice) + invoice.message_post( + body=_( + "This invoice has been created automatically via file import. " + "Origin: %s." + ) + % (origin or _("unspecified")) + ) + return invoice + + @api.model + def create_invoice_webservice( + self, invoice_file_b64, invoice_filename, origin, company_id=None + ): + # for invoice_file_b64, we accept it as bytes AND str + # to avoid "Object of type bytes is not JSON serializable" + assert invoice_file_b64 + if isinstance(invoice_file_b64, str): + invoice_file_b64 = invoice_file_b64.encode("utf8") + assert isinstance(invoice_file_b64, bytes) + assert isinstance(invoice_filename, str) + aiico = self.env["account.invoice.import.config"] + if company_id is None: + company_id = self.env.company.id + logger.info( + "Starting to import invoice file %s in company ID %d", + invoice_filename, + company_id, + ) + parsed_inv = self.parse_invoice(invoice_file_b64, invoice_filename) + partner = self._match_partner(parsed_inv["partner"], parsed_inv["chatter_msg"]) + partner = partner.commercial_partner_id + existing_inv = self.invoice_already_exists(partner, parsed_inv) + if existing_inv: + logger.warning( + "This supplier invoice already exists " + "in Odoo (ID %d number %s supplier number %s)", + existing_inv.id, + existing_inv.name, + parsed_inv.get("invoice_number"), + ) + return False + import_configs = aiico.search( + [("partner_id", "=", partner.id), ("company_id", "=", company_id)] + ) + if not import_configs: + logger.warning( + "Missing invoice import configuration " + "for partner '%s' in company ID %d.", + partner.display_name, + company_id, + ) + return False + elif len(import_configs) == 1: + import_config = import_configs.convert_to_import_config() + else: + logger.info( + "There are %d invoice import configs for partner %s in company ID %d. " + "Using the first one '%s''", + len(import_configs), + partner.display_name, + company_id, + import_configs[0].name, + ) + import_config = import_configs[0].convert_to_import_config() + invoice = self.create_invoice(parsed_inv, import_config, origin) + return invoice.id + + @api.model + def _prepare_global_adjustment_line(self, diff_amount, invoice, import_config): + amlo = self.env["account.move.line"] + prec = invoice.currency_id.rounding + sign = diff_amount > 0 and 1 or -1 + il_vals = { + "name": _("Adjustment"), + "quantity": sign, + "price_unit": diff_amount * sign, + } + # no taxes nor product on such a global adjustment line + if import_config["invoice_line_method"] == "nline_no_product": + il_vals["account_id"] = import_config["account"].id + elif import_config["invoice_line_method"] == "nline_static_product": + account = amlo.get_invoice_line_account( + invoice.type, + import_config["product"], + invoice.fiscal_position_id, + invoice.company_id, + ) + il_vals["account_id"] = account.id + elif import_config["invoice_line_method"] == "nline_auto_product": + res_cmp = float_compare(diff_amount, 0, precision_rounding=prec) + company = invoice.company_id + if res_cmp > 0: + if not company.adjustment_debit_account_id: + raise UserError( + _( + "You must configure the 'Adjustment Debit Account' " + "on the Accounting Configuration page." + ) + ) + il_vals["account_id"] = company.adjustment_debit_account_id.id + else: + if not company.adjustment_credit_account_id: + raise UserError( + _( + "You must configure the 'Adjustment Credit Account' " + "on the Accounting Configuration page." + ) + ) + il_vals["account_id"] = company.adjustment_credit_account_id.id + logger.debug("Prepared global ajustment invoice line %s", il_vals) + return il_vals + + @api.model + def post_process_invoice(self, parsed_inv, invoice, import_config): + if parsed_inv.get("type") in ("out_invoice", "out_refund"): + return + inv_cur = invoice.currency_id + prec = inv_cur.rounding + company_cur = invoice.company_id.currency_id + account_prec = company_cur.rounding + # If untaxed amount is wrong, create adjustment lines + if ( + import_config["invoice_line_method"].startswith("nline") + and invoice.invoice_line_ids + and float_compare( + parsed_inv["amount_untaxed"], + invoice.amount_untaxed, + precision_rounding=prec, + ) + ): + # Try to find the line that has a problem + # TODO : on invoice creation, the lines are in the same + # order, but not on invoice update... + for i in range(len(parsed_inv["lines"])): + if "price_subtotal" not in parsed_inv["lines"][i]: + continue + iline = invoice.invoice_line_ids[i] + odoo_subtotal = iline.price_subtotal + parsed_subtotal = parsed_inv["lines"][i]["price_subtotal"] + diff_amount = inv_cur.round(parsed_subtotal - odoo_subtotal) + if not inv_cur.is_zero(diff_amount): + logger.info( + "Price subtotal difference found on invoice line %d " + "(source:%s, odoo:%s, diff:%s).", + i + 1, + parsed_subtotal, + odoo_subtotal, + diff_amount, + ) + copy_dict = { + "name": _("Adjustment on %s") % iline.name, + "quantity": 1, + "price_unit": diff_amount, + "price_subtotal": False, + "debit": False, + "credit": False, + "amount_currency": False, + "price_total": False, + } + if import_config["invoice_line_method"] == "nline_auto_product": + copy_dict["product_id"] = False + # Add the adjustment line + iline.with_context(check_move_validity=False).copy(copy_dict) + invoice.with_context( + check_move_validity=False + )._recompute_dynamic_lines(recompute_all_taxes=True) + invoice._check_balanced() + logger.info("Adjustment invoice line created") + # Fallback: create global ajustment line + if float_compare( + parsed_inv["amount_untaxed"], + invoice.amount_untaxed, + precision_rounding=prec, + ): + diff_amount = inv_cur.round( + parsed_inv["amount_untaxed"] - invoice.amount_untaxed + ) + logger.info( + "Amount untaxed difference found " "(source: %s, odoo:%s, diff:%s)", + parsed_inv["amount_untaxed"], + invoice.amount_untaxed, + diff_amount, + ) + il_vals = self._prepare_global_adjustment_line( + diff_amount, invoice, import_config + ) + il_vals["move_id"] = invoice.id + mline = ( + self.env["account.move.line"] + .with_context(check_move_validity=False) + .create(il_vals) + ) + invoice.with_context(check_move_validity=False)._recompute_dynamic_lines( + recompute_all_taxes=True + ) + invoice._check_balanced() + logger.info("Global adjustment invoice line created ID %d", mline.id) + assert not float_compare( + parsed_inv["amount_untaxed"], + invoice.amount_untaxed, + precision_rounding=prec, + ) + # Force tax amount if necessary + if float_compare( + invoice.amount_total, parsed_inv["amount_total"], precision_rounding=prec + ): + diff_tax_amount = parsed_inv["amount_total"] - invoice.amount_total + + has_tax_line = False + for mline in invoice.line_ids: + # select first tax line + if mline.tax_line_id and not company_cur.is_zero(mline.amount_currency): + has_tax_line = True + new_amount_currency = inv_cur.round( + mline.amount_currency + diff_tax_amount + ) + invoice.message_post( + body=_( + "The tax amount has been forced to %s " + "(amount computed by Odoo was: %s)." + ) + % ( + format_amount( + self.env, new_amount_currency, invoice.currency_id + ), + format_amount( + self.env, mline.amount_currency, invoice.currency_id + ), + ) + ) + new_balance = invoice.currency_id._convert( + new_amount_currency, + invoice.company_id.currency_id, + invoice.company_id, + invoice.date, + ) + vals = {"amount_currency": new_amount_currency} + if ( + float_compare(new_balance, 0, precision_rounding=account_prec) + > 0 + ): + vals["debit"] = new_balance + vals["credit"] = 0 + else: + vals["debit"] = 0 + vals["credit"] = new_balance * -1 + logger.info("Force VAT amount with diff=%s", diff_tax_amount) + mline.with_context(check_move_validity=False).write(vals) + invoice.with_context( + check_move_validity=False + )._recompute_dynamic_lines() + invoice._check_balanced() + break + if not has_tax_line: + raise UserError( + _( + "The total amount is different from the untaxed amount, " + "but no tax has been configured !" + ) + ) + assert not float_compare( + parsed_inv["amount_total"], + invoice.amount_total, + precision_rounding=prec, + ) + + def update_invoice_lines(self, parsed_inv, invoice, seller): + chatter = parsed_inv["chatter_msg"] + amlo = self.env["account.move.line"] + qty_prec = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + existing_lines = [] + for eline in invoice.invoice_line_ids: + price_unit = 0.0 + if not float_is_zero(eline.quantity, precision_digits=qty_prec): + price_unit = eline.price_subtotal / float(eline.quantity) + existing_lines.append( + { + "product": eline.product_id or False, + "name": eline.name, + "qty": eline.quantity, + "uom": eline.product_uom_id, + "line": eline, + "price_unit": price_unit, + } + ) + compare_res = self.compare_lines( + existing_lines, parsed_inv["lines"], chatter, seller=seller + ) + if not compare_res: + return + for eline, cdict in list(compare_res["to_update"].items()): + write_vals = {} + if cdict.get("qty"): + chatter.append( + _( + "The quantity has been updated on the invoice line " + "with product '%s' from %s to %s %s" + ) + % ( + eline.product_id.display_name, + cdict["qty"][0], + cdict["qty"][1], + eline.product_uom_id.name, + ) + ) + write_vals["quantity"] = cdict["qty"][1] + if cdict.get("price_unit"): + chatter.append( + _( + "The unit price has been updated on the invoice " + "line with product '%s' from %s to %s %s" + ) + % ( + eline.product_id.display_name, + eline.price_unit, + cdict["price_unit"][1], # TODO fix + invoice.currency_id.name, + ) + ) + write_vals["price_unit"] = cdict["price_unit"][1] + if write_vals: + eline.write(write_vals) + if compare_res["to_remove"]: + to_remove_label = [ + "{} {} x {}".format( + line.quantity, line.product_uom_id.name, line.product_id.name + ) + for line in compare_res["to_remove"] + ] + chatter.append( + _("%d invoice line(s) deleted: %s") + % (len(compare_res["to_remove"]), ", ".join(to_remove_label)) + ) + compare_res["to_remove"].unlink() + if compare_res["to_add"]: + to_create_label = [] + for add in compare_res["to_add"]: + line_vals = self._prepare_create_invoice_line( + add["product"], add["uom"], add["import_line"], invoice + ) + new_line = amlo.create(line_vals) + to_create_label.append( + "%s %s x %s" + % (new_line.quantity, new_line.product_uom_id.name, new_line.name) + ) + chatter.append( + _("%d new invoice line(s) created: %s") + % (len(compare_res["to_add"]), ", ".join(to_create_label)) + ) + invoice.compute_taxes() + return True + + @api.model + def _prepare_create_invoice_line(self, product, uom, import_line, invoice): + new_line = self.env["account.move.line"].new( + {"move_id": invoice, "qty": import_line["qty"], "product_id": product} + ) + new_line._onchange_product_id() + vals = { + f: new_line._fields[f].convert_to_write(new_line[f], new_line) + for f in new_line._cache + } + vals.update( + { + "product_id": product.id, + "price_unit": import_line.get("price_unit"), + "quantity": import_line["qty"], + "move_id": invoice.id, + } + ) + return vals + + @api.model + def _prepare_update_invoice_vals(self, parsed_inv, invoice): + vals = { + "ref": parsed_inv.get("invoice_number"), + "invoice_date": parsed_inv.get("date"), + } + if parsed_inv.get("date_due"): + vals["invoice_date_due"] = parsed_inv["date_due"] + if parsed_inv.get("iban"): + company = invoice.company_id + partner_bank = self._match_partner_bank( + invoice.commercial_partner_id, + parsed_inv["iban"], + parsed_inv.get("bic"), + parsed_inv["chatter_msg"], + create_if_not_found=company.invoice_import_create_bank_account, + ) + if partner_bank: + vals["partner_bank_id"] = partner_bank.id + return vals + + def update_invoice(self): + """Called by the button of the wizard (step 'update-from-invoice')""" + self.ensure_one() + invoice = self.invoice_id + if not invoice: + raise UserError(_("You must select a supplier invoice or refund to update")) + parsed_inv = self.get_parsed_invoice() + if self.partner_id: + # True if state='update' ; False when state='update-from-invoice' + parsed_inv["partner"]["recordset"] = self.partner_id + partner = self._match_partner( + parsed_inv["partner"], parsed_inv["chatter_msg"], partner_type="supplier" + ) + partner = partner.commercial_partner_id + if partner != invoice.commercial_partner_id: + raise UserError( + _( + "The supplier of the imported invoice (%s) is different from " + "the supplier of the invoice to update (%s)." + ) + % (partner.name, invoice.commercial_partner_id.name) + ) + if not self.import_config_id: + raise UserError(_("You must select an Invoice Import Configuration.")) + import_config = self.import_config_id.convert_to_import_config() + currency = self._match_currency( + parsed_inv.get("currency"), parsed_inv["chatter_msg"] + ) + if currency != invoice.currency_id: + raise UserError( + _( + "The currency of the imported invoice (%s) is different from " + "the currency of the existing invoice (%s)" + ) + % (currency.name, invoice.currency_id.name) + ) + vals = self._prepare_update_invoice_vals(parsed_inv, invoice) + logger.debug("Updating supplier invoice with vals=%s", vals) + self.invoice_id.write(vals) + if ( + parsed_inv.get("lines") + and import_config["invoice_line_method"] == "nline_auto_product" + ): + self.update_invoice_lines(parsed_inv, invoice, partner) + self.post_process_invoice(parsed_inv, invoice, import_config) + if import_config["account_analytic"]: + invoice.invoice_line_ids.write( + {"account_analytic_id": import_config["account_analytic"].id} + ) + self.post_create_or_update(parsed_inv, invoice) + logger.info( + "Supplier invoice ID %d updated via import of file %s", + invoice.id, + self.invoice_filename, + ) + invoice.message_post( + body=_( + "This invoice has been updated automatically via the import " + "of file %s" + ) + % self.invoice_filename + ) + action = self.env.ref("account.action_move_in_invoice_type").sudo().read()[0] + action.update( + { + "view_mode": "form,tree,kanban", + "views": False, + "view_id": False, + "res_id": invoice.id, + } + ) + return action + + def xpath_to_dict_helper(self, xml_root, xpath_dict, namespaces): + for key, value in xpath_dict.items(): + if isinstance(value, list): + isdate = isfloat = False + if "date" in key: + isdate = True + elif "amount" in key: + isfloat = True + xpath_dict[key] = self.multi_xpath_helper( + xml_root, value, namespaces, isdate=isdate, isfloat=isfloat + ) + if not xpath_dict[key]: + logger.debug("pb") + elif isinstance(value, dict): + xpath_dict[key] = self.xpath_to_dict_helper(xml_root, value, namespaces) + return xpath_dict + # TODO: think about blocking required fields + + def multi_xpath_helper( + self, xml_root, xpath_list, namespaces, isdate=False, isfloat=False + ): + assert isinstance(xpath_list, list) + for xpath in xpath_list: + xpath_res = xml_root.xpath(xpath, namespaces=namespaces) + if xpath_res and xpath_res[0].text: + if isdate: + if ( + xpath_res[0].attrib + and xpath_res[0].attrib.get("format") != "102" + ): + raise UserError(_("Only the date format 102 is supported ")) + date_dt = datetime.strptime(xpath_res[0].text, "%Y%m%d") + date_str = fields.Date.to_string(date_dt) + return date_str + elif isfloat: + res_float = float(xpath_res[0].text) + return res_float + else: + return xpath_res[0].text + return False + + def raw_multi_xpath_helper(self, xml_root, xpath_list, namespaces): + for xpath in xpath_list: + xpath_res = xml_root.xpath(xpath, namespaces=namespaces) + if xpath_res: + return xpath_res + return [] + + @api.model + def get_precision_rounding_from_currency_helper(self, parsed_inv): + try: + currency = self._match_currency(parsed_inv["currency"], []) + precision_rounding = currency.rounding + except Exception: + precision_rounding = self.env.company.currency_id.rounding + parsed_inv["currency_rounding"] = precision_rounding + return precision_rounding + + @api.model + def message_new(self, msg_dict, custom_values=None): + """Process the message data from a fetchmail configuration + + The caller expects us to create a record so we always return an empty + one even though the actual result is the imported invoice, if the + message content allows it. + """ + logger.info( + "New email received associated with account.invoice.import: " + "From: %s, Subject: %s, Date: %s, Message ID: %s. Executing " + "with user %s ID %d", + msg_dict.get("email_from"), + msg_dict.get("subject"), + msg_dict.get("date"), + msg_dict.get("message_id"), + self.env.user.name, + self.env.user.id, + ) + # It seems that the "Odoo-way" to handle multi-company in E-mail + # gateways is by using mail.aliases associated with users that + # don't switch company (I haven't found any other way), which + # is not convenient because you may have to create new users + # for that purpose only. So I implemented my own mechanism, + # based on the destination email address. + # This method is called (indirectly) by the fetchmail cron which + # is run by default as admin and retreive all incoming email in + # all email accounts. We want to keep this default behavior, + # and, in multi-company environnement, differentiate the company + # per destination email address + company_id = False + all_companies = self.env["res.company"].search_read( + [], ["invoice_import_email"] + ) + if len(all_companies) > 1: # multi-company setup + for company in all_companies: + if company["invoice_import_email"]: + company_dest_email = company["invoice_import_email"].strip() + if company_dest_email in msg_dict.get( + "to", "" + ) or company_dest_email in msg_dict.get("cc", ""): + company_id = company["id"] + logger.info( + "Matched with %s: importing invoices in company " "ID %d", + company_dest_email, + company_id, + ) + break + if not company_id: + logger.error( + "Invoice import mail gateway in multi-company setup: " + "invoice_import_email of the companies of this DB was " + "not found as destination of this email (to: %s, cc: %s). " + "Ignoring this email.", + msg_dict["email_to"], + msg_dict["cc"], + ) + return self.create({}) + else: # mono-company setup + company_id = all_companies[0]["id"] + + self = self.with_company(company_id) + i = 0 + if msg_dict.get("attachments"): + i += 1 + for attach in msg_dict["attachments"]: + logger.info( + "Attachment %d: %s. Trying to import it as an invoice", + i, + attach.fname, + ) + origin = _("email sent by %s on %s with subject %s") % ( + msg_dict.get("email_from"), + msg_dict.get("date"), + msg_dict.get("subject"), + ) + try: + invoice_id = self.create_invoice_webservice( + base64.b64encode(attach.content), + attach.fname, + origin, + company_id=company_id, + ) + logger.info( + "Invoice ID %d created from email attachment %s.", + invoice_id, + attach.fname, + ) + except Exception as e: + logger.error( + "Failed to import invoice from mail attachment %s. Error: %s", + attach.fname, + e, + ) + else: + logger.info("The email has no attachments, skipped.") + return self.create({}) diff --git a/account_invoice_import/wizard/account_invoice_import_view.xml b/account_invoice_import/wizard/account_invoice_import_view.xml new file mode 100644 index 0000000000..725a0124da --- /dev/null +++ b/account_invoice_import/wizard/account_invoice_import_view.xml @@ -0,0 +1,171 @@ + + + + + account.invoice.import + +
+
+

Upload below the supplier invoice/refund as PDF or XML file: Odoo will create a draft supplier invoice/refund. Supported formats:

+
    +

    If the format you need is not listed above, you should install an additionnal Odoo module that adds support for that format (e.g. account_invoice_import_facturx, account_invoice_import_ubl, account_invoice_import_invoice2data, etc).

    +

    If there are several invoice import configurations for the supplier of the invoice, Odoo will ask you to choose one of them. If there is an existing draft invoice for that supplier, Odoo will propose you to update that draft invoice or create a new draft invoice.

    +
+
+

The supplier has several invoice import configurations: please select the one you want to use for this import.

+
+
+

Some draft supplier invoices/refunds have been found for the supplier of the invoice you are importing; one of them may correspond to the invoice you are importing. You can either select an existing draft supplier invoice to update or create a new draft invoice.

+
+ + + + + + + + + + + + + + + + + + +