From 1008ef2789fd09f1942ed22dddf05b4e196590ed Mon Sep 17 00:00:00 2001 From: manu Date: Wed, 8 May 2024 18:39:47 +0200 Subject: [PATCH] l10n_es_aeat_sii_oca: inicio refactorizacion --- l10n_es_aeat_sii_oca/README.rst | 26 +- l10n_es_aeat_sii_oca/__manifest__.py | 11 +- .../data/aeat_sii_map_data.xml | 312 +++++++++--------- .../data/aeat_sii_queue_job.xml | 17 - .../data/ir_config_parameter_data.xml | 9 + l10n_es_aeat_sii_oca/data/ir_cron.xml | 12 + .../data/l10n.es.aeat.map.tax.line.tax.csv | 115 +++++++ l10n_es_aeat_sii_oca/hooks.py | 10 +- .../migrations/16.0.1.4.0/post-migration.py | 18 - l10n_es_aeat_sii_oca/models/__init__.py | 2 +- l10n_es_aeat_sii_oca/models/account_move.py | 102 ++++-- l10n_es_aeat_sii_oca/models/aeat_sii_map.py | 4 +- .../models/ir_cron_trigger.py | 32 ++ l10n_es_aeat_sii_oca/models/queue_job.py | 18 - l10n_es_aeat_sii_oca/models/res_company.py | 8 +- l10n_es_aeat_sii_oca/models/sii_mixin.py | 70 ++-- l10n_es_aeat_sii_oca/readme/CONFIGURE.md | 23 -- l10n_es_aeat_sii_oca/readme/CONTRIBUTORS.md | 1 + l10n_es_aeat_sii_oca/security/aeat_sii.xml | 17 +- .../security/ir.model.access.csv | 2 +- .../static/description/index.html | 25 +- .../tests/test_l10n_es_aeat_sii.py | 41 +-- .../views/account_fiscal_position_view.xml | 22 +- .../views/account_journal_view.xml | 4 +- .../views/account_move_views.xml | 86 ++--- .../views/aeat_sii_map_view.xml | 2 +- ...ob_views.xml => ir_cron_trigger_views.xml} | 17 +- .../views/res_company_view.xml | 17 +- .../views/res_partner_views.xml | 5 +- .../wizards/account_move_reversal_views.xml | 6 +- .../wizards/account_move_send_sii.py | 38 +-- .../wizards/account_move_send_sii_views.xml | 43 ++- 32 files changed, 590 insertions(+), 525 deletions(-) delete mode 100644 l10n_es_aeat_sii_oca/data/aeat_sii_queue_job.xml create mode 100644 l10n_es_aeat_sii_oca/data/ir_config_parameter_data.xml create mode 100644 l10n_es_aeat_sii_oca/data/ir_cron.xml create mode 100644 l10n_es_aeat_sii_oca/data/l10n.es.aeat.map.tax.line.tax.csv delete mode 100644 l10n_es_aeat_sii_oca/migrations/16.0.1.4.0/post-migration.py create mode 100644 l10n_es_aeat_sii_oca/models/ir_cron_trigger.py delete mode 100644 l10n_es_aeat_sii_oca/models/queue_job.py rename l10n_es_aeat_sii_oca/views/{queue_job_views.xml => ir_cron_trigger_views.xml} (58%) diff --git a/l10n_es_aeat_sii_oca/README.rst b/l10n_es_aeat_sii_oca/README.rst index 529f71f0ae7..36d28f6b763 100644 --- a/l10n_es_aeat_sii_oca/README.rst +++ b/l10n_es_aeat_sii_oca/README.rst @@ -7,7 +7,7 @@ Suministro Inmediato de Información en el IVA !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:72d10dec2690f12511331aae6305750eeadddbc7c8f9a9923d197dfa5af114ba + !! source digest: sha256:dd4688bc2a1c4bd195d8dd4949f98b380e1ba0858c791d7685e37e7a4a60f9d0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png @@ -70,29 +70,6 @@ En Linux se pueden usar los siguientes comandos: - Clave privada: "openssl pkcs12 -in Certifcado.p12 -nocerts -out privateKey.pem -nodes" -Además, el módulo queue_job necesita estar configurado de una de estas -formas: - -1. Ajustando variables de entorno: - - ODOO_QUEUE_JOB_CHANNELS=root:4 - - u otro canal de configuración. Por defecto es root:1 - - Si xmlrpc_port no está definido: ODOO_QUEUE_JOB_PORT=8069 - -2. Otra alternativa es usuando un fichero de configuración: - - [options] (...) workers = 4 server_wide_modules = - web,base_sparse_field,queue_job - - (...) [queue_job] channels = root:4 - -3. Por último, arrancando Odoo con - --load=web,base_sparse_field,queue_job y --workers más grande que 1. - -Más información http://odoo-connector.com - Usage ===== @@ -159,6 +136,7 @@ Contributors - `Sygel `__: - Valentin Vinagre + - Manuel Regidor - `Tecnativa `__: diff --git a/l10n_es_aeat_sii_oca/__manifest__.py b/l10n_es_aeat_sii_oca/__manifest__.py index f2a81be3882..d6bd65bcd11 100644 --- a/l10n_es_aeat_sii_oca/__manifest__.py +++ b/l10n_es_aeat_sii_oca/__manifest__.py @@ -14,11 +14,12 @@ # Copyright 2017-2023 Tecnativa - Pedro M. Baeza # Copyright 2023 Aures Tic - Jose Zambudio # Copyright 2023 Pol Reig +# Copyright 2024 Manuel Regidor # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "Suministro Inmediato de Información en el IVA", - "version": "16.0.1.7.0", + "version": "17.0.1.0.0", "category": "Accounting & Finance", "website": "https://github.com/OCA/l10n-spain", "author": "Acysos S.L.," @@ -39,12 +40,11 @@ "external_dependencies": {"python": ["zeep", "requests"]}, "depends": [ "account_invoice_refund_link", - "l10n_es", "l10n_es_aeat", - "queue_job", ], "data": [ - "data/aeat_sii_queue_job.xml", + "data/ir_config_parameter_data.xml", + "data/ir_cron.xml", "data/aeat_sii_tax_agency_data.xml", "views/res_company_view.xml", "views/account_move_views.xml", @@ -53,11 +53,12 @@ "views/aeat_sii_mapping_registration_keys_view.xml", "data/aeat_sii_mapping_registration_keys_data.xml", "views/aeat_sii_map_view.xml", + "data/l10n.es.aeat.map.tax.line.tax.csv", "data/aeat_sii_map_data.xml", "security/ir.model.access.csv", "security/aeat_sii.xml", "views/product_view.xml", - "views/queue_job_views.xml", + "views/ir_cron_trigger_views.xml", "views/account_fiscal_position_view.xml", "views/res_partner_views.xml", "views/aeat_tax_agency_view.xml", diff --git a/l10n_es_aeat_sii_oca/data/aeat_sii_map_data.xml b/l10n_es_aeat_sii_oca/data/aeat_sii_map_data.xml index 29b3ab9bc5e..f190c9b08e8 100644 --- a/l10n_es_aeat_sii_oca/data/aeat_sii_map_data.xml +++ b/l10n_es_aeat_sii_oca/data/aeat_sii_map_data.xml @@ -18,13 +18,13 @@ SFESB @@ -33,9 +33,9 @@ SFESISP @@ -44,9 +44,9 @@ SFENS @@ -55,11 +55,11 @@ SFESNS @@ -68,13 +68,13 @@ SFESS @@ -83,10 +83,10 @@ SFESBE @@ -95,9 +95,9 @@ SFESSE @@ -106,43 +106,43 @@ SFRS @@ -151,9 +151,9 @@ SFRSA @@ -162,19 +162,19 @@ SFRISP @@ -183,17 +183,17 @@ SFRBI @@ -202,10 +202,10 @@ SFRNS @@ -214,18 +214,18 @@ RE @@ -234,11 +234,11 @@ SFRND @@ -247,41 +247,41 @@ NotIncludedInTotal @@ -293,24 +293,24 @@ > NotIncludedInTotalNegative @@ -322,9 +322,9 @@ > BaseNotIncludedInTotal diff --git a/l10n_es_aeat_sii_oca/data/aeat_sii_queue_job.xml b/l10n_es_aeat_sii_oca/data/aeat_sii_queue_job.xml deleted file mode 100644 index 9f5498e6415..00000000000 --- a/l10n_es_aeat_sii_oca/data/aeat_sii_queue_job.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - invoice_validate_sii - - - - - confirm_one_document - - - - - cancel_one_invoice - - - diff --git a/l10n_es_aeat_sii_oca/data/ir_config_parameter_data.xml b/l10n_es_aeat_sii_oca/data/ir_config_parameter_data.xml new file mode 100644 index 00000000000..e5757af6993 --- /dev/null +++ b/l10n_es_aeat_sii_oca/data/ir_config_parameter_data.xml @@ -0,0 +1,9 @@ + + + + + l10n_es_aeat_sii_oca.sii_batch + 50 + + diff --git a/l10n_es_aeat_sii_oca/data/ir_cron.xml b/l10n_es_aeat_sii_oca/data/ir_cron.xml new file mode 100644 index 00000000000..d620221f36a --- /dev/null +++ b/l10n_es_aeat_sii_oca/data/ir_cron.xml @@ -0,0 +1,12 @@ + + + + Send Invoices to SII + + code + model._send_to_sii() + 1 + hours + -1 + + diff --git a/l10n_es_aeat_sii_oca/data/l10n.es.aeat.map.tax.line.tax.csv b/l10n_es_aeat_sii_oca/data/l10n.es.aeat.map.tax.line.tax.csv new file mode 100644 index 00000000000..477bca7ea3f --- /dev/null +++ b/l10n_es_aeat_sii_oca/data/l10n.es.aeat.map.tax.line.tax.csv @@ -0,0 +1,115 @@ +id,name +s_iva21b,account_tax_template_s_iva21b +s_iva0b,account_tax_template_s_iva0b +s_iva4b,account_tax_template_s_iva4b +s_iva5b,account_tax_template_s_iva5b +s_iva10b,account_tax_template_s_iva10b +s_iva0_isp,account_tax_template_s_iva0_isp +s_iva_ns_b,account_tax_template_s_iva_ns_b +s_iva_e,account_tax_template_s_iva_e +s_iva0_sp_i,account_tax_template_s_iva0_sp_i +s_iva_ns,account_tax_template_s_iva_ns +s_iva21s,account_tax_template_s_iva21s +s_iva10s,account_tax_template_s_iva10s +s_iva0s,account_tax_template_s_iva0s +s_iva4s,account_tax_template_s_iva4s +s_iva5s,account_tax_template_s_iva5s +s_iva0_ic,account_tax_template_s_iva0_ic +s_iva0_e,account_tax_template_s_iva0_e +s_iva0,account_tax_template_s_iva0 +p_iva21_bc,account_tax_template_p_iva21_bc +p_iva21_sc,account_tax_template_p_iva21_sc +p_iva10_bc,account_tax_template_p_iva10_bc +p_iva10_sc,account_tax_template_p_iva10_sc +p_iva5_bc,account_tax_template_p_iva5_bc +p_iva5_sc,account_tax_template_p_iva5_sc +p_iva4_bc,account_tax_template_p_iva4_bc +p_iva4_sc,account_tax_template_p_iva4_sc +p_iva0_bc,account_tax_template_p_iva0_bc +p_iva0_s_bc,account_tax_template_p_iva0_s_bc +p_iva0_s_sc,account_tax_template_p_iva0_s_sc +p_iva0_ic_bc,account_tax_template_p_iva0_ic_bc +p_iva0_ic_sc,account_tax_template_p_iva0_ic_sc +p_iva0_ibc,account_tax_template_p_iva0_ibc +p_iva4_bi,account_tax_template_p_iva4_bi +p_iva10_bi,account_tax_template_p_iva10_bi +p_iva21_bi,account_tax_template_p_iva21_bi +p_iva4_sp_in,account_tax_template_p_iva4_sp_in +p_iva10_sp_in,account_tax_template_p_iva10_sp_in +p_iva21_sp_in,account_tax_template_p_iva21_sp_in +p_iva4_ic_bc,account_tax_template_p_iva4_ic_bc +p_iva10_ic_bc,account_tax_template_p_iva10_ic_bc +p_iva21_ic_bc,account_tax_template_p_iva21_ic_bc +p_iva5_ic_bc,account_tax_template_p_iva5_ic_bc +p_iva5_ic_sc,account_tax_template_p_iva5_ic_sc +p_iva4_ic_bi,account_tax_template_p_iva4_ic_bi +p_iva10_ic_bi,account_tax_template_p_iva10_ic_bi +p_iva21_ic_bi,account_tax_template_p_iva21_ic_bi +p_iva4_ibc,account_tax_template_p_iva4_ibc +p_iva5_ibc,account_tax_template_p_iva5_ibc +p_iva10_ibc,account_tax_template_p_iva10_ibc +p_iva21_ibc,account_tax_template_p_iva21_ibc +p_iva4_ibi,account_tax_template_p_iva4_ibi +p_iva10_ibi,account_tax_template_p_iva10_ibi +p_iva21_ibi,account_tax_template_p_iva21_ibi +p_iva12_agr,account_tax_template_p_iva12_agr +p_iva21_isp,account_tax_template_p_iva21_isp +p_iva10_isp,account_tax_template_p_iva10_isp +p_iva4_isp,account_tax_template_p_iva4_isp +p_iva21_isp_bi,account_tax_template_p_iva21_isp_bi +p_iva10_isp_bi,account_tax_template_p_iva10_isp_bi +p_iva4_isp_bi,account_tax_template_p_iva4_isp_bi +p_iva21_sp_ex,account_tax_template_p_iva21_sp_ex +p_iva10_sp_ex,account_tax_template_p_iva10_sp_ex +p_iva5_isc,account_tax_template_p_iva5_isc +p_iva4_sp_ex,account_tax_template_p_iva4_sp_ex +p_iva0_isc,account_tax_template_p_iva0_isc +p_iva0_ns,account_tax_template_p_iva0_ns +p_iva0_ns_b,account_tax_template_p_iva0_ns_b +p_req52,account_tax_template_p_req52 +s_req52,account_tax_template_s_req52 +p_req014,account_tax_template_p_req014 +s_req014,account_tax_template_s_req014 +p_req062,account_tax_template_p_req062 +s_req062,account_tax_template_s_req062 +p_req05,account_tax_template_p_req05 +s_req05,account_tax_template_s_req05 +p_req0,account_tax_template_p_req0 +s_req0,account_tax_template_s_req0 +p_iva0_nd,account_tax_template_p_iva0_nd +p_iva10_nd,account_tax_template_p_iva10_nd +p_iva4_nd,account_tax_template_p_iva4_nd +s_irpf1,account_tax_template_s_irpf1 +p_irpf1,account_tax_template_p_irpf1 +s_irpf2,account_tax_template_s_irpf2 +p_irpf2,account_tax_template_p_irpf2 +s_irpf7,account_tax_template_s_irpf7 +p_irpf7,account_tax_template_p_irpf7 +p_irpf7e,account_tax_template_p_irpf7e +s_irpf9,account_tax_template_s_irpf9 +p_irpf9,account_tax_template_p_irpf9 +s_irpf15,account_tax_template_s_irpf15 +p_irpf15,account_tax_template_p_irpf15 +p_irpf15e,account_tax_template_p_irpf15e +s_irpf18,account_tax_template_s_irpf18 +p_irpf18,account_tax_template_p_irpf18 +s_irpf19,account_tax_template_s_irpf19 +s_irpf19a,account_tax_template_s_irpf19a +s_irpf195a,account_tax_template_s_irpf195a +p_irpf19,account_tax_template_p_irpf19 +p_irpf19a,account_tax_template_p_irpf19a +p_irpf195a,account_tax_template_p_irpf195a +s_irpf20,account_tax_template_s_irpf20 +s_irpf20a,account_tax_template_s_irpf20a +p_irpf20,account_tax_template_p_irpf20 +p_irpf20a,account_tax_template_p_irpf20a +s_irpf21,account_tax_template_s_irpf21 +s_irpf21a,account_tax_template_s_irpf21a +p_irpf21a,account_tax_template_p_irpf21a +p_irpf21p,account_tax_template_p_irpf21p +p_irpf21t,account_tax_template_p_irpf21t +p_irpf21td,account_tax_template_p_irpf21td +p_irpf21te,account_tax_template_p_irpf21te +s_irpf24,account_tax_template_s_irpf24 +p_irpf24,account_tax_template_p_irpf24 +s_iva0_ns,account_tax_template_s_iva0_ns diff --git a/l10n_es_aeat_sii_oca/hooks.py b/l10n_es_aeat_sii_oca/hooks.py index cdb44e89268..d9f110e8be1 100644 --- a/l10n_es_aeat_sii_oca/hooks.py +++ b/l10n_es_aeat_sii_oca/hooks.py @@ -2,12 +2,10 @@ # Copyright 2021 Tecnativa - João Marques # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from odoo import SUPERUSER_ID, api - -def add_key_to_existing_invoices(cr, registry): +def add_key_to_existing_invoices(env): """This post-init-hook will update all existing invoices""" - env = api.Environment(cr, SUPERUSER_ID, {}) + # env = api.Environment(cr, SUPERUSER_ID, {}) invoice_obj = env["account.move"] invoices = invoice_obj.search( [ @@ -27,7 +25,7 @@ def add_key_to_existing_invoices(cr, registry): [("code", "=", "01"), ("type", "=", "purchase")], limit=1 ) if purchase_key: - cr.execute( + env.cr.execute( """ UPDATE account_move SET sii_registration_key = %s @@ -35,7 +33,7 @@ def add_key_to_existing_invoices(cr, registry): (purchase_key[0].id,), ) if sale_key: - cr.execute( + env.cr.execute( """ UPDATE account_move SET sii_registration_key = %s diff --git a/l10n_es_aeat_sii_oca/migrations/16.0.1.4.0/post-migration.py b/l10n_es_aeat_sii_oca/migrations/16.0.1.4.0/post-migration.py deleted file mode 100644 index cd4a20aebec..00000000000 --- a/l10n_es_aeat_sii_oca/migrations/16.0.1.4.0/post-migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2023 Factor Libre S.L. -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -def migrate(cr, version): - cr.execute( - """ - UPDATE - queue_job - SET - method_name = REPLACE(method_name, 'confirm_one_invoice', 'confirm_one_document'), - name = REPLACE(name, 'confirm_one_invoice', 'confirm_one_document'), - func_string = REPLACE(func_string, 'confirm_one_invoice', 'confirm_one_document') - WHERE - method_name = 'confirm_one_invoice' AND - state in ('wait_dependencies', 'pending', 'enqueued') - """ - ) diff --git a/l10n_es_aeat_sii_oca/models/__init__.py b/l10n_es_aeat_sii_oca/models/__init__.py index 3cf6d177d86..de3a1e7f42e 100644 --- a/l10n_es_aeat_sii_oca/models/__init__.py +++ b/l10n_es_aeat_sii_oca/models/__init__.py @@ -3,7 +3,7 @@ from . import aeat_sii_mapping_registration_keys from . import aeat_sii_map from . import product_product -from . import queue_job +from . import ir_cron_trigger from . import account_fiscal_position from . import sii_mixin from . import account_move diff --git a/l10n_es_aeat_sii_oca/models/account_move.py b/l10n_es_aeat_sii_oca/models/account_move.py index d423efef22e..256c6866f14 100644 --- a/l10n_es_aeat_sii_oca/models/account_move.py +++ b/l10n_es_aeat_sii_oca/models/account_move.py @@ -14,6 +14,7 @@ import json import logging +from datetime import datetime from odoo import _, api, exceptions, fields, models from odoo.modules.registry import Registry @@ -22,18 +23,6 @@ _logger = logging.getLogger(__name__) -try: - from odoo.addons.queue_job.job import job -except ImportError: - _logger.debug("Can not `import queue_job`.") - import functools - - def empty_decorator_factory(*argv, **kwargs): - return functools.partial - - job = empty_decorator_factory - - class AccountMove(models.Model): _name = "account.move" _inherit = ["account.move", "sii.mixin"] @@ -94,12 +83,12 @@ def _default_sii_refund_type(self): "The invoice number should start with LC, QZC, QRC, A01 or A02.", copy=False, ) - invoice_jobs_ids = fields.Many2many( - comodel_name="queue.job", + invoice_cron_trigger_ids = fields.Many2many( + comodel_name="ir.cron.trigger", column1="invoice_id", - column2="job_id", - relation="account_move_queue_job_rel", - string="Connector Jobs", + column2="trigger_id", + relation="account_move_cron_trigger_rel", + string="Cron Triggers", copy=False, ) @@ -619,9 +608,10 @@ def process_send_sii(self): "context": self.env.context, } - def _get_sii_jobs_field_name(self): - return "invoice_jobs_ids" + def _get_sii_triggers_field_name(self): + return "invoice_cron_trigger_ids" + @api.model def _get_valid_document_states(self): return SII_VALID_INVOICE_STATES @@ -684,33 +674,30 @@ def cancel_sii(self): and i.sii_state in ["sent", "sent_w_errors", "sent_modified"] ) ) - if not invoices._cancel_sii_jobs(): + if not invoices._cancel_sii_triggers(): raise exceptions.UserError( _( "You can not communicate the cancellation of this invoice " - "at this moment because there is a job running!" + "at this moment. Please, try again later." ) ) - queue_obj = self.env["queue.job"] for invoice in invoices: company = invoice.company_id - if not company.use_connector: + cron_trigger_obj = self.env["ir.cron.trigger"].sudo() + sii_send_cron = self.env.ref("l10n_es_aeat_sii_oca.invoice_send_to_sii") + if not company.use_cron: invoice._cancel_invoice_to_sii() else: - eta = company._get_sii_eta() - new_delay = ( - self.sudo() - .with_context(company_id=company.id) - .with_delay(eta=eta) - .cancel_one_invoice() + sii_sending_time = company._get_sii_sending_time() + trigger = cron_trigger_obj.create( + {"cron_id": sii_send_cron.id, "call_at": sii_sending_time} ) - job = queue_obj.search([("uuid", "=", new_delay.uuid)], limit=1) - invoice.sudo().invoice_jobs_ids |= job + invoice.sudo().invoice_cron_trigger_ids |= trigger def button_cancel(self): - if not self._cancel_sii_jobs(): + if not self._cancel_sii_triggers(): raise exceptions.UserError( - _("You can not cancel this invoice because" " there is a job running!") + _("You cannot cancel this invoice. Please, try again later.") ) res = super().button_cancel() for invoice in self.filtered(lambda x: x.sii_enabled): @@ -723,11 +710,11 @@ def button_cancel(self): return res def button_draft(self): - if not self._cancel_sii_jobs(): + if not self._cancel_sii_triggers(): raise exceptions.UserError( _( "You can not set to draft this invoice because" - " there is a job running!" + " the SII cron could not be cancelled." ) ) return super().button_draft() @@ -807,7 +794,7 @@ def _reverse_moves(self, default_values_list=None, cancel=False): # OVERRIDE if not default_values_list: default_values_list = [{} for move in self] - for move, default_values in zip(self, default_values_list): + for move, default_values in zip(self, default_values_list, strict=False): if move.sii_enabled: extra_dict = {} sii_refund_type = self.env.context.get("sii_refund_type", False) @@ -828,3 +815,46 @@ def _reverse_moves(self, default_values_list=None, cancel=False): def cancel_one_invoice(self): self.sudo()._cancel_invoice_to_sii() + + @api.model + def _send_to_sii(self): + documents = all_documents = self.search( + [ + ("state", "in", self._get_valid_document_states()), + ( + "sii_state", + "not in", + ["sent", "cancelled"], + ), + ("sii_send_date", "<=", fields.Datetime.now()), + ] + ) + if documents: + cron_trigger_obj = self.env["ir.cron.trigger"].sudo() + sii_send_cron = self.env.ref("l10n_es_aeat_sii_oca.invoice_send_to_sii") + remaining_documents = False + batch = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("l10n_es_aeat_sii_oca.sii_batch") + ) + if batch: + try: + batch = int(batch) + except ValueError as e: + raise exceptions.UserError( + _( + "The value in l10n_es_aeat_sii_oca.sii_batch system" + " parameter must be an integer. Please, check the " + "value of the parameter." + ) + ) from e + if batch: + documents = all_documents[:batch] + remaining_documents = all_documents - documents + documents.confirm_one_document() + for document in remaining_documents: + trigger = cron_trigger_obj.create( + {"cron_id": sii_send_cron.id, "call_at": datetime.now()} + ) + document.sudo().invoice_cron_trigger_ids |= trigger diff --git a/l10n_es_aeat_sii_oca/models/aeat_sii_map.py b/l10n_es_aeat_sii_oca/models/aeat_sii_map.py index 434d39f370d..4a8d8dec9b5 100644 --- a/l10n_es_aeat_sii_oca/models/aeat_sii_map.py +++ b/l10n_es_aeat_sii_oca/models/aeat_sii_map.py @@ -59,7 +59,9 @@ class AeatSiiMapLines(models.Model): code = fields.Char(required=True) name = fields.Char() - taxes = fields.Many2many(comodel_name="account.tax.template") + tax_xmlid_ids = fields.Many2many( + comodel_name="l10n.es.aeat.map.tax.line.tax", string="Taxes templates" + ) sii_map_id = fields.Many2one( comodel_name="aeat.sii.map", string="Aeat SII Map", ondelete="cascade" ) diff --git a/l10n_es_aeat_sii_oca/models/ir_cron_trigger.py b/l10n_es_aeat_sii_oca/models/ir_cron_trigger.py new file mode 100644 index 00000000000..6fd8bf6f6b2 --- /dev/null +++ b/l10n_es_aeat_sii_oca/models/ir_cron_trigger.py @@ -0,0 +1,32 @@ +# Copyright 2017 Tecnativa - Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class IrCronTrigger(models.Model): + _inherit = "ir.cron.trigger" + + def do_now(self): + documents = self.env["account.move"].search( + [("invoice_cron_trigger_ids", "in", self.ids)] + ) + documents.write({"sii_send_date": fields.Datetime.now()}) + self.sudo().write({"call_at": fields.Datetime.now()}) + + def cancel_now(self): + documents = self.env["account.move"].search( + [("invoice_cron_trigger_ids", "in", self.ids)] + ) + documents.write({"sii_send_date": False}) + self.sudo().unlink() + + def reschedule_sudo(self): + documents = self.env["account.move"].search( + [("invoice_cron_trigger_ids", "in", self.ids)] + ) + for document in documents: + document.write( + {"sii_send_date": document.company_id._get_sii_sending_time()} + ) diff --git a/l10n_es_aeat_sii_oca/models/queue_job.py b/l10n_es_aeat_sii_oca/models/queue_job.py deleted file mode 100644 index 618ecfe0440..00000000000 --- a/l10n_es_aeat_sii_oca/models/queue_job.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2017 Tecnativa - Pedro M. Baeza -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -from odoo import models - - -class QueueJob(models.Model): - _inherit = "queue.job" - - def do_now(self): - self.sudo().write({"eta": False}) - - def cancel_now(self): - self.sudo().filtered(lambda x: x.state in ["pending", "enqueued"]).unlink() - - def requeue_sudo(self): - self.sudo().requeue() diff --git a/l10n_es_aeat_sii_oca/models/res_company.py b/l10n_es_aeat_sii_oca/models/res_company.py index 606ba1e711b..8ad8748623e 100644 --- a/l10n_es_aeat_sii_oca/models/res_company.py +++ b/l10n_es_aeat_sii_oca/models/res_company.py @@ -55,8 +55,8 @@ class ResCompany(models.Model): help="By default, the invoice is sent/queued in validation process. " "With manual method, there's a button to send the invoice.", ) - use_connector = fields.Boolean( - help="Check it to use connector instead of sending the invoice " + use_cron = fields.Boolean( + help="Check it to use a cron instead of sending the invoice " "directly when it's validated", ) send_mode = fields.Selection( @@ -70,7 +70,9 @@ class ResCompany(models.Model): sent_time = fields.Float() delay_time = fields.Float() - def _get_sii_eta(self): + def _get_sii_sending_time(self): + if self.send_mode == "auto": + return datetime.now() if self.send_mode == "fixed": tz = self.env.context.get("tz", self.env.user.partner_id.tz) offset = datetime.now(pytz.timezone(tz)).strftime("%z") if tz else "+00" diff --git a/l10n_es_aeat_sii_oca/models/sii_mixin.py b/l10n_es_aeat_sii_oca/models/sii_mixin.py index 64402047fff..2fa710d6b03 100644 --- a/l10n_es_aeat_sii_oca/models/sii_mixin.py +++ b/l10n_es_aeat_sii_oca/models/sii_mixin.py @@ -7,8 +7,6 @@ import json import logging -from requests import Session - from odoo import _, api, exceptions, fields, models from odoo.exceptions import UserError, ValidationError from odoo.modules.registry import Registry @@ -147,6 +145,7 @@ class SiiMixin(models.AbstractModel): "greater o equal to 100 000 000,00 euros.", compute="_compute_macrodata", ) + sii_send_date = fields.Datetime(string="SII Send Date") def _compute_sii_refund_type(self): self.sii_refund_type = False @@ -191,11 +190,12 @@ def _compute_sii_registration_key(self): @api.depends("sii_registration_key") def _compute_sii_registration_key_code(self): """ - Para evitar tiempos de instalación largos en BBDD grandes, es necesario que - sólo dependa de sii_registration_key, ya que en caso de añadirlo odoo buscará - todos los movimientos y cuando escribamos el key, aunque sea un campo no almacenado - A partir de v16.0 este cambio ya no es necesario, ya que el sistema ya revisa que el - campo sea almacenado o que este visualizandose (en caché) + Para evitar tiempos de instalación largos en BBDD grandes, es necesario + que sólo dependa de sii_registration_key, ya que en caso de añadirlo + odoo buscará todos los movimientos y cuando escribamos el key, aunque + sea un campo no almacenado + A partir de v16.0 este cambio ya no es necesario, ya que el sistema ya + revisa que el campo sea almacenado o que este visualizandose (en caché) """ for record in self: record.sii_registration_key_code = record.sii_registration_key.code @@ -254,8 +254,14 @@ def _get_sii_taxes_map(self, codes, date): ], limit=1, ) - tax_templates = sii_map.map_lines.filtered(lambda x: x.code in codes).taxes - return self.company_id.get_taxes_from_templates(tax_templates) + tax_templates = sii_map.map_lines.filtered( + lambda x: x.code in codes + ).tax_xmlid_ids + taxes = self.env["account.tax"] + for template in tax_templates: + tax_id = self.company_id._get_tax_id_from_xmlid(template.name) + taxes |= self.env["account.tax"].browse(tax_id) + return taxes def _change_date_format(self, date): datetimeobject = fields.Date.to_date(date) @@ -286,15 +292,15 @@ def _get_sii_header(self, tipo_comunicacion=False, cancellation=False): header.update({"TipoComunicacion": tipo_comunicacion}) return header - def _get_sii_jobs_field_name(self): + def _get_sii_triggers_field_name(self): raise NotImplementedError() - def _cancel_sii_jobs(self): - for queue in self.sudo().mapped(self._get_sii_jobs_field_name()): - if queue.state == "started": - return False - elif queue.state in ("pending", "enqueued", "failed"): - queue.unlink() + def _cancel_sii_triggers(self): + try: + self.sudo().write({"sii_send_date": False}) + self.sudo().mapped(self._get_sii_triggers_field_name()).unlink() + except Exception: + return False return True def _get_valid_document_states(self): @@ -308,11 +314,11 @@ def send_sii(self): and document.sii_state not in ["sent", "cancelled"] ) ) - if not documents._cancel_sii_jobs(): + if not documents._cancel_sii_triggers(): raise UserError( _( - "You can not communicate this document at this moment " - "because there is a job running!" + "You can not communicate this document at this moment. " + "Please, try again later." ) ) documents._process_sii_send() @@ -321,22 +327,24 @@ def _process_sii_send(self): """Process document sending to the SII. Adds general checks from configuration parameters and document availability for SII. If the document is to be sent the decides the send method: direct send or - via connector depending on 'Use connector' configuration""" - queue_obj = self.env["queue.job"].sudo() + via cron depending on 'Use cron' configuration""" + cron_trigger_obj = self.env["ir.cron.trigger"].sudo() + sii_send_cron = self.env.ref("l10n_es_aeat_sii_oca.invoice_send_to_sii") for record in self: company = record.company_id - if not company.use_connector: + if not company.use_cron: record.confirm_one_document() else: - eta = company._get_sii_eta() - new_delay = ( - record.sudo() - .with_context(company_id=company.id) - .with_delay(eta=eta if not record.sii_send_failed else False) - .confirm_one_document() + sii_sending_time = company._get_sii_sending_time() + trigger = cron_trigger_obj.create( + {"cron_id": sii_send_cron.id, "call_at": sii_sending_time} + ) + setattr( + record.sudo(), + self._get_sii_triggers_field_name(), + [(4, trigger.id)], ) - job = queue_obj.search([("uuid", "=", new_delay.uuid)], limit=1) - setattr(record.sudo(), self._get_sii_jobs_field_name(), [(4, job.id)]) + self.sii_send_date = sii_sending_time def _bind_sii(self, client, port_name, address=None): self.ensure_one() @@ -406,7 +414,7 @@ def _sii_check_exceptions(self): and not is_simplified_invoice ): raise UserError(_("The partner has not a VAT configured.")) - if not self.company_id.chart_template_id: + if not self.company_id.chart_template: raise UserError( _("You have to select what account chart template use this" " company.") ) diff --git a/l10n_es_aeat_sii_oca/readme/CONFIGURE.md b/l10n_es_aeat_sii_oca/readme/CONFIGURE.md index 8af09257815..2cfcb6999a9 100644 --- a/l10n_es_aeat_sii_oca/readme/CONFIGURE.md +++ b/l10n_es_aeat_sii_oca/readme/CONFIGURE.md @@ -14,26 +14,3 @@ En Linux se pueden usar los siguientes comandos: publicCert.crt -nodes" - Clave privada: "openssl pkcs12 -in Certifcado.p12 -nocerts -out privateKey.pem -nodes" - -Además, el módulo queue_job necesita estar configurado de una de estas -formas: - -1. Ajustando variables de entorno: - - > ODOO_QUEUE_JOB_CHANNELS=root:4 - - u otro canal de configuración. Por defecto es root:1 - - Si xmlrpc_port no está definido: ODOO_QUEUE_JOB_PORT=8069 - -2. Otra alternativa es usuando un fichero de configuración: - - > \[options\] (...) workers = 4 server_wide_modules = - > web,base_sparse_field,queue_job - > - > (...) \[queue_job\] channels = root:4 - -3. Por último, arrancando Odoo con - --load=web,base_sparse_field,queue_job y --workers más grande que 1. - -Más información diff --git a/l10n_es_aeat_sii_oca/readme/CONTRIBUTORS.md b/l10n_es_aeat_sii_oca/readme/CONTRIBUTORS.md index 08b26876333..90d18dc281e 100644 --- a/l10n_es_aeat_sii_oca/readme/CONTRIBUTORS.md +++ b/l10n_es_aeat_sii_oca/readme/CONTRIBUTORS.md @@ -13,6 +13,7 @@ - Eric Antonés - NuoBiT Solutions, S.L. \<\> - [Sygel](https://www.sygel.es): - Valentin Vinagre + - Manuel Regidor - [Tecnativa](https://www.tecnativa.com): - Pedro M. Baeza - João Marques diff --git a/l10n_es_aeat_sii_oca/security/aeat_sii.xml b/l10n_es_aeat_sii_oca/security/aeat_sii.xml index 9bba8a75b33..e9aab4e6430 100644 --- a/l10n_es_aeat_sii_oca/security/aeat_sii.xml +++ b/l10n_es_aeat_sii_oca/security/aeat_sii.xml @@ -1,17 +1,18 @@ - - Queue job AEAT SII visibility - + + Triggers AEAT SII visibility + [('channel', '=', 'root.invoice_validate_sii')] + eval="[('cron_id', '=', ref('l10n_es_aeat_sii_oca.invoice_send_to_sii'))]" + /> - - Queue job manager - + diff --git a/l10n_es_aeat_sii_oca/security/ir.model.access.csv b/l10n_es_aeat_sii_oca/security/ir.model.access.csv index b8f8db011b9..4c4b5ebbc44 100644 --- a/l10n_es_aeat_sii_oca/security/ir.model.access.csv +++ b/l10n_es_aeat_sii_oca/security/ir.model.access.csv @@ -6,5 +6,5 @@ access_model_aeat_sii_map_lines_aeat,aeat.sii.map.lines aeat,model_aeat_sii_map_ access_model_aeat_sii_mapping_registration_keys_admin,aeat.sii.mapping.registration.keys admin,model_aeat_sii_mapping_registration_keys,base.group_system,1,1,1,1 access_model_aeat_sii_mapping_registration_keys_aeat,aeat.sii.mapping.registration.keys aeat,model_aeat_sii_mapping_registration_keys,l10n_es_aeat.group_account_aeat,1,0,0,0 access_model_aeat_sii_mapping_registration_keys_aeat_account,aeat.sii.mapping.registration.keys aeat,model_aeat_sii_mapping_registration_keys,account.group_account_invoice,1,0,0,0 -access_queue_job,access_queue_job aeat,queue_job.model_queue_job,l10n_es_aeat.group_account_aeat,1,0,0,0 +access_ir_cron_trigger,access_ir_cron_trigger aeat,base.model_ir_cron_trigger,l10n_es_aeat.group_account_aeat,1,0,0,0 access_wizard_send_sii,access_wizard_send_sii,model_wizard_send_sii,l10n_es_aeat.group_account_aeat,1,1,1,1 diff --git a/l10n_es_aeat_sii_oca/static/description/index.html b/l10n_es_aeat_sii_oca/static/description/index.html index aa659a5cd02..c10f712fe39 100644 --- a/l10n_es_aeat_sii_oca/static/description/index.html +++ b/l10n_es_aeat_sii_oca/static/description/index.html @@ -366,7 +366,7 @@

Suministro Inmediato de Información en el IVA

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:72d10dec2690f12511331aae6305750eeadddbc7c8f9a9923d197dfa5af114ba +!! source digest: sha256:dd4688bc2a1c4bd195d8dd4949f98b380e1ba0858c791d7685e37e7a4a60f9d0 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

Módulo para la presentación inmediata del IVA @@ -418,28 +418,6 @@

Configuration

  • Clave privada: “openssl pkcs12 -in Certifcado.p12 -nocerts -out privateKey.pem -nodes”
  • -

    Además, el módulo queue_job necesita estar configurado de una de estas -formas:

    -
      -
    1. Ajustando variables de entorno:

      -
      -

      ODOO_QUEUE_JOB_CHANNELS=root:4

      -
      -

      u otro canal de configuración. Por defecto es root:1

      -

      Si xmlrpc_port no está definido: ODOO_QUEUE_JOB_PORT=8069

      -
    2. -
    3. Otra alternativa es usuando un fichero de configuración:

      -
      -

      [options] (…) workers = 4 server_wide_modules = -web,base_sparse_field,queue_job

      -

      (…) [queue_job] channels = root:4

      -
      -
    4. -
    5. Por último, arrancando Odoo con -–load=web,base_sparse_field,queue_job y –workers más grande que 1.

      -
    6. -
    -

    Más información http://odoo-connector.com

    Usage

    @@ -504,6 +482,7 @@

    Contributors

  • Eric Antonés - NuoBiT Solutions, S.L. <eantones@nuobit.com>
  • Sygel:
    • Valentin Vinagre
    • +
    • Manuel Regidor
  • Tecnativa:
      diff --git a/l10n_es_aeat_sii_oca/tests/test_l10n_es_aeat_sii.py b/l10n_es_aeat_sii_oca/tests/test_l10n_es_aeat_sii.py index 01ebe7401ec..b3e8fd99b2b 100644 --- a/l10n_es_aeat_sii_oca/tests/test_l10n_es_aeat_sii.py +++ b/l10n_es_aeat_sii_oca/tests/test_l10n_es_aeat_sii.py @@ -9,7 +9,7 @@ import json from odoo import exceptions -from odoo.modules.module import get_resource_path +from odoo.tools.misc import file_path from odoo.addons.l10n_es_aeat.tests.test_l10n_es_aeat_certificate import ( TestL10nEsAeatCertificateBase, @@ -52,11 +52,9 @@ def _create_and_test_invoice_sii_dict( for line in lines: taxes = self.env["account.tax"] for tax in line[1]: - if "." in tax: - xml_id = tax - else: - xml_id = f"l10n_es.{self.company.id}_account_tax_template_{tax}" - taxes += self.env.ref(xml_id) + xml_id = f"account_tax_template_{tax}" + tax_id = self.company._get_tax_id_from_xmlid(xml_id) + taxes += self.env["account.tax"].browse(tax_id) tax_names.append(tax) vals.append({"price_unit": line[0], "taxes": taxes}) return self._compare_sii_dict( @@ -107,7 +105,7 @@ def _compare_sii_dict( vals.update(extra_vals) invoice = self.env["account.move"].create(vals) result_dict = invoice._get_sii_invoice_dict() - path = get_resource_path(module, "tests/json", json_file) + path = file_path(f"l10n_es_aeat_sii_oca/tests/json/{json_file}") if not path: raise Exception("Incorrect JSON file: %s" % json_file) with open(path) as f: @@ -150,14 +148,14 @@ def setUpClass(cls): {"name": "Test product", "sii_exempt_cause": "E5"} ) cls.account_expense = cls.env.ref( - "l10n_es.%s_account_common_600" % cls.company.id + "account.%s_account_common_600" % cls.company.id ) cls.invoice = cls._create_invoice("out_invoice") cls.company.write( { "sii_enabled": True, "sii_test": True, - "use_connector": True, + "use_cron": True, "vat": "ESU2687761C", "sii_description_method": "manual", "tax_agency_id": cls.env.ref("l10n_es_aeat.aeat_tax_agency_spain"), @@ -203,7 +201,7 @@ def test_intracomunitary_customer_extracomunitary_delivery(self): "vat": "FR23334175221", } ) - fp_extra = self.browse_ref(f"l10n_es.{self.company.id}_fp_extra") + fp_extra = self.browse_ref(f"account.{self.company.id}_fp_extra") fp_extra.sii_partner_identification_type = "3" invoice = self.invoice.copy( {"partner_id": eu_customer.id, "fiscal_position_id": fp_extra.id} @@ -219,7 +217,7 @@ def test_intracomunitary_customer_extracomunitary_delivery(self): ) def test_job_creation(self): - self.assertTrue(self.invoice.invoice_jobs_ids) + self.assertTrue(self.invoice.invoice_cron_trigger_ids) def test_partner_sii_enabled(self): company_02 = self.env["res.company"].create({"name": "Company 02"}) @@ -314,11 +312,6 @@ def test_get_invoice_data(self): self._create_and_test_invoice_sii_dict(inv_type, lines, extra_vals) return - def test_action_cancel(self): - self.invoice.invoice_jobs_ids.state = "started" - with self.assertRaises(exceptions.UserError): - self.invoice.button_cancel() - def test_sii_description(self): company = self.invoice.company_id company.write( @@ -381,22 +374,6 @@ def _check_tax_agencies(self, invoice): invoice.company_id.tax_agency_id = False self._check_binding_address(invoice) - def test_tax_agencies_sandbox(self): - self.sii_cert.company_id = self.invoice.company_id.id - self._activate_certificate() - self.invoice.company_id.sii_test = True - self._check_tax_agencies(self.invoice) - in_invoice = self._create_invoice("in_invoice") - self._check_tax_agencies(in_invoice) - - def test_tax_agencies_production(self): - self.sii_cert.company_id = self.invoice.company_id.id - self._activate_certificate() - self.invoice.company_id.sii_test = False - self._check_tax_agencies(self.invoice) - in_invoice = self._create_invoice("in_invoice") - self._check_tax_agencies(in_invoice) - def test_refund_sii_refund_type(self): invoice = self.env["account.move"].create( { diff --git a/l10n_es_aeat_sii_oca/views/account_fiscal_position_view.xml b/l10n_es_aeat_sii_oca/views/account_fiscal_position_view.xml index a53d71edbc0..598662a4a29 100644 --- a/l10n_es_aeat_sii_oca/views/account_fiscal_position_view.xml +++ b/l10n_es_aeat_sii_oca/views/account_fiscal_position_view.xml @@ -11,31 +11,21 @@ - + - - + + diff --git a/l10n_es_aeat_sii_oca/views/account_journal_view.xml b/l10n_es_aeat_sii_oca/views/account_journal_view.xml index 6e710e38eae..62dd9027e71 100644 --- a/l10n_es_aeat_sii_oca/views/account_journal_view.xml +++ b/l10n_es_aeat_sii_oca/views/account_journal_view.xml @@ -7,9 +7,7 @@ - {'invisible': [('type', 'not in', ('sale', 'purchase'))]} + type not in ['sale', 'purchase'] diff --git a/l10n_es_aeat_sii_oca/views/account_move_views.xml b/l10n_es_aeat_sii_oca/views/account_move_views.xml index 7c854f8c5dd..a8ce2a21625 100644 --- a/l10n_es_aeat_sii_oca/views/account_move_views.xml +++ b/l10n_es_aeat_sii_oca/views/account_move_views.xml @@ -12,13 +12,12 @@ {'invisible': [('move_type', 'not in', ('in_invoice', 'out_invoice', 'out_refund', 'in_refund'))]} + name="invisible" + >move_type not in ['in_invoice', 'out_invoice', 'out_refund', 'in_refund'] - {'required': [('thirdparty_invoice', '=', True)], 'invisible': [('thirdparty_invoice', '=', False)]} + thirdparty_invoice + not thirdparty_invoice - + - + - + @@ -165,18 +149,18 @@ diff --git a/l10n_es_aeat_sii_oca/views/aeat_sii_map_view.xml b/l10n_es_aeat_sii_oca/views/aeat_sii_map_view.xml index 111a02c793c..43a02d03b7a 100644 --- a/l10n_es_aeat_sii_oca/views/aeat_sii_map_view.xml +++ b/l10n_es_aeat_sii_oca/views/aeat_sii_map_view.xml @@ -52,7 +52,7 @@ - + diff --git a/l10n_es_aeat_sii_oca/views/queue_job_views.xml b/l10n_es_aeat_sii_oca/views/ir_cron_trigger_views.xml similarity index 58% rename from l10n_es_aeat_sii_oca/views/queue_job_views.xml rename to l10n_es_aeat_sii_oca/views/ir_cron_trigger_views.xml index ed9b89fe6e5..5eb8de7ad5c 100644 --- a/l10n_es_aeat_sii_oca/views/queue_job_views.xml +++ b/l10n_es_aeat_sii_oca/views/ir_cron_trigger_views.xml @@ -2,35 +2,30 @@ - - queue.job + + ir.cron.trigger - - - - + +