diff --git a/l10n_pt_account_invoicexpress/README.rst b/l10n_pt_account_invoicexpress/README.rst new file mode 100644 index 00000000..192c7186 --- /dev/null +++ b/l10n_pt_account_invoicexpress/README.rst @@ -0,0 +1,173 @@ +================================== +Portugal InvoiceXpress Integration +================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:febd884e95a05fac4097689cbe058fd34de64fe248489680016b15ad9852d89f + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |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%2Fl10n--portugal-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-portugal/tree/18.0/l10n_pt_account_invoicexpress + :alt: OCA/l10n-portugal +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-portugal-18-0/l10n-portugal-18-0-l10n_pt_account_invoicexpress + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-portugal&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Generate Portuguese tax authority legal Invoices ("Faturas") using +InvoiceXpress. + +InvoiceXpress is a paid service. Visit https://invoicexpress.com for +more details. + +Once the InvoiceXpress connection is configured, the invoice CONFIRM +button automatically generates the InvoiceXpress invoice. + +If the InvoiceXpress Invoice email template is configured, the +InvoiceXpress service will also send the invoice by email, using the +details in Odoo configured email template. + +This replaces the Odoo SEND & PRINT button, since only the InvoiceXpress +generated document should be used. Having other print layouts for the +invoice is not allowed by the Portuguese Tax Authority. + +Legal transport documents ("Guias de Transporte" e "Guias de Remessa) +are also supported through an extension module. For this please ensure +that "l10n_pt_stock_invoicexpress" is installed. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure: + +- Navigate to Settings/General Settings. + +- In the "Invoicing" configuration section, locate the "InvoiceXpress" + subsection: There you can set: + + - InvoiceXpress Account Name + - InvoiceXpress API Key + - InvoiceXpress Invoice Email Template + +- In "Invoicing/Accounting" configuration section, locate the Sales + Journals, and set the "InvoiceXpress Doc Type". This is required + before invoices before any invoice can be created. + +The email template is used to prepare the details for the email the +InvoiceXpress service sends with the invoice: + +- "To" email address +- "Cc" email address +- Subject +- Body + +Translations are supported, and the customer language is used to render +the email details. + +The InvoiceXpress configuration is per company, and can also be modified +directly on the Company form, if the Developer Mode is enabled. + +On the Journal form, for sales journals, a flag is available to allow +disabling the InvoiceXpress integration. This can be useful for a +journal that was used temporarily to import invoice history, for +reporting purposes, ond was then disabled, or to allow that journal to +use a different legal invoicing system. + +Note that all invoices generated MUST use a certified invoicing system. +For a portuguese company, you cannot have some of the invoices being +generated by Odoo itself. So use this option at your own risk, and only +in the cases you are sure to be compliant with TPortugues Tax Authority +invoicing regulations. + +Usage +===== + +On an Invoice, the CONFIRM button automatically generates an invoice on +the InvoiceXpress service. + +The "Email InvoiceXpress" button requests the InvoiceXpress service to +send an email with a copy of the legal document. + +The Invoice form shows an "InvoiceXpress" tab containing details for the +corresponding InvoiceXpress document. + +Invoices: + +- Added support to the different documents types: Invoice, Invoice + Receipt, Simplified Invoice. The default document type is set on the + Journal, and can be changed on the Invoice form. +- Use the invoice commercial partner for the name and address, instead + of the invoice contact. +- Added support for the Terms and Conditions/Observations field +- Added to Credit Notes the link to the source Invoice + +The monthly SAF-T file should be downloaded from the InvoiceXpress +website. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Open Source Integrators + +Contributors +------------ + +- Daniel Reis , `Open Source + Integrators `__ + +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. + +.. |maintainer-dreispt| image:: https://github.com/dreispt.png?size=40px + :target: https://github.com/dreispt + :alt: dreispt + +Current `maintainer `__: + +|maintainer-dreispt| + +This module is part of the `OCA/l10n-portugal `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_pt_account_invoicexpress/__init__.py b/l10n_pt_account_invoicexpress/__init__.py new file mode 100644 index 00000000..6a0c7727 --- /dev/null +++ b/l10n_pt_account_invoicexpress/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_pt_account_invoicexpress/__manifest__.py b/l10n_pt_account_invoicexpress/__manifest__.py new file mode 100644 index 00000000..6d43d738 --- /dev/null +++ b/l10n_pt_account_invoicexpress/__manifest__.py @@ -0,0 +1,28 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Portugal InvoiceXpress Integration", + "summary": "Portuguese certified invoices using InvoiceXpress", + "version": "18.0.1.0.0", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/l10n-portugal", + "category": "Accounting/Localizations/EDI", + "maintainers": ["dreispt"], + "development_status": "Production/Stable", + "depends": ["l10n_pt_vat", "account"], + "data": [ + "views/res_config_settings.xml", + "views/account_journal_view.xml", + "views/account_tax_view.xml", + "views/account_move_view.xml", + "views/res_company_view.xml", + "views/res_country_view.xml", + "data/mail_template.xml", + "data/res.country.csv", + ], + "images": ["static/description/cover.png"], + "application": True, + "installable": True, +} diff --git a/l10n_pt_account_invoicexpress/data/country_mapping.txt b/l10n_pt_account_invoicexpress/data/country_mapping.txt new file mode 100644 index 00000000..43456f0a --- /dev/null +++ b/l10n_pt_account_invoicexpress/data/country_mapping.txt @@ -0,0 +1,43 @@ +Odoo country list was mapped to the InvoiceXpress names as documented at +https://invoicexpress.com/api-v2/documentation/appendix + +These InvoiceXpress country names found no macth in Odoo: + +Burma +Canton and Enderbury Islands +Dronning Maud Land +East Timor +International Monetary Fund +Ivory Coast +Johnston Island +Kampuchea +Midway Islands +Netherlands Antilles +Samoa (America) +Tahiti +Upper Volta +Vatican +Wake Island +Western Samoa +Zaïre + +These Odoo countries found no match in the InvoiceXpress list: + +id,name +base.aq,Antarctica +base.bq,Bonaire, Sint Eustatius and Saba +base.bv,Bouvet Island +base.tf,French Southern Territories +base.va,Holy See (Vatican City State) +base.xk,Kosovo +base.bl,Saint Barthélémy +base.mf,Saint Martin (French part) +base.pm,Saint Pierre and Miquelon +base.sx,Sint Maarten (Dutch part) +base.gs,South Georgia and the South Sandwich Islands +base.ss,South Sudan +base.tk,Tokelau +base.to,Tonga +base.um,USA Minor Outlying Islands +base.vi,Virgin Islands (USA) +base.ax,Åland Islands diff --git a/l10n_pt_account_invoicexpress/data/mail_template.xml b/l10n_pt_account_invoicexpress/data/mail_template.xml new file mode 100644 index 00000000..a1c1cc0f --- /dev/null +++ b/l10n_pt_account_invoicexpress/data/mail_template.xml @@ -0,0 +1,28 @@ + + + InvoiceXpress: Send Invoice by Email + + Sua Fatura {{ object.name }} + {{ object.partner_id.email }} + {{ object.env.user.email }} + +

+Olá, +
+Enviamos em anexo a fatura + + relativa à sua encomenda + +. +

+Obrigado + +
+ + +

+
+ {{ object.partner_id.lang }} + +
+
diff --git a/l10n_pt_account_invoicexpress/data/res.country.csv b/l10n_pt_account_invoicexpress/data/res.country.csv new file mode 100644 index 00000000..ce9d31ca --- /dev/null +++ b/l10n_pt_account_invoicexpress/data/res.country.csv @@ -0,0 +1,234 @@ +id,invoicexpress_name +base.af,Afghanistan +base.al,Albania +base.dz,Algeria +base.as,American Samoa +base.ad,Andorra +base.ao,Angola +base.ai,Anguilla +base.ag,Antigua and Barbuda +base.ar,Argentina +base.am,Armenia +base.aw,Aruba +base.au,Australia +base.at,Austria +base.az,Azerbaijan +base.bs,Bahamas +base.bh,Bahrain +base.bd,Bangladesh +base.bb,Barbados +base.by,Belarus +base.be,Belgium +base.bz,Belize +base.bj,Benin +base.bm,Bermuda +base.bt,Bhutan +base.bo,Bolivia +base.ba,Bosnia-Herzegovina +base.bw,Botswana +base.br,Brazil +base.io,British Indian Ocean Territory +base.bn,Brunei +base.bg,Bulgaria +base.bf,Burkina Faso +base.bi,Burundi +base.kh,Cambodia +base.cm,Cameroon +base.ca,Canada +base.cv,Cape Verde +base.ky,Cayman Islands +base.cf,Central African Republic +base.td,Chad +base.cl,Chile +base.cn,China +base.cx,Christmas Island +base.cc,Cocos (Keeling) Islands +base.co,Colombia +base.km,Comoros +base.cg,Congo +base.ck,Cook Islands +base.cr,Costa Rica +base.hr,Croatia +base.cu,Cuba +base.cw,Curaçao +base.cy,Cyprus +base.cz,Czech Republic +base.ci,Côte d’Ivoire +base.cd,"Congo, Democratic Republic" +base.dk,Denmark +base.dj,Djibouti +base.dm,Dominica +base.do,Dominican Republic +base.ec,Ecuador +base.eg,Egypt +base.sv,El Salvador +base.gq,Equatorial Guinea +base.er,Eritrea +base.ee,Estonia +base.et,Ethiopia +base.fk,Faeroe Islands (Føroyar) +base.fo,Falkland Islands +base.fj,Fiji +base.fi,Finland +base.fr,France +base.gf,French Guiana +base.pf,French Polynesia +base.ga,Gabon +base.gm,Gambia +base.ge,Georgia +base.de,Germany +base.gh,Ghana +base.gi,Gibraltar +base.gr,Greece +base.gl,Greenland +base.gd,Grenada +base.gp,Guadeloupe +base.gu,Guam +base.gt,Guatemala +base.gg,Guernsey +base.gn,Guinea +base.gw,Guinea-Bissau +base.gy,Guyana +base.ht,Haiti +base.hm,Heard and McDonald Islands +base.hn,Honduras +base.hk,Hong Kong +base.hu,Hungary +base.is,Iceland +base.in,India +base.id,Indonesia +base.ir,Iran +base.iq,Iraq +base.ie,Ireland +base.im,Isle of Man +base.il,Israel +base.it,Italy +base.jm,Jamaica +base.jp,Japan +base.je,Jersey +base.jo,Jordan +base.kz,Kazakhstan +base.ke,Kenya +base.ki,Kiribati +base.kw,Kuwait +base.kg,Kyrgyzstan +base.la,Laos +base.lv,Latvia +base.lb,Lebanon +base.ls,Lesotho +base.lr,Liberia +base.ly,Libya +base.li,Liechtenstein +base.lt,Lithuania +base.lu,Luxembourg +base.mo,Macau +base.mg,Madagascar +base.mw,Malawi +base.my,Malaysia +base.mv,Maldives +base.ml,Mali +base.mt,Malta +base.mh,Marshall Islands +base.mq,Martinique +base.mr,Mauritania +base.mu,Mauritius +base.yt,Mayotte +base.mx,Mexico +base.fm,Micronesia +base.md,Moldova +base.mc,Monaco +base.mn,Mongolia +base.me,Montenegro +base.ms,Montserrat +base.ma,Morocco +base.mz,Mozambique +base.mm,Myanmar +base.na,Nauru +base.nr,Namibia +base.np,Nepal +base.nl,Netherlands +base.nc,New Caledonia +base.nz,New Zealand +base.ni,Nicaragua +base.ne,Niger +base.ng,Nigeria +base.nu,Niue +base.nf,Norfolk Island +base.kp,"Korea, North" +base.mk,Macedonia (Former Yug. Rep.) +base.mp,Northern Mariana Islands +base.no,Norway +base.om,Oman +base.pk,Pakistan +base.pw,Palau +base.pa,Panama +base.pg,Papua New Guinea +base.py,Paraguay +base.pe,Peru +base.ph,Philippines +base.pn,Pitcairn Island +base.pl,Poland +base.pt,Portugal +base.pr,Puerto Rico +base.qa,Qatar +base.ro,Romania +base.ru,Russia +base.rw,Rwanda +base.re,Reunion +base.sh,St. Helena +base.kn,St. Kitts and Nevis +base.lc,St. Lucia +base.vc,St. Vincent and the Grenadines +base.ws,Samoa (Western) +base.sm,San Marino +base.sa,Saudi Arabia +base.sn,Sénégal +base.rs,Serbia +base.sc,Seychelles +base.sl,Sierra Leone +base.sg,Singapore +base.sk,Slovakia +base.si,Slovenia +base.sb,Solomon Islands +base.so,Somalia +base.za,South Africa +base.kr,"Korea, South" +base.es,Spain +base.lk,Sri Lanka +base.ps,Palestine +base.sd,Sudan +base.sr,Suriname +base.sj,Svalbard and Jan Mayen Islands +base.sz,Swaziland +base.se,Sweden +base.ch,Switzerland +base.sy,Syria +base.st,São Tomé and Príncipe +base.tw,Taiwan +base.tj,Tajikistan +base.tz,Tanzania +base.th,Thailand +base.tl,Timor-Leste +base.tg,Togo +base.tt,Trinidad and Tobago +base.tn,Tunisia +base.tr,Turkey +base.tm,Turkmenistan +base.tc,Turks and Caicos Islands +base.tv,Tuvalu +base.ug,Uganda +base.ua,Ukraine +base.ae,United Arab Emirates +base.uk,Great Britain +base.us,United States +base.uy,Uruguay +base.uz,Uzbekistan +base.vu,Vanuatu +base.ve,Venezuela +base.vn,Vietnam +base.vg,Virgin Islands +base.wf,Wallis and Futuna Islands +base.eh,Western Sahara +base.ye,Yemen +base.zm,Zambia +base.zw,Zimbabwe diff --git a/l10n_pt_account_invoicexpress/i18n/l10n_pt_account_invoicexpress.pot b/l10n_pt_account_invoicexpress/i18n/l10n_pt_account_invoicexpress.pot new file mode 100644 index 00000000..26a3e441 --- /dev/null +++ b/l10n_pt_account_invoicexpress/i18n/l10n_pt_account_invoicexpress.pot @@ -0,0 +1,415 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_pt_account_invoicexpress +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid " Generate an API key" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,body_html:l10n_pt_account_invoicexpress.email_template_invoice +msgid "" +"

\n" +"Olá,\n" +"
\n" +"Enviamos em anexo a fatura \n" +"\n" +" relativa à sua encomenda \n" +"\n" +".\n" +"

\n" +"Obrigado\n" +"\n" +"
\n" +" \n" +"
\n" +"

\n" +" " +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__can_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__can_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__can_invoicexpress +msgid "Can Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__can_invoicexpress_email +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__can_invoicexpress_email +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__can_invoicexpress_email +msgid "Can Invoicexpress Email" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_partner +msgid "Contact" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_country +msgid "Country" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__credit_note +msgid "Credit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__debit_note +msgid "Debit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_company__has_invoicexpress +msgid "Easy to use indicator if InvoiceXpress is enabled and can be used" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_move_form +msgid "Email InvoiceXpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Email sent by InvoiceXpress:
  • To: %(email)s
  • Cc: " +"%(cc)s
" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_invoicexpress.py:0 +#, python-format +msgid "" +"Error running API request (%(status_code)s %(reason)s):\n" +"%(json)s" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__vat_moss_credit_note +msgid "Europe VAT MOSS Credit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__vat_moss_invoice +msgid "Europe VAT MOSS Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__has_invoicexpress +msgid "Has Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__invoice +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__invoice +msgid "Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Invoice %s is not registered in InvoiceXpress yet." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__invoice_receipt +msgid "Invoice and Receipt" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Invoice is missing the InvoiceXpress document type!" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_move_form +msgid "InvoiceXpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_api_key +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_api_key +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress API Key" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_account_name +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_account_name +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Account Name" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_compnay_form_invoicexpress +msgid "InvoiceXpress Configuration" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_permalink +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_permalink +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_permalink +msgid "InvoiceXpress Doc Link" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_template_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_template_id +msgid "InvoiceXpress Email Template" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_tax__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_partner__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_users__invoicexpress_id +msgid "InvoiceXpress ID" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Invoice Email Template" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_invoicexpress +msgid "InvoiceXpress connector" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"InvoiceXpress record has been created for this invoice:
  • InvoiceXpress" +" Id: {inv_xpress_id}
  • {inv_xpress_link}
" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "InvoiceXpress record has been modified to Paid." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,name:l10n_pt_account_invoicexpress.email_template_invoice +msgid "InvoiceXpress: Send Invoice by Email" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__invoice_receipt +msgid "Invoices Receipt" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_journal_form +msgid "Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_journal__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_doc_type +msgid "Invoicexpress Doc Type" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_country__invoicexpress_name +msgid "Invoicexpress Name" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_journal__use_invoicexpress +msgid "" +"Invoicexpress service is only used if checked. Only relevant for Sales " +"journals." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_journal +msgid "Journal" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Journal %s is missing the InvoiceXpress document type configuration!" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__journal_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__journal_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__journal_type +msgid "Journal Type" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Kindly add the invoice date and invoice due date." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__none +msgid "No InvoiceXpress document" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "No address to send invoice email to." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "None" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Please configure the InvoiceXpress email template at Settings > General " +"Setting, InvoiceXpress section" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_bank_statement_line__journal_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_move__journal_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_payment__journal_type +msgid "" +"Select 'Sale' for customer invoices journals.\n" +"Select 'Purchase' for vendor bills journals.\n" +"Select 'Cash' or 'Bank' for journals that are used in customer or vendor payments.\n" +"Select 'General' for miscellaneous operations journals." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_journal__invoicexpress_doc_type +msgid "" +"Select the type of legal invoice document to be created by InvoiceXpress." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_doc_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_doc_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_doc_type +msgid "" +"Select the type of legal invoice document to be created by InvoiceXpress. If" +" unset, InvoiceXpress will not be used." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__simplified_invoice +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__simplified_invoice +msgid "Simplified Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_invoicexpress.py:0 +#, python-format +msgid "" +"Something went wrong on API key. You should check the field\n" +" %(field:res.config.settings.invoicexpress_account_name)s in\n" +" %(menu:base_setup.menu_config)s." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Something went wrong: the InvoiceXpress response is missing a sequence " +"number." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Something went wrong: the InvoiceXpress response looks empty." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,subject:l10n_pt_account_invoicexpress.email_template_invoice +msgid "Sua Fatura {{ object.name }}" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_tax +msgid "Tax" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_journal__use_invoicexpress +msgid "Use Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_template_id +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_template_id +msgid "" +"Used to generate the To, Cc, Subject and Body for the email sent by the " +"InvoiceXpress service" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "View Document" +msgstr "" diff --git a/l10n_pt_account_invoicexpress/i18n/pt.po b/l10n_pt_account_invoicexpress/i18n/pt.po new file mode 100644 index 00000000..eaa85e30 --- /dev/null +++ b/l10n_pt_account_invoicexpress/i18n/pt.po @@ -0,0 +1,438 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_pt_account_invoicexpress +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-11-18 20:36+0000\n" +"Last-Translator: Daniel Reis \n" +"Language-Team: none\n" +"Language: pt\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 4.3.2\n" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid " Generate an API key" +msgstr " Gerar uma chave de API" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,body_html:l10n_pt_account_invoicexpress.email_template_invoice +msgid "" +"

\n" +"Olá,\n" +"
\n" +"Enviamos em anexo a fatura \n" +"\n" +" relativa à sua encomenda \n" +"\n" +".\n" +"

\n" +"Obrigado\n" +"\n" +"
\n" +" \n" +"
\n" +"

\n" +" " +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__can_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__can_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__can_invoicexpress +msgid "Can Invoicexpress" +msgstr "Pode usar InvoiceXpress" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__can_invoicexpress_email +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__can_invoicexpress_email +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__can_invoicexpress_email +msgid "Can Invoicexpress Email" +msgstr "Pode enviar email InvoiceXpress" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_company +msgid "Companies" +msgstr "Empresas" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_config_settings +msgid "Config Settings" +msgstr "Configurações" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_res_country +msgid "Country" +msgstr "País" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__credit_note +msgid "Credit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__debit_note +msgid "Debit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_company__has_invoicexpress +msgid "Easy to use indicator if InvoiceXpress is enabled and can be used" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_move_form +msgid "Email InvoiceXpress" +msgstr "Email InvoiceXpress" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Email sent by InvoiceXpress:
  • To: %(email)s
  • Cc: %(cc)s
  • " +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_invoicexpress.py:0 +#, python-format +msgid "" +"Error running API request (%(status_code)s %(reason)s):\n" +"%(json)s" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__vat_moss_credit_note +msgid "Europe VAT MOSS Credit Note" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__vat_moss_invoice +msgid "Europe VAT MOSS Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__has_invoicexpress +msgid "Has Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__invoice +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__invoice +msgid "Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Invoice %s is not registered in InvoiceXpress yet." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__invoice_receipt +msgid "Invoice and Receipt" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Invoice is missing the InvoiceXpress document type!" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_move_form +msgid "InvoiceXpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_api_key +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_api_key +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress API Key" +msgstr "InvoiceXpress Chave API" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_account_name +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_account_name +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Account Name" +msgstr "InvoiceXpress Nome da Conta" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_compnay_form_invoicexpress +msgid "InvoiceXpress Configuration" +msgstr "Configuração InvoiceXpress" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_permalink +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_permalink +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_permalink +msgid "InvoiceXpress Doc Link" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_template_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_template_id +msgid "InvoiceXpress Email Template" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_tax__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_partner__invoicexpress_id +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_users__invoicexpress_id +msgid "InvoiceXpress ID" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Invoice Email Template" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_invoicexpress +msgid "InvoiceXpress connector" +msgstr "Conector InvoiceXpress" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"InvoiceXpress record has been created for this invoice:
    • InvoiceXpress " +"Id: {inv_xpress_id}
    • {inv_xpress_link}
    " +msgstr "" +"O registo InvoiceXpress foi criado para esta fatura:
    • InvoiceXpress " +"Id: {inv_xpress_id}
    • {inv_xpress_link}
    " + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "InvoiceXpress record has been modified to Paid." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,name:l10n_pt_account_invoicexpress.email_template_invoice +msgid "InvoiceXpress: Send Invoice by Email" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__invoice_receipt +msgid "Invoices Receipt" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_account_invoicexpress.view_account_journal_form +msgid "Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_journal__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_doc_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_doc_type +msgid "Invoicexpress Doc Type" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_res_country__invoicexpress_name +msgid "Invoicexpress Name" +msgstr "Nome Invoicexpress" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_journal__use_invoicexpress +msgid "" +"Invoicexpress service is only used if checked. Only relevant for Sales " +"journals." +msgstr "" +"Serviço InvoiceXpress só será usado se estiver assinalado. Relevante apenas " +"para Diários de Vendas." + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_journal +msgid "Journal" +msgstr "Diário" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Journal %s is missing the InvoiceXpress document type configuration!" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_bank_statement_line__journal_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_move__journal_type +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_payment__journal_type +msgid "Journal Type" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Kindly add the invoice date and invoice due date." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__none +msgid "No InvoiceXpress document" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "No address to send invoice email to." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "None" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Please configure the InvoiceXpress email template at Settings > General " +"Setting, InvoiceXpress section" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_bank_statement_line__journal_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_move__journal_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_payment__journal_type +msgid "" +"Select 'Sale' for customer invoices journals.\n" +"Select 'Purchase' for vendor bills journals.\n" +"Select 'Cash' or 'Bank' for journals that are used in customer or vendor " +"payments.\n" +"Select 'General' for miscellaneous operations journals." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_journal__invoicexpress_doc_type +msgid "" +"Select the type of legal invoice document to be created by InvoiceXpress." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_bank_statement_line__invoicexpress_doc_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_move__invoicexpress_doc_type +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_account_payment__invoicexpress_doc_type +msgid "" +"Select the type of legal invoice document to be created by InvoiceXpress. If " +"unset, InvoiceXpress will not be used." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_journal__invoicexpress_doc_type__simplified_invoice +#: model:ir.model.fields.selection,name:l10n_pt_account_invoicexpress.selection__account_move__invoicexpress_doc_type__simplified_invoice +msgid "Simplified Invoice" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_invoicexpress.py:0 +#, python-format +msgid "" +"Something went wrong on API key. You should check the field\n" +" %(field:res.config.settings.invoicexpress_account_name)s in\n" +" %(menu:base_setup.menu_config)s." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "" +"Something went wrong: the InvoiceXpress response is missing a sequence " +"number." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "Something went wrong: the InvoiceXpress response looks empty." +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:mail.template,subject:l10n_pt_account_invoicexpress.email_template_invoice +msgid "Sua Fatura {{ object.name }}" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model,name:l10n_pt_account_invoicexpress.model_account_tax +msgid "Tax" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_account_invoicexpress.field_account_journal__use_invoicexpress +msgid "Use Invoicexpress" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_company__invoicexpress_template_id +#: model:ir.model.fields,help:l10n_pt_account_invoicexpress.field_res_config_settings__invoicexpress_template_id +msgid "" +"Used to generate the To, Cc, Subject and Body for the email sent by the " +"InvoiceXpress service" +msgstr "" + +#. module: l10n_pt_account_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_account_invoicexpress/models/account_move.py:0 +#, python-format +msgid "View Document" +msgstr "" + +#, python-format +#~ msgid "Email sent by InvoiceXpress:
    • To: {}
    • Cc: {}
    " +#~ msgstr "" +#~ "Email enviado por InvoiceXpress:
    • To: {}
    • Cc: {}
    " + +#~ msgid "Display Name" +#~ msgstr "Nome de apresentação" + +#~ msgid "Error running API request ({} {}): {}" +#~ msgstr "Erro na chamada à API ({} {}): {}" + +#~ msgid " View Document" +#~ msgstr " Ver Documento" + +#~ msgid "Invoice %s is not registerd in InvoiceXpress yet." +#~ msgstr "Fatura %s não está registada no InvoiceXpress." diff --git a/l10n_pt_account_invoicexpress/models/__init__.py b/l10n_pt_account_invoicexpress/models/__init__.py new file mode 100644 index 00000000..5c3f2fe8 --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/__init__.py @@ -0,0 +1,11 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_invoicexpress +from . import account_journal +from . import account_move +from . import account_tax +from . import res_company +from . import res_config_settings +from . import res_country +from . import res_partner diff --git a/l10n_pt_account_invoicexpress/models/account_invoicexpress.py b/l10n_pt_account_invoicexpress/models/account_invoicexpress.py new file mode 100644 index 00000000..47157216 --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/account_invoicexpress.py @@ -0,0 +1,113 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +# Reference: https://github.com/bitmario/invoicexpress-api-python + +import json +import logging +import pprint + +import requests +from werkzeug.urls import url_join + +from odoo import _, exceptions, models + +_logger = logging.getLogger(__name__) + + +class InvoiceXpress(models.AbstractModel): + _name = "account.invoicexpress" + _description = "InvoiceXpress connector" + + def _get_config(self, company): + account_name = company.invoicexpress_account_name + api_key = company.invoicexpress_api_key + if not account_name or not api_key: + error_msg = _( + """Something went wrong on API key. You should check the field + %(field:res.config.settings.invoicexpress_account_name)s in + %(menu:base_setup.menu_config)s.""" + ) + raise self.env["res.config.settings"].get_config_warning(error_msg) + return {"account_name": account_name, "api_key": api_key} + + def _build_url(self, config, path): + base_url = "https://{}.app.invoicexpress.com/".format(config["account_name"]) + return url_join(base_url, path) + + def _build_headers(self, config, headers_add=None): + headers = {"content-type": "application/json", "accept": "application/json"} + if headers_add: + headers.update(headers_add) + return headers + + def _build_params(self, config, params_add): + params = {"api_key": config["api_key"]} + if params_add: + params.update(params_add) + return params + + def _check_http_status(self, response): + """ + You can perform up to 100 requests per minute for each Account. If you exceed + this limit, you’ll get a 429 Too Many Requests response for subsequent requests. + + We recommend you handle 429 responses so your integration retries requests + automatically. + """ + # TODO: implement request rate limit + if response.status_code not in [200, 201]: + if response.json(): + msg = "\n".join( + "- " + (x.get("error") or repr(x)) + for x in response.json().get("errors", []) + ) + else: + msg = repr(response.json()) + raise exceptions.ValidationError( + _("Error running API request (%(status_code)s %(reason)s):\n%(json)s") + % { + "status_code": response.status_code, + "reason": response.reason, + "json": msg, + } + ) + + def call( + self, + company, + endpoint, + verb="GET", + headers=None, + params=None, + payload=None, + raise_errors=True, + ): + config = self._get_config(company) + request_url = self._build_url(config, endpoint) + request_headers = self._build_headers(config, headers) + request_params = self._build_params(config, params) + request_data = payload and json.dumps(payload) or "" + _logger.debug( + "\nRequest for %s %s:\n%s", + request_url, + verb, + pprint.pformat(payload, indent=1), + ) + response = requests.request( + verb, + request_url, + params=request_params, + data=request_data, + headers=request_headers, + timeout=15, + ) + _logger.debug( + "\nResponse %s: %s", + response.status_code, + pprint.pformat(response.json(), indent=1) + if response.text.startswith("{") + else response.text, + ) + if raise_errors: + self._check_http_status(response) + return response diff --git a/l10n_pt_account_invoicexpress/models/account_journal.py b/l10n_pt_account_invoicexpress/models/account_journal.py new file mode 100644 index 00000000..19d0428c --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/account_journal.py @@ -0,0 +1,33 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + @api.depends("invoicexpress_doc_type") + def _compute_use_invoicexpress(self): + for journal in self: + journal.use_invoicexpress = ( + journal.invoicexpress_doc_type + and journal.invoicexpress_doc_type != "none" + and journal.company_id.has_invoicexpress + ) + + invoicexpress_doc_type = fields.Selection( + [ + ("invoice", "Invoice"), + ("invoice_receipt", "Invoices Receipt"), + ("simplified_invoice", "Simplified Invoice"), + ("none", "No InvoiceXpress document"), + ], + help="Select the type of legal invoice document" + " to be created by InvoiceXpress.", + ) + use_invoicexpress = fields.Boolean( + compute="_compute_use_invoicexpress", + help="Invoicexpress service is only used if checked." + " Only relevant for Sales journals.", + ) diff --git a/l10n_pt_account_invoicexpress/models/account_move.py b/l10n_pt_account_invoicexpress/models/account_move.py new file mode 100644 index 00000000..b5ac8084 --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/account_move.py @@ -0,0 +1,359 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import uuid + +from markupsafe import Markup + +from odoo import _, api, exceptions, fields, models + + +class AccountMove(models.Model): + _inherit = "account.move" + + @api.depends("restrict_mode_hash_table", "state") + def _compute_show_reset_to_draft_button(self): + super()._compute_show_reset_to_draft_button() + # InvoiceXpress generated invoices can't be set to Draft + self.filtered("invoicexpress_id").write({"show_reset_to_draft_button": False}) + return + + @api.depends("move_type", "journal_id.use_invoicexpress") + def _compute_can_invoicexpress(self): + for invoice in self: + invoice.can_invoicexpress = ( + invoice.journal_id.use_invoicexpress and invoice.is_sale_document() + ) + + @api.depends("can_invoicexpress", "company_id.invoicexpress_template_id") + def _compute_can_invoicexpress_email(self): + for invoice in self: + invoice.can_invoicexpress_email = ( + invoice.can_invoicexpress + and invoice.company_id.invoicexpress_template_id + ) + + @api.depends("move_type", "journal_id", "partner_shipping_id") + def _compute_invoicexpress_doc_type(self): + """ + The type of document to create: invoices, invoice_receipts, + simplified_invoices, vat_moss_invoices, credit_notes or debit_notes. + """ + invoices = self.filtered("journal_id.use_invoicexpress") + for invoice in invoices: + doctype = invoice.journal_id.invoicexpress_doc_type + if not doctype or doctype == "none": + res = None + elif invoice.move_type == "out_refund": + res = "credit_note" + else: + res = doctype + invoice.invoicexpress_doc_type = res + + journal_type = fields.Selection( + related="journal_id.type", string="Journal Type", readonly=True + ) + invoicexpress_id = fields.Char("InvoiceXpress ID", copy=False, readonly=True) + invoicexpress_permalink = fields.Char( + "InvoiceXpress Doc Link", copy=False, readonly=True + ) + can_invoicexpress = fields.Boolean(compute="_compute_can_invoicexpress") + can_invoicexpress_email = fields.Boolean(compute="_compute_can_invoicexpress_email") + + invoicexpress_doc_type = fields.Selection( + [ + ("invoice", "Invoice"), + ("invoice_receipt", "Invoice and Receipt"), + ("simplified_invoice", "Simplified Invoice"), + ("vat_moss_invoice", "Europe VAT MOSS Invoice"), + ("vat_moss_credit_note", "Europe VAT MOSS Credit Note"), + ("debit_note", "Debit Note"), + ("credit_note", "Credit Note"), + ], + compute="_compute_invoicexpress_doc_type", + store=True, + readonly=False, + copy=False, + help="Select the type of legal invoice document" + " to be created by InvoiceXpress." + " If unset, InvoiceXpress will not be used.", + ) + + @api.constrains("journal_id", "company_id") + def _check_invoicexpress_doctype_config(self): + """ + Ensure Journal configuration was not forgotten. + """ + sale_invoices = self.filtered(lambda x: x.journal_id.type == "sale") + for invoice in sale_invoices: + journal_doctype = invoice.journal_id.invoicexpress_doc_type + has_invoicexpress = invoice.company_id.has_invoicexpress + if not journal_doctype and has_invoicexpress: + raise exceptions.UserError( + _( + "Journal %s is missing the InvoiceXpress" + " document type configuration!" + ) + % invoice.journal_id.display_name + ) + + @api.model + def _get_invoicexpress_prefix(self, doctype): + return { + "invoice": "FT", + "invoice_receipt": "FR", + "simplified_invoice": "FS", + "vat_moss_invoice": "FVM", + # vat_moss_credit_note does not have a prefix! + "credit_note": "NC", + "debit_note": "ND", + }.get(doctype) + + def _prepare_invoicexpress_lines(self): + # FIXME: set user lang, based on country? + lines = self.invoice_line_ids.filtered( + lambda x: x.display_type not in ("line_section", "line_note") + ) + # Ensure Taxes are created on InvoiceXpress + lines.mapped("tax_ids").action_invoicexpress_tax_create() + items = [] + for line in lines: + tax = line.tax_ids[:1] + # If not tax set, force zero VAT + tax_detail = {"name": tax.name or "IVA0", "value": tax.amount or 0.0} + # Because InvoiceXpress expects unit_price in EUR, + # check if we need to convert + # line currency to company currency + # (company should use EUR as default currency) + if line.currency_id == line.company_id.currency_id: + price_unit = line.price_unit + else: + price_unit = line.currency_id._convert( + line.price_unit, + line.company_id.currency_id, + line.company_id, + line.move_id.invoice_date + or line.move_id.date + or fields.Date.context_today(line), + ) + items.append( + { + "name": line.product_id.default_code + or line.product_id.display_name, + "description": line._get_invoicexpress_descr(), + "unit_price": price_unit, + "quantity": line.quantity, + "discount": line.discount, + "tax": tax_detail, + } + ) + return items + + def _get_invoicexpress_partner(self): + # Hook to customize the "client" values to use + return self.commercial_partner_id + + def _prepare_invoicexpress_vals(self): + self.ensure_one() + if not self.invoice_date and self.invoice_date_due: + raise exceptions.UserError( + _("Kindly add the invoice date and invoice due date.") + ) + customer = self._get_invoicexpress_partner() + customer_vals = customer.set_invoicexpress_contact() + items = self._prepare_invoicexpress_lines() + proprietary_uid = "ODOO" + str(uuid.uuid4()).replace("-", "") + invoice_data = { + "invoice": { + "date": self.invoice_date.strftime("%d/%m/%Y"), + "due_date": self.invoice_date_due.strftime("%d/%m/%Y"), + "reference": self.ref or "", + "client": customer_vals, + "observations": self.narration or "", + "items": items, + }, + "proprietary_uid": proprietary_uid, + } + exempt_code = self.l10npt_vat_exempt_reason.code + if exempt_code: + invoice_data["invoice"]["tax_exemption"] = exempt_code + if self.company_id.currency_id != self.currency_id: + currency_rate = self.env["res.currency"]._get_conversion_rate( + self.company_id.currency_id, + self.currency_id, + self.company_id, + self.invoice_date, + ) + invoice_data["invoice"].update( + {"currency_code": self.currency_id.name, "rate": str(currency_rate)} + ) + doctype = self.invoicexpress_doc_type + if doctype in ("credit_note", "debit_note"): + owner_invoice_num = self.reversed_entry_id.invoicexpress_id + if owner_invoice_num: + invoice_data["invoice"]["owner_invoice_id"] = owner_invoice_num + return invoice_data + + def _update_invoicexpress_status(self): + inv_xpress_link_name = _("View Document") + inv_xpress_link = ( + f"{inv_xpress_link_name}" + ) + msg = _( + "InvoiceXpress record has been created for this invoice:" + "
    • InvoiceXpress Id: {inv_xpress_id}
    • " + "
    • {inv_xpress_link}
    " + ).format(inv_xpress_id=self.invoicexpress_id, inv_xpress_link=inv_xpress_link) + self.message_post(body=Markup(msg)) + + def action_create_invoicexpress_invoice(self): + InvoiceXpress = self.env["account.invoicexpress"] + for invoice in self.filtered("can_invoicexpress"): + doctype = invoice.invoicexpress_doc_type + if not doctype: + raise exceptions.UserError( + _("Invoice is missing the InvoiceXpress document type!") + ) + payload = invoice._prepare_invoicexpress_vals() + response = InvoiceXpress.call( + invoice.company_id, f"{doctype}s.json", "POST", payload=payload + ).json() + values = response.get(doctype) + if not values: + raise exceptions.UserError( + _("Something went wrong: the InvoiceXpress response looks empty.") + ) + invoice.invoicexpress_id = values.get("id") + invoice.invoicexpress_permalink = values.get("permalink") + response1 = InvoiceXpress.call( + invoice.company_id, + f"{doctype}s/{invoice.invoicexpress_id}/change-state.json", + "PUT", + payload={"invoice": {"state": "finalized"}}, + raise_errors=True, + ).json() + values1 = response1.get(doctype) + seqnum = values1 and values1.get("inverted_sequence_number") + if not seqnum: + raise exceptions.UserError( + _( + "Something went wrong: the InvoiceXpress response" + " is missing a sequence number." + ) + ) + prefix = self._get_invoicexpress_prefix(doctype) + invx_number = f"{prefix} {seqnum}" if prefix else seqnum + if invoice.payment_reference == invoice.name: + invoice.payment_reference = invx_number + invoice.name = invx_number + invoice._update_invoicexpress_status() + + def _prepare_invoicexpress_email_vals(self, ignore_no_config=False): + self.ensure_one() + template_id = self.company_id.invoicexpress_template_id + if not template_id and not ignore_no_config: + raise exceptions.UserError( + _( + "Please configure the InvoiceXpress email template" + " at Settings > General Setting, InvoiceXpress section" + ) + ) + values = template_id._generate_template( + [self.id], ["subject", "body_html", "email_to", "email_cc"] + )[self.id] + if not values.get("email_to") and not ignore_no_config: + raise exceptions.UserError(_("No address to send invoice email to.")) + email_data = None + if template_id and values["email_to"]: + email_data = { + "message": { + "client": {"email": values["email_to"], "save": "0"}, + "cc": values["email_cc"], + "subject": values["subject"], + "body": values["body_html"], + } + } + return email_data + + def action_send_invoicexpress_email(self, ignore_no_config=False): + InvoiceXpress = self.env["account.invoicexpress"] + for invoice in self.filtered("can_invoicexpress_email"): + if not invoice.invoicexpress_id: + raise exceptions.UserError( + _("Invoice %s is not registered in InvoiceXpress yet.") + % invoice.name + ) + doctype = invoice.invoicexpress_doc_type + endpoint = f"{doctype}s/{invoice.invoicexpress_id}/email-document.json" + payload = invoice._prepare_invoicexpress_email_vals(ignore_no_config) + if payload: + InvoiceXpress.call(invoice.company_id, endpoint, "PUT", payload=payload) + msg = _( + "Email sent by InvoiceXpress:
    • To: " + "{email}
    • Cc:{cc}
    " + ).format( + email=payload["message"]["client"]["email"], + cc=payload["message"]["cc"] or _("None"), + ) + invoice.message_post(body=Markup(msg)) + + def _post(self, soft=True): + res = super()._post(soft=soft) + for invoice in self: + if not invoice.invoicexpress_id: + invoice._check_invoicexpress_doctype_config() + invoice.action_create_invoicexpress_invoice() + invoice.action_send_invoicexpress_email(ignore_no_config=True) + return res + + def _track_subtype(self, init_values): + res = super()._track_subtype(init_values) + if "payment_state" in init_values and self.payment_state == "paid": + for invoice in self: + if invoice.invoicexpress_id: + invoice._mark_invoice_paid() + return res + + def _mark_invoice_paid(self): + InvoiceXpress = self.env["account.invoicexpress"] + for invoice in self.filtered("can_invoicexpress"): + doctype = invoice.invoicexpress_doc_type + if not doctype: + raise exceptions.UserError( + _("Invoice is missing the InvoiceXpress document type!") + ) + response = InvoiceXpress.call( + invoice.company_id, + f"{doctype}s/{invoice.invoicexpress_id}/change-state.json", + "PUT", + payload={"invoice": {"state": "settled"}}, + raise_errors=True, + ).json() + values = response.get(doctype) + seqnum = values and values.get("inverted_sequence_number") + if not seqnum: + raise exceptions.UserError( + _( + "Something went wrong: the InvoiceXpress response" + " is missing a sequence number." + ) + ) + msg = _("InvoiceXpress record has been modified to Paid.") + self.message_post(body=Markup(msg)) + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + def _get_invoicexpress_descr(self): + """ + Remove Odoo product code from description, + since it is already presented in a the Code column + """ + res = self.name + ref = self.product_id.default_code + prefix = f"[{ref}] " + if ref and self.name.startswith(prefix): + res = self.name[len(prefix) :] + return res diff --git a/l10n_pt_account_invoicexpress/models/account_tax.py b/l10n_pt_account_invoicexpress/models/account_tax.py new file mode 100644 index 00000000..7fad5ecb --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/account_tax.py @@ -0,0 +1,59 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountTax(models.Model): + _inherit = "account.tax" + + invoicexpress_id = fields.Char("InvoiceXpress ID", copy=False, readonly=True) + + @api.model + def _map_invoicexpress_taxes(self, company): + """ + Retrieves all InvoiceXpress taxes, an maps them + to the existing Odoo taxes + """ + InvoiceXpress = self.env["account.invoicexpress"] + response = InvoiceXpress.call(company, "taxes.json", "GET") + invx_taxes_dict = {x["name"]: x for x in response.json().get("taxes", [])} + odoo_taxes = self.search( + [("type_tax_use", "=", "sale"), ("company_id", "=", company.id)] + ) + for odoo_tax in odoo_taxes: + invx_tax_vals = invx_taxes_dict.get(odoo_tax.name) + if invx_tax_vals: + odoo_tax._update_invoicexpress_status(invx_tax_vals) + + def _prepare_invoicexpress_vals(self): + self.ensure_one() + tax_data = { + "tax": { + "name": self.name, + "value": str(self.amount), + "region": self.l10n_pt_fiscal_zone or "", + } + } + return tax_data + + def _update_invoicexpress_status(self, result): + self.invoicexpress_id = result.get("id") + + def action_invoicexpress_tax_create(self): + InvoiceXpress = self.env["account.invoicexpress"] + verb = "POST" + endpoint = "taxes.json" + for tax in self.filtered(lambda x: not x.invoicexpress_id): + payload = tax._prepare_invoicexpress_vals() + response = InvoiceXpress.call( + tax.company_id, endpoint, verb, payload=payload, raise_errors=False + ) + if response.status_code == 422: + # Tax name already exists, map missing invoicexpress_ids + self._map_invoicexpress_taxes(tax.company_id) + else: + InvoiceXpress._check_http_status(response) + response_json = response.json() + if "tax" in response_json: + tax._update_invoicexpress_status(response_json["tax"]) diff --git a/l10n_pt_account_invoicexpress/models/res_company.py b/l10n_pt_account_invoicexpress/models/res_company.py new file mode 100644 index 00000000..8c6a0d05 --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/res_company.py @@ -0,0 +1,35 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class Company(models.Model): + _inherit = "res.company" + + @api.depends("country_code", "invoicexpress_account_name", "invoicexpress_api_key") + def _compute_has_invoicexpress(self): + for company in self: + company.has_invoicexpress = ( + company.country_code == "PT" + and company.invoicexpress_account_name + and company.invoicexpress_api_key + ) + + invoicexpress_account_name = fields.Char(string="InvoiceXpress Account Name") + invoicexpress_api_key = fields.Char(string="InvoiceXpress API Key") + has_invoicexpress = fields.Boolean( + compute="_compute_has_invoicexpress", + help="Easy to use indicator if InvoiceXpress is enabled and can be used", + ) + + invoicexpress_template_id = fields.Many2one( + "mail.template", + "InvoiceXpress Email Template", + domain="[('model', '=', 'account.move')]", + default=lambda self: self.env.ref( + "l10n_pt_account_invoicexpress.email_template_invoice", False + ), + help="Used to generate the To, Cc, Subject and Body" + " for the email sent by the InvoiceXpress service", + ) diff --git a/l10n_pt_account_invoicexpress/models/res_config_settings.py b/l10n_pt_account_invoicexpress/models/res_config_settings.py new file mode 100644 index 00000000..12a47eee --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/res_config_settings.py @@ -0,0 +1,19 @@ +# Copyright (C) 2021 Open Source Integrators + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + invoicexpress_account_name = fields.Char( + related="company_id.invoicexpress_account_name", + readonly=False, + ) + invoicexpress_api_key = fields.Char( + related="company_id.invoicexpress_api_key", + readonly=False, + ) + invoicexpress_template_id = fields.Many2one( + related="company_id.invoicexpress_template_id", readonly=False + ) diff --git a/l10n_pt_account_invoicexpress/models/res_country.py b/l10n_pt_account_invoicexpress/models/res_country.py new file mode 100644 index 00000000..80cb9683 --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/res_country.py @@ -0,0 +1,10 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class Country(models.Model): + _inherit = "res.country" + + invoicexpress_name = fields.Char() diff --git a/l10n_pt_account_invoicexpress/models/res_partner.py b/l10n_pt_account_invoicexpress/models/res_partner.py new file mode 100644 index 00000000..3a1a3f4a --- /dev/null +++ b/l10n_pt_account_invoicexpress/models/res_partner.py @@ -0,0 +1,76 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + invoicexpress_id = fields.Char("InvoiceXpress ID", copy=False, readonly=True) + + def _prepare_invoicexpress_vals(self): + self.ensure_one() + vals = { + "name": self.name, + "code": f"ODOO-{self.ref or self.id}", + "email": self.email, + "address": ", ".join(filter(None, [self.street, self.street2])), + "city": self.city, + "postal_code": self.zip, + "country": self.country_id.invoicexpress_name, + "fiscal_id": self.vat, + "website": self.website, + "phone": self.phone, + } + # InvoiceXpress document language (pt, es or rn) + # Outside PT and ES use english + # Could be a requirement for some border authorities + country_code = self.country_id.code + if country_code == "ES": + vals["language"] = "es" + elif country_code == "PT": + vals["language"] = "pt" + elif country_code: + vals["language"] = "en" + return {k: v for k, v in vals.items() if v} + + def set_invoicexpress_contact(self): + self.ensure_one() + self.vat and self.check_vat() # Double check VAT is right + InvoiceXpress = self.env["account.invoicexpress"] + company = self.company_id or self.env.company + doctype = "client" + vals = self._prepare_invoicexpress_vals() + invx_id_to_update = self.invoicexpress_id + if not invx_id_to_update: + # Create: POST /clients.json + response = InvoiceXpress.call( + company, + f"{doctype}s.json", + "POST", + payload={"client": vals}, + raise_errors=False, + ) + if response.status_code == 422: # Oh, it already exists! + response = InvoiceXpress.call( + company, + f"{doctype}s/find-by-code.json", + "GET", + params={"client_code": vals["code"]}, + ) + values = response.json().get(doctype) + invx_id_to_update = values.get("id") # Update is needed! + values = response.json().get(doctype, {}) + self.invoicexpress_id = values.get("id") + + if invx_id_to_update: + # Update: PUT /clients/$(client-id).json + response = InvoiceXpress.call( + company, + f"{doctype}s/{self.invoicexpress_id}.json", + "PUT", + payload={"client": vals}, + raise_errors=True, + ) + return {"name": vals["name"], "code": vals["code"]} diff --git a/l10n_pt_account_invoicexpress/pyproject.toml b/l10n_pt_account_invoicexpress/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/l10n_pt_account_invoicexpress/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_pt_account_invoicexpress/readme/CONFIGURE.md b/l10n_pt_account_invoicexpress/readme/CONFIGURE.md new file mode 100644 index 00000000..fc9882d5 --- /dev/null +++ b/l10n_pt_account_invoicexpress/readme/CONFIGURE.md @@ -0,0 +1,38 @@ +To configure: + +- Navigate to Settings/General Settings. +- In the "Invoicing" configuration section, locate the + "InvoiceXpress" subsection: There you can set: + - InvoiceXpress Account Name + - InvoiceXpress API Key + - InvoiceXpress Invoice Email Template + +- In "Invoicing/Accounting" configuration section, locate the + Sales Journals, and set the "InvoiceXpress Doc Type". + This is required before invoices before any invoice can be created. + +The email template is used to prepare the details for the email the +InvoiceXpress service sends with the invoice: + +- "To" email address +- "Cc" email address +- Subject +- Body + +Translations are supported, and the customer language is used to render +the email details. + +The InvoiceXpress configuration is per company, and can also be modified +directly on the Company form, if the Developer Mode is enabled. + +On the Journal form, for sales journals, a flag is available to allow +disabling the InvoiceXpress integration. This can be useful for a +journal that was used temporarily to import invoice history, for +reporting purposes, ond was then disabled, or to allow that journal to +use a different legal invoicing system. + +Note that all invoices generated MUST use a certified invoicing system. +For a portuguese company, you cannot have some of the invoices being +generated by Odoo itself. So use this option at your own risk, and only +in the cases you are sure to be compliant with TPortugues Tax Authority +invoicing regulations. diff --git a/l10n_pt_account_invoicexpress/readme/CONTRIBUTORS.md b/l10n_pt_account_invoicexpress/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..69bb8f2b --- /dev/null +++ b/l10n_pt_account_invoicexpress/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Daniel Reis \<\>, [Open Source + Integrators](https://www.opensourceintegrators.eu) diff --git a/l10n_pt_account_invoicexpress/readme/DESCRIPTION.md b/l10n_pt_account_invoicexpress/readme/DESCRIPTION.md new file mode 100644 index 00000000..1b8d5348 --- /dev/null +++ b/l10n_pt_account_invoicexpress/readme/DESCRIPTION.md @@ -0,0 +1,21 @@ +Generate Portuguese tax authority legal Invoices ("Faturas") using +InvoiceXpress. + + +InvoiceXpress is a paid service. Visit for +more details. + +Once the InvoiceXpress connection is configured, the invoice CONFIRM +button automatically generates the InvoiceXpress invoice. + +If the InvoiceXpress Invoice email template is configured, the +InvoiceXpress service will also send the invoice by email, using the +details in Odoo configured email template. + +This replaces the Odoo SEND & PRINT button, since only the InvoiceXpress +generated document should be used. Having other print layouts for the +invoice is not allowed by the Portuguese Tax Authority. + +Legal transport documents ("Guias de Transporte" e "Guias de Remessa) +are also supported through an extension module. For this please ensure that +"l10n_pt_stock_invoicexpress" is installed. diff --git a/l10n_pt_account_invoicexpress/readme/USAGE.md b/l10n_pt_account_invoicexpress/readme/USAGE.md new file mode 100644 index 00000000..c0ad0dba --- /dev/null +++ b/l10n_pt_account_invoicexpress/readme/USAGE.md @@ -0,0 +1,21 @@ +On an Invoice, the CONFIRM button automatically generates an invoice on +the InvoiceXpress service. + +The "Email InvoiceXpress" button requests the InvoiceXpress service to +send an email with a copy of the legal document. + +The Invoice form shows an "InvoiceXpress" tab containing details for the +corresponding InvoiceXpress document. + +Invoices: + +- Added support to the different documents types: Invoice, Invoice + Receipt, Simplified Invoice. The default document type is set on the + Journal, and can be changed on the Invoice form. +- Use the invoice commercial partner for the name and address, instead + of the invoice contact. +- Added support for the Terms and Conditions/Observations field +- Added to Credit Notes the link to the source Invoice + + +The monthly SAF-T file should be downloaded from the InvoiceXpress website. diff --git a/l10n_pt_account_invoicexpress/static/description/cover.png b/l10n_pt_account_invoicexpress/static/description/cover.png new file mode 100644 index 00000000..ae9b85ea Binary files /dev/null and b/l10n_pt_account_invoicexpress/static/description/cover.png differ diff --git a/l10n_pt_account_invoicexpress/static/description/icon.png b/l10n_pt_account_invoicexpress/static/description/icon.png new file mode 100644 index 00000000..34c78e0a Binary files /dev/null and b/l10n_pt_account_invoicexpress/static/description/icon.png differ diff --git a/l10n_pt_account_invoicexpress/static/description/index.html b/l10n_pt_account_invoicexpress/static/description/index.html new file mode 100644 index 00000000..7e58760e --- /dev/null +++ b/l10n_pt_account_invoicexpress/static/description/index.html @@ -0,0 +1,502 @@ + + + + + +Portugal InvoiceXpress Integration + + + +
    +

    Portugal InvoiceXpress Integration

    + + +

    Production/Stable License: AGPL-3 OCA/l10n-portugal Translate me on Weblate Try me on Runboat

    +

    Generate Portuguese tax authority legal Invoices (“Faturas”) using +InvoiceXpress.

    +

    InvoiceXpress is a paid service. Visit https://invoicexpress.com for +more details.

    +

    Once the InvoiceXpress connection is configured, the invoice CONFIRM +button automatically generates the InvoiceXpress invoice.

    +

    If the InvoiceXpress Invoice email template is configured, the +InvoiceXpress service will also send the invoice by email, using the +details in Odoo configured email template.

    +

    This replaces the Odoo SEND & PRINT button, since only the InvoiceXpress +generated document should be used. Having other print layouts for the +invoice is not allowed by the Portuguese Tax Authority.

    +

    Legal transport documents (“Guias de Transporte” e “Guias de Remessa) +are also supported through an extension module. For this please ensure +that “l10n_pt_stock_invoicexpress” is installed.

    +

    Table of contents

    + +
    +

    Configuration

    +

    To configure:

    +
      +
    • Navigate to Settings/General Settings.
    • +
    • In the “Invoicing” configuration section, locate the “InvoiceXpress” +subsection: There you can set:
        +
      • InvoiceXpress Account Name
      • +
      • InvoiceXpress API Key
      • +
      • InvoiceXpress Invoice Email Template
      • +
      +
    • +
    • In “Invoicing/Accounting” configuration section, locate the Sales +Journals, and set the “InvoiceXpress Doc Type”. This is required +before invoices before any invoice can be created.
    • +
    +

    The email template is used to prepare the details for the email the +InvoiceXpress service sends with the invoice:

    +
      +
    • “To” email address
    • +
    • “Cc” email address
    • +
    • Subject
    • +
    • Body
    • +
    +

    Translations are supported, and the customer language is used to render +the email details.

    +

    The InvoiceXpress configuration is per company, and can also be modified +directly on the Company form, if the Developer Mode is enabled.

    +

    On the Journal form, for sales journals, a flag is available to allow +disabling the InvoiceXpress integration. This can be useful for a +journal that was used temporarily to import invoice history, for +reporting purposes, ond was then disabled, or to allow that journal to +use a different legal invoicing system.

    +

    Note that all invoices generated MUST use a certified invoicing system. +For a portuguese company, you cannot have some of the invoices being +generated by Odoo itself. So use this option at your own risk, and only +in the cases you are sure to be compliant with TPortugues Tax Authority +invoicing regulations.

    +
    +
    +

    Usage

    +

    On an Invoice, the CONFIRM button automatically generates an invoice on +the InvoiceXpress service.

    +

    The “Email InvoiceXpress” button requests the InvoiceXpress service to +send an email with a copy of the legal document.

    +

    The Invoice form shows an “InvoiceXpress” tab containing details for the +corresponding InvoiceXpress document.

    +

    Invoices:

    +
      +
    • Added support to the different documents types: Invoice, Invoice +Receipt, Simplified Invoice. The default document type is set on the +Journal, and can be changed on the Invoice form.
    • +
    • Use the invoice commercial partner for the name and address, instead +of the invoice contact.
    • +
    • Added support for the Terms and Conditions/Observations field
    • +
    • Added to Credit Notes the link to the source Invoice
    • +
    +

    The monthly SAF-T file should be downloaded from the InvoiceXpress +website.

    +
    +
    +

    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 to smash it by providing a detailed and welcomed +feedback.

    +

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

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Open Source Integrators
    • +
    +
    + +
    +

    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.

    +

    Current maintainer:

    +

    dreispt

    +

    This module is part of the OCA/l10n-portugal project on GitHub.

    +

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

    +
    +
    +
    + + diff --git a/l10n_pt_account_invoicexpress/tests/__init__.py b/l10n_pt_account_invoicexpress/tests/__init__.py new file mode 100644 index 00000000..eb140e4f --- /dev/null +++ b/l10n_pt_account_invoicexpress/tests/__init__.py @@ -0,0 +1 @@ +from . import test_invoicexpress diff --git a/l10n_pt_account_invoicexpress/tests/test_invoicexpress.py b/l10n_pt_account_invoicexpress/tests/test_invoicexpress.py new file mode 100644 index 00000000..e8d859d8 --- /dev/null +++ b/l10n_pt_account_invoicexpress/tests/test_invoicexpress.py @@ -0,0 +1,128 @@ +from unittest.mock import Mock, patch + +import requests + +from odoo import fields +from odoo.tests import Form, common + + +def mock_response(json, status_code=200): + mock_response = Mock() + mock_response.json.return_value = json + mock_response.text = str(json) + mock_response.status_code = status_code + return mock_response + + +@common.tagged("-at_install", "post_install") +class TestInvoiceXpress(common.TransactionCase): + def setUp(self): + super().setUp() + + self.company = self.env.company + self.company.write( + { + "invoicexpress_account_name": "ACCOUNT", + "invoicexpress_api_key": "APIKEY", + "country_id": self.env.ref("base.pt").id, + } + ) + Journal = self.env["account.journal"] + self.sale_journals = Journal.search([("type", "=", "sale")]) + + self.AccountMove = self.env["account.move"] + self.ProductProduct = self.env["product.product"] + self.ResPartner = self.env["res.partner"] + self.AccountTax = self.env["account.tax"] + + self.productA = self.ProductProduct.create( + {"name": "Product A", "list_price": "2.0"} + ) + self.productB = self.ProductProduct.create( + {"name": "Product B", "list_price": "3.0"} + ) + + self.pt_country = self.env.ref("base.pt") + self.partnerA = self.ResPartner.create( + { + "name": "Customer A", + "country_id": self.pt_country.id, + "city": "Porto", + "zip": "2000-555", + } + ) + + def test_res_partner__prepare_invoicexpress_vals(self): + partner_PT = self.partnerA.copy({"country_id": self.env.ref("base.pt").id}) + partner_PT_vals = partner_PT._prepare_invoicexpress_vals() + self.assertEqual( + partner_PT_vals["language"], "pt", "Address in Portugal uses pt language" + ) + partner_ES = self.partnerA.copy({"country_id": self.env.ref("base.es").id}) + partner_ES_vals = partner_ES._prepare_invoicexpress_vals() + self.assertEqual( + partner_ES_vals["language"], "es", "Address in Spain uses es language" + ) + partner_FR = self.partnerA.copy({"country_id": self.env.ref("base.fr").id}) + partner_FR_vals = partner_FR._prepare_invoicexpress_vals() + self.assertEqual( + partner_FR_vals["language"], + "en", + "Address not in Spain or Portugal uses en language", + ) + + def test_010_get_config_and_base_url(self): + API = self.env["account.invoicexpress"] + url = API._build_url(API._get_config(self.company), "dummy.json") + self.assertEqual(url, "https://ACCOUNT.app.invoicexpress.com/dummy.json") + + @patch.object(requests, "request") + def test_100_create_invoicexpress_tax(self, mock_request): + mock_request.return_value = mock_response( + { + "tax": { + "id": 12345, + "name": "IVA23", + "value": 23.0, + "region": "PT", + "default_tax": 1, + } + } + ) + taxA = self.env["account.tax"].create( + { + "name": "IVA23", + "type_tax_use": "sale", + "amount_type": "percent", + "amount": 23.0, + } + ) + taxA.action_invoicexpress_tax_create() + self.assertEqual(taxA.invoicexpress_id, "12345") + + @patch.object(requests, "request") + def test_101_create_invoicexpress_invoice(self, mock_request): + mock_request.return_value = mock_response( + { + "invoice_receipt": { + "id": 12345678, + "inverted_sequence_number": "MYSEQ/123", + } + } + ) + # Ensure Journal is configured + self.sale_journals.write({"invoicexpress_doc_type": "invoice_receipt"}) + # Create the Invoice + move_form = Form(self.AccountMove.with_context(default_move_type="out_invoice")) + move_form.invoice_date = fields.Date.today() + move_form.partner_id = self.partnerA + products = [self.productA, self.productB] + + for product in products: + with move_form.invoice_line_ids.new() as line_form: + line_form.product_id = product + invoice = move_form.save() + invoice.action_post() + self.assertEqual(invoice.invoicexpress_doc_type, "invoice_receipt") + self.assertEqual(invoice.invoicexpress_id, "12345678") + self.assertEqual(invoice.name, "FR MYSEQ/123") diff --git a/l10n_pt_account_invoicexpress/views/account_journal_view.xml b/l10n_pt_account_invoicexpress/views/account_journal_view.xml new file mode 100644 index 00000000..0931d991 --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/account_journal_view.xml @@ -0,0 +1,16 @@ + + + + Journal Form: add Invoicexpress + account.journal + + + + + + + + + + + diff --git a/l10n_pt_account_invoicexpress/views/account_move_view.xml b/l10n_pt_account_invoicexpress/views/account_move_view.xml new file mode 100644 index 00000000..3aa848c8 --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/account_move_view.xml @@ -0,0 +1,52 @@ + + + + + account.move.form + account.move + + +
    + + + +
    + + + + + + + + + + +
    +
    +
    diff --git a/l10n_pt_account_invoicexpress/views/account_tax_view.xml b/l10n_pt_account_invoicexpress/views/account_tax_view.xml new file mode 100644 index 00000000..a51a3fa1 --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/account_tax_view.xml @@ -0,0 +1,14 @@ + + + + + account.tax.form + account.tax + + + + + + + + diff --git a/l10n_pt_account_invoicexpress/views/res_company_view.xml b/l10n_pt_account_invoicexpress/views/res_company_view.xml new file mode 100644 index 00000000..7f634d26 --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/res_company_view.xml @@ -0,0 +1,21 @@ + + + + InvoiceXpress Company Configuration + res.company + form + + + + + + + + + + + + + + + diff --git a/l10n_pt_account_invoicexpress/views/res_config_settings.xml b/l10n_pt_account_invoicexpress/views/res_config_settings.xml new file mode 100644 index 00000000..cb2c37d4 --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/res_config_settings.xml @@ -0,0 +1,48 @@ + + + res.config.settings.view.form.invoicexpress + + res.config.settings + + + + + + + Generate an API key +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/l10n_pt_account_invoicexpress/views/res_country_view.xml b/l10n_pt_account_invoicexpress/views/res_country_view.xml new file mode 100644 index 00000000..041e65ec --- /dev/null +++ b/l10n_pt_account_invoicexpress/views/res_country_view.xml @@ -0,0 +1,12 @@ + + + Country: add InvoiceXpress name + res.country + + + + + + + + diff --git a/l10n_pt_stock_invoicexpress/README.rst b/l10n_pt_stock_invoicexpress/README.rst new file mode 100644 index 00000000..828d5644 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/README.rst @@ -0,0 +1,129 @@ +================================================ +Portugal InvoiceXpress Legal Transport Documents +================================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:7d6a253f704fca774ae3fd1b77119c2174ee6d0d7e678a1c1672f9da5d24565e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png + :target: https://odoo-community.org/page/development-status + :alt: Production/Stable +.. |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%2Fl10n--portugal-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-portugal/tree/18.0/l10n_pt_stock_invoicexpress + :alt: OCA/l10n-portugal +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-portugal-18-0/l10n-portugal-18-0-l10n_pt_stock_invoicexpress + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-portugal&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Generate Portuguese tax authority legal transport documents ("Guias de +Transporte") using InvoiceXpress. + +The InvoiceXpress document is automatically generated when an ougoing +Transfer or Delivery Order is validated. + +This feature depends on the InvoiceXpress Invoice generation feature. +See +https://github.com/OCA/l10n-portugal/blob/14.0/l10n_pt_account_invoicexpress/README.rst +for more details. + +**UPDATE November/2021:** + +Deliveries: + +- Added support to the different document types, Transport ("Guia de + Transporte") and Shipment ("Guia de Remessa"). The default document + type is set on the Operation Type. +- Changed the line description to be the Product name, instead of the + original Sales Order description, so that it uses the most up to date + product description. +- Added tax details to the document lines. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +This feature depends on the InvoiceXpress Incoice generation feature. +See +https://github.com/OCA/l10n-portugal/blob/14.0/l10n_pt_account_invoicexpress/README.rst +for configuration details. + +An additional "InvoiceXpress Delivery Email" option is available, to +configure the email template preparing the details for the emailto be +sent by the InvoiceXpress service. + +Usage +===== + +On Delivery Orders and Internal Transfer documents, validating the +transfer automatically generates an InvoiceXpress "Guia de Transporte" +for the Done quantities. + +The "Email InvoiceXpress" requests the InvoiceXpress service to send an +email with a copy of the legal document. The content of this email can +be configure 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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Open Source Integrators + +Contributors +------------ + +- Daniel Reis , `Open Source + Integrators `__ + +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. + +.. |maintainer-dreispt| image:: https://github.com/dreispt.png?size=40px + :target: https://github.com/dreispt + :alt: dreispt + +Current `maintainer `__: + +|maintainer-dreispt| + +This module is part of the `OCA/l10n-portugal `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_pt_stock_invoicexpress/__init__.py b/l10n_pt_stock_invoicexpress/__init__.py new file mode 100644 index 00000000..6a0c7727 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/l10n_pt_stock_invoicexpress/__manifest__.py b/l10n_pt_stock_invoicexpress/__manifest__.py new file mode 100644 index 00000000..8e037ec2 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Portugal InvoiceXpress Legal Transport Documents", + "summary": "Portuguese legal transport and shipping documents" + " (Guias de Transporte e Guias de Remessa) generated with InvoiceXpress", + "version": "18.0.1.0.0", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/l10n-portugal", + "category": "Accounting/Localizations/EDI", + "maintainers": ["dreispt"], + "development_status": "Production/Stable", + "depends": ["l10n_pt_account_invoicexpress", "sale_stock"], + "data": [ + "views/res_company_view.xml", + "views/res_config_settings.xml", + "views/stock_picking_view.xml", + "views/stock_picking_type_view.xml", + "data/mail_template.xml", + ], + "images": ["static/description/cover.png"], + "installable": True, + "auto_install": True, +} diff --git a/l10n_pt_stock_invoicexpress/data/mail_template.xml b/l10n_pt_stock_invoicexpress/data/mail_template.xml new file mode 100644 index 00000000..9e821432 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/data/mail_template.xml @@ -0,0 +1,28 @@ + + + InvoiceXpress: Send Delivery by Email + + Entrega {{ object.name }} + {{ object.partner_id.email }} + {{ object.env.user.email }} + +

    +Olá, +
    +Enviamos em anexo a Guia de Trasporte + + relativa à sua encomenda + +. +

    +Obrigado + +
    + + +

    +
    + {{ object.partner_id.lang }} + +
    +
    diff --git a/l10n_pt_stock_invoicexpress/i18n/l10n_pt_stock_invoicexpress.pot b/l10n_pt_stock_invoicexpress/i18n/l10n_pt_stock_invoicexpress.pot new file mode 100644 index 00000000..e593570d --- /dev/null +++ b/l10n_pt_stock_invoicexpress/i18n/l10n_pt_stock_invoicexpress.pot @@ -0,0 +1,264 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_pt_stock_invoicexpress +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "%(name)s" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,body_html:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "" +"

    \n" +"Olá,\n" +"
    \n" +"Enviamos em anexo a Guia de Trasporte \n" +"\n" +" relativa à sua encomenda \n" +"\n" +".\n" +"

    \n" +"Obrigado\n" +"\n" +"
    \n" +" \n" +"
    \n" +"

    \n" +" " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__can_invoicexpress +msgid "Can Invoicexpress" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__can_invoicexpress_email +msgid "Can Invoicexpress Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_partner +msgid "Contact" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Delivery %s is not registered in InvoiceXpress yet." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__devolution +msgid "Devolução / Return" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"Email sent by InvoiceXpress:
    • To: %(email)s/li>
    • Cc: " +"%(cc)s
    " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,subject:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "Entrega {{ object.name }}" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__shipping +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__shipping +msgid "Guia de Remessa / Shipping" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__transport +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__transport +msgid "Guia de Transporte / Transport" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_send_email +msgid "" +"If unchecked, both the InvoiceXpress email and the Delivery email won't be " +"sent." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_doc_type +msgid "InvX Doc Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_send_email +msgid "InvX Send Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.view_stock_picking_form +msgid "InvoiceXpress" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_res_company__invoicexpress_delivery_template_id +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_res_config_settings__invoicexpress_delivery_template_id +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Delivery Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_permalink +msgid "InvoiceXpress Doc Link" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.view_stock_picking_form +msgid "InvoiceXpress Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_id +msgid "InvoiceXpress ID" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_number +msgid "InvoiceXpress Number" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"InvoiceXpress record has been created for this delivery " +"order:
    • Number: " +"%(inv_xpress_num)s
    • %(inv_xpress_link)s
    " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,name:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "InvoiceXpress: Send Delivery by Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking_type__invoicexpress_doc_type +msgid "Invoicexpress Doc Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__license_plate +msgid "License Plate" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__none +msgid "No InvoiceXpress document" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "No address to send delivery document email to." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "None" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"Please configure the InvoiceXpress Delivery email template at Settings > " +"General Setting, InvoiceXpress section" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Scheduled Date should be bigger then current datetime!" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_doc_type +msgid "" +"Select the type of legal delivery document to be created by InvoiceXpress." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking_type__invoicexpress_doc_type +msgid "" +"Select the type of legal delivery document to be created by InvoiceXpress. " +"If unset" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Something went wrong: the InvoiceXpress response looks empty." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__l10npt_transport_doc_due_date +msgid "Transport Doc. Validity" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_res_company__invoicexpress_delivery_template_id +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_res_config_settings__invoicexpress_delivery_template_id +msgid "" +"Used to generate the To, Cc, Subject and Body for the InvoiceXpress email " +"sending the Delivery document." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "View Document" +msgstr "" diff --git a/l10n_pt_stock_invoicexpress/i18n/pt.po b/l10n_pt_stock_invoicexpress/i18n/pt.po new file mode 100644 index 00000000..1e5d42a7 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/i18n/pt.po @@ -0,0 +1,335 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_pt_stock_invoicexpress +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-09-24 13:34+0000\n" +"Last-Translator: Daniel Reis \n" +"Language-Team: none\n" +"Language: pt\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 4.3.2\n" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "%(name)s" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,body_html:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "" +"

    \n" +"Olá,\n" +"
    \n" +"Enviamos em anexo a Guia de Trasporte \n" +"\n" +" relativa à sua encomenda \n" +"\n" +".\n" +"

    \n" +"Obrigado\n" +"\n" +"
    \n" +" \n" +"
    \n" +"

    \n" +" " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__can_invoicexpress +msgid "Can Invoicexpress" +msgstr "Permite Invoicexpress" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__can_invoicexpress_email +msgid "Can Invoicexpress Email" +msgstr "Pode enviar email Invoicexpress" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_company +msgid "Companies" +msgstr "Empresas" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_config_settings +msgid "Config Settings" +msgstr "Configuração de Definições" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_res_partner +msgid "Contact" +msgstr "Contacto" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Delivery %s is not registered in InvoiceXpress yet." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__devolution +msgid "Devolução / Return" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"Email sent by InvoiceXpress:
    • To: %(email)s/li>
    • Cc: %(cc)s
    " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,subject:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "Entrega {{ object.name }}" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__shipping +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__shipping +msgid "Guia de Remessa / Shipping" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking__invoicexpress_doc_type__transport +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__transport +msgid "Guia de Transporte / Transport" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_send_email +msgid "" +"If unchecked, both the InvoiceXpress email and the Delivery email won't be " +"sent." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_doc_type +msgid "InvX Doc Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_send_email +msgid "InvX Send Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.view_stock_picking_form +msgid "InvoiceXpress" +msgstr "InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_res_company__invoicexpress_delivery_template_id +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_res_config_settings__invoicexpress_delivery_template_id +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.res_config_settings_view_form +msgid "InvoiceXpress Delivery Email" +msgstr "Email Entrega InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_permalink +msgid "InvoiceXpress Doc Link" +msgstr "Ligação documento InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#: model_terms:ir.ui.view,arch_db:l10n_pt_stock_invoicexpress.view_stock_picking_form +msgid "InvoiceXpress Email" +msgstr "Email InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_id +msgid "InvoiceXpress ID" +msgstr "InvoiceXpress ID" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_number +msgid "InvoiceXpress Number" +msgstr "Número InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"InvoiceXpress record has been created for this delivery order:" +"
    • Number: %(inv_xpress_num)s
    • %(inv_xpress_link)s
    " +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:mail.template,name:l10n_pt_stock_invoicexpress.email_template_delivery +msgid "InvoiceXpress: Send Delivery by Email" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking_type__invoicexpress_doc_type +msgid "Invoicexpress Doc Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__license_plate +msgid "License Plate" +msgstr "Matrícula" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields.selection,name:l10n_pt_stock_invoicexpress.selection__stock_picking_type__invoicexpress_doc_type__none +msgid "No InvoiceXpress document" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "No address to send delivery document email to." +msgstr "Falta o endereço para onde enviar o email com a Guia de Transporte." + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "None" +msgstr "Nenhum" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "" +"Please configure the InvoiceXpress Delivery email template at Settings > " +"General Setting, InvoiceXpress section" +msgstr "" +"Por favor configure o modelo email InvoiceXpress para entregas em Definições " +"> Definições Gerais, secção InvoiceXpress" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Scheduled Date should be bigger then current datetime!" +msgstr "Data prevista deve ser posterior à data e hora atual!" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking__invoicexpress_doc_type +msgid "" +"Select the type of legal delivery document to be created by InvoiceXpress." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_stock_picking_type__invoicexpress_doc_type +msgid "" +"Select the type of legal delivery document to be created by InvoiceXpress. " +"If unset" +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "Something went wrong: the InvoiceXpress response looks empty." +msgstr "" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model,name:l10n_pt_stock_invoicexpress.model_stock_picking +msgid "Transfer" +msgstr "Transferência" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,field_description:l10n_pt_stock_invoicexpress.field_stock_picking__l10npt_transport_doc_due_date +msgid "Transport Doc. Validity" +msgstr "Validade Doc. Transporte" + +#. module: l10n_pt_stock_invoicexpress +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_res_company__invoicexpress_delivery_template_id +#: model:ir.model.fields,help:l10n_pt_stock_invoicexpress.field_res_config_settings__invoicexpress_delivery_template_id +msgid "" +"Used to generate the To, Cc, Subject and Body for the InvoiceXpress email " +"sending the Delivery document." +msgstr "" +"Usado para gerar Para, Cc, Assunto e Corpo do email InvoiceXpress enviado " +"com a Guia de Transporte." + +#. module: l10n_pt_stock_invoicexpress +#. odoo-python +#: code:addons/l10n_pt_stock_invoicexpress/models/stock_picking.py:0 +#, python-format +msgid "View Document" +msgstr "" + +#, python-format +#~ msgid "Email sent by InvoiceXpress:
    • To: {}
    • Cc: {}
    " +#~ msgstr "" +#~ "Email enviado pelo InvoiceXpress:
    • Para: {}
    • Cc: {}
    " + +#, python-format +#~ msgid "" +#~ "InvoiceXpress record has been created for this delivery order:" +#~ "
    • Number: {inv_xpress_num}
    • {inv_xpress_link}
    " +#~ msgstr "" +#~ "Registo InvoiceXpress foi criado para esta entrega:
    • Número: " +#~ "{inv_xpress_num}
    • {inv_xpress_link}
    " + +#~ msgid "" +#~ "

    \n" +#~ "Olá,\n" +#~ "
    \n" +#~ "Enviamos em anexo a Guia de Trasporte ${object.name | safe}\n" +#~ "% if object.ref:\n" +#~ " relativa à sua encomenda ${object.ref | safe}\n" +#~ "% endif\n" +#~ ".\n" +#~ "

    \n" +#~ "Obrigado\n" +#~ "% if user.signature:\n" +#~ "
    \n" +#~ " ${user.signature | safe}\n" +#~ "% endif\n" +#~ "

    \n" +#~ " " +#~ msgstr "" +#~ "

    \n" +#~ "Olá,\n" +#~ "
    \n" +#~ "Enviamos em anexo a Guia de Transporte ${object.name | safe}\n" +#~ "% if object.ref:\n" +#~ " relativa à sua encomenda ${object.ref | safe}\n" +#~ "% endif\n" +#~ ".\n" +#~ "

    \n" +#~ "Obrigado\n" +#~ "% if user.signature:\n" +#~ "
    \n" +#~ " ${user.signature | safe}\n" +#~ "% endif\n" +#~ "

    \n" +#~ " " + +#~ msgid "Display Name" +#~ msgstr "Nome a Exibir" + +#~ msgid "Entrega ${object.name | safe}" +#~ msgstr "Entrega ${object.name | safe}" + +#~ msgid "ID" +#~ msgstr "ID" + +#~ msgid "Last Modified on" +#~ msgstr "Última Modificação em" + +#~ msgid "View Document" +#~ msgstr "Ver Documento" + +#~ msgid "Delivery %s is not registerd in InvoiceXpress yet." +#~ msgstr "Entrega %s ainda não está registada no InvoiceXpress." diff --git a/l10n_pt_stock_invoicexpress/models/__init__.py b/l10n_pt_stock_invoicexpress/models/__init__.py new file mode 100644 index 00000000..52e25fe2 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/__init__.py @@ -0,0 +1,8 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import stock_picking +from . import stock_picking_type +from . import res_config_settings +from . import res_company +from . import res_partner diff --git a/l10n_pt_stock_invoicexpress/models/res_company.py b/l10n_pt_stock_invoicexpress/models/res_company.py new file mode 100644 index 00000000..c186432e --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/res_company.py @@ -0,0 +1,54 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class Company(models.Model): + _inherit = "res.company" + + invoicexpress_delivery_template_id = fields.Many2one( + "mail.template", + "InvoiceXpress Delivery Email", + domain="[('model', '=', 'stock.picking')]", + default=lambda self: self.env.ref( + "l10n_pt_stock_invoicexpress.email_template_delivey", False + ), + help="Used to generate the To, Cc, Subject and Body" + " for the InvoiceXpress email sending the Delivery document.", + ) + + def _update_default_doctype(self): + """ + When enabling InvoiceXpress, apply defaults + to existing delivery/outgoing operation Types + that don't have a doc type set yet. + """ + for company in self.filtered("has_invoicexpress"): + pick_types_todo = self.env["stock.picking.type"].search( + [ + ("company_id", "=", company.id), + ("invoicexpress_doc_type", "=", False), + ] + ) + for picktype in pick_types_todo: + picktype.invoicexpress_doc_type = ( + picktype._default_invoicexpress_doc_type() + ) + + @api.model + def create_multi(self, vals_list): + res = super().create_multi(vals_list) + for vals in vals_list: + if [ + x in vals + for x in ("invoicexpress_account_name", "invoicexpress_api_key") + ]: + res._update_default_doctype() + return res + + def write(self, vals): + res = super().write(vals) + if [x in vals for x in ("invoicexpress_account_name", "invoicexpress_api_key")]: + self._update_default_doctype() + return res diff --git a/l10n_pt_stock_invoicexpress/models/res_config_settings.py b/l10n_pt_stock_invoicexpress/models/res_config_settings.py new file mode 100644 index 00000000..f14dc6b8 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + invoicexpress_delivery_template_id = fields.Many2one( + related="company_id.invoicexpress_delivery_template_id", readonly=False + ) diff --git a/l10n_pt_stock_invoicexpress/models/res_partner.py b/l10n_pt_stock_invoicexpress/models/res_partner.py new file mode 100644 index 00000000..af5784ad --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/res_partner.py @@ -0,0 +1,18 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + def _prepare_invoicexpress_shipping_vals(self): + self.ensure_one() + return { + "email": self.email or "", + "detail": ", ".join(filter(None, [self.street, self.street2])) or "", + "city": self.city or "", + "postal_code": self.zip or "", + "country": self.country_id.invoicexpress_name or "", + } diff --git a/l10n_pt_stock_invoicexpress/models/stock_picking.py b/l10n_pt_stock_invoicexpress/models/stock_picking.py new file mode 100644 index 00000000..a062ca7d --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/stock_picking.py @@ -0,0 +1,294 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from markupsafe import Markup + +from odoo import _, api, exceptions, fields, models +from odoo.tools import format_datetime + +_logger = logging.getLogger(__name__) + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + @api.depends("picking_type_id", "company_id.has_invoicexpress") + def _compute_can_invoicexpress(self): + for delivery in self: + delivery.can_invoicexpress = ( + delivery.company_id.has_invoicexpress + and delivery.invoicexpress_doc_type + ) + + @api.depends("can_invoicexpress", "company_id.invoicexpress_delivery_template_id") + def _compute_can_invoicexpress_email(self): + for delivery in self: + delivery.can_invoicexpress_email = ( + delivery.can_invoicexpress + and delivery.company_id.invoicexpress_delivery_template_id + ) + + @api.depends("can_invoicexpress_email", "invoicexpress_doc_type") + def _compute_invoicexpress_send_email(self): + for delivery in self: + delivery.invoicexpress_send_email = ( + delivery.can_invoicexpress_email + and delivery.invoicexpress_doc_type != "devolution" + ) + + @api.depends("scheduled_date") + def _compute_l10npt_transport_doc_due_date(self): + for doc in self: + doc.l10npt_transport_doc_due_date = fields.Date.add( + doc.scheduled_date, days=7 + ) + + @api.depends("picking_type_id") + def _compute_invoicexpress_doc_type(self): + """ + Return the doc type, read from the Operation Type. + Also detect devolutions, and then use the appropriate type instead. + """ + for pick in self: + pick_doc_type = pick.picking_type_id.invoicexpress_doc_type + country = pick.partner_id.country_id + is_PT = not country or country.code == "PT" + # TODO: Automatic support for devolutions + # Disabled for now, should be used for supplier devolutions only? + # return_orig_moves = pick.move_ids_without_package.origin_returned_move_id + # if return_orig_moves.mapped("picking_id.invoicexpress_id"): + # pick.invoicexpress_doc_type = "devolution" + if pick_doc_type and pick_doc_type != "none" and is_PT: + pick.invoicexpress_doc_type = pick_doc_type + + license_plate = fields.Char() + invoicexpress_id = fields.Char("InvoiceXpress ID", copy=False, readonly=True) + invoicexpress_number = fields.Char( + "InvoiceXpress Number", copy=False, readonly=True + ) + invoicexpress_permalink = fields.Char( + "InvoiceXpress Doc Link", copy=False, readonly=True + ) + l10npt_transport_doc_due_date = fields.Date( + "Transport Doc. Validity", + compute="_compute_l10npt_transport_doc_due_date", + store=True, + readonly=False, + ) + can_invoicexpress = fields.Boolean(compute="_compute_can_invoicexpress") + can_invoicexpress_email = fields.Boolean(compute="_compute_can_invoicexpress_email") + invoicexpress_send_email = fields.Boolean( + "InvX Send Email", + compute="_compute_invoicexpress_send_email", + store=True, + readonly=False, + copy=False, + help="If unchecked, both the InvoiceXpress email" + " and the Delivery email won't be sent.", + ) + invoicexpress_doc_type = fields.Selection( + [ + ("transport", "Guia de Transporte / Transport"), + ("shipping", "Guia de Remessa / Shipping"), + ("devolution", "Devolução / Return"), + ], + string="InvX Doc Type", + compute="_compute_invoicexpress_doc_type", + store=True, + readonly=False, + copy=False, + help="Select the type of legal delivery document" + " to be created by InvoiceXpress.", + ) + + def _send_confirmation_email(self): + # Only send Delivery emails if the InvoiceXpress checkbox is selected + to_send = self.filtered("invoicexpress_send_email") + return super(StockPicking, to_send)._send_confirmation_email() + + @api.model + def _get_invoicexpress_prefix(self, doctype): + return { + "transport": "GT", + "shipping": "GR", + "devolution": "GD", + }.get(doctype) + + def _prepare_invoicexpress_lines(self): + lines = self.move_ids_without_package.filtered("quantity") + # Ensure Taxes are created on InvoiceXpress + lines.mapped("sale_line_id.tax_id").action_invoicexpress_tax_create() + items = [] + for line in lines: + tax = line.sale_line_id.tax_id[:1] + # tax_detail = {"name": tax.name, "value": tax.amount} if tax else {} + tax_detail = {"name": tax.name} if tax else {} + items.append( + { + "name": line.product_id.default_code + or line.product_id.display_name, + "description": line.product_id.name or "", # line.name for SO desc + # TODO: add an option to allow having the prices set? + "unit_price": 0.0, # line.sale_line_id.price_unit, + "quantity": line.quantity, + "discount": line.sale_line_id.discount, + "tax": tax_detail, + } + ) + return items + + def _prepare_invoicexpress_vals(self): + self.ensure_one() + shipping_date = fields.Datetime.add(fields.Datetime.now(), minutes=5) + if shipping_date < fields.Datetime.now(): + raise exceptions.ValidationError( + _("Scheduled Date should be bigger then current datetime!") + ) + customer = self.partner_id.commercial_partner_id + customer_vals = customer.set_invoicexpress_contact() + if self.location_id.usage == "internal": # Outgoing + address_from = self.picking_type_id.warehouse_id.partner_id + address_to = self.partner_id + elif self.location_dest_id.usage == "internal": # Incoming => Return + address_from = self.partner_id + address_to = self.picking_type_id.warehouse_id.partner_id + addr_from_vals = address_from._prepare_invoicexpress_shipping_vals() + addr_to_vals = address_to._prepare_invoicexpress_shipping_vals() + + doctype = self.invoicexpress_doc_type + item_vals = self._prepare_invoicexpress_lines() + return { + doctype: { + "date": shipping_date.strftime("%d/%m/%Y"), + "due_date": ( + self.l10npt_transport_doc_due_date or shipping_date + ).strftime("%d/%m/%Y"), + "loaded_at": format_datetime( + self.env, shipping_date, dt_format="dd/MM/yyyy HH:mm:ss" + ), + "license_plate": self.license_plate or "", + "address_from": addr_from_vals, + "address_to": addr_to_vals, + "reference": self.origin or "", + "client": customer_vals, + "items": item_vals, + } + } + + def _update_invoicexpress_status(self): + inv_xpress_link_name = _("View Document") + inv_xpress_link = _( + "{name}" + ).format(link=self.invoicexpress_permalink, name=inv_xpress_link_name) + msg = _( + "InvoiceXpress record has been created for this delivery order:
      " + "
    • Number: {inv_xpress_num}
    • " + "
    • {inv_xpress_link}
    " + ).format( + inv_xpress_num=self.invoicexpress_number, inv_xpress_link=inv_xpress_link + ) + self.message_post(body=Markup(msg)) + + def action_create_invoicexpress_delivery(self): + """ + Generate legal "Guia de Transporte", for customer deliveries + or transfers between warehouses. + We allow generating more than one for the same Odoo document. + """ + InvoiceXpress = self.env["account.invoicexpress"] + for delivery in self.filtered("can_invoicexpress"): + payload = delivery._prepare_invoicexpress_vals() + doctype = delivery.invoicexpress_doc_type + response = InvoiceXpress.call( + delivery.company_id, f"{doctype}s.json", "POST", payload=payload + ) + values = response.json().get(doctype) + if not values: + raise exceptions.UserError( + _("Something went wrong: the InvoiceXpress response looks empty.") + ) + delivery.invoicexpress_id = values.get("id") + delivery.invoicexpress_permalink = values.get("permalink") + response1 = InvoiceXpress.call( + delivery.company_id, + "{}s/{}/change-state.json".format(doctype, values["id"]), + "PUT", + payload={doctype: {"state": "finalized"}}, + ) + values1 = response1.json().get(doctype) + prefix = self._get_invoicexpress_prefix(doctype) + seqnum = values1["inverted_sequence_number"] + invx_number = f"{prefix} {seqnum}" + delivery.invoicexpress_number = invx_number + delivery._update_invoicexpress_status() + + def _prepare_invoicexpress_email_vals(self, ignore_no_config=False): + self.ensure_one() + template_id = self.company_id.invoicexpress_delivery_template_id + values = template_id._generate_template( + [self.id], ["subject", "body_html", "email_to", "email_cc"] + )[self.id] + if not template_id and not ignore_no_config: + raise exceptions.UserError( + _( + "Please configure the InvoiceXpress Delivery email template" + " at Settings > General Setting, InvoiceXpress section" + ) + ) + if not values.get("email_to") and not ignore_no_config: + raise exceptions.UserError( + _("No address to send delivery document email to.") + ) + email_data = None + if template_id and values["email_to"]: + email_data = { + "message": { + "client": {"email": values["email_to"], "save": "0"}, + "cc": values["email_cc"], + "subject": values["subject"], + "body": values["body_html"], + } + } + return email_data + + def action_send_invoicexpress_delivery(self, ignore_no_config=False): + InvoiceXpress = self.env["account.invoicexpress"] + for delivery in self.filtered("invoicexpress_send_email"): + if not delivery.invoicexpress_id: + raise exceptions.UserError( + _("Delivery %s is not registered in InvoiceXpress yet."), + delivery.name, + ) + doctype = delivery.invoicexpress_doc_type + endpoint = f"{doctype}s/{delivery.invoicexpress_id}/email-document.json" + payload = delivery._prepare_invoicexpress_email_vals(ignore_no_config) + if payload: + InvoiceXpress.call( + delivery.company_id, endpoint, "PUT", payload=payload + ) + msg = _( + "Email sent by InvoiceXpress:
    • To: " + "{email}/li>
    • Cc: {cc}
    " + ).format( + email=payload["message"]["client"]["email"], + cc=payload["message"]["cc"] or _("None"), + ) + delivery.message_post(Markup(body=msg)) + + def button_validate(self): + """ + Automatically generate legal transport docs for PT customers + """ + res = super().button_validate() + if res is True: # do not enter if the result is a dict, only if it is True + missing_country = self.filtered(lambda x: not x.partner_id.country_id) + if missing_country: + raise exceptions.UserError(_("Please set the country of the partner.")) + to_invoicexpress = self.filtered( + lambda x: x.partner_id.country_id.code == "PT" + ) + to_invoicexpress.action_create_invoicexpress_delivery() + to_invoicexpress.action_send_invoicexpress_delivery(ignore_no_config=True) + return res diff --git a/l10n_pt_stock_invoicexpress/models/stock_picking_type.py b/l10n_pt_stock_invoicexpress/models/stock_picking_type.py new file mode 100644 index 00000000..30ae9adb --- /dev/null +++ b/l10n_pt_stock_invoicexpress/models/stock_picking_type.py @@ -0,0 +1,26 @@ +# Copyright (C) 2021 Open Source Integrators +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockPickingType(models.Model): + _inherit = "stock.picking.type" + + def _default_invoicexpress_doc_type(self): + return ( + "transport" + if self.company_id.has_invoicexpress and self.code == "outgoing" + else "none" + ) + + invoicexpress_doc_type = fields.Selection( + [ + ("transport", "Guia de Transporte / Transport"), + ("shipping", "Guia de Remessa / Shipping"), + ("none", "No InvoiceXpress document"), + ], + default="transport", + help="Select the type of legal delivery document" + " to be created by InvoiceXpress. If unset", + ) diff --git a/l10n_pt_stock_invoicexpress/pyproject.toml b/l10n_pt_stock_invoicexpress/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/l10n_pt_stock_invoicexpress/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_pt_stock_invoicexpress/readme/CONFIGURE.md b/l10n_pt_stock_invoicexpress/readme/CONFIGURE.md new file mode 100644 index 00000000..970e0c31 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/readme/CONFIGURE.md @@ -0,0 +1,8 @@ +This feature depends on the InvoiceXpress Incoice generation feature. +See + +for configuration details. + +An additional "InvoiceXpress Delivery Email" option is available, to +configure the email template preparing the details for the emailto be +sent by the InvoiceXpress service. diff --git a/l10n_pt_stock_invoicexpress/readme/CONTRIBUTORS.md b/l10n_pt_stock_invoicexpress/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..69bb8f2b --- /dev/null +++ b/l10n_pt_stock_invoicexpress/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Daniel Reis \<\>, [Open Source + Integrators](https://www.opensourceintegrators.eu) diff --git a/l10n_pt_stock_invoicexpress/readme/DESCRIPTION.md b/l10n_pt_stock_invoicexpress/readme/DESCRIPTION.md new file mode 100644 index 00000000..e5202e49 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/readme/DESCRIPTION.md @@ -0,0 +1,22 @@ +Generate Portuguese tax authority legal transport documents ("Guias de +Transporte") using InvoiceXpress. + +The InvoiceXpress document is automatically generated when an ougoing +Transfer or Delivery Order is validated. + +This feature depends on the InvoiceXpress Invoice generation feature. +See + +for more details. + +**UPDATE November/2021:** + +Deliveries: + +- Added support to the different document types, Transport ("Guia de + Transporte") and Shipment ("Guia de Remessa"). The default document + type is set on the Operation Type. +- Changed the line description to be the Product name, instead of the + original Sales Order description, so that it uses the most up to date + product description. +- Added tax details to the document lines. diff --git a/l10n_pt_stock_invoicexpress/readme/USAGE.md b/l10n_pt_stock_invoicexpress/readme/USAGE.md new file mode 100644 index 00000000..2e1bf65b --- /dev/null +++ b/l10n_pt_stock_invoicexpress/readme/USAGE.md @@ -0,0 +1,7 @@ +On Delivery Orders and Internal Transfer documents, validating the +transfer automatically generates an InvoiceXpress "Guia de Transporte" +for the Done quantities. + +The "Email InvoiceXpress" requests the InvoiceXpress service to send an +email with a copy of the legal document. The content of this email can +be configure in Odoo. diff --git a/l10n_pt_stock_invoicexpress/static/description/cover.png b/l10n_pt_stock_invoicexpress/static/description/cover.png new file mode 100644 index 00000000..ae9b85ea Binary files /dev/null and b/l10n_pt_stock_invoicexpress/static/description/cover.png differ diff --git a/l10n_pt_stock_invoicexpress/static/description/icon.png b/l10n_pt_stock_invoicexpress/static/description/icon.png new file mode 100644 index 00000000..34c78e0a Binary files /dev/null and b/l10n_pt_stock_invoicexpress/static/description/icon.png differ diff --git a/l10n_pt_stock_invoicexpress/static/description/index.html b/l10n_pt_stock_invoicexpress/static/description/index.html new file mode 100644 index 00000000..41bde2f4 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/static/description/index.html @@ -0,0 +1,465 @@ + + + + + +Portugal InvoiceXpress Legal Transport Documents + + + + + + diff --git a/l10n_pt_stock_invoicexpress/tests/__init__.py b/l10n_pt_stock_invoicexpress/tests/__init__.py new file mode 100644 index 00000000..eb140e4f --- /dev/null +++ b/l10n_pt_stock_invoicexpress/tests/__init__.py @@ -0,0 +1 @@ +from . import test_invoicexpress diff --git a/l10n_pt_stock_invoicexpress/tests/test_invoicexpress.py b/l10n_pt_stock_invoicexpress/tests/test_invoicexpress.py new file mode 100644 index 00000000..af8dec02 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/tests/test_invoicexpress.py @@ -0,0 +1,68 @@ +from datetime import timedelta +from unittest.mock import Mock, patch + +import requests + +from odoo import fields +from odoo.tests import Form, common + +from odoo.addons.l10n_pt_account_invoicexpress.tests.test_invoicexpress import ( + TestInvoiceXpress, +) + + +def mock_response(json, status_code=200): + mock_response = Mock() + mock_response.json.return_value = json + mock_response.text = str(json) + mock_response.status_code = status_code + return mock_response + + +@common.tagged("-at_install", "post_install") +class TestInvoiceXpressStock(TestInvoiceXpress): + def setUp(self): + super().setUp() + self.StockPicking = self.env["stock.picking"] + + @patch.object(requests, "request") + def test_102_create_invoicexpress_picking(self, mock_request): + mock_request.return_value = mock_response( + {"transport": {"id": 12345678, "inverted_sequence_number": "MYSEQ/123"}} + ) + stock_location = self.env.ref("stock.stock_location_stock") + self.warehouse = self.env["stock.warehouse"].search( + [("lot_stock_id", "=", stock_location.id)], limit=1 + ) + # Setup defaults for Operation Types + self.warehouse.company_id._update_default_doctype() + # Create a new picking with one product + picking_form = Form(self.StockPicking) + picking_form.partner_id = self.partnerA + picking_form.picking_type_id = self.warehouse.out_type_id + scheduled_date = fields.Datetime.now() + timedelta(days=1) + picking_form.scheduled_date = scheduled_date + picking_form.origin = "Picking-Test" + with picking_form.move_ids_without_package.new() as move_line: + move_line.product_id = self.productA + move_line.product_uom_qty = 2 + self.delivery_order = picking_form.save() + self.assertTrue(self.delivery_order.scheduled_date) + + self.assertEqual( + self.delivery_order.partner_id.country_id, + self.pt_country, + "Country is Portugal", + ) + + self.delivery_order.action_confirm() + self.delivery_order.action_assign() + self.delivery_order.move_line_ids.filtered( + lambda ml: ml.product_id == self.productA + ).quantity = 2.0 + self.assertEqual( + self.delivery_order.state, "assigned", "Delivery Order assigned" + ) + + self.delivery_order.button_validate() + self.assertTrue(self.delivery_order.invoicexpress_id) diff --git a/l10n_pt_stock_invoicexpress/views/res_company_view.xml b/l10n_pt_stock_invoicexpress/views/res_company_view.xml new file mode 100644 index 00000000..a7430f45 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/views/res_company_view.xml @@ -0,0 +1,14 @@ + + + + InvoiceXpress Stock Company Configuration + res.company + form + + + + + + + + diff --git a/l10n_pt_stock_invoicexpress/views/res_config_settings.xml b/l10n_pt_stock_invoicexpress/views/res_config_settings.xml new file mode 100644 index 00000000..c028a536 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/views/res_config_settings.xml @@ -0,0 +1,22 @@ + + + res.config.settings.view.form.invoicexpress + + res.config.settings + + +
    +
    +
    +
    +
    +
    +
    diff --git a/l10n_pt_stock_invoicexpress/views/stock_picking_type_view.xml b/l10n_pt_stock_invoicexpress/views/stock_picking_type_view.xml new file mode 100644 index 00000000..6cdd206e --- /dev/null +++ b/l10n_pt_stock_invoicexpress/views/stock_picking_type_view.xml @@ -0,0 +1,12 @@ + + + InvoiceXpress: add fields + stock.picking.type + + + + + + + + diff --git a/l10n_pt_stock_invoicexpress/views/stock_picking_view.xml b/l10n_pt_stock_invoicexpress/views/stock_picking_view.xml new file mode 100644 index 00000000..23e38807 --- /dev/null +++ b/l10n_pt_stock_invoicexpress/views/stock_picking_view.xml @@ -0,0 +1,52 @@ + + + + + stock.picking.form + stock.picking + + + + + + + + + + + + + + + + + + + + +