[MIG] base_report_to_printer: Migration to 18.0

This commit is contained in:
trisdoan
2024-10-14 11:10:05 +07:00
parent 05716e3f2c
commit d0276e819c
25 changed files with 342 additions and 229 deletions

View File

@@ -175,6 +175,13 @@ Contributors
- Hughes Damry <hughes.damry@acsone.eu> - Hughes Damry <hughes.damry@acsone.eu>
- Akim Juillerat <akim.juillerat@camptocamp.com> - Akim Juillerat <akim.juillerat@camptocamp.com>
- Jacques-Etienne Baudoux (BCIM) <je@bcim.be> - Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
- Tris Doan <tridm@trobz.com>
Other credits
-------------
The migration of this module from 17.0 to 18.0 was financially supported
by Camptocamp.
Maintainers Maintainers
----------- -----------

View File

@@ -7,7 +7,7 @@
{ {
"name": "Report to printer", "name": "Report to printer",
"version": "17.0.1.0.1", "version": "18.0.1.0.0",
"category": "Generic Modules/Base", "category": "Generic Modules/Base",
"author": "Agile Business Group & Domsense, Pegueroles SCP, NaN," "author": "Agile Business Group & Domsense, Pegueroles SCP, NaN,"
" LasLabs, Camptocamp, Odoo Community Association (OCA)," " LasLabs, Camptocamp, Odoo Community Association (OCA),"

View File

@@ -9,25 +9,19 @@
<field name="name">Send to Client</field> <field name="name">Send to Client</field>
<field name="action_type">client</field> <field name="action_type">client</field>
</record> </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"> <record forcecreate="True" id="ir_cron_update_printers" model="ir.cron">
<field name="name">Update Printers Jobs</field> <field name="name">Update Printers Jobs</field>
<field name="active" eval="True" /> <field name="active" eval="True" />
<field name="user_id" ref="base.user_root" /> <field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field> <field name="interval_number">1</field>
<field name="interval_type">minutes</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="model_id" ref="base_report_to_printer.model_printing_server" />
<field name="state">code</field> <field name="state">code</field>
<field name="code">model.action_update_jobs()</field> <field name="code">model.action_update_jobs()</field>
</record> </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> </odoo>

View File

@@ -157,7 +157,7 @@ class IrActionsReport(models.Model):
_("This report type (%s) is not supported by direct printing!") _("This report type (%s) is not supported by direct printing!")
% str(self.report_type) % str(self.report_type)
) )
method_name = "_render_qweb_%s" % (report_type) method_name = f"_render_qweb_{report_type}"
document, doc_format = getattr( document, doc_format = getattr(
self.with_context(must_skip_send_to_printer=True), method_name self.with_context(must_skip_send_to_printer=True), method_name
)(self.report_name, record_ids, data=data) )(self.report_name, record_ids, data=data)

View File

@@ -5,14 +5,14 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # 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): class PrintingAction(models.Model):
_name = "printing.action" _name = "printing.action"
_description = "Print Job Action" _description = "Print Job Action"
@api.model @property
def _available_action_types(self): def _available_action_types(self):
return [ return [
("server", "Send to Printer"), ("server", "Send to Printer"),

View File

@@ -178,7 +178,7 @@ class PrintingPrinter(models.Model):
options = {} options = {}
for option, value in print_opts.items(): for option, value in print_opts.items():
try: try:
options.update(getattr(self, "_set_option_%s" % option)(report, value)) options.update(getattr(self, f"_set_option_{option}")(report, value))
except AttributeError: except AttributeError:
options[option] = str(value) options[option] = str(value)
return options return options

View File

@@ -22,7 +22,7 @@ class PrintingReportXmlAction(models.Model):
comodel_name="res.users", string="User", required=True, ondelete="cascade" comodel_name="res.users", string="User", required=True, ondelete="cascade"
) )
action = fields.Selection( 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, required=True,
) )
printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer") printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer")

View File

@@ -11,19 +11,27 @@ from odoo import api, fields, models
class ResUsers(models.Model): class ResUsers(models.Model):
_inherit = "res.users" _inherit = "res.users"
@api.model @property
def _user_available_action_types(self): def _user_available_action_types(self):
return [ return [
(code, string) (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" 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( printing_printer_id = fields.Many2one(
comodel_name="printing.printer", string="Default Printer" 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 @property
def SELF_READABLE_FIELDS(self): def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + ["printing_action", "printing_printer_id"] return super().SELF_READABLE_FIELDS + ["printing_action", "printing_printer_id"]

View File

@@ -15,3 +15,4 @@
- Hughes Damry \<<hughes.damry@acsone.eu>\> - Hughes Damry \<<hughes.damry@acsone.eu>\>
- Akim Juillerat \<<akim.juillerat@camptocamp.com>\> - Akim Juillerat \<<akim.juillerat@camptocamp.com>\>
- Jacques-Etienne Baudoux (BCIM) \<<je@bcim.be>\> - Jacques-Etienne Baudoux (BCIM) \<<je@bcim.be>\>
- Tris Doan \<<tridm@trobz.com>\>

View File

@@ -0,0 +1 @@
The migration of this module from 17.0 to 18.0 was financially supported by Camptocamp.

View File

@@ -409,7 +409,8 @@ preprinted paper such as payment slip.</p>
<li><a class="reference internal" href="#credits" id="toc-entry-9">Credits</a><ul> <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="#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="#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> </ul>
</li> </li>
</ul> </ul>
@@ -528,10 +529,16 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<li>Hughes Damry &lt;<a class="reference external" href="mailto:hughes.damry&#64;acsone.eu">hughes.damry&#64;acsone.eu</a>&gt;</li> <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>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>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> </ul>
</div> </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"> <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> <p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"> <a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /> <img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />

View File

@@ -1,5 +1,4 @@
/** @odoo-module */ import {markup} from "@odoo/owl";
import {Markup} from "web.utils";
import {_t} from "@web/core/l10n/translation"; import {_t} from "@web/core/l10n/translation";
import {registry} from "@web/core/registry"; import {registry} from "@web/core/registry";
@@ -38,14 +37,14 @@ async function cupsReportActionHandler(action, options, env) {
// Just so the translation engine detects them as it doesn't do it inside // Just so the translation engine detects them as it doesn't do it inside
// template strings // template strings
const terms = { const terms = {
the_report: env._t("The report"), the_report: _t("The report"),
couldnt_be_printed: env._t( couldnt_be_printed: _t(
"couldn't be printed. Click on the button below to download it" "couldn't be printed. Click on the button below to download it"
), ),
issue_on: env._t("Issue on"), issue_on: _t("Issue on"),
}; };
const notificationRemove = env.services.notification.add( const notificationRemove = env.services.notification.add(
Markup( markup(
`<p>${terms.the_report} <strong>${action.name}</strong> ${terms.couldnt_be_printed}</p>` `<p>${terms.the_report} <strong>${action.name}</strong> ${terms.couldnt_be_printed}</p>`
), ),
{ {
@@ -55,7 +54,7 @@ async function cupsReportActionHandler(action, options, env) {
messageIsHtml: true, messageIsHtml: true,
buttons: [ buttons: [
{ {
name: env._t("Print"), name: _t("Print"),
primary: true, primary: true,
icon: "fa-print", icon: "fa-print",
onClick: async () => { onClick: async () => {

View File

@@ -2,6 +2,7 @@
# Copyright 2016 SYLEAM # Copyright 2016 SYLEAM
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from unittest import mock from unittest import mock
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
@@ -57,14 +58,14 @@ class TestIrActionsReportXml(TransactionCase):
def test_print_action_for_report_name_gets_report(self): def test_print_action_for_report_name_gets_report(self):
"""It should get report by name""" """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" expect = "test"
self.Model.print_action_for_report_name(expect) self.Model.print_action_for_report_name(expect)
mk.assert_called_once_with(expect) mk.assert_called_once_with(expect)
def test_print_action_for_report_name_returns_if_no_report(self): def test_print_action_for_report_name_returns_if_no_report(self):
"""It should return empty dict when no matching report""" """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" expect = "test"
mk.return_value = False mk.return_value = False
res = self.Model.print_action_for_report_name(expect) res = self.Model.print_action_for_report_name(expect)
@@ -72,7 +73,7 @@ class TestIrActionsReportXml(TransactionCase):
def test_print_action_for_report_name_returns_if_report(self): def test_print_action_for_report_name_returns_if_report(self):
"""It should return correct serializable result for behaviour""" """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") res = self.Model.print_action_for_report_name("test")
behaviour = mk().behaviour() behaviour = mk().behaviour()
expect = { expect = {
@@ -102,6 +103,9 @@ class TestIrActionsReportXml(TransactionCase):
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
self.env.user.printing_action = "client" self.env.user.printing_action = "client"
self.env.user.printing_printer_id = self.new_printer() self.env.user.printing_printer_id = self.new_printer()
with (
self.assertLogs(level=logging.WARNING) as logs,
):
self.assertEqual( self.assertEqual(
report.behaviour(), report.behaviour(),
{ {
@@ -110,6 +114,8 @@ class TestIrActionsReportXml(TransactionCase):
"tray": False, "tray": False,
}, },
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_behaviour_report_values(self): def test_behaviour_report_values(self):
"""It should return the action and printer from report""" """It should return the action and printer from report"""
@@ -117,6 +123,9 @@ class TestIrActionsReportXml(TransactionCase):
self.env.user.printing_action = "client" self.env.user.printing_action = "client"
report.property_printing_action_id = self.new_action() report.property_printing_action_id = self.new_action()
report.printing_printer_id = self.new_printer() report.printing_printer_id = self.new_printer()
with (
self.assertLogs(level=logging.WARNING) as logs,
):
self.assertEqual( self.assertEqual(
report.behaviour(), report.behaviour(),
{ {
@@ -125,6 +134,8 @@ class TestIrActionsReportXml(TransactionCase):
"tray": False, "tray": False,
}, },
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_behaviour_user_action(self): def test_behaviour_user_action(self):
"""It should return the action and printer from user action""" """It should return the action and printer from user action"""
@@ -188,6 +199,9 @@ class TestIrActionsReportXml(TransactionCase):
printing_action = self.new_printing_action() printing_action = self.new_printing_action()
printing_action.user_id = self.env.user printing_action.user_id = self.env.user
printing_action.printer_id = self.new_printer() printing_action.printer_id = self.new_printer()
with (
self.assertLogs(level=logging.WARNING) as logs,
):
self.assertEqual( self.assertEqual(
report.behaviour(), report.behaviour(),
{ {
@@ -196,6 +210,8 @@ class TestIrActionsReportXml(TransactionCase):
"tray": False, "tray": False,
}, },
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_behaviour_printing_action_user_defaults(self): def test_behaviour_printing_action_user_defaults(self):
"""It should return the action and printer from user with printing """It should return the action and printer from user with printing
@@ -215,12 +231,23 @@ class TestIrActionsReportXml(TransactionCase):
""" """
It should return the correct tray It should return the correct tray
""" """
with (
self.assertLogs(level=logging.WARNING) as logs,
):
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
action = self.env["printing.report.xml.action"].create( action = self.env["printing.report.xml.action"].create(
{"user_id": self.env.user.id, "report_id": report.id, "action": "server"} {
"user_id": self.env.user.id,
"report_id": report.id,
"action": "server",
}
) )
printer = self.new_printer() printer = self.new_printer()
tray_vals = {"name": "Tray", "system_name": "Tray", "printer_id": printer.id} tray_vals = {
"name": "Tray",
"system_name": "Tray",
"printer_id": printer.id,
}
user_tray = self.new_tray({"system_name": "User tray"}, tray_vals) user_tray = self.new_tray({"system_name": "User tray"}, tray_vals)
report_tray = self.new_tray({"system_name": "Report tray"}, tray_vals) report_tray = self.new_tray({"system_name": "Report tray"}, tray_vals)
action_tray = self.new_tray({"system_name": "Action tray"}, tray_vals) action_tray = self.new_tray({"system_name": "Action tray"}, tray_vals)
@@ -266,6 +293,9 @@ class TestIrActionsReportXml(TransactionCase):
report.printer_tray_id = report_tray report.printer_tray_id = report_tray
action.printer_tray_id = action_tray action.printer_tray_id = action_tray
self.assertEqual("Action tray", report.behaviour()["tray"]) self.assertEqual("Action tray", report.behaviour()["tray"])
self.assertEqual(len(logs.records), 6)
for record in logs.records:
self.assertEqual(record.levelno, logging.WARNING)
def test_onchange_printer_tray_id_empty(self): def test_onchange_printer_tray_id_empty(self):
action = self.Model.new({"printer_tray_id": False}) action = self.Model.new({"printer_tray_id": False})
@@ -305,6 +335,9 @@ class TestIrActionsReportXml(TransactionCase):
printing_action.user_id = self.env.user printing_action.user_id = self.env.user
printing_action.printer_id = self.new_printer() printing_action.printer_id = self.new_printer()
printing_action.printer_id.multi_thread = True printing_action.printer_id.multi_thread = True
with (
self.assertLogs(level=logging.WARNING) as logs,
):
self.assertEqual( self.assertEqual(
report.behaviour(), report.behaviour(),
{ {
@@ -313,3 +346,5 @@ class TestIrActionsReportXml(TransactionCase):
"tray": False, "tray": False,
}, },
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)

View File

@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from unittest import mock from unittest import mock
from odoo import fields from odoo import fields
@@ -42,9 +43,12 @@ class TestPrintingJob(TransactionCase):
values["printer_id"] = printer.id values["printer_id"] = printer.id
return self.env["printing.job"].create(values) return self.env["printing.job"].create(values)
@mock.patch("%s.cups" % model) def test_cancel_job_error(self):
def test_cancel_job_error(self, cups):
"""It should catch any exception from CUPS and update status""" """It should catch any exception from CUPS and update status"""
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
printer = self.new_printer() printer = self.new_printer()
job = self.new_job(printer, {"job_id_cups": 2}) job = self.new_job(printer, {"job_id_cups": 2})
@@ -52,7 +56,10 @@ class TestPrintingJob(TransactionCase):
cups.Connection.side_effect = None cups.Connection.side_effect = None
self.assertEqual(cups.Connection().cancelJob.call_count, 0) self.assertEqual(cups.Connection().cancelJob.call_count, 0)
@mock.patch("%s.cups" % model) self.assertEqual(len(logs.records), 3)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
@mock.patch(f"{model}.cups")
def test_cancel_job(self, cups): def test_cancel_job(self, cups):
"""It should catch any exception from CUPS and update status""" """It should catch any exception from CUPS and update status"""
printer = self.new_printer() printer = self.new_printer()

View File

@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
import tempfile import tempfile
from unittest import mock from unittest import mock
@@ -79,11 +80,11 @@ class TestPrintingPrinter(TransactionCase):
) )
self.assertTrue("InputSlot" in self.Model.print_options(report, tray="Test")) 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): def test_print_report(self, cups):
"""It should print a report through CUPS""" """It should print a report through CUPS"""
fd, file_name = tempfile.mkstemp() 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 mkstemp.return_value = fd, file_name
printer = self.new_record() printer = self.new_record()
printer.print_document(self.report, b"content to print", doc_format="pdf") printer.print_document(self.report, b"content to print", doc_format="pdf")
@@ -91,37 +92,52 @@ class TestPrintingPrinter(TransactionCase):
printer.system_name, file_name, file_name, options={} printer.system_name, file_name, file_name, options={}
) )
@mock.patch("%s.cups" % server_model) def test_print_report_error(self):
def test_print_report_error(self, cups):
"""It should print a report through CUPS""" """It should print a report through CUPS"""
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
fd, file_name = tempfile.mkstemp() 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 mkstemp.return_value = fd, file_name
printer = self.new_record() printer = self.new_record()
with self.assertRaises(UserError): with self.assertRaises(UserError):
printer.print_document( printer.print_document(
self.report, b"content to print", doc_format="pdf" self.report, b"content to print", doc_format="pdf"
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
@mock.patch("%s.cups" % server_model) def test_print_file(self):
def test_print_file(self, cups):
"""It should print a file through CUPS""" """It should print a file through CUPS"""
with (
mock.patch(f"{server_model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
file_name = "file_name" file_name = "file_name"
printer = self.new_record() printer = self.new_record()
printer.print_file(file_name, "pdf") printer.print_file(file_name, "pdf")
cups.Connection().printFile.assert_called_once_with( cups.Connection().printFile.assert_called_once_with(
printer.system_name, file_name, file_name, options={} 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):
def test_print_file_error(self, cups):
"""It should print a file through CUPS""" """It should print a file through CUPS"""
with (
mock.patch(f"{server_model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
file_name = "file_name" file_name = "file_name"
printer = self.new_record() printer = self.new_record()
with self.assertRaises(UserError): with self.assertRaises(UserError):
printer.print_file(file_name) printer.print_file(file_name)
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_set_default(self): def test_set_default(self):
"""It should set a single record as default""" """It should set a single record as default"""
@@ -142,7 +158,7 @@ class TestPrintingPrinter(TransactionCase):
printer.unset_default() printer.unset_default()
self.assertFalse(printer.default) self.assertFalse(printer.default)
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_cancel_all_jobs(self, cups): def test_cancel_all_jobs(self, cups):
"""It should cancel all jobs""" """It should cancel all jobs"""
printer = self.new_record() printer = self.new_record()
@@ -151,7 +167,7 @@ class TestPrintingPrinter(TransactionCase):
name=printer.system_name, purge_jobs=False 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): def test_cancel_and_purge_all_jobs(self, cups):
"""It should cancel all jobs""" """It should cancel all jobs"""
printer = self.new_record() printer = self.new_record()
@@ -160,21 +176,21 @@ class TestPrintingPrinter(TransactionCase):
name=printer.system_name, purge_jobs=True 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): def test_enable_printer(self, cups):
"""It should enable the printer""" """It should enable the printer"""
printer = self.new_record() printer = self.new_record()
printer.enable() printer.enable()
cups.Connection().enablePrinter.assert_called_once_with(printer.system_name) 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): def test_disable_printer(self, cups):
"""It should disable the printer""" """It should disable the printer"""
printer = self.new_record() printer = self.new_record()
printer.disable() printer.disable()
cups.Connection().disablePrinter.assert_called_once_with(printer.system_name) 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): def test_print_test_page(self, cups):
"""It should print a test page""" """It should print a test page"""
printer = self.new_record() printer = self.new_record()

View File

@@ -99,7 +99,7 @@ class TestPrintingPrinter(TransactionCase):
} }
} }
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_update_printers(self, cups): def test_update_printers(self, cups):
""" """
Check that the update_printers method calls _prepare_update_from_cups Check that the update_printers method calls _prepare_update_from_cups
@@ -111,7 +111,7 @@ class TestPrintingPrinter(TransactionCase):
self.ServerModel.update_printers() self.ServerModel.update_printers()
self.assertEqual(self.printer.name, "My custom name") self.assertEqual(self.printer.name, "My custom name")
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups_no_ppd(self, cups): def test_prepare_update_from_cups_no_ppd(self, cups):
""" """
Check that the tray_ids field has no value when no PPD is available Check that the tray_ids field has no value when no PPD is available
@@ -124,7 +124,7 @@ class TestPrintingPrinter(TransactionCase):
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertFalse("tray_ids" in vals) self.assertFalse("tray_ids" in vals)
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups_empty_ppd(self, cups): def test_prepare_update_from_cups_empty_ppd(self, cups):
""" """
Check that the tray_ids field has no value when the PPD file has Check that the tray_ids field has no value when the PPD file has
@@ -142,7 +142,7 @@ class TestPrintingPrinter(TransactionCase):
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertFalse("tray_ids" in vals) self.assertFalse("tray_ids" in vals)
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
@mock.patch("os.unlink") @mock.patch("os.unlink")
def test_prepare_update_from_cups_unlink_error(self, os_unlink, cups): def test_prepare_update_from_cups_unlink_error(self, os_unlink, cups):
""" """
@@ -159,7 +159,7 @@ class TestPrintingPrinter(TransactionCase):
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.printer._prepare_update_from_cups(connection, cups_printer) self.printer._prepare_update_from_cups(connection, cups_printer)
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
@mock.patch("os.unlink") @mock.patch("os.unlink")
def test_prepare_update_from_cups_unlink_error_enoent(self, os_unlink, cups): def test_prepare_update_from_cups_unlink_error_enoent(self, os_unlink, cups):
""" """
@@ -181,7 +181,7 @@ class TestPrintingPrinter(TransactionCase):
[(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})], [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
) )
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups(self, cups): def test_prepare_update_from_cups(self, cups):
""" """
Check the return value when adding a single tray Check the return value when adding a single tray
@@ -197,7 +197,7 @@ class TestPrintingPrinter(TransactionCase):
[(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})], [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
) )
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups_with_multiple_trays(self, cups): def test_prepare_update_from_cups_with_multiple_trays(self, cups):
""" """
Check the return value when adding multiple trays at once Check the return value when adding multiple trays at once
@@ -216,7 +216,7 @@ class TestPrintingPrinter(TransactionCase):
], ],
) )
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups_already_known_trays(self, cups): def test_prepare_update_from_cups_already_known_trays(self, cups):
""" """
Check that calling the method twice doesn't create the trays multiple Check that calling the method twice doesn't create the trays multiple
@@ -236,7 +236,7 @@ class TestPrintingPrinter(TransactionCase):
[(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})], [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
) )
@mock.patch("%s.cups" % server_model) @mock.patch(f"{server_model}.cups")
def test_prepare_update_from_cups_unknown_trays(self, cups): def test_prepare_update_from_cups_unknown_trays(self, cups):
""" """
Check that trays which are not in the PPD file are removed from Odoo Check that trays which are not in the PPD file are removed from Odoo

View File

@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from unittest import mock from unittest import mock
from odoo.exceptions import UserError from odoo.exceptions import UserError
@@ -36,7 +37,7 @@ class TestPrintingPrinterWizard(TransactionCase):
"uri": self.printer_vals["device-uri"], "uri": self.printer_vals["device-uri"],
} }
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_action_ok_inits_connection(self, cups): def test_action_ok_inits_connection(self, cups):
"""It should initialize CUPS connection""" """It should initialize CUPS connection"""
self.Model.action_ok() self.Model.action_ok()
@@ -44,7 +45,7 @@ class TestPrintingPrinterWizard(TransactionCase):
host=self.server.address, port=self.server.port host=self.server.address, port=self.server.port
) )
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_action_ok_gets_printers(self, cups): def test_action_ok_gets_printers(self, cups):
"""It should get printers from CUPS""" """It should get printers from CUPS"""
cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals} cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
@@ -52,14 +53,19 @@ class TestPrintingPrinterWizard(TransactionCase):
self.Model.action_ok() self.Model.action_ok()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
@mock.patch("%s.cups" % model) def test_action_ok_raises_warning_on_error(self):
def test_action_ok_raises_warning_on_error(self, cups):
"""It should raise Warning on any error""" """It should raise Warning on any error"""
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = StopTest cups.Connection.side_effect = StopTest
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.Model.action_ok() self.Model.action_ok()
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_action_ok_creates_new_printer(self, cups): def test_action_ok_creates_new_printer(self, cups):
"""It should create new printer w/ proper vals""" """It should create new printer w/ proper vals"""
cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals} cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
@@ -75,7 +81,7 @@ class TestPrintingPrinterWizard(TransactionCase):
self.assertEqual(val, rec_id[key]) self.assertEqual(val, rec_id[key])
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_action_ok_skips_existing_printer(self, cups): def test_action_ok_skips_existing_printer(self, cups):
"""It should not recreate existing printers""" """It should not recreate existing printers"""
cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals} cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}

View File

@@ -1,6 +1,7 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from unittest import mock from unittest import mock
from odoo import fields from odoo import fields
@@ -43,15 +44,20 @@ class TestPrintingServer(TransactionCase):
values["printer_id"] = printer.id values["printer_id"] = printer.id
return self.env["printing.job"].create(values) return self.env["printing.job"].create(values)
@mock.patch("%s.cups" % model) def test_update_printers_error(self):
def test_update_printers_error(self, cups):
"""It should catch any exception from CUPS and update status""" """It should catch any exception from CUPS and update status"""
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
rec_id = self.new_printer() rec_id = self.new_printer()
self.Model.update_printers() self.Model.update_printers()
self.assertEqual("server-error", rec_id.status) self.assertEqual("server-error", rec_id.status)
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_printers_inits_cups(self, cups): def test_update_printers_inits_cups(self, cups):
"""It should init CUPS connection""" """It should init CUPS connection"""
self.new_printer() self.new_printer()
@@ -60,29 +66,29 @@ class TestPrintingServer(TransactionCase):
host=self.server.address, port=self.server.port host=self.server.address, port=self.server.port
) )
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_printers_gets_all_printers(self, cups): def test_update_printers_gets_all_printers(self, cups):
"""It should get all printers from CUPS server""" """It should get all printers from CUPS server"""
self.new_printer() self.new_printer()
self.Model.update_printers() self.Model.update_printers()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_printers_search(self, cups): def test_update_printers_search(self, cups):
"""It should search all when no domain""" """It should search all when no domain"""
with mock.patch("%s.search" % model_base) as search: with mock.patch(f"{model_base}.search") as search:
self.Model.update_printers() self.Model.update_printers()
search.assert_called_once_with([]) search.assert_called_once_with([])
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_printers_search_domain(self, cups): def test_update_printers_search_domain(self, cups):
"""It should use specific domain for search""" """It should use specific domain for search"""
with mock.patch("%s.search" % model_base) as search: with mock.patch(f"{model_base}.search") as search:
expect = [("id", ">", 0)] expect = [("id", ">", 0)]
self.Model.update_printers(expect) self.Model.update_printers(expect)
search.assert_called_once_with(expect) search.assert_called_once_with(expect)
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_printers_update_unavailable(self, cups): def test_update_printers_update_unavailable(self, cups):
"""It should update status when printer is unavailable""" """It should update status when printer is unavailable"""
rec_id = self.new_printer() rec_id = self.new_printer()
@@ -90,7 +96,7 @@ class TestPrintingServer(TransactionCase):
self.Model.action_update_printers() self.Model.action_update_printers()
self.assertEqual("unavailable", rec_id.status) self.assertEqual("unavailable", rec_id.status)
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_archived_printers(self, cups): def test_update_archived_printers(self, cups):
"""It should update status even if printer is archived""" """It should update status even if printer is archived"""
rec_id = self.new_printer() rec_id = self.new_printer()
@@ -103,7 +109,7 @@ class TestPrintingServer(TransactionCase):
rec_id.status, rec_id.status,
) )
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_jobs_cron(self, cups): def test_update_jobs_cron(self, cups):
"""It should get all jobs from CUPS server""" """It should get all jobs from CUPS server"""
self.new_printer() self.new_printer()
@@ -125,7 +131,7 @@ class TestPrintingServer(TransactionCase):
], ],
) )
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_jobs_button(self, cups): def test_update_jobs_button(self, cups):
"""It should get all jobs from CUPS server""" """It should get all jobs from CUPS server"""
self.new_printer() self.new_printer()
@@ -147,17 +153,22 @@ class TestPrintingServer(TransactionCase):
], ],
) )
@mock.patch("%s.cups" % model) def test_update_jobs_error(self):
def test_update_jobs_error(self, cups):
"""It should catch any exception from CUPS and update status""" """It should catch any exception from CUPS and update status"""
with (
mock.patch(f"{model}.cups") as cups,
self.assertLogs(level=logging.WARNING) as logs,
):
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
self.new_printer() self.new_printer()
self.server.update_jobs() self.server.update_jobs()
cups.Connection.assert_called_with( cups.Connection.assert_called_with(
host=self.server.address, port=self.server.port host=self.server.address, port=self.server.port
) )
self.assertEqual(len(logs.records), 2)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_jobs_uncompleted(self, cups): def test_update_jobs_uncompleted(self, cups):
""" """
It should search which jobs have been completed since last update It should search which jobs have been completed since last update
@@ -182,7 +193,7 @@ class TestPrintingServer(TransactionCase):
], ],
) )
@mock.patch("%s.cups" % model) @mock.patch(f"{model}.cups")
def test_update_jobs(self, cups): def test_update_jobs(self, cups):
""" """
It should update all jobs, known or not It should update all jobs, known or not

View File

@@ -2,6 +2,7 @@
# Copyright 2017 Tecnativa - Jairo Llopis # Copyright 2017 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from unittest import mock from unittest import mock
from odoo import exceptions from odoo import exceptions
@@ -128,11 +129,14 @@ class TestReport(common.HttpCase):
def test_render_qweb_pdf_printable(self): def test_render_qweb_pdf_printable(self):
"""It should print the report, only if it is printable""" """It should print the report, only if it is printable"""
with mock.patch( with (
mock.patch(
"odoo.addons.base_report_to_printer.models." "odoo.addons.base_report_to_printer.models."
"printing_printer.PrintingPrinter." "printing_printer.PrintingPrinter."
"print_document" "print_document"
) as print_document: ) as print_document,
self.assertLogs(level=logging.WARNING) as logs,
):
self.report.property_printing_action_id.action_type = "server" self.report.property_printing_action_id.action_type = "server"
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
document = self.report._render_qweb_pdf( document = self.report._render_qweb_pdf(
@@ -145,14 +149,19 @@ class TestReport(common.HttpCase):
doc_format="qweb-pdf", doc_format="qweb-pdf",
tray=False, tray=False,
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_render_qweb_text_printable(self): def test_render_qweb_text_printable(self):
"""It should print the report, only if it is printable""" """It should print the report, only if it is printable"""
with mock.patch( with (
mock.patch(
"odoo.addons.base_report_to_printer.models." "odoo.addons.base_report_to_printer.models."
"printing_printer.PrintingPrinter." "printing_printer.PrintingPrinter."
"print_document" "print_document"
) as print_document: ) as print_document,
self.assertLogs(level=logging.WARNING) as logs,
):
self.report_text.property_printing_action_id.action_type = "server" self.report_text.property_printing_action_id.action_type = "server"
self.report_text.printing_printer_id = self.new_printer() self.report_text.printing_printer_id = self.new_printer()
document = self.report_text._render_qweb_text( document = self.report_text._render_qweb_text(
@@ -165,29 +174,41 @@ class TestReport(common.HttpCase):
doc_format="qweb-text", doc_format="qweb-text",
tray=False, tray=False,
) )
self.assertEqual(len(logs.records), 1)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_print_document_not_printable(self): def test_print_document_not_printable(self):
"""It should print the report, regardless of the defined behaviour""" """It should print the report, regardless of the defined behaviour"""
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
with mock.patch( with (
mock.patch(
"odoo.addons.base_report_to_printer.models." "odoo.addons.base_report_to_printer.models."
"printing_printer.PrintingPrinter." "printing_printer.PrintingPrinter."
"print_document" "print_document"
) as print_document: ) as print_document,
self.assertLogs(level=logging.WARNING) as logs,
):
self.report.print_document(self.partners.ids) self.report.print_document(self.partners.ids)
print_document.assert_called_once() print_document.assert_called_once()
self.assertEqual(len(logs.records), 2)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_print_document_printable(self): def test_print_document_printable(self):
"""It should print the report, regardless of the defined behaviour""" """It should print the report, regardless of the defined behaviour"""
self.report.property_printing_action_id.action_type = "server" self.report.property_printing_action_id.action_type = "server"
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
with mock.patch( with (
mock.patch(
"odoo.addons.base_report_to_printer.models." "odoo.addons.base_report_to_printer.models."
"printing_printer.PrintingPrinter." "printing_printer.PrintingPrinter."
"print_document" "print_document"
) as print_document: ) as print_document,
self.assertLogs(level=logging.WARNING) as logs,
):
self.report.print_document(self.partners.ids) self.report.print_document(self.partners.ids)
print_document.assert_called_once() print_document.assert_called_once()
self.assertEqual(len(logs.records), 2)
self.assertEqual(logs.records[0].levelno, logging.WARNING)
def test_print_document_no_printer(self): def test_print_document_no_printer(self):
"""It should raise an error""" """It should raise an error"""

View File

@@ -36,11 +36,11 @@
<field name="name">printing.job.tree (in base_report_to_printer)</field> <field name="name">printing.job.tree (in base_report_to_printer)</field>
<field name="model">printing.job</field> <field name="model">printing.job</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list>
<field name="name" /> <field name="name" />
<field name="job_id_cups" /> <field name="job_id_cups" />
<field name="job_state" /> <field name="job_state" />
</tree> </list>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -101,7 +101,7 @@
<field name="name">printing.printer.tree (in base_report_to_printer)</field> <field name="name">printing.printer.tree (in base_report_to_printer)</field>
<field name="model">printing.printer</field> <field name="model">printing.printer</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree <list
decoration-success="status=='available'" decoration-success="status=='available'"
decoration-warning="status=='printing'" decoration-warning="status=='printing'"
decoration-danger="status not in ['printing', 'available']" decoration-danger="status not in ['printing', 'available']"
@@ -111,7 +111,7 @@
<field name="system_name" /> <field name="system_name" />
<field name="server_id" /> <field name="server_id" />
<field name="status" /> <field name="status" />
</tree> </list>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="printing_printer_view_search"> <record model="ir.ui.view" id="printing_printer_view_search">
@@ -135,7 +135,7 @@
<field name="name">Show Printers</field> <field name="name">Show Printers</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">printing.printer</field> <field name="res_model">printing.printer</field>
<field name="view_mode">tree,form</field> <field name="view_mode">list,form</field>
</record> </record>
<menuitem <menuitem
name="Printers" name="Printers"

View File

@@ -21,12 +21,12 @@
>printing.report.xml.action.tree (in base_report_to_printer)</field> >printing.report.xml.action.tree (in base_report_to_printer)</field>
<field name="model">printing.report.xml.action</field> <field name="model">printing.report.xml.action</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list>
<field name="user_id" /> <field name="user_id" />
<field name="action" /> <field name="action" />
<field name="printer_id" /> <field name="printer_id" />
<field name="printer_tray_id" /> <field name="printer_tray_id" />
</tree> </list>
</field> </field>
</record> </record>
<!-- Add a shorcut to "Actions/Report" in the Printing menu --> <!-- Add a shorcut to "Actions/Report" in the Printing menu -->

View File

@@ -45,11 +45,11 @@
<field name="name">printing.server.tree (in base_report_to_printer)</field> <field name="name">printing.server.tree (in base_report_to_printer)</field>
<field name="model">printing.server</field> <field name="model">printing.server</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list>
<field name="name" /> <field name="name" />
<field name="address" /> <field name="address" />
<field name="port" /> <field name="port" />
</tree> </list>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="printing_server_view_search"> <record model="ir.ui.view" id="printing_server_view_search">
@@ -67,7 +67,7 @@
<field name="name">Servers</field> <field name="name">Servers</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">printing.server</field> <field name="res_model">printing.server</field>
<field name="view_mode">tree,form</field> <field name="view_mode">list,form</field>
</record> </record>
<menuitem <menuitem
name="Servers" name="Servers"

View File

@@ -8,11 +8,11 @@
<group> <group>
<field name="printer_id" /> <field name="printer_id" />
<field name="attachment_line_ids"> <field name="attachment_line_ids">
<tree editable="top"> <list editable="top">
<field name="attachment_id" create="0" /> <field name="attachment_id" create="0" />
<field name="record_name" /> <field name="record_name" />
<field name="copies" /> <field name="copies" />
</tree> </list>
</field> </field>
</group> </group>
<footer> <footer>

View File

@@ -20,7 +20,7 @@ class PrintingPrinterUpdateWizard(models.TransientModel):
return { return {
"name": "Printers", "name": "Printers",
"view_mode": "tree,form", "view_mode": "list,form",
"res_model": "printing.printer", "res_model": "printing.printer",
"type": "ir.actions.act_window", "type": "ir.actions.act_window",
"target": "current", "target": "current",