Merge PR #379 into 16.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2024-11-14 18:22:24 +00:00
15 changed files with 186 additions and 41 deletions

View File

@@ -0,0 +1,5 @@
{
"pull_requests": {
"OCA/report-print-send#275": "It's the migration PR..."
}
}

View File

@@ -106,6 +106,13 @@ Guidelines for use:
When no tray is configured for a report and a user, the
default tray setup on the CUPS server is used.
Known issues / Roadmap
======================
- With threaded printing there's no download fallback when the issue isn't detected by
the CUPS Odoo backend. To able to do it, we would need to notify the bus or use
web_notify for it.
Changelog
=========
@@ -163,6 +170,11 @@ Contributors
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
* `Tecnativa <https://www.tecnativa.com>`_:
* Sergio Teruel
* David Vidal
Maintainers
~~~~~~~~~~~

View File

@@ -18,6 +18,7 @@
"data": [
"data/printing_data.xml",
"security/security.xml",
"security/ir.model.access.csv",
"views/printing_printer.xml",
"views/printing_server.xml",
"views/printing_job.xml",

View File

@@ -3,9 +3,11 @@
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# Copyright 2024 Tecnativa - Sergio Teruel
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import threading
from odoo import _, api, exceptions, fields, models
from odoo import _, api, exceptions, fields, models, registry
from odoo.tools.safe_eval import safe_eval, time
REPORT_TYPES = {"qweb-pdf": "pdf", "qweb-text": "text"}
@@ -57,6 +59,8 @@ class IrActionsReport(models.Model):
"skip_printer_exception"
):
serializable_result["printer_exception"] = True
if self.env.context.get("force_print_to_client"):
serializable_result["action"] = "client"
return serializable_result
def _get_user_default_print_behaviour(self):
@@ -118,6 +122,32 @@ class IrActionsReport(models.Model):
result["printer_exception"] = True
return result
def print_document_client_action(self, record_ids, data=None):
behaviour = self.behaviour()
printer = behaviour.pop("printer", None)
if printer.multi_thread:
@self.env.cr.postcommit.add
def _launch_print_thread():
threaded_calculation = threading.Thread(
target=self.print_document_threaded,
args=(self.id, record_ids, data),
)
threaded_calculation.start()
return True
else:
try:
return self.print_document(record_ids, data=data)
except Exception:
return
def print_document_threaded(self, report_id, record_ids, data):
with registry(self._cr.dbname).cursor() as cr:
self = self.with_env(self.env(cr=cr))
report = self.env["ir.actions.report"].browse(report_id)
report.print_document(record_ids, data)
def print_document(self, record_ids, data=None):
"""Print a document, do not return the document file"""
report_type = REPORT_TYPES.get(self.report_type)
@@ -146,6 +176,7 @@ class IrActionsReport(models.Model):
else:
title = self.report_name
behaviour["title"] = title
behaviour["res_ids"] = record_ids
# TODO should we use doc_format instead of report_type
return printer.print_document(
self, document, doc_format=self.report_type, **behaviour

View File

@@ -12,7 +12,7 @@ import logging
import os
from tempfile import mkstemp
from odoo import fields, models
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
@@ -67,11 +67,19 @@ class PrintingPrinter(models.Model):
tray_ids = fields.One2many(
comodel_name="printing.tray", inverse_name="printer_id", string="Paper Sources"
)
multi_thread = fields.Boolean(
compute="_compute_multi_thread", readonly=False, store=True
)
@api.depends("server_id.multi_thread")
def _compute_multi_thread(self):
for printer in self:
printer.multi_thread = printer.server_id.multi_thread
def _prepare_update_from_cups(self, cups_connection, cups_printer):
mapping = {3: "available", 4: "printing", 5: "error"}
cups_vals = {
"name": cups_printer["printer-info"],
"name": self.name or cups_printer["printer-info"],
"model": cups_printer.get("printer-make-and-model", False),
"location": cups_printer.get("printer-location", False),
"uri": cups_printer.get("device-uri", False),

View File

@@ -42,6 +42,7 @@ class PrintingServer(models.Model):
string="Printers List",
help="List of printers available on this server.",
)
multi_thread = fields.Boolean()
def _open_connection(self, raise_on_error=False):
self.ensure_one()
@@ -121,6 +122,11 @@ class PrintingServer(models.Model):
printer_values["server_id"] = server.id
updated_printers.append(name)
# We want to keep any existing customized name over existing printer
# We want also to rely in the system name as a fallback to avoid
# empty names.
if not printer_values.get("name") and not printer.name:
printer_values["name"] = name
if not printer:
printer_values["system_name"] = name
printer.create(printer_values)

View File

@@ -15,3 +15,8 @@
* Hughes Damry <hughes.damry@acsone.eu>
* Akim Juillerat <akim.juillerat@camptocamp.com>
* Jacques-Etienne Baudoux (BCIM) <je@bcim.be>
* `Tecnativa <https://www.tecnativa.com>`_:
* Sergio Teruel
* David Vidal

View File

@@ -0,0 +1,3 @@
- With threaded printing there's no download fallback when the issue isn't detected by
the CUPS Odoo backend. To able to do it, we would need to notify the bus or use
web_notify for it.

View File

@@ -0,0 +1,2 @@
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_printing_printer_update_wizard,printers update,model_printing_printer_update_wizard,base.group_system,1,1,1,1
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
2 access_printing_printer_update_wizard printers update model_printing_printer_update_wizard base.group_system 1 1 1 1

View File

@@ -396,16 +396,17 @@ preprinted paper such as payment slip.</p>
<li><a class="reference internal" href="#installation" id="toc-entry-1">Installation</a></li>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-3">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="toc-entry-4">Changelog</a><ul>
<li><a class="reference internal" href="#section-1" id="toc-entry-5">13.0.1.0.0 (2019-09-30)</a></li>
<li><a class="reference internal" href="#section-2" id="toc-entry-6">12.0.1.0.0 (2018-02-04)</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-4">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#changelog" id="toc-entry-5">Changelog</a><ul>
<li><a class="reference internal" href="#section-1" id="toc-entry-6">13.0.1.0.0 (2019-09-30)</a></li>
<li><a class="reference internal" href="#section-2" id="toc-entry-7">12.0.1.0.0 (2018-02-04)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-7">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-8">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-9">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-10">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-11">Maintainers</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-8">Bug Tracker</a></li>
<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>
</ul>
</li>
</ul>
@@ -456,23 +457,31 @@ change these in <em>Settings &gt; Printing &gt; Reports</em> in
<p>When no tray is configured for a report and a user, the
default tray setup on the CUPS server is used.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-4">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>With threaded printing theres no download fallback when the issue isnt detected by
the CUPS Odoo backend. To able to do it, we would need to notify the bus or use
web_notify for it.</li>
</ul>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#toc-entry-4">Changelog</a></h1>
<h1><a class="toc-backref" href="#toc-entry-5">Changelog</a></h1>
<div class="section" id="section-1">
<h2><a class="toc-backref" href="#toc-entry-5">13.0.1.0.0 (2019-09-30)</a></h2>
<h2><a class="toc-backref" href="#toc-entry-6">13.0.1.0.0 (2019-09-30)</a></h2>
<ul class="simple">
<li>[RELEASE] Port from V12.</li>
</ul>
</div>
<div class="section" id="section-2">
<h2><a class="toc-backref" href="#toc-entry-6">12.0.1.0.0 (2018-02-04)</a></h2>
<h2><a class="toc-backref" href="#toc-entry-7">12.0.1.0.0 (2018-02-04)</a></h2>
<ul class="simple">
<li>[RELEASE] Port from V11.</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-7">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#toc-entry-8">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/report-print-send/issues">GitHub Issues</a>.
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
@@ -480,9 +489,9 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-8">Credits</a></h1>
<h1><a class="toc-backref" href="#toc-entry-9">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-9">Authors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-10">Authors</a></h2>
<ul class="simple">
<li>Agile Business Group &amp; Domsense</li>
<li>Pegueroles SCP</li>
@@ -493,7 +502,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-10">Contributors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-11">Contributors</a></h2>
<ul class="simple">
<li>Ferran Pegueroles &lt;<a class="reference external" href="mailto:ferran&#64;pegueroles.com">ferran&#64;pegueroles.com</a>&gt;</li>
<li>Albert Cervera i Areny &lt;<a class="reference external" href="mailto:albert&#64;nan-tic.com">albert&#64;nan-tic.com</a>&gt;</li>
@@ -512,10 +521,15 @@ 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>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><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>Sergio Teruel</li>
<li>David Vidal</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-12">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" />

View File

@@ -1,4 +1,5 @@
/** @odoo-module */
import {Markup} from "web.utils";
import {registry} from "@web/core/registry";
async function cupsReportActionHandler(action, options, env) {
@@ -8,36 +9,73 @@ async function cupsReportActionHandler(action, options, env) {
const print_action = await orm.call(
"ir.actions.report",
"print_action_for_report_name",
[action.report_name]
[action.report_name],
{context: {force_print_to_client: action.context.force_print_to_client}}
);
if (
print_action &&
print_action.action === "server" &&
!print_action.printer_exception
) {
const result = await orm.call("ir.actions.report", "print_document", [
action.id,
action.context.active_ids,
action.data,
]);
var printer_exception = print_action.printer_exception;
if (print_action && print_action.action === "server" && !printer_exception) {
// The Odoo CUPS backend is ok. We try to print into the printer
const result = await orm.call(
"ir.actions.report",
"print_document_client_action",
[action.id, action.context.active_ids, action.data]
);
if (result) {
env.services.notification.add(env._t("Successfully sent to printer!"), {
type: "success",
});
} else {
env.services.notification.add(env._t("Could not send to printer!"), {
type: "danger",
});
return true;
// In case of exception during the job, we won't get any response. So we
// should flag the exception and notify the user
}
return true;
env.services.notification.add(env._t("Could not sent to printer!"), {
type: "danger",
});
printer_exception = true;
}
if (print_action.printer_exception) {
env.services.notification.add(
env._t("The printer couldn't be reached. Downloading document instead"),
if (print_action && print_action.action === "server" && printer_exception) {
// Just so the translation engine detects them as it doesn't do it inside
// template strings
const terms = {
the_report: env._t("The report"),
couldnt_be_printed: env._t(
"couldn't be printed. Click on the button below to download it"
),
issue_on: env._t("Issue on"),
};
const notificationRemove = env.services.notification.add(
Markup(
`<p>${terms.the_report} <strong>${action.name}</strong> ${terms.couldnt_be_printed}</p>`
),
{
title: `${terms.issue_on} ${print_action.printer_name}`,
type: "warning",
sticky: true,
messageIsHtml: true,
buttons: [
{
name: env._t("Print"),
primary: true,
icon: "fa-print",
onClick: async () => {
const context = {
force_print_to_client: true,
must_skip_send_to_printer: true,
};
env.services.user.updateContext(context);
await env.services.action.doAction(
{type: "ir.actions.report", ...action},
{
additionalContext: context,
}
);
notificationRemove();
},
},
],
}
);
return true;
}
}
}

View File

@@ -295,3 +295,20 @@ class TestIrActionsReportXml(TransactionCase):
self.assertEqual(action.printer_tray_id, tray)
action.onchange_printing_printer_id()
self.assertFalse(action.printer_tray_id)
def test_print_in_new_thread(self):
"""It should return the action and printer from printing action in other thread"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = "server"
printing_action = self.new_printing_action()
printing_action.user_id = self.env.user
printing_action.printer_id = self.new_printer()
printing_action.printer_id.multi_thread = True
self.assertEqual(
report.behaviour(),
{
"action": printing_action.action,
"printer": printing_action.printer_id,
"tray": False,
},
)

View File

@@ -40,7 +40,7 @@ class TestPrintingPrinter(TransactionCase):
self.server = self.env["printing.server"].create({})
self.printer = self.env["printing.printer"].create(
{
"name": "Printer",
"name": "",
"server_id": self.server.id,
"system_name": "Sys Name",
"default": True,
@@ -105,10 +105,11 @@ class TestPrintingPrinter(TransactionCase):
Check that the update_printers method calls _prepare_update_from_cups
"""
self.mock_cups_ppd(cups, file_name=False)
self.assertEqual(self.printer.name, "Printer")
self.ServerModel.update_printers()
self.assertEqual(self.printer.name, "info")
self.printer.name = "My custom name"
self.ServerModel.update_printers()
self.assertEqual(self.printer.name, "My custom name")
@mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups_no_ppd(self, cups):

View File

@@ -78,6 +78,7 @@
<field name="location" />
<field name="status" />
<field name="status_message" />
<field name="multi_thread" />
</group>
<group string="Trays" name="trays">
<field name="tray_ids" nolabel="1" colspan="2">

View File

@@ -31,6 +31,7 @@
<field name="user" />
<field name="password" />
<field name="encryption_policy" />
<field name="multi_thread" />
</group>
<group name="printers" string="Printers">
<field name="printer_ids" nolabel="1" colspan="2" />