Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 7 additions & 24 deletions account_credit_control/models/credit_control_communication.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
# Copyright 2020 Manuel Calero - Tecnativa
# Copyright 2023 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import threading

from odoo import _, api, fields, models, modules, registry, tools
from odoo import _, api, fields, models
from odoo.tools.misc import format_amount, format_date


Expand Down Expand Up @@ -233,36 +232,20 @@ def _generate_emails(self):
comm._send_mails()

def _send_mails(self):
# Launch process in new thread to improve the user speedup
if not tools.config["test_enable"] and not modules.module.current_test:

@self.env.cr.postcommit.add
def _launch_print_thread():
threaded_calculation = threading.Thread(
target=self.send_mails_threaded,
args=self.ids,
)
threaded_calculation.start()
else:
self._send_communications_by_email()

def send_mails_threaded(self, record_ids):
with registry(self._cr.dbname).cursor() as cr:
self = self.with_env(self.env(cr=cr))
communications = self.browse(record_ids)
communications._send_communications_by_email()
# in account_credit_control_queue_job, override this method
# to loop over self and call _send_communications_by_email with delay
self._send_communications_by_email()

def _send_communications_by_email(self):
for comm in self:
comm.message_mail_with_source(
# in mass_mail mode, the subtype is dropped, which is used by the
# postprocessing that marks control lines as sent.lines
comm.message_post_with_source(
comm.policy_level_id.email_template_id,
subtype_id=self.env["ir.model.data"]._xmlid_to_res_id(
"account_credit_control.mt_request"
),
)
comm.credit_control_line_ids.filtered(
lambda line: line.state == "queued"
).state = "sent"

def _mark_credit_line_as_sent(self):
lines = self.mapped("credit_control_line_ids")
Expand Down
10 changes: 0 additions & 10 deletions account_credit_control/models/credit_control_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,6 @@ def run_channel_action(self):
comm_obj = self.env["credit.control.communication"]
comms = comm_obj._generate_comm_from_credit_lines(email_lines)
comms._generate_emails()
# Notify user that the emails will be sent in background
self.env["bus.bus"]._sendone(
self.env.user.partner_id,
"simple_notification",
{
"type": "info",
"title": _("Notifications"),
"message": _("The emails will be sent in the background"),
},
)
if letter_lines:
wiz = self.env["credit.control.printer"].create(
{"line_ids": letter_lines.ids}
Expand Down
35 changes: 31 additions & 4 deletions account_credit_control/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
class Mail(models.Model):
_inherit = "mail.mail"

def _postprocess_sent_message(
self, success_pids, failure_reason=False, failure_type=None
):
"""Mark credit control lines states."""
def _update_control_line_status(self):
for mail in self:
msg = mail.mail_message_id
if msg.model != "credit.control.communication":
Expand All @@ -23,8 +20,38 @@ def _postprocess_sent_message(
)
new_state = "sent" if mail.state == "sent" else "email_error"
lines.write({"state": new_state})

def _postprocess_sent_message(
self, success_pids, failure_reason=False, failure_type=None
):
"""Mark credit control lines states."""
self._update_control_line_status()
return super()._postprocess_sent_message(
success_pids=success_pids,
failure_reason=failure_reason,
failure_type=failure_type,
)

def _send(
self,
auto_commit=False,
raise_exception=False,
smtp_session=None,
alias_domain_id=False,
mail_server=False,
post_send_callback=None,
):
# because of
# https://github.com/odoo/odoo/blob/bcba6c0dda4818e67a9023beb26593a7d74ff6a6/
# addons/mail/models/mail_mail.py#L606-L607
# we don't go through _postprocess_sent_message if the address is blacklisted
no_postprocess = self.filtered(lambda m: m.state != "outgoing")
no_postprocess._update_control_line_status()
return super()._send(
auto_commit=auto_commit,
raise_exception=raise_exception,
smtp_session=smtp_session,
alias_domain_id=alias_domain_id,
mail_server=mail_server,
post_send_callback=post_send_callback,
)
52 changes: 33 additions & 19 deletions account_credit_control/tests/test_credit_control_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
from odoo.addons.account.tests.common import AccountTestInvoicingCommon


@tagged("post_install", "-at_install")
class TestCreditControlRun(AccountTestInvoicingCommon):
class TestCreditControlRunCase(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
Expand Down Expand Up @@ -79,6 +78,9 @@ def setUpClass(cls):
cls.invoice = invoice_form.save()
cls.invoice.action_post()


@tagged("post_install", "-at_install")
class TestCreditControlRun(TestCreditControlRunCase):
def test_check_run_date(self):
"""
Create a control run older than the last control run
Expand Down Expand Up @@ -127,10 +129,10 @@ def test_generate_credit_lines(self):
)

report_regex = (
rf'<p>Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br></p>"
rf'Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br>"
)
regex_result = re.match(report_regex, control_run.report)
regex_result = re.search(report_regex, control_run.report)
self.assertIsNotNone(regex_result)

def test_generate_credit_lines_with_max_level(self):
Expand Down Expand Up @@ -213,10 +215,10 @@ def test_wiz_print_lines(self):
self.assertEqual(control_run.state, "done")

report_regex = (
rf'<p>Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br></p>"
rf'Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br>"
)
regex_result = re.match(report_regex, control_run.report)
regex_result = re.search(report_regex, control_run.report)
self.assertIsNotNone(regex_result)

# Mark lines to be send
Expand Down Expand Up @@ -248,10 +250,10 @@ def test_wiz_credit_control_emailer(self):
self.assertEqual(control_run.state, "done")

report_regex = (
rf'<p>Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br></p>"
rf'Policy "<b>{self.policy.name}</b>" has generated <b>'
r"\d+ Credit Control Lines.</b><br>"
)
regex_result = re.match(report_regex, control_run.report)
regex_result = re.search(report_regex, control_run.report)
self.assertIsNotNone(regex_result)

# Mark lines to be send
Expand Down Expand Up @@ -288,30 +290,42 @@ def test_sent_email_invoice_detail(self):
{"name": "to_be_sent", "line_ids": [(6, 0, control_lines.ids)]}
)
marker.mark_lines()
emailer_obj = self.env["credit.control.emailer"].with_context(
domain_notifications_email="test@example.com"
)
wiz_emailer = emailer_obj.create({})
wiz_emailer = self.env["credit.control.emailer"].create({})
wiz_emailer.line_ids = control_lines
self.env.user.company_id.email = "test@example.com"
with RecordCapturer(self.env["credit.control.communication"], []) as capture:
wiz_emailer.email_lines()
wiz_emailer.with_context(queue_job__no_delay=True).email_lines()
new_communication = capture.records
self.assertEqual(len(new_communication), 1)
self.assertEqual(len(new_communication.message_ids), 1)
# Verify that the email include the invoice details.
self.assertIn("Invoices summary", new_communication.message_ids.body)
self.assertIn(self.invoice.name, new_communication.message_ids.body)

def test_sent_email_no_invoice_detail(self):
"""
Verify that the email is sent and does not include the invoice details
"""
policy_level_expected = self.env.ref("account_credit_control.3_time_1")
self.invoice.partner_id.email = "test@test.com"
self.env.user.company_id.email = "test@example.com"
control_run = self.env["credit.control.run"].create(
{"date": fields.Date.today(), "policy_ids": [(6, 0, [self.policy.id])]}
)
control_run.with_context(lang="en_US").generate_credit_lines()
self.assertTrue(len(self.invoice.credit_control_line_ids), 1)
control_lines = self.invoice.credit_control_line_ids
self.assertEqual(control_lines.policy_level_id, policy_level_expected)
# CASE 2: set the policy level to show invoice details = False
control_lines.policy_level_id.mail_show_invoice_detail = False
control_lines.state = "to_be_sent"
marker = self.env["credit.control.marker"].create(
{"name": "to_be_sent", "line_ids": [(6, 0, control_lines.ids)]}
)
marker.mark_lines()
wiz_emailer = emailer_obj.create({})
wiz_emailer = self.env["credit.control.emailer"].create({})
wiz_emailer.line_ids = control_lines
with RecordCapturer(self.env["credit.control.communication"], []) as capture:
wiz_emailer.email_lines()
wiz_emailer.with_context(queue_job__no_delay=True).email_lines()
new_communication = capture.records
self.assertEqual(len(new_communication), 1)
self.assertEqual(len(new_communication.message_ids), 1)
Expand Down
72 changes: 72 additions & 0 deletions account_credit_control_queue_job/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
======================
Account Credit Control
======================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:e19d48059d05f79acf49652dc0b98548511fb71b832aa80fa9164df08895de55
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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%2Fcredit--control-lightgray.png?logo=github
:target: https://github.com/OCA/credit-control/tree/18.0/account_credit_control_queue_job
:alt: OCA/credit-control
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/credit-control-18-0/credit-control-18-0-account_credit_control_queue_job
: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/credit-control&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Once this module is installed, the emails will be sent in individual
jobs.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/credit-control/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 <https://github.com/OCA/credit-control/issues/new?body=module:%20account_credit_control_queue_job%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* 360 ERP

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.

This module is part of the `OCA/credit-control <https://github.com/OCA/credit-control/tree/18.0/account_credit_control_queue_job>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
3 changes: 3 additions & 0 deletions account_credit_control_queue_job/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
from . import wizard
19 changes: 19 additions & 0 deletions account_credit_control_queue_job/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 360ERP (<https://www.360erp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Account Credit Control",
"version": "18.0.1.0.1",
"author": "360 ERP, Odoo Community Association (OCA)",
"category": "Finance",
"depends": [
"account_credit_control",
"queue_job_batch",
],
"website": "https://github.com/OCA/credit-control",
"data": ["wizard/res_config_settings.xml"],
"installable": True,
"auto_install": False,
"license": "AGPL-3",
"application": True,
}
3 changes: 3 additions & 0 deletions account_credit_control_queue_job/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import credit_control_communication
from . import credit_control_run
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright 2025 360ERP (<https://www.360erp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, models
from odoo.tools import split_every


class CreditControlCommunication(models.Model):
_inherit = "credit.control.communication"

def _send_mails(self):
key = "account_credit_control_queue_job.batch_size"
batch_size = self.env["ir.config_parameter"].sudo().get_param(key)
try:
batch_size = max(1, int(batch_size))
except Exception: # pylint: disable=broad-except
batch_size = 1
batch_name = _("Credit Control Emails")
batch = self.env["queue.job.batch"].get_new_batch(batch_name)
for comms in split_every(batch_size, self.ids, self.browse):
if batch_size > 1:
desc = _("Sending credit control emails for ids: %s") % comms.ids
else:
desc = _("Sending credit control email for %s") % comms.partner_id.name
comms.with_context(job_batch=batch).with_delay(
description=desc
)._send_communications_by_email()
19 changes: 19 additions & 0 deletions account_credit_control_queue_job/models/credit_control_run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 360ERP (<https://www.360erp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import _, models


class CreditControlRun(models.Model):
_inherit = "credit.control.run"

def run_channel_action(self):
res = super().run_channel_action()
target = self.env.user.partner_id
msg = {
"type": "info",
"title": _("Jobs enqueued"),
"message": _("The emails will be sent in the background"),
}
self.env["bus.bus"]._sendone(target, "simple_notification", msg)
return res
3 changes: 3 additions & 0 deletions account_credit_control_queue_job/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
1 change: 1 addition & 0 deletions account_credit_control_queue_job/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Once this module is installed, the emails will be sent in individual jobs.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading