diff --git a/addons/account_edi/data/cron.xml b/addons/account_edi/data/cron.xml index 1567942b8dc..1f47dbd7d02 100644 --- a/addons/account_edi/data/cron.xml +++ b/addons/account_edi/data/cron.xml @@ -3,9 +3,9 @@ EDI : Perform web services operations code - model.search([('state', 'in', ('to_send', 'to_cancel'))])._process_documents_web_services(job_count=20) + model._cron_process_documents_web_services(job_count=20) 1 - hours + days -1 False diff --git a/addons/account_edi/models/account_edi_document.py b/addons/account_edi/models/account_edi_document.py index 6a44e7f3ea7..57eb1db13f5 100644 --- a/addons/account_edi/models/account_edi_document.py +++ b/addons/account_edi/models/account_edi_document.py @@ -190,13 +190,16 @@ class AccountEdiDocument(models.Model): self._process_job(documents, doc_type) def _process_documents_web_services(self, job_count=None, with_commit=True): - """ Post and cancel all the documents that need a web service. This is called by CRON. + ''' Post and cancel all the documents that need a web service. - :param job_count: Limit to the number of jobs to process among the ones that are available for treatment. - """ - jobs = self.filtered(lambda d: d.edi_format_id._needs_web_services())._prepare_jobs() - jobs = jobs[0:job_count or len(jobs)] - for documents, doc_type in jobs: + :param job_count: The maximum number of jobs to process if specified. + :param with_commit: Flag indicating a commit should be made between each job. + :return: The number of remaining jobs to process. + ''' + all_jobs = self.filtered(lambda d: d.edi_format_id._needs_web_services())._prepare_jobs() + jobs_to_process = all_jobs[0:job_count] if job_count else all_jobs + + for documents, doc_type in jobs_to_process: move_to_cancel = documents.filtered(lambda doc: doc.attachment_id \ and doc.state == 'to_cancel' \ and doc.move_id.is_invoice(include_receipts=True) \ @@ -220,5 +223,20 @@ class AccountEdiDocument(models.Model): else: raise e else: - if with_commit and len(jobs) > 1: + if with_commit and len(jobs_to_process) > 1: self.env.cr.commit() + + return len(all_jobs) - len(jobs_to_process) + + @api.model + def _cron_process_documents_web_services(self, job_count=None): + ''' Method called by the EDI cron processing all web-services. + + :param job_count: Limit explicitely the number of web service calls. If not provided, process all. + ''' + edi_documents = self.search([('state', 'in', ('to_send', 'to_cancel'))]) + nb_remaining_jobs = edi_documents._process_documents_web_services(job_count=job_count) + + # Mark the CRON to be triggered again asap since there is some remaining jobs to process. + if nb_remaining_jobs > 0: + self.env.ref('account_edi.ir_cron_edi_network')._trigger() diff --git a/addons/account_edi/models/account_move.py b/addons/account_edi/models/account_move.py index 1db21d29058..44f24199779 100644 --- a/addons/account_edi/models/account_move.py +++ b/addons/account_edi/models/account_move.py @@ -261,6 +261,7 @@ class AccountMove(models.Model): self.env['account.edi.document'].create(edi_document_vals_list) posted.edi_document_ids._process_documents_no_web_services() + self.env.ref('account_edi.ir_cron_edi_network')._trigger() return posted def button_cancel(self): @@ -271,6 +272,7 @@ class AccountMove(models.Model): self.edi_document_ids.filtered(lambda doc: doc.attachment_id).write({'state': 'to_cancel', 'error': False, 'blocking_level': False}) self.edi_document_ids.filtered(lambda doc: not doc.attachment_id).write({'state': 'cancelled', 'error': False, 'blocking_level': False}) self.edi_document_ids._process_documents_no_web_services() + self.env.ref('account_edi.ir_cron_edi_network')._trigger() return res @@ -353,7 +355,7 @@ class AccountMove(models.Model): def action_process_edi_web_services(self): docs = self.edi_document_ids.filtered(lambda d: d.state in ('to_send', 'to_cancel') and d.blocking_level != 'error') - docs._process_documents_web_services(with_commit=False) + docs._process_documents_web_services() def action_retry_edi_documents_error(self): self.edi_document_ids.write({'error': False, 'blocking_level': False}) diff --git a/addons/account_edi/tests/test_edi.py b/addons/account_edi/tests/test_edi.py index bfd8f6f8518..04df5f12879 100644 --- a/addons/account_edi/tests/test_edi.py +++ b/addons/account_edi/tests/test_edi.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import Command from odoo.addons.account_edi.tests.common import AccountEdiTestCommon, _mocked_post_two_steps, _generate_mocked_needs_web_services, _mocked_cancel_failed, _generate_mocked_support_batching from unittest.mock import patch +from odoo.addons.base.tests.test_ir_cron import CronMixinCase -class TestAccountEdi(AccountEdiTestCommon): +class TestAccountEdi(AccountEdiTestCommon, CronMixinCase): @classmethod def setUpClass(cls, chart_template_ref=None, edi_format_ref=None): @@ -93,7 +95,7 @@ class TestAccountEdi(AccountEdiTestCommon): self.invoice.button_cancel_posted_moves() self.assertEqual(doc.state, 'to_cancel') self.assertEqual(self.invoice.state, 'posted') - doc._process_documents_web_services() + doc._process_documents_web_services(with_commit=False) self.assertEqual(doc.state, 'cancelled') self.assertEqual(self.invoice.state, 'cancel') @@ -120,7 +122,7 @@ class TestAccountEdi(AccountEdiTestCommon): self.invoice.button_cancel_posted_moves() self.assertEqual(doc.state, 'to_cancel') self.assertEqual(self.invoice.state, 'posted') - doc._process_documents_web_services() + doc._process_documents_web_services(with_commit=False) self.assertEqual(doc.state, 'to_cancel') self.assertEqual(self.invoice.state, 'posted') @@ -196,3 +198,32 @@ class TestAccountEdi(AccountEdiTestCommon): doc2.move_id.ref = 'batch1' to_process = edi_docs._prepare_jobs() self.assertEqual(len(to_process), 2) + + def test_cron_triggers(self): + with self.capture_triggers('account_edi.ir_cron_edi_network') as capt, \ + self.mock_edi(_needs_web_services_method=_generate_mocked_needs_web_services(True)): + self.invoice._get_edi_document(self.edi_format) + self.invoice.action_post() + capt.records.ensure_one() + + def test_cron_self_trigger(self): + # Process single job by CRON call (and thus, disable the auto-commit). + edi_cron = self.env.ref('account_edi.ir_cron_edi_network') + edi_cron.code = 'model._cron_process_documents_web_services(job_count=1)' + + # Create invoices. + invoices = self.env['account.move'].create([{ + 'move_type': 'out_invoice', + 'invoice_date': '2019-01-01', + 'date': '2019-01-01', + 'partner_id': self.partner_a.id, + 'invoice_line_ids': [Command.create({'product_id': self.product_a.id})], + } for i in range(4)]) + + with self.capture_triggers('account_edi.ir_cron_edi_network') as capt, \ + self.mock_edi(_needs_web_services_method=_generate_mocked_needs_web_services(True)): + invoices.action_post() + + self.env.ref('account_edi.ir_cron_edi_network').method_direct_trigger() + self.assertEqual(len(capt.records), 2, "Not all records have been processed in this run, the cron should " + "re-trigger itself to process some more later")