[IMP] account_edi: Replace high velocity CRON by CRON triggers
So, instead of waiting for the cron every hour, we create a cron trigger, which is created/activated every time we post/cancel an invoice. When the CRON worker is available, it can take the job immediately. To be sure that we don't miss any crons or that events might be needed that are not triggered, we still launch the cron every day. A test was added to check that the crons are correctly triggered. closes odoo/odoo#67088 Signed-off-by: Josse Colpaert <jco@openerp.com>
This commit is contained in:
committed by
Josse Colpaert
parent
7faa4c3bc6
commit
c9f9f859ca
@@ -3,9 +3,9 @@
|
||||
<field name="name">EDI : Perform web services operations</field>
|
||||
<field name="model_id" ref="model_account_edi_document"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.search([('state', 'in', ('to_send', 'to_cancel'))])._process_documents_web_services(job_count=20)</field>
|
||||
<field name="code">model._cron_process_documents_web_services(job_count=20)</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">hours</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="active">False</field>
|
||||
</record>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user