Skip to content

Commit

Permalink
[MIG] base_report_to_printer: Migration to 18.0
Browse files Browse the repository at this point in the history
  • Loading branch information
trisdoan committed Nov 19, 2024
1 parent 05716e3 commit 4fd9a30
Show file tree
Hide file tree
Showing 24 changed files with 186 additions and 129 deletions.
7 changes: 7 additions & 0 deletions base_report_to_printer/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ Contributors
- Hughes Damry <[email protected]>
- Akim Juillerat <[email protected]>
- Jacques-Etienne Baudoux (BCIM) <[email protected]>
- Tris Doan <[email protected]>

Other credits
-------------

The migration of this module from 17.0 to 18.0 was financially supported
by Camptocamp.

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion base_report_to_printer/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

{
"name": "Report to printer",
"version": "17.0.1.0.1",
"version": "18.0.1.0.0",
"category": "Generic Modules/Base",
"author": "Agile Business Group & Domsense, Pegueroles SCP, NaN,"
" LasLabs, Camptocamp, Odoo Community Association (OCA),"
Expand Down
16 changes: 5 additions & 11 deletions base_report_to_printer/data/printing_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,19 @@
<field name="name">Send to Client</field>
<field name="action_type">client</field>
</record>
<!-- properties -->
<record forcecreate="True" id="property_printing_action_id" model="ir.property">
<field name="name">property_printing_action_id</field>
<field
name="fields_id"
search="[('model', '=', 'ir.actions.report'), ('name', '=', 'property_printing_action_id')]"
/>
<field name="value" eval="'printing.action,' + str(printing_action_2)" />
</record>
<record forcecreate="True" id="ir_cron_update_printers" model="ir.cron">
<field name="name">Update Printers Jobs</field>
<field name="active" eval="True" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
<field name="model_id" ref="base_report_to_printer.model_printing_server" />
<field name="state">code</field>
<field name="code">model.action_update_jobs()</field>
</record>
<function
model="ir.default"
name="set"
eval="('ir.actions.report', 'property_printing_action_id', obj().env.ref('base_report_to_printer.printing_action_2').id)"
/>
</odoo>
2 changes: 1 addition & 1 deletion base_report_to_printer/models/ir_actions_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def print_document(self, record_ids, data=None):
_("This report type (%s) is not supported by direct printing!")
% str(self.report_type)
)
method_name = "_render_qweb_%s" % (report_type)
method_name = f"_render_qweb_{report_type}"
document, doc_format = getattr(
self.with_context(must_skip_send_to_printer=True), method_name
)(self.report_name, record_ids, data=data)
Expand Down
4 changes: 2 additions & 2 deletions base_report_to_printer/models/printing_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo import fields, models


class PrintingAction(models.Model):
_name = "printing.action"
_description = "Print Job Action"

@api.model
@property
def _available_action_types(self):
return [
("server", "Send to Printer"),
Expand Down
2 changes: 1 addition & 1 deletion base_report_to_printer/models/printing_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def print_options(self, report=None, **print_opts):
options = {}
for option, value in print_opts.items():
try:
options.update(getattr(self, "_set_option_%s" % option)(report, value))
options.update(getattr(self, f"_set_option_{option}")(report, value))
except AttributeError:
options[option] = str(value)
return options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PrintingReportXmlAction(models.Model):
comodel_name="res.users", string="User", required=True, ondelete="cascade"
)
action = fields.Selection(
selection=lambda s: s.env["printing.action"]._available_action_types(),
selection=lambda s: s.env["printing.action"]._available_action_types,
required=True,
)
printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer")
Expand Down
14 changes: 11 additions & 3 deletions base_report_to_printer/models/res_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,27 @@
class ResUsers(models.Model):
_inherit = "res.users"

@api.model
@property
def _user_available_action_types(self):
return [
(code, string)
for code, string in self.env["printing.action"]._available_action_types()
for code, string in self.env["printing.action"]._available_action_types
if code != "user_default"
]

printing_action = fields.Selection(selection=_user_available_action_types)
printing_action = fields.Selection(
selection=lambda self: self._user_available_action_types
)
printing_printer_id = fields.Many2one(
comodel_name="printing.printer", string="Default Printer"
)

@api.constrains("printing_action")
def _check_printing_action(self):
for rec in self:
if rec.printing_action == "user_default":
raise ValueError("user_default should not be available")

@property
def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + ["printing_action", "printing_printer_id"]
Expand Down
1 change: 1 addition & 0 deletions base_report_to_printer/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
- Hughes Damry \<<[email protected]>\>
- Akim Juillerat \<<[email protected]>\>
- Jacques-Etienne Baudoux (BCIM) \<<[email protected]>\>
- Tris Doan \<<[email protected]>\>
1 change: 1 addition & 0 deletions base_report_to_printer/readme/CREDITS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp.
11 changes: 9 additions & 2 deletions base_report_to_printer/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ <h1 class="title">Report to printer</h1>
<li><a class="reference internal" href="#credits" id="toc-entry-9">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-10">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-11">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-12">Maintainers</a></li>
<li><a class="reference internal" href="#other-credits" id="toc-entry-12">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-13">Maintainers</a></li>
</ul>
</li>
</ul>
Expand Down Expand Up @@ -528,10 +529,16 @@ <h2><a class="toc-backref" href="#toc-entry-11">Contributors</a></h2>
<li>Hughes Damry &lt;<a class="reference external" href="mailto:hughes.damry&#64;acsone.eu">hughes.damry&#64;acsone.eu</a>&gt;</li>
<li>Akim Juillerat &lt;<a class="reference external" href="mailto:akim.juillerat&#64;camptocamp.com">akim.juillerat&#64;camptocamp.com</a>&gt;</li>
<li>Jacques-Etienne Baudoux (BCIM) &lt;<a class="reference external" href="mailto:je&#64;bcim.be">je&#64;bcim.be</a>&gt;</li>
<li>Tris Doan &lt;<a class="reference external" href="mailto:tridm&#64;trobz.com">tridm&#64;trobz.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#toc-entry-12">Other credits</a></h2>
<p>The migration of this module from 17.0 to 18.0 was financially supported
by Camptocamp.</p>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-12">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-13">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @odoo-module */
import {Markup} from "web.utils";
import {markup} from "@odoo/owl";
import {_t} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry";

Expand Down Expand Up @@ -45,7 +44,7 @@ async function cupsReportActionHandler(action, options, env) {
issue_on: env._t("Issue on"),
};
const notificationRemove = env.services.notification.add(
Markup(
markup(
`<p>${terms.the_report} <strong>${action.name}</strong> ${terms.couldnt_be_printed}</p>`
),
{
Expand Down
6 changes: 3 additions & 3 deletions base_report_to_printer/tests/test_ir_actions_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,22 @@ def new_tray(self, vals=None, defaults=None):

def test_print_action_for_report_name_gets_report(self):
"""It should get report by name"""
with mock.patch("%s._get_report_from_name" % model) as mk:
with mock.patch(f"{model}._get_report_from_name") as mk:
expect = "test"
self.Model.print_action_for_report_name(expect)
mk.assert_called_once_with(expect)

def test_print_action_for_report_name_returns_if_no_report(self):
"""It should return empty dict when no matching report"""
with mock.patch("%s._get_report_from_name" % model) as mk:
with mock.patch(f"{model}._get_report_from_name") as mk:
expect = "test"
mk.return_value = False
res = self.Model.print_action_for_report_name(expect)
self.assertDictEqual({}, res)

def test_print_action_for_report_name_returns_if_report(self):
"""It should return correct serializable result for behaviour"""
with mock.patch("%s._get_report_from_name" % model) as mk:
with mock.patch(f"{model}._get_report_from_name") as mk:
res = self.Model.print_action_for_report_name("test")
behaviour = mk().behaviour()
expect = {
Expand Down
25 changes: 16 additions & 9 deletions base_report_to_printer/tests/test_printing_job.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import logging
from unittest import mock

from odoo import fields
Expand Down Expand Up @@ -42,17 +43,23 @@ def new_job(self, printer, vals=None):
values["printer_id"] = printer.id
return self.env["printing.job"].create(values)

@mock.patch("%s.cups" % model)
def test_cancel_job_error(self, cups):
def test_cancel_job_error(self):
"""It should catch any exception from CUPS and update status"""
cups.Connection.side_effect = Exception
printer = self.new_printer()
job = self.new_job(printer, {"job_id_cups": 2})
job.action_cancel()
cups.Connection.side_effect = None
self.assertEqual(cups.Connection().cancelJob.call_count, 0)
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception
printer = self.new_printer()
job = self.new_job(printer, {"job_id_cups": 2})
job.action_cancel()
cups.Connection.side_effect = None
self.assertEqual(cups.Connection().cancelJob.call_count, 0)

self.assertEqual(len(logs.records), 3)
self.assertEqual(logs.records[0].levelno, logging.WARNING)

@mock.patch("%s.cups" % model)
@mock.patch(f"{model}.cups")
def test_cancel_job(self, cups):
"""It should catch any exception from CUPS and update status"""
printer = self.new_printer()
Expand Down
84 changes: 50 additions & 34 deletions base_report_to_printer/tests/test_printing_printer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import logging
import tempfile
from unittest import mock

Expand Down Expand Up @@ -79,49 +80,64 @@ def test_print_options(self):
)
self.assertTrue("InputSlot" in self.Model.print_options(report, tray="Test"))

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_print_report(self, cups):
"""It should print a report through CUPS"""
fd, file_name = tempfile.mkstemp()
with mock.patch("%s.mkstemp" % model) as mkstemp:
with mock.patch(f"{model}.mkstemp") as mkstemp:
mkstemp.return_value = fd, file_name
printer = self.new_record()
printer.print_document(self.report, b"content to print", doc_format="pdf")
cups.Connection().printFile.assert_called_once_with(
printer.system_name, file_name, file_name, options={}
)

@mock.patch("%s.cups" % server_model)
def test_print_report_error(self, cups):
def test_print_report_error(self):
"""It should print a report through CUPS"""
cups.Connection.side_effect = Exception
fd, file_name = tempfile.mkstemp()
with mock.patch("%s.mkstemp" % model) as mkstemp:
mkstemp.return_value = fd, file_name
printer = self.new_record()
with self.assertRaises(UserError):
printer.print_document(
self.report, b"content to print", doc_format="pdf"
)

@mock.patch("%s.cups" % server_model)
def test_print_file(self, cups):
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception
fd, file_name = tempfile.mkstemp()
with mock.patch(f"{model}.mkstemp") as mkstemp:
mkstemp.return_value = fd, file_name
printer = self.new_record()
with self.assertRaises(UserError):
printer.print_document(
self.report, b"content to print", doc_format="pdf"
)
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)

def test_print_file(self):
"""It should print a file through CUPS"""
file_name = "file_name"
printer = self.new_record()
printer.print_file(file_name, "pdf")
cups.Connection().printFile.assert_called_once_with(
printer.system_name, file_name, file_name, options={}
)
with (
mock.patch(f"{server_model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
file_name = "file_name"
printer = self.new_record()
printer.print_file(file_name, "pdf")
cups.Connection().printFile.assert_called_once_with(
printer.system_name, file_name, file_name, options={}
)
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)

@mock.patch("%s.cups" % server_model)
def test_print_file_error(self, cups):
def test_print_file_error(self):
"""It should print a file through CUPS"""
cups.Connection.side_effect = Exception
file_name = "file_name"
printer = self.new_record()
with self.assertRaises(UserError):
printer.print_file(file_name)
with (
mock.patch(f"{server_model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception
file_name = "file_name"
printer = self.new_record()
with self.assertRaises(UserError):
printer.print_file(file_name)
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)

def test_set_default(self):
"""It should set a single record as default"""
Expand All @@ -142,7 +158,7 @@ def test_unset_default(self):
printer.unset_default()
self.assertFalse(printer.default)

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_cancel_all_jobs(self, cups):
"""It should cancel all jobs"""
printer = self.new_record()
Expand All @@ -151,7 +167,7 @@ def test_cancel_all_jobs(self, cups):
name=printer.system_name, purge_jobs=False
)

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_cancel_and_purge_all_jobs(self, cups):
"""It should cancel all jobs"""
printer = self.new_record()
Expand All @@ -160,21 +176,21 @@ def test_cancel_and_purge_all_jobs(self, cups):
name=printer.system_name, purge_jobs=True
)

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_enable_printer(self, cups):
"""It should enable the printer"""
printer = self.new_record()
printer.enable()
cups.Connection().enablePrinter.assert_called_once_with(printer.system_name)

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_disable_printer(self, cups):
"""It should disable the printer"""
printer = self.new_record()
printer.disable()
cups.Connection().disablePrinter.assert_called_once_with(printer.system_name)

@mock.patch("%s.cups" % server_model)
@mock.patch(f"{server_model}.cups")
def test_print_test_page(self, cups):
"""It should print a test page"""
printer = self.new_record()
Expand Down
Loading

0 comments on commit 4fd9a30

Please sign in to comment.