diff --git a/l10n_es_vat_prorate/README.rst b/l10n_es_vat_prorate/README.rst new file mode 100644 index 00000000000..b7dff73aa7c --- /dev/null +++ b/l10n_es_vat_prorate/README.rst @@ -0,0 +1,134 @@ +=============== +Prorrata de IVA +=============== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c5c6996e6f770ca45a4ced8f953744d9b5ba985f4606fa5cb4c8a2979387ee16 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--spain-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-spain/tree/18.0/l10n_es_vat_prorate + :alt: OCA/l10n-spain +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-spain-18-0/l10n-spain-18-0-l10n_es_vat_prorate + :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-spain&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +El módulo nos divide los IVA según la prorrata de la compañía. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Para configurar la prorrata general: + +- Acceda a la compañía y marque que aplica prorrata. +- Defina el porcentaje de prorrata (se aplica por fechas). + +Los impuestos de las facturas de proveedor se dividirán según la +prorrata activa. + +Para configurar la prorrata especial: + +- Acceda a la compañía y marque que aplica prorrata de IVA especial. +- Marque el valor por defecto de la prorrata especial. + +En las líneas de factura aparecerá un nuevo campo editable, con el valor +por defecto configurado, que permitirá definir que líneas serán +prorrateadas. + +Known issues / Roadmap +====================== + +- No compatible con criterio de caja. + +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 +------- + +* Creu Blanca +* Tecnativa + +Contributors +------------ + +- Enric Tobella +- `Tecnativa `__: + + - Pedro M. Baeza + - Carolina Fernandez + +- `Sygel `__: + + - Harald Panten + - Manuel Regidor + - Alberto Martínez + - Anxo82 + - ValentinVinagre + +- `Moduon `__: + + - Rafael Blasco + - Andrii Kompaniiets + - Emilio Pascual + +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-rafaelbn| image:: https://github.com/rafaelbn.png?size=40px + :target: https://github.com/rafaelbn + :alt: rafaelbn +.. |maintainer-Andrii9090| image:: https://github.com/Andrii9090.png?size=40px + :target: https://github.com/Andrii9090 + :alt: Andrii9090 +.. |maintainer-EmilioPascual| image:: https://github.com/EmilioPascual.png?size=40px + :target: https://github.com/EmilioPascual + :alt: EmilioPascual + +Current `maintainers `__: + +|maintainer-rafaelbn| |maintainer-Andrii9090| |maintainer-EmilioPascual| + +This module is part of the `OCA/l10n-spain `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_es_vat_prorate/__init__.py b/l10n_es_vat_prorate/__init__.py new file mode 100644 index 00000000000..6d58305f5dd --- /dev/null +++ b/l10n_es_vat_prorate/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import pre_init_hook diff --git a/l10n_es_vat_prorate/__manifest__.py b/l10n_es_vat_prorate/__manifest__.py new file mode 100644 index 00000000000..eaefaba70b2 --- /dev/null +++ b/l10n_es_vat_prorate/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2022 Creu Blanca +# Copyright 2023 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Prorrata de IVA", + "summary": "Prorrata de IVA para la localización española", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Creu Blanca, Tecnativa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-spain", + "pre_init_hook": "pre_init_hook", + "depends": ["l10n_es_aeat"], + "maintainers": ["rafaelbn", "Andrii9090", "EmilioPascual"], + "data": [ + "security/ir.model.access.csv", + "views/account_move_views.xml", + "views/account_tax_views.xml", + "views/res_company_prorate_views.xml", + "views/res_company_views.xml", + ], +} diff --git a/l10n_es_vat_prorate/hooks.py b/l10n_es_vat_prorate/hooks.py new file mode 100644 index 00000000000..e6444ee3388 --- /dev/null +++ b/l10n_es_vat_prorate/hooks.py @@ -0,0 +1,27 @@ +# Copyright 2024 Alberto Martínez +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import logging + + +def pre_init_hook(env): + """Create computed columns if not exists when the module is installed""" + logger = logging.getLogger(__name__) + logger.info("Prepopulating stored related fields") + env.cr.execute( + """ + ALTER TABLE account_move + ADD COLUMN IF NOT EXISTS prorate_id integer; + """ + ) + env.cr.execute( + """ + ALTER TABLE account_move + ADD COLUMN IF NOT EXISTS with_special_vat_prorate BOOLEAN; + """ + ) + env.cr.execute( + """ + ALTER TABLE account_move_line + ADD COLUMN IF NOT EXISTS with_vat_prorate BOOLEAN; + """ + ) diff --git a/l10n_es_vat_prorate/i18n/ca.po b/l10n_es_vat_prorate/i18n/ca.po new file mode 100644 index 00000000000..1193b4a5528 --- /dev/null +++ b/l10n_es_vat_prorate/i18n/ca.po @@ -0,0 +1,215 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_es_vat_prorate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2025-07-16 18:23+0200\n" +"Last-Translator: Pedro M. Baeza \n" +"Language-Team: none\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Poedit 3.6\n" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Accounts to apply the recompute" +msgstr "Comptes per aplicar el recalcul" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company +msgid "Companies" +msgstr "Companyies" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__company_id +msgid "Company" +msgstr "Empresa" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +msgid "Company with VAT prorate" +msgstr "Empresa amb prorrateig de l'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_uid +msgid "Created by" +msgstr "Creat per" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_date +msgid "Created on" +msgstr "Creat el" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__date +msgid "Date" +msgstr "Data" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__display_name +msgid "Display Name" +msgstr "Nom de visualització" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__general +msgid "General" +msgstr "General" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__id +msgid "ID" +msgstr "Identificació" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "" +"If the Special VAT Prorate is enabled, this value indicates whether all the " +"invoice lines will be prorated by default" +msgstr "" +"Si el prorrateig especial d'IVA està habilitat, aquest valor indica si totes " +"les línies de factura es prorratejaran per defecte" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "" +"If the special prorate is enabled, you will be able to select which invoice " +"lines will be prorated." +msgstr "" +"Si el prorrateig especial està habilitat, podreu seleccionar quines línies " +"de factura es prorratejaran." + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "If this option is enabled, all invoice lines with VAT will be prorated" +msgstr "" +"Si aquesta opció està activada, es prorratejaran les línies de factura amb " +"IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "Is vat prorate" +msgstr "És prorrateig de tines" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move +msgid "Journal Entry" +msgstr "Entrada del diari" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move_line +msgid "Journal Item" +msgstr "Element del diari" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_uid +msgid "Last Updated by" +msgstr "Última actualització el" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_date +msgid "Last Updated on" +msgstr "Última actualització a" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__prorate_id +msgid "Prorate" +msgstr "Prorrateig" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Prorate Account" +msgstr "Compte prorratejat" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__special +msgid "Special" +msgstr "Especial" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "Special VAT Prorate Default" +msgstr "Taxa especial d'IVA per defecte" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_tax +msgid "Tax" +msgstr "Impost" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "The line is a vat prorate" +msgstr "La línia és una prorrata d'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +msgid "The line will create a vat prorate" +msgstr "La línia crearà un prorate d'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "Type" +msgstr "Tipus" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.res_company_form_view +msgid "VAT Prorate" +msgstr "Prorrateig de l'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company_vat_prorate +msgid "VAT Prorate table" +msgstr "Taula de prorrateig de l'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.constraint,message:l10n_es_vat_prorate.constraint_res_company_vat_prorate_vat_prorate_percent_amount +msgid "VAT prorate must be between 0.01 and 100!" +msgstr "El prorrateig de l'IVA ha d'estar entre 0,01 i 100!" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.account_move_form_view +msgid "VAT prorate?" +msgstr "¿Prorrateig IVA?" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__vat_prorate_ids +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__vat_prorate +msgid "Vat Prorate" +msgstr "Prorrateig de l'IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__with_special_vat_prorate +msgid "With Special Vat Prorate" +msgstr "Amb prorrateig d'IVA especial" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "With VAT Prorate" +msgstr "Amb prorrateig d'IVA" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You can't have a special VAT prorrate of 100%" +msgstr "No podeu tenir un prorrateig especial d'IVA del 100%" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You must complete VAT prorate information" +msgstr "Heu d'omplir la informació del prorrateig de l'IVA" diff --git a/l10n_es_vat_prorate/i18n/es.po b/l10n_es_vat_prorate/i18n/es.po new file mode 100644 index 00000000000..a52abf651e8 --- /dev/null +++ b/l10n_es_vat_prorate/i18n/es.po @@ -0,0 +1,215 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_es_vat_prorate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2025-07-16 18:22+0200\n" +"Last-Translator: Pedro M. Baeza \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Poedit 3.6\n" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Accounts to apply the recompute" +msgstr "Cuentas para aplicar el recálculo" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company +msgid "Companies" +msgstr "Compañías" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__company_id +msgid "Company" +msgstr "Companía" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +msgid "Company with VAT prorate" +msgstr "Compañía con prorrata del IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__date +msgid "Date" +msgstr "Fecha" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__general +msgid "General" +msgstr "General" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__id +msgid "ID" +msgstr "ID (identificación)" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "" +"If the Special VAT Prorate is enabled, this value indicates whether all the " +"invoice lines will be prorated by default" +msgstr "" +"Si la prorrata especial de IVA esta establecida, este valor indica si todas " +"las líneas de factura serán prorrateadas por defecto" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "" +"If the special prorate is enabled, you will be able to select which invoice " +"lines will be prorated." +msgstr "" +"Si la prorrata especial esta establecida, podrás seleccionar que lineas de " +"factura serán prorrateadas." + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "If this option is enabled, all invoice lines with VAT will be prorated" +msgstr "" +"Si esta opción está habilitada, las líneas de facturas con IVA se " +"prorratearán" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "Is vat prorate" +msgstr "Es una prorrata de IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move +msgid "Journal Entry" +msgstr "Entrada de diario" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move_line +msgid "Journal Item" +msgstr "Artículo Diario" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_uid +msgid "Last Updated by" +msgstr "Actualizado por Última vez por" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__prorate_id +msgid "Prorate" +msgstr "Prorrata" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Prorate Account" +msgstr "Cuenta prorrateada" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__special +msgid "Special" +msgstr "Especial" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "Special VAT Prorate Default" +msgstr "Prorrata especial de IVA por defecto" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_tax +msgid "Tax" +msgstr "Impuesto" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "The line is a vat prorate" +msgstr "La línea es una prorrata de IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +msgid "The line will create a vat prorate" +msgstr "La línea creará una prorrata de IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "Type" +msgstr "Tipo" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.res_company_form_view +msgid "VAT Prorate" +msgstr "Prorrata del IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company_vat_prorate +msgid "VAT Prorate table" +msgstr "Tabla de prorrateo del IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.constraint,message:l10n_es_vat_prorate.constraint_res_company_vat_prorate_vat_prorate_percent_amount +msgid "VAT prorate must be between 0.01 and 100!" +msgstr "¡La prorrata del IVA debe estar comprendida entre 0,01 y 100!" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.account_move_form_view +msgid "VAT prorate?" +msgstr "¿Prorrata IVA?" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__vat_prorate_ids +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__vat_prorate +msgid "Vat Prorate" +msgstr "Prorrata del IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__with_special_vat_prorate +msgid "With Special Vat Prorate" +msgstr "Con prorrata especial de IVA" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "With VAT Prorate" +msgstr "Con prorrata de IVA" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You can't have a special VAT prorrate of 100%" +msgstr "No puede tener una prorrata especial de IVA del 100%" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You must complete VAT prorate information" +msgstr "Debe cumplimentar la información sobre el prorrateo del IVA" diff --git a/l10n_es_vat_prorate/i18n/l10n_es_vat_prorate.pot b/l10n_es_vat_prorate/i18n/l10n_es_vat_prorate.pot new file mode 100644 index 00000000000..4b0aab92ade --- /dev/null +++ b/l10n_es_vat_prorate/i18n/l10n_es_vat_prorate.pot @@ -0,0 +1,205 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_es_vat_prorate +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.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_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Accounts to apply the recompute" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__company_id +msgid "Company" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +msgid "Company with VAT prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_uid +msgid "Created by" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__create_date +msgid "Created on" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__date +msgid "Date" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__general +msgid "General" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__id +msgid "ID" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "" +"If the Special VAT Prorate is enabled, this value indicates whether all the " +"invoice lines will be prorated by default" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "" +"If the special prorate is enabled, you will be able to select which invoice " +"lines will be prorated." +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_tax__company_with_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "If this option is enabled, all invoice lines with VAT will be prorated" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "Is vat prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__write_date +msgid "Last Updated on" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__prorate_id +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__prorate_id +msgid "Prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__prorate_account_ids +msgid "Prorate Account" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields.selection,name:l10n_es_vat_prorate.selection__res_company_vat_prorate__type__special +msgid "Special" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__special_vat_prorate_default +msgid "Special VAT Prorate Default" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_account_tax +msgid "Tax" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__vat_prorate +msgid "The line is a vat prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,help:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +msgid "The line will create a vat prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__type +msgid "Type" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.res_company_form_view +msgid "VAT Prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model,name:l10n_es_vat_prorate.model_res_company_vat_prorate +msgid "VAT Prorate table" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.constraint,message:l10n_es_vat_prorate.constraint_res_company_vat_prorate_vat_prorate_percent_amount +msgid "VAT prorate must be between 0.01 and 100!" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model_terms:ir.ui.view,arch_db:l10n_es_vat_prorate.account_move_form_view +msgid "VAT prorate?" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__vat_prorate_ids +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company_vat_prorate__vat_prorate +msgid "Vat Prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_bank_statement_line__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move__with_special_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_payment__with_special_vat_prorate +msgid "With Special Vat Prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_move_line__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_account_tax__with_vat_prorate +#: model:ir.model.fields,field_description:l10n_es_vat_prorate.field_res_company__with_vat_prorate +msgid "With VAT Prorate" +msgstr "" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You can't have a special VAT prorrate of 100%" +msgstr "" + +#. module: l10n_es_vat_prorate +#. odoo-python +#: code:addons/l10n_es_vat_prorate/models/res_company.py:0 +#, python-format +msgid "You must complete VAT prorate information" +msgstr "" diff --git a/l10n_es_vat_prorate/models/__init__.py b/l10n_es_vat_prorate/models/__init__.py new file mode 100644 index 00000000000..99a3f5fb2f8 --- /dev/null +++ b/l10n_es_vat_prorate/models/__init__.py @@ -0,0 +1,4 @@ +from . import res_company +from . import account_move +from . import account_tax +from . import prorate_taxes diff --git a/l10n_es_vat_prorate/models/account_move.py b/l10n_es_vat_prorate/models/account_move.py new file mode 100644 index 00000000000..20d4ed4e7f3 --- /dev/null +++ b/l10n_es_vat_prorate/models/account_move.py @@ -0,0 +1,291 @@ +# Copyright 2021 Creu Blanca +# Copyright 2023 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later(http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models +from odoo.tools import float_round + + +class AccountMove(models.Model): + _inherit = "account.move" + + prorate_id = fields.Many2one( + "res.company.vat.prorate", + compute="_compute_prorate_id", + store=True, + ) + with_special_vat_prorate = fields.Boolean( + compute="_compute_prorate_id", + store=True, + ) + + @api.depends("company_id", "date", "invoice_date") + def _compute_prorate_id(self): + self.prorate_id = False + self.with_special_vat_prorate = False + for rec in self: + if rec.company_id.with_vat_prorate: + prorate_date = rec.date or rec.invoice_date or fields.Date.today() + if rec.move_type == "in_refund": + prorate_date = ( + rec.reversed_entry_id.date or rec.reversed_entry_id.invoice_date + if rec.reversed_entry_id + else prorate_date + ) + rec.prorate_id = rec.company_id.get_prorate(prorate_date) + rec.with_special_vat_prorate = rec.prorate_id.type == "special" + + def button_draft(self): + res = super().button_draft() + for move in self: + move._apply_vat_prorate() + return res + + def _calculate_vat_prorate(self, invoice_lines): + """This method calculates tax_total and prorate_total for each line of + the invoice that has taxes with with_vat_prorate set to True. + In the case of special prorate, count all taxes + with with_vat_prorate True and False from all invoice lines + """ + taxes_with_prorate = {} + prorate_vals = {} + for invoice_line in invoice_lines: + prorate = invoice_line.move_id.prorate_id.vat_prorate / 100.0 + prec = invoice_line.move_id.currency_id.rounding + account_id = invoice_line.account_id.id + analytic_distribution = invoice_line.analytic_distribution + # It's need to group by tax_id, analytic_distribution and account_id + uniq_line_key = f"{analytic_distribution}|{account_id}" + for tax in invoice_line.tax_ids.filtered(lambda t: t.with_vat_prorate): + tax_total = float_round( + invoice_line.balance * tax.amount / 100, precision_rounding=prec + ) + prorate_total = float_round( + tax_total * prorate, precision_rounding=prec + ) + if tax.id in taxes_with_prorate: + taxes_with_prorate[tax.id]["tax_total"] += tax_total + else: + taxes_with_prorate[tax.id] = { + "tax_total": tax_total, + } + if tax.id in prorate_vals: + if uniq_line_key in prorate_vals[tax.id]: + prorate_vals[tax.id][uniq_line_key]["tax_total"] += tax_total + prorate_vals[tax.id][uniq_line_key]["prorate_total"] += ( + prorate_total + ) + else: + prorate_vals[tax.id].update( + { + uniq_line_key: { + "tax_total": tax_total, + "prorate_total": prorate_total, + "account_id": account_id, + "analytic_distribution": analytic_distribution, + } + } + ) + else: + prorate_vals.update( + { + tax.id: { + uniq_line_key: { + "tax_total": tax_total, + "prorate_total": prorate_total, + "account_id": account_id, + "analytic_distribution": analytic_distribution, + } + } + } + ) + if self.with_special_vat_prorate: + taxes_with_prorate = self._calculate_all_tax_with_prorate( + taxes_with_prorate + ) + return (taxes_with_prorate, prorate_vals) + + def _calculate_all_tax_with_prorate(self, taxes_with_prorate): + """ + In the case of special prorate, count all taxes on invoice lines + with the with_vat_prorate False. + It's need to recover the total tax and apply the prorate correctly. + """ + self.ensure_one() + for invoice_line in self.invoice_line_ids.filtered_domain( + [("with_vat_prorate", "=", False)] + ): + prec = invoice_line.move_id.currency_id.rounding + for tax in invoice_line.tax_ids.filtered(lambda t: t.with_vat_prorate): + if tax.id in taxes_with_prorate: + taxes_with_prorate[tax.id]["tax_total"] += float_round( + invoice_line.balance * tax.amount / 100, precision_rounding=prec + ) + else: + taxes_with_prorate.update( + { + tax.id: { + "tax_total": float_round( + invoice_line.balance * tax.amount / 100, + precision_rounding=prec, + ) + } + } + ) + return taxes_with_prorate + + def _get_lines_with_tax_prorate(self): + return self.line_ids.filtered_domain( + [ + ("vat_prorate", "=", False), + ("tax_line_id.with_vat_prorate", "=", True), + ] + ) + + def _get_invoice_line_with_prorate(self): + self.ensure_one() + return self.invoice_line_ids.filtered_domain([("with_vat_prorate", "=", True)]) + + def _apply_vat_prorate(self): + """Recalculate move.line_ids by applying the prorate. + If a move line with with_tax_prorate=True already has a prorate_line, + we need to recalculate the prorate and tax. + If a move line with with_tax_prorate=True does not have a + prorate_line, we need to use the copy method to create a new line + with the prorate applied and recalculate prorate balance and tax balance + We group the prorate lines by Tax->Account->Analytic Distribution. + """ + invoice_lines_with_prorate = self._get_invoice_line_with_prorate() + taxes_with_prorate, prorate_vals = self._calculate_vat_prorate( + invoice_lines_with_prorate + ) + line_to_update = {"line_ids": []} + tax_lines = self._get_lines_with_tax_prorate() + prorate_lines = tax_lines.prorate_line_ids.filtered_domain( + [("vat_prorate", "=", True)] + ) + prorate_lines.with_context( + dynamic_unlink=True + ).unlink() # Remove all prorate lines + for tax_id, prorate_taxes in prorate_vals.items(): + prorate_amount = 0 + tax_line = tax_lines.filtered_domain( + [("tax_line_id", "=", tax_id), ("account_type", "=", "asset_current")] + ) + prorate_line_ids = [] + for prorate_data in prorate_taxes.values(): + prorate_line = tax_line.copy() + tax_total = prorate_data.get("tax_total", 0) + prorate_total = prorate_data.get("prorate_total", 0) + prorate_amount += tax_total - prorate_total + prorate_line_ids.append(prorate_line.id) + line_to_update["line_ids"].append( + ( + 1, + prorate_line.id, + { + "balance": tax_total - prorate_total, + "account_id": prorate_data["account_id"], + "analytic_distribution": prorate_data[ + "analytic_distribution" + ], + "vat_prorate": True, + }, + ) + ) + tax_val = taxes_with_prorate.pop(tax_id) + line_to_update["line_ids"].append( + [ + 1, + tax_line.id, + { + "balance": tax_val["tax_total"] - prorate_amount, + "prorate_line_ids": [fields.Command.set(prorate_line_ids)], + }, + ] + ) + for tax_id, tax_val in taxes_with_prorate.items(): + tax_line = tax_lines.filtered_domain([("tax_line_id", "=", tax_id)]) + line_to_update["line_ids"].append( + [ + 1, + tax_line.id, + { + "balance": tax_val["tax_total"], + }, + ] + ) + self.with_context(skip_vat_prorate=True).write(line_to_update) + + @api.model_create_multi + def create(self, vals_list): + moves = super().create(vals_list) + for move in moves: + if move.move_type in ["in_invoice", "in_refund"]: + move._apply_vat_prorate() + return moves + + def write(self, vals): + res = super().write(vals) + for move in self: + if ( + move.move_type in ["in_invoice", "in_refund"] + and not self.env.context.get("skip_vat_prorate", False) + and move.state == "draft" + and ( + "line_ds" in vals + and len(vals["line_ids"]) + or "invoice_line_ids" in vals + ) + or any( + key in vals + for key in [ + "partner_id", + "currency_id", + "invoice_currency_rate", + "prorate_id", + "date", + "invoice_date", + ] + ) + ): + move._apply_vat_prorate() + return res + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + vat_prorate = fields.Boolean( + string="Is vat prorate", + help="The line is a vat prorate", + copy=False, + ) + + with_vat_prorate = fields.Boolean( + compute="_compute_with_vat_prorate", + store=True, + readonly=False, + ) + + prorate_line_ids = fields.Many2many( + "account.move.line", + relation="account_move_line_prorate_move_line_rel", + column1="account_move_line_id", + column2="prorate_move_line_id", + ) + + @api.depends("move_id.prorate_id", "company_id") + def _compute_with_vat_prorate(self): + for rec in self: + rec.with_vat_prorate = rec.move_id.company_id.with_vat_prorate and ( + rec.move_id.prorate_id.type == "general" + or rec.move_id.prorate_id.special_vat_prorate_default + ) + + def _process_aeat_tax_fee_info(self, res, tax, sign): + result = super()._process_aeat_tax_fee_info(res, tax, sign) + if self.vat_prorate: + res[tax]["deductible_amount"] -= self.balance * sign + return result diff --git a/l10n_es_vat_prorate/models/account_tax.py b/l10n_es_vat_prorate/models/account_tax.py new file mode 100644 index 00000000000..22c063a2b35 --- /dev/null +++ b/l10n_es_vat_prorate/models/account_tax.py @@ -0,0 +1,54 @@ +# Copyright 2022 Creu Blanca +# Copyright 2023 Tecnativa - Pedro M. Baeza +# Copyright 2024 Sygel - Manuel Regidor +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models + +from .prorate_taxes import PRORATE_TAXES + + +class AccountTax(models.Model): + _inherit = "account.tax" + + with_vat_prorate = fields.Boolean( + compute="_compute_with_vat_prorate", + store=True, + readonly=False, + string="With VAT Prorate", + ) + prorate_account_ids = fields.Many2many( + "account.account", + compute="_compute_with_vat_prorate", + store=True, + readonly=True, + help="Accounts to apply the recompute", + ) + company_with_vat_prorate = fields.Boolean( + related="company_id.with_vat_prorate", + string="Company with VAT prorate", + ) + + @api.depends("company_id.with_vat_prorate") + def _compute_with_vat_prorate(self): + for tax in self: + with_vat_prorate = False + prorate_account_ids = [] + if tax.company_with_vat_prorate and tax.get_external_id().get(tax.id): + xml_id = ( + tax.get_external_id() + .get(tax.id) + .split(f"account.{tax.company_id.id}_")[-1] + ) + if xml_id in PRORATE_TAXES: + with_vat_prorate = True + if PRORATE_TAXES.get(xml_id).get("prorate_account_template_ids"): + prorate_taxes = self.company_id._get_prorate_accounts() + prorate_account_ids = [(5, 0, 0)] + for account_from_tmpl_id in prorate_taxes.get(xml_id).get( + "prorate_account_ids" + ): + prorate_account_ids.append((4, account_from_tmpl_id)) + tax.with_vat_prorate = with_vat_prorate + tax.prorate_account_ids = prorate_account_ids diff --git a/l10n_es_vat_prorate/models/prorate_taxes.py b/l10n_es_vat_prorate/models/prorate_taxes.py new file mode 100644 index 00000000000..a1d71a7d67b --- /dev/null +++ b/l10n_es_vat_prorate/models/prorate_taxes.py @@ -0,0 +1,83 @@ +PRORATE_TAXES = { + "account_tax_template_p_iva21_bc": {}, + "account_tax_template_p_iva21_sc": {}, + "account_tax_template_p_iva4_bi": {}, + "account_tax_template_p_iva4_sc": {}, + "account_tax_template_p_iva21_bi": {}, + "account_tax_template_p_iva10_bc": {}, + "account_tax_template_p_iva4_bc": {}, + "account_tax_template_p_iva10_sc": {}, + "account_tax_template_p_iva105_gan": {}, + "account_tax_template_p_iva10_bi": {}, + "account_tax_template_p_iva12_agr": {}, + "account_tax_template_p_iva4_ibc": {}, + "account_tax_template_p_iva21_ibc": {}, + "account_tax_template_p_iva21_ibi": {}, + "account_tax_template_p_iva4_ibi": {}, + "account_tax_template_p_iva10_ibc": {}, + "account_tax_template_p_iva10_ibi": {}, + "account_tax_template_p_iva21_sp_in": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva21_ic_bc": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva21_ic_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_sp_ex": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva10_sp_ex": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva21_sp_ex": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_ic_bc": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_ic_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva10_ic_bc": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva10_ic_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva5_ic_bc": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva5_ic_sc": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva5_ibc": {}, + "account_tax_template_p_iva5_isc": {}, + "account_tax_template_p_iva5_bc": {}, + "account_tax_template_p_iva5_sc": {}, + "account_tax_template_p_iva10_sp_in": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_sp_in": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_isp": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva10_isp": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva21_isp": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva4_isp_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva10_isp_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, + "account_tax_template_p_iva21_isp_bi": { + "prorate_account_template_ids": ["account_common_472"], + }, +} diff --git a/l10n_es_vat_prorate/models/res_company.py b/l10n_es_vat_prorate/models/res_company.py new file mode 100644 index 00000000000..96572ddfb12 --- /dev/null +++ b/l10n_es_vat_prorate/models/res_company.py @@ -0,0 +1,86 @@ +# Copyright 2022 Creu Blanca +# Copyright 2023 Tecnativa Carolina Fernandez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import ormcache + +from .prorate_taxes import PRORATE_TAXES + + +class ResCompany(models.Model): + _inherit = "res.company" + + with_vat_prorate = fields.Boolean( + string="With VAT Prorate", + help="If this option is enabled, all invoice lines with VAT will be prorated", + ) + vat_prorate_ids = fields.One2many( + "res.company.vat.prorate", inverse_name="company_id" + ) + + @ormcache("self") + def _get_prorate_accounts(self): + prorate_taxes_mapping = {} + for tax_tmpl in PRORATE_TAXES: + if PRORATE_TAXES.get(tax_tmpl).get("prorate_account_template_ids"): + prorate_account_ids = [] + vals = PRORATE_TAXES.get(tax_tmpl) + for account_tmpl in vals.get("prorate_account_template_ids"): + account_from_tmpl_id = self._get_account_id_from_xmlid(account_tmpl) + if account_from_tmpl_id: + prorate_account_ids.append(account_from_tmpl_id) + prorate_taxes_mapping[tax_tmpl] = { + "prorate_account_ids": prorate_account_ids, + } + return prorate_taxes_mapping + + def get_prorate(self, date): + self.ensure_one() + return self.env["res.company.vat.prorate"].search( + [("company_id", "=", self.id), ("date", "<=", date)], + order="date DESC", + limit=1, + ) + + @api.constrains("with_vat_prorate", "vat_prorate_ids") + def _check_vat_prorate_ids(self): + for rec in self: + if rec.with_vat_prorate and not rec.vat_prorate_ids: + raise ValidationError( + self.env._("You must complete VAT prorate information") + ) + + +class ResCompanyVatProrate(models.Model): + _name = "res.company.vat.prorate" + _description = "VAT Prorate table" + _rec_name = "date" + _order = "date DESC" + + company_id = fields.Many2one("res.company", required=True) + date = fields.Date(required=True, default=fields.Date.today()) + type = fields.Selection( + selection=[("general", "General"), ("special", "Special")], + required=True, + default="general", + help="If the general prorate is enabled, all invoice lines will be " + "prorated. If the special prorate is enabled, you will be able " + "to select which invoice lines will be prorated.", + ) + special_vat_prorate_default = fields.Boolean( + string="Special VAT Prorate Default", + help="If the Special VAT Prorate is enabled, this value indicates " + "whether all the invoice lines will be prorated by default. On the " + "other hand, all the invoice lines will be not prorated by default.", + ) + vat_prorate = fields.Float() + + _sql_constraints = [ + ( + "vat_prorate_percent_amount", + "CHECK (vat_prorate > 0 and vat_prorate < 100)", + "VAT prorate must be between 0.01 and 99.9!", + ), + ] diff --git a/l10n_es_vat_prorate/pyproject.toml b/l10n_es_vat_prorate/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/l10n_es_vat_prorate/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_es_vat_prorate/readme/CONFIGURE.md b/l10n_es_vat_prorate/readme/CONFIGURE.md new file mode 100644 index 00000000000..f12af50dbb5 --- /dev/null +++ b/l10n_es_vat_prorate/readme/CONFIGURE.md @@ -0,0 +1,16 @@ +Para configurar la prorrata general: + +- Acceda a la compañía y marque que aplica prorrata. +- Defina el porcentaje de prorrata (se aplica por fechas). + +Los impuestos de las facturas de proveedor se dividirán según la +prorrata activa. + + +Para configurar la prorrata especial: + +- Acceda a la compañía y marque que aplica prorrata de IVA especial. +- Marque el valor por defecto de la prorrata especial. + +En las líneas de factura aparecerá un nuevo campo editable, con el valor por defecto configurado, +que permitirá definir que líneas serán prorrateadas. diff --git a/l10n_es_vat_prorate/readme/CONTRIBUTORS.md b/l10n_es_vat_prorate/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..16034b719e9 --- /dev/null +++ b/l10n_es_vat_prorate/readme/CONTRIBUTORS.md @@ -0,0 +1,14 @@ +- Enric Tobella +- [Tecnativa](https://www.tecnativa.com/): + - Pedro M. Baeza + - Carolina Fernandez +- [Sygel](https://www.sygel.es/): + - Harald Panten + - Manuel Regidor + - Alberto Martínez + - Anxo82 + - ValentinVinagre +- [Moduon](https://www.moduon.team/): + - Rafael Blasco + - Andrii Kompaniiets + - Emilio Pascual diff --git a/l10n_es_vat_prorate/readme/DESCRIPTION.md b/l10n_es_vat_prorate/readme/DESCRIPTION.md new file mode 100644 index 00000000000..932b7999819 --- /dev/null +++ b/l10n_es_vat_prorate/readme/DESCRIPTION.md @@ -0,0 +1 @@ +El módulo nos divide los IVA según la prorrata de la compañía. diff --git a/l10n_es_vat_prorate/readme/ROADMAP.md b/l10n_es_vat_prorate/readme/ROADMAP.md new file mode 100644 index 00000000000..697f96f7440 --- /dev/null +++ b/l10n_es_vat_prorate/readme/ROADMAP.md @@ -0,0 +1 @@ +- No compatible con criterio de caja. diff --git a/l10n_es_vat_prorate/security/ir.model.access.csv b/l10n_es_vat_prorate/security/ir.model.access.csv new file mode 100644 index 00000000000..d3ada602141 --- /dev/null +++ b/l10n_es_vat_prorate/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_res_company_vat_prorate,access res.company.vat.prorate,model_res_company_vat_prorate,base.group_user,1,0,0,0 +manage_res_company_vat_prorate,manage res.company.vat.prorate,model_res_company_vat_prorate,base.group_system,1,1,1,1 diff --git a/l10n_es_vat_prorate/static/description/icon.png b/l10n_es_vat_prorate/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/l10n_es_vat_prorate/static/description/icon.png differ diff --git a/l10n_es_vat_prorate/static/description/index.html b/l10n_es_vat_prorate/static/description/index.html new file mode 100644 index 00000000000..721ce7fb3d1 --- /dev/null +++ b/l10n_es_vat_prorate/static/description/index.html @@ -0,0 +1,471 @@ + + + + + +Prorrata de IVA + + + +
+

Prorrata de IVA

+ + +

Beta License: AGPL-3 OCA/l10n-spain Translate me on Weblate Try me on Runboat

+

El módulo nos divide los IVA según la prorrata de la compañía.

+

Table of contents

+ +
+

Configuration

+

Para configurar la prorrata general:

+
    +
  • Acceda a la compañía y marque que aplica prorrata.
  • +
  • Defina el porcentaje de prorrata (se aplica por fechas).
  • +
+

Los impuestos de las facturas de proveedor se dividirán según la +prorrata activa.

+

Para configurar la prorrata especial:

+
    +
  • Acceda a la compañía y marque que aplica prorrata de IVA especial.
  • +
  • Marque el valor por defecto de la prorrata especial.
  • +
+

En las líneas de factura aparecerá un nuevo campo editable, con el valor +por defecto configurado, que permitirá definir que líneas serán +prorrateadas.

+
+
+

Known issues / Roadmap

+
    +
  • No compatible con criterio de caja.
  • +
+
+
+

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

+
    +
  • Creu Blanca
  • +
  • Tecnativa
  • +
+
+
+

Contributors

+
    +
  • Enric Tobella
  • +
  • Tecnativa:
      +
    • Pedro M. Baeza
    • +
    • Carolina Fernandez
    • +
    +
  • +
  • Sygel:
      +
    • Harald Panten
    • +
    • Manuel Regidor
    • +
    • Alberto Martínez
    • +
    • Anxo82
    • +
    • ValentinVinagre
    • +
    +
  • +
  • Moduon:
      +
    • Rafael Blasco
    • +
    • Andrii Kompaniiets
    • +
    • Emilio Pascual
    • +
    +
  • +
+
+
+

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 maintainers:

+

rafaelbn Andrii9090 EmilioPascual

+

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

+

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

+
+
+
+ + diff --git a/l10n_es_vat_prorate/tests/__init__.py b/l10n_es_vat_prorate/tests/__init__.py new file mode 100644 index 00000000000..8a97c0ee3c5 --- /dev/null +++ b/l10n_es_vat_prorate/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_vat_prorate +from . import test_prorate_sii diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva0_ns_p_iva10_bc_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva0_ns_p_iva10_bc_dict.json new file mode 100644 index 00000000000..3b06ac26f8a --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva0_ns_p_iva10_bc_dict.json @@ -0,0 +1,25 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0005", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 320.0, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": 100.0}, + {"BaseImponible": 200.0, "TipoImpositivo": "10.0", "CuotaSoportada": 20.0} + ] + } + }, + "CuotaDeducible": 4.0 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_dict.json new file mode 100644 index 00000000000..8fa6101b890 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_dict.json @@ -0,0 +1,24 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0006", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 91.66, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": 83.33, "CuotaSoportada": 8.33, "TipoImpositivo": "10.0"} + ] + } + }, + "CuotaDeducible": 1.67 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf19_p_iva21_sc_p_irpf19_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf19_p_iva21_sc_p_irpf19_dict.json new file mode 100644 index 00000000000..d04a0a5fe43 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf19_p_iva21_sc_p_irpf19_dict.json @@ -0,0 +1,25 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0001", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 352.0, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": 100.0, "CuotaSoportada": 10.0, "TipoImpositivo": "10.0"}, + {"BaseImponible": 200.0, "CuotaSoportada": 42.0, "TipoImpositivo": "21.0"} + ] + } + }, + "CuotaDeducible": 10.4 + }, + "PeriodoLiquidacion": {"Periodo": "02", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf1_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf1_dict.json new file mode 100644 index 00000000000..4190b5408de --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_irpf1_dict.json @@ -0,0 +1,24 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0007", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 91.66, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": 83.33, "CuotaSoportada": 8.33, "TipoImpositivo": "10.0"} + ] + } + }, + "CuotaDeducible": 1.67 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_req014_p_iva21_sc_p_req52_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_req014_p_iva21_sc_p_req52_dict.json new file mode 100644 index 00000000000..479deeeaca3 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva10_bc_p_req014_p_iva21_sc_p_req52_dict.json @@ -0,0 +1,45 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0003", + "IDEmisorFactura": { + "NIF": "F35999705" + } + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": { + "NombreRazon": "Test partner", + "NIF": "F35999705" + }, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 363.8, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + { + "BaseImponible": 100, + "CuotaSoportada": 10, + "TipoImpositivo": "10.0", + "CuotaRecargoEquivalencia": 1.4, + "TipoRecargoEquivalencia": 1.4 + }, + { + "BaseImponible": 200, + "CuotaSoportada": 42, + "TipoImpositivo": "21.0", + "CuotaRecargoEquivalencia": 10.4, + "TipoRecargoEquivalencia": 5.2 + } + ] + } + }, + "CuotaDeducible": 10.4 + }, + "PeriodoLiquidacion": { + "Periodo": "01", + "Ejercicio": 2020 + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva21_sp_ex_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva21_sp_ex_dict.json new file mode 100644 index 00000000000..0a8c589ddd4 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_invoice_p_iva21_sp_ex_dict.json @@ -0,0 +1,24 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0004", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "F1", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": 121.0, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "InversionSujetoPasivo": { + "DetalleIVA": [ + {"BaseImponible": 100.0, "CuotaSoportada": 21.0, "TipoImpositivo": "21.0"} + ] + } + }, + "CuotaDeducible": 4.2 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva10_bc_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva10_bc_dict.json new file mode 100644 index 00000000000..19f2037c52f --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva10_bc_dict.json @@ -0,0 +1,25 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0002", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "R4", + "TipoRectificativa": "I", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": -110.0, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": -100.0, "CuotaSoportada": -10.0, "TipoImpositivo": "10.0"} + ] + } + }, + "CuotaDeducible": -2.0 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva21_sp_in_dict.json b/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva21_sp_in_dict.json new file mode 100644 index 00000000000..d01902a34ba --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_in_refund_p_iva21_sp_in_dict.json @@ -0,0 +1,25 @@ +{ + "IDFactura": { + "FechaExpedicionFacturaEmisor": "01-01-2020", + "NumSerieFacturaEmisor": "sup0008", + "IDEmisorFactura": {"NIF": "F35999705"} + }, + "FacturaRecibida": { + "TipoFactura": "R4", + "TipoRectificativa": "I", + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"}, + "DescripcionOperacion": "/", + "ClaveRegimenEspecialOTrascendencia": "01", + "ImporteTotal": -121.0, + "FechaRegContable": "01-10-2020", + "DesgloseFactura": { + "DesgloseIVA": { + "DetalleIVA": [ + {"BaseImponible": -100.0, "CuotaSoportada": -21.0, "TipoImpositivo": "21.0"} + ] + } + }, + "CuotaDeducible": -4.2 + }, + "PeriodoLiquidacion": {"Periodo": "01", "Ejercicio": 2020} +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva0_sp_i_s_iva0_g_i_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva0_sp_i_s_iva0_g_i_dict.json new file mode 100644 index 00000000000..91ff3ca82da --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva0_sp_i_s_iva0_g_i_dict.json @@ -0,0 +1,36 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "NoSujeta": { + "ImporteTAIReglasLocalizacion": 100.0 + } + }, + "Entrega": { + "Sujeta": { + "Exenta": { + "DetalleExenta": [ + { + "BaseImponible": 200, + "CausaExencion": "E5" + } + ] + } + } + } + } + }, + "ImporteTotal": 300.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_dict.json new file mode 100644 index 00000000000..baf835b7051 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_dict.json @@ -0,0 +1,33 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseFactura": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": 83.33, + "CuotaRepercutida": 8.33 + } + ] + } + } + } + } + }, + "ImporteTotal": 91.66, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_irpf1_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_irpf1_dict.json new file mode 100644 index 00000000000..baf835b7051 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_irpf1_dict.json @@ -0,0 +1,33 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseFactura": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": 83.33, + "CuotaRepercutida": 8.33 + } + ] + } + } + } + } + }, + "ImporteTotal": 91.66, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva0_ns_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva0_ns_dict.json new file mode 100644 index 00000000000..89377e1c915 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva0_ns_dict.json @@ -0,0 +1,33 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseFactura": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": 100.0, + "CuotaRepercutida": 10.0 + } + ] + } + } + } + } + }, + "ImporteTotal": 110.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva21s_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva21s_dict.json new file mode 100644 index 00000000000..51b1761eb3d --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_iva21s_dict.json @@ -0,0 +1,51 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "21.0", + "BaseImponible": 200.0, + "CuotaRepercutida": 42.0 + } + ] + } + } + } + }, + "Entrega": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": 100.0, + "CuotaRepercutida": 10.0 + } + ] + } + } + } + } + } + }, + "ImporteTotal": 352.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_req014_s_iva21s_s_req52_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_req014_s_iva21s_s_req52_dict.json new file mode 100644 index 00000000000..51831dd1f43 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva10b_s_req014_s_iva21s_s_req52_dict.json @@ -0,0 +1,55 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "21.0", + "BaseImponible": 200.0, + "CuotaRepercutida": 42.0, + "CuotaRecargoEquivalencia": 10.4, + "TipoRecargoEquivalencia": 5.2 + } + ] + } + } + } + }, + "Entrega": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": 100.0, + "CuotaRepercutida": 10.0, + "CuotaRecargoEquivalencia": 1.4, + "TipoRecargoEquivalencia": 1.4 + } + ] + } + } + } + } + } + }, + "ImporteTotal": 363.8, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva_e_s_iva0_g_e_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva_e_s_iva0_g_e_dict.json new file mode 100644 index 00000000000..91ff3ca82da --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_invoice_s_iva_e_s_iva0_g_e_dict.json @@ -0,0 +1,36 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "F1", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "NoSujeta": { + "ImporteTAIReglasLocalizacion": 100.0 + } + }, + "Entrega": { + "Sujeta": { + "Exenta": { + "DetalleExenta": [ + { + "BaseImponible": 200, + "CausaExencion": "E5" + } + ] + } + } + } + } + }, + "ImporteTotal": 300.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva0_sp_i_s_iva0_g_i_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva0_sp_i_s_iva0_g_i_dict.json new file mode 100644 index 00000000000..eadbe2d5583 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva0_sp_i_s_iva0_g_i_dict.json @@ -0,0 +1,37 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "R1", + "TipoRectificativa": "I", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "NoSujeta": { + "ImporteTAIReglasLocalizacion": -100.0 + } + }, + "Entrega": { + "Sujeta": { + "Exenta": { + "DetalleExenta": [ + { + "BaseImponible": -200.0, + "CausaExencion": "E5" + } + ] + } + } + } + } + }, + "ImporteTotal": -300.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva10b_s_iva10b_s_iva21s_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva10b_s_iva10b_s_iva21s_dict.json new file mode 100644 index 00000000000..5f29e0531ba --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva10b_s_iva10b_s_iva21s_dict.json @@ -0,0 +1,52 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "R1", + "TipoRectificativa": "I", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "21.0", + "BaseImponible": -200.0, + "CuotaRepercutida": -42.0 + } + ] + } + } + } + }, + "Entrega": { + "Sujeta": { + "NoExenta": { + "TipoNoExenta": "S1", + "DesgloseIVA": { + "DetalleIVA": [ + { + "TipoImpositivo": "10.0", + "BaseImponible": -200.0, + "CuotaRepercutida": -20.0 + } + ] + } + } + } + } + } + }, + "ImporteTotal": -462.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva_e_s_iva0_g_e_dict.json b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva_e_s_iva0_g_e_dict.json new file mode 100644 index 00000000000..eadbe2d5583 --- /dev/null +++ b/l10n_es_vat_prorate/tests/json/sii_out_refund_s_iva_e_s_iva0_g_e_dict.json @@ -0,0 +1,37 @@ +{ + "IDFactura": { + "IDEmisorFactura": {"NIF": "U2687761C"}, + "NumSerieFacturaEmisor": "TEST001", + "FechaExpedicionFacturaEmisor": "01-01-2020" + }, + "PeriodoLiquidacion": {"Ejercicio": 2020, "Periodo": "01"}, + "FacturaExpedida": { + "TipoFactura": "R1", + "TipoRectificativa": "I", + "ClaveRegimenEspecialOTrascendencia": "01", + "DescripcionOperacion": "/", + "TipoDesglose": { + "DesgloseTipoOperacion": { + "PrestacionServicios": { + "NoSujeta": { + "ImporteTAIReglasLocalizacion": -100.0 + } + }, + "Entrega": { + "Sujeta": { + "Exenta": { + "DetalleExenta": [ + { + "BaseImponible": -200.0, + "CausaExencion": "E5" + } + ] + } + } + } + } + }, + "ImporteTotal": -300.0, + "Contraparte": {"NombreRazon": "Test partner", "NIF": "F35999705"} + } +} diff --git a/l10n_es_vat_prorate/tests/test_prorate_sii.py b/l10n_es_vat_prorate/tests/test_prorate_sii.py new file mode 100644 index 00000000000..c9c9b800268 --- /dev/null +++ b/l10n_es_vat_prorate/tests/test_prorate_sii.py @@ -0,0 +1,123 @@ +from datetime import date + +from odoo.tests.common import tagged + +try: + from odoo.addons.l10n_es_aeat_sii_oca.tests.test_l10n_es_aeat_sii import ( + TestL10nEsAeatSiiBase, + ) +except ImportError: + TestL10nEsAeatSiiBase = object + + +@tagged("-at_install", "post_install") +class TestSIIVatProrate(TestL10nEsAeatSiiBase): + @classmethod + def setUpClass(cls): + try: + super().setUpClass() + except Exception: + cls.skipTest(cls, "l10n_es_aeat_sii_oca seems not installed") + cls.company.write( + { + "with_vat_prorate": True, + "vat_prorate_ids": [ + (0, 0, {"date": date(2020, 1, 1), "vat_prorate": 20}), + (0, 0, {"date": date(2021, 1, 1), "vat_prorate": 10}), + ], + } + ) + # Make sure the currency rate 1.2 + cls.usd = cls.env.ref("base.USD") + cls.usd.rate_ids.unlink() + cls.usd.rate_ids.create( + {"name": "2000-01-01", "rate": 1.2, "currency_id": cls.usd.id} + ) + + def test_get_invoice_data(self): + mapping = [ + ("out_invoice", [(100, ["s_iva10b"]), (200, ["s_iva21s"])], {}), + ("out_invoice", [(100, ["s_iva10b"]), (200, ["s_iva0_ns"])], {}), + ( + "out_invoice", + [(100, ["s_iva10b", "s_req014"]), (200, ["s_iva21s", "s_req52"])], + {}, + ), + ( + "out_refund", + [(100, ["s_iva10b"]), (100, ["s_iva10b"]), (200, ["s_iva21s"])], + {}, + ), + ("out_invoice", [(100, ["s_iva0_sp_i"]), (200, ["s_iva0_g_i"])], {}), + ("out_refund", [(100, ["s_iva0_sp_i"]), (200, ["s_iva0_g_i"])], {}), + ("out_invoice", [(100, ["s_iva_e"]), (200, ["s_iva0_g_e"])], {}), + ("out_refund", [(100, ["s_iva_e"]), (200, ["s_iva0_g_e"])], {}), + ( + "in_invoice", + [(100, ["p_iva10_bc", "p_irpf19"]), (200, ["p_iva21_sc", "p_irpf19"])], + { + "ref": "sup0001", + "date": "2020-02-01", + "sii_account_registration_date": "2020-10-01", + }, + ), + ( + "in_refund", + [(100, ["p_iva10_bc"])], + {"ref": "sup0002", "sii_account_registration_date": "2020-10-01"}, + ), + ( + "in_invoice", + [(100, ["p_iva10_bc", "p_req014"]), (200, ["p_iva21_sc", "p_req52"])], + {"ref": "sup0003", "sii_account_registration_date": "2020-10-01"}, + ), + ( + "in_invoice", + [(100, ["p_iva21_sp_ex"])], + {"ref": "sup0004", "sii_account_registration_date": "2020-10-01"}, + ), + ( + "in_invoice", + [(100, ["p_iva0_ns"]), (200, ["p_iva10_bc"])], + {"ref": "sup0005", "sii_account_registration_date": "2020-10-01"}, + ), + # Out invoice with currency + ("out_invoice", [(100, ["s_iva10b"])], {"currency_id": self.usd.id}), + # Out invoice with currency and with not included in total amount + ( + "out_invoice", + [(100, ["s_iva10b", "s_irpf1"])], + {"currency_id": self.usd.id}, + ), + # In invoice with currency + ( + "in_invoice", + [(100, ["p_iva10_bc"])], + { + "ref": "sup0006", + "sii_account_registration_date": "2020-10-01", + "currency_id": self.usd.id, + }, + ), + # In invoice with currency and with not included in total amount + ( + "in_invoice", + [(100, ["p_iva10_bc", "p_irpf1"])], + { + "ref": "sup0007", + "sii_account_registration_date": "2020-10-01", + "currency_id": self.usd.id, + }, + ), + # Intra-community supplier refund with ImporteTotal with "one side" + ( + "in_refund", + [(100, ["p_iva21_sp_in"])], + {"ref": "sup0008", "sii_account_registration_date": "2020-10-01"}, + ), + ] + for inv_type, lines, extra_vals in mapping: + self._create_and_test_invoice_sii_dict( + inv_type, lines, extra_vals, module="l10n_es_vat_prorate" + ) + return diff --git a/l10n_es_vat_prorate/tests/test_vat_prorate.py b/l10n_es_vat_prorate/tests/test_vat_prorate.py new file mode 100644 index 00000000000..4122e01e839 --- /dev/null +++ b/l10n_es_vat_prorate/tests/test_vat_prorate.py @@ -0,0 +1,258 @@ +# Copyright 2022 Creu Blanca +# Copyright 2023 Tecnativa - Pedro M. Baeza +# Copyright 2023 Tecnativa - Carolina Fernandez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import date + +from psycopg2 import IntegrityError, errors + +from odoo import exceptions +from odoo.tests.common import tagged +from odoo.tools import mute_logger + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestVatProrate(AccountTestInvoicingCommon): + @classmethod + @AccountTestInvoicingCommon.setup_chart_template("es_pymes") + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, + mail_create_nolog=True, + mail_create_nosubscribe=True, + mail_notrack=True, + no_reset_password=True, + tracking_disable=True, + ) + ) + cls.env.company.write( + { + "with_vat_prorate": True, + "vat_prorate_ids": [ + (0, 0, {"date": date(2000, 1, 1), "vat_prorate": 10}), + (0, 0, {"date": date(2001, 1, 1), "vat_prorate": 20}), + ], + } + ) + cls.product_b.write( + { + "supplier_taxes_id": [(6, 0, cls.tax_purchase_a.ids)], + "taxes_id": [(6, 0, cls.tax_sale_a.ids)], + } + ) + cls.analytic_plan = cls.env["account.analytic.plan"].create({"name": "Default"}) + cls.analytic_account = cls.env["account.analytic.account"].create( + { + "name": "Test analytic account", + "plan_id": cls.analytic_plan.id, + } + ) + + # Put `zzz` for making sure that the test is executed in last place, as the cursor + # gets incoherent after returning from the SQL constraint violation + @mute_logger("odoo.sql_db") + def test_zzz_company_vat_prorate_out_of_range(self): + vat_prorate_line = self.env.company.vat_prorate_ids[0] + with self.assertRaises(IntegrityError): + vat_prorate_line.vat_prorate = 200 + + @mute_logger("odoo.sql_db") + def test_company_special_vat_prorate_out_of_range(self): + # A error should be displayed if special prorates are 100% + prorate_id = self.env.company.vat_prorate_ids[0] + with self.assertRaises(errors.CheckViolation): + prorate_id.write({"type": "special", "vat_prorate": 100}) + + def test_no_company_vat_prorate_information(self): + self.assertTrue(self.env.company.vat_prorate_ids) + with self.assertRaises(exceptions.ValidationError): + self.env.company.write({"vat_prorate_ids": False}) + + def test_no_prorate_in_invoice(self): + self.env.company.write( + {"with_vat_prorate": False} + ) # We want to be sure that it is executed properly + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_different_accounts_in_invoice(self): + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(6, len(invoice.line_ids)) + self.assertEqual(3, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + # Deal with analytics + invoice.line_ids[0].analytic_distribution = {self.analytic_account.id: 100} + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual( + 1, + len(invoice.line_ids.filtered(lambda r: r.analytic_distribution)), + ) + + def test_prorate_tax_with_prorate_account(self): + # 21% EU S (Services) tax + tax = self.env.ref( + f"account.{self.env.company.id}_account_tax_template_p_iva21_sp_in" + ) + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b], taxes=[tax] + ) + self.assertEqual(7, len(invoice.line_ids)) + self.assertEqual(4, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_same_accounts_in_invoice(self): + self.product_b.property_account_expense_id = self.company_data[ + "default_account_expense" + ] + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(5, len(invoice.line_ids)) + tax_lines = invoice.line_ids.filtered(lambda r: r.tax_line_id) + self.assertEqual(2, len(tax_lines)) + self.assertEqual(1, len(tax_lines.filtered("vat_prorate"))) + # One of the tax lines should have expense account and the other the tax account + self.assertNotEqual(tax_lines[0].account_id, tax_lines[1].account_id) + + def test_no_prorate_in_refund(self): + self.env.company.write( + {"with_vat_prorate": False} + ) # We want to be sure that it is executed properly + invoice = self.init_invoice( + "in_refund", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_different_accounts_in_refund(self): + invoice = self.init_invoice( + "in_refund", products=[self.product_a, self.product_b] + ) + self.assertEqual(6, len(invoice.line_ids)) + self.assertEqual(3, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_same_accounts_in_refund(self): + self.product_b.property_account_expense_id = self.company_data[ + "default_account_expense" + ] + invoice = self.init_invoice( + "in_refund", products=[self.product_a, self.product_b] + ) + self.assertEqual(5, len(invoice.line_ids)) + self.assertEqual(2, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_no_prorate_out_invoice(self): + self.env.company.write( + {"with_vat_prorate": False} + ) # We want to be sure that it is executed properly + invoice = self.init_invoice( + "out_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_out_invoice(self): + invoice = self.init_invoice( + "out_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_no_prorate_out_refund(self): + self.env.company.write( + {"with_vat_prorate": False} + ) # We want to be sure that it is executed properly + invoice = self.init_invoice( + "out_refund", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_out_refund(self): + invoice = self.init_invoice( + "out_refund", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered(lambda r: r.tax_line_id))) + + def test_prorate_make_refund(self): + invoice = self.init_invoice("in_invoice", products=[self.product_a]) + invoice.action_post() + wizard = ( + self.env["account.move.reversal"] + .with_context(active_ids=invoice.ids, active_model="account.move") + .create({"journal_id": invoice.journal_id.id}) + ) + wizard.reverse_moves() + refund = wizard.new_move_ids + self.assertEqual(len(refund.line_ids), 4) + tax_lines = refund.line_ids.filtered(lambda r: r.tax_line_id) + self.assertEqual(len(tax_lines), 2) + # One of the tax lines should have expense account and the other the tax account + self.assertNotEqual(tax_lines[0].account_id, tax_lines[1].account_id) + + def test_special_prorate_default_true(self): + self.env.company.vat_prorate_ids[0].write( + { + "type": "special", + "special_vat_prorate_default": True, + } + ) + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(6, len(invoice.line_ids)) + self.assertEqual(2, len(invoice.line_ids.filtered("vat_prorate"))) + + def test_special_prorate_default_false(self): + self.env.company.vat_prorate_ids[0].write( + { + "type": "special", + "special_vat_prorate_default": False, + } + ) + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + self.assertEqual(4, len(invoice.line_ids)) + self.assertEqual(0, len(invoice.line_ids.filtered("vat_prorate"))) + + def test_special_prorate_mixed(self): + self.env.company.vat_prorate_ids[0].write( + { + "type": "special", + "special_vat_prorate_default": True, + } + ) + invoice = self.init_invoice( + "in_invoice", products=[self.product_a, self.product_b] + ) + invoice.write( + { + "invoice_line_ids": [ + (1, line.id, {"with_vat_prorate": False}) + for line in invoice.invoice_line_ids.filtered( + lambda r: r.product_id == self.product_a + ) + ] + } + ) + self.assertEqual(5, len(invoice.line_ids)) + self.assertEqual(1, len(invoice.line_ids.filtered("vat_prorate"))) + inv_line_b = invoice.line_ids.filtered(lambda r: r.product_id == self.product_b) + prorate_id = self.env.company.vat_prorate_ids[0] + self.assertAlmostEqual( + invoice.line_ids.filtered("vat_prorate").debit, + inv_line_b.price_subtotal + * inv_line_b.tax_ids[:1].amount + * (100 - prorate_id.vat_prorate) + / 10000, + ) diff --git a/l10n_es_vat_prorate/views/account_move_views.xml b/l10n_es_vat_prorate/views/account_move_views.xml new file mode 100644 index 00000000000..9fe16f43e5d --- /dev/null +++ b/l10n_es_vat_prorate/views/account_move_views.xml @@ -0,0 +1,47 @@ + + + + + account.move.form (in l10n_es_vat_prorate) + account.move + + + + + + + debit==0 and credit == 0 + + + + + + + + + + + + + + + [('display_type', 'not in', ('line_section', 'line_note')), ('parent_state', '!=', 'cancel'), '!', '&', ('debit', '=', 0.0), ('credit', '=', 0.0)] + + diff --git a/l10n_es_vat_prorate/views/account_tax_views.xml b/l10n_es_vat_prorate/views/account_tax_views.xml new file mode 100644 index 00000000000..2f28d3b246f --- /dev/null +++ b/l10n_es_vat_prorate/views/account_tax_views.xml @@ -0,0 +1,24 @@ + + + + + account.tax.form (in l10n_es_vat_prorate) + account.tax + + + + + + + + + + diff --git a/l10n_es_vat_prorate/views/res_company_prorate_views.xml b/l10n_es_vat_prorate/views/res_company_prorate_views.xml new file mode 100644 index 00000000000..7579660619d --- /dev/null +++ b/l10n_es_vat_prorate/views/res_company_prorate_views.xml @@ -0,0 +1,40 @@ + + + + + res.company.vat.prorate.form (in l10n_es_vat_prorate) + res.company.vat.prorate + +
+
+ + + + + + + + + + + + + res.company.vat.prorate.list (in l10n_es_vat_prorate) + res.company.vat.prorate + + + + + + + + + + diff --git a/l10n_es_vat_prorate/views/res_company_views.xml b/l10n_es_vat_prorate/views/res_company_views.xml new file mode 100644 index 00000000000..52864431687 --- /dev/null +++ b/l10n_es_vat_prorate/views/res_company_views.xml @@ -0,0 +1,24 @@ + + + + + res.company.form (in l10n_es_vat_prorate) + res.company + + + + + + + + + + + + +