mirror of
https://github.com/OCA/report-print-send.git
synced 2025-02-16 07:11:31 +02:00
@@ -89,9 +89,8 @@ this will not be displayed by CUPS web interface or in Odoo. To see this
|
|||||||
information, you need to change the configuration of your CUPS server
|
information, you need to change the configuration of your CUPS server
|
||||||
and set the JobPrivateValue directive to "none" (or some other list of
|
and set the JobPrivateValue directive to "none" (or some other list of
|
||||||
values which does not include "job-name") , and reload the server. See
|
values which does not include "job-name") , and reload the server. See
|
||||||
cupsd.conf(5)
|
cupsd.conf(5) <https://www.cups.org/doc/man-cupsd.conf.html> for
|
||||||
<`https://www.cups.org/doc/man-cupsd.conf.html\\> <https://www.cups.org/doc/man-cupsd.conf.html\>>`__
|
details.
|
||||||
for details.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
@@ -112,6 +111,13 @@ Guidelines for use:
|
|||||||
When no tray is configured for a report and a user, the default tray
|
When no tray is configured for a report and a user, the default tray
|
||||||
setup on the CUPS server is used.
|
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
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"data": [
|
"data": [
|
||||||
"data/printing_data.xml",
|
"data/printing_data.xml",
|
||||||
"security/security.xml",
|
"security/security.xml",
|
||||||
|
"security/ir.model.access.csv",
|
||||||
"views/printing_printer.xml",
|
"views/printing_printer.xml",
|
||||||
"views/printing_server.xml",
|
"views/printing_server.xml",
|
||||||
"views/printing_job.xml",
|
"views/printing_job.xml",
|
||||||
|
|||||||
@@ -921,7 +921,7 @@ msgstr "Guide"
|
|||||||
#: code:addons/base_report_to_printer/wizards/print_attachment_report.py:0
|
#: code:addons/base_report_to_printer/wizards/print_attachment_report.py:0
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "{name} ({copies} copies)"
|
msgid "{name} ({copies} copies)"
|
||||||
msgstr "{namn} ({kopior} kopior)"
|
msgstr "{name} ({copies} kopior)"
|
||||||
|
|
||||||
#~ msgid "Job"
|
#~ msgid "Job"
|
||||||
#~ msgstr "Jobb"
|
#~ msgstr "Jobb"
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
|
||||||
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
|
||||||
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.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).
|
# 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
|
from odoo.tools.safe_eval import safe_eval, time
|
||||||
|
|
||||||
REPORT_TYPES = {"qweb-pdf": "pdf", "qweb-text": "text"}
|
REPORT_TYPES = {"qweb-pdf": "pdf", "qweb-text": "text"}
|
||||||
@@ -53,6 +55,12 @@ class IrActionsReport(models.Model):
|
|||||||
"action": result["action"],
|
"action": result["action"],
|
||||||
"printer_name": result["printer"].name,
|
"printer_name": result["printer"].name,
|
||||||
}
|
}
|
||||||
|
if result.get("printer_exception") and not self.env.context.get(
|
||||||
|
"skip_printer_exception"
|
||||||
|
):
|
||||||
|
serializable_result["printer_exception"] = True
|
||||||
|
if self.env.context.get("force_print_to_client"):
|
||||||
|
serializable_result["action"] = "client"
|
||||||
return serializable_result
|
return serializable_result
|
||||||
|
|
||||||
def _get_user_default_print_behaviour(self):
|
def _get_user_default_print_behaviour(self):
|
||||||
@@ -97,8 +105,49 @@ class IrActionsReport(models.Model):
|
|||||||
# For some reason design takes report defaults over
|
# For some reason design takes report defaults over
|
||||||
# False action entries so we must allow for that here
|
# False action entries so we must allow for that here
|
||||||
result.update({k: v for k, v in print_action.behaviour().items() if v})
|
result.update({k: v for k, v in print_action.behaviour().items() if v})
|
||||||
|
printer = result.get("printer")
|
||||||
|
if printer:
|
||||||
|
# When no printer is available we can fallback to the default behavior
|
||||||
|
# letting the user to manually print the reports.
|
||||||
|
try:
|
||||||
|
printer.server_id._open_connection(raise_on_error=True)
|
||||||
|
printer_exception = printer.status in [
|
||||||
|
"error",
|
||||||
|
"server-error",
|
||||||
|
"unavailable",
|
||||||
|
]
|
||||||
|
except Exception:
|
||||||
|
printer_exception = True
|
||||||
|
if printer_exception and not self.env.context.get("skip_printer_exception"):
|
||||||
|
result["printer_exception"] = True
|
||||||
return result
|
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):
|
def print_document(self, record_ids, data=None):
|
||||||
"""Print a document, do not return the document file"""
|
"""Print a document, do not return the document file"""
|
||||||
report_type = REPORT_TYPES.get(self.report_type)
|
report_type = REPORT_TYPES.get(self.report_type)
|
||||||
@@ -127,6 +176,7 @@ class IrActionsReport(models.Model):
|
|||||||
else:
|
else:
|
||||||
title = self.report_name
|
title = self.report_name
|
||||||
behaviour["title"] = title
|
behaviour["title"] = title
|
||||||
|
behaviour["res_ids"] = record_ids
|
||||||
# TODO should we use doc_format instead of report_type
|
# TODO should we use doc_format instead of report_type
|
||||||
return printer.print_document(
|
return printer.print_document(
|
||||||
self, document, doc_format=self.report_type, **behaviour
|
self, document, doc_format=self.report_type, **behaviour
|
||||||
@@ -140,7 +190,12 @@ class IrActionsReport(models.Model):
|
|||||||
"""
|
"""
|
||||||
if self.env.context.get("must_skip_send_to_printer"):
|
if self.env.context.get("must_skip_send_to_printer"):
|
||||||
return False
|
return False
|
||||||
if behaviour["action"] == "server" and printer and document:
|
if (
|
||||||
|
behaviour["action"] == "server"
|
||||||
|
and printer
|
||||||
|
and document
|
||||||
|
and not behaviour.get("printer_exception")
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class PrintingJob(models.Model):
|
|||||||
|
|
||||||
name = fields.Char(help="Job name.")
|
name = fields.Char(help="Job name.")
|
||||||
active = fields.Boolean(
|
active = fields.Boolean(
|
||||||
default=True, help="Unchecked if the job is purged from cups."
|
default=True, help="Unchecked if the job is purged from CUPS."
|
||||||
)
|
)
|
||||||
job_id_cups = fields.Integer(
|
job_id_cups = fields.Integer(
|
||||||
string="Job ID", required=True, help="CUPS id for this job."
|
string="Job ID", required=True, help="CUPS id for this job."
|
||||||
@@ -40,11 +40,15 @@ class PrintingJob(models.Model):
|
|||||||
help="Percentage of progress for this job.",
|
help="Percentage of progress for this job.",
|
||||||
)
|
)
|
||||||
time_at_creation = fields.Datetime(
|
time_at_creation = fields.Datetime(
|
||||||
required=True, help="Date and time of creation for this job."
|
string="Creation Date",
|
||||||
|
required=True,
|
||||||
|
help="Date and time of creation of this job.",
|
||||||
|
)
|
||||||
|
time_at_processing = fields.Datetime(
|
||||||
|
string="Processing Date", help="Date and time of process for this job."
|
||||||
)
|
)
|
||||||
time_at_processing = fields.Datetime(help="Date and time of process for this job.")
|
|
||||||
time_at_completed = fields.Datetime(
|
time_at_completed = fields.Datetime(
|
||||||
help="Date and time of completion for this job."
|
string="Completion Date", help="Date and time of completion for this job."
|
||||||
)
|
)
|
||||||
job_state = fields.Selection(
|
job_state = fields.Selection(
|
||||||
selection=[
|
selection=[
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
from tempfile import mkstemp
|
from tempfile import mkstemp
|
||||||
|
|
||||||
from odoo import fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -67,11 +67,19 @@ class PrintingPrinter(models.Model):
|
|||||||
tray_ids = fields.One2many(
|
tray_ids = fields.One2many(
|
||||||
comodel_name="printing.tray", inverse_name="printer_id", string="Paper Sources"
|
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):
|
def _prepare_update_from_cups(self, cups_connection, cups_printer):
|
||||||
mapping = {3: "available", 4: "printing", 5: "error"}
|
mapping = {3: "available", 4: "printing", 5: "error"}
|
||||||
cups_vals = {
|
cups_vals = {
|
||||||
"name": cups_printer["printer-info"],
|
"name": self.name or cups_printer["printer-info"],
|
||||||
"model": cups_printer.get("printer-make-and-model", False),
|
"model": cups_printer.get("printer-make-and-model", False),
|
||||||
"location": cups_printer.get("printer-location", False),
|
"location": cups_printer.get("printer-location", False),
|
||||||
"uri": cups_printer.get("device-uri", False),
|
"uri": cups_printer.get("device-uri", False),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class PrintingServer(models.Model):
|
|||||||
string="Printers List",
|
string="Printers List",
|
||||||
help="List of printers available on this server.",
|
help="List of printers available on this server.",
|
||||||
)
|
)
|
||||||
|
multi_thread = fields.Boolean()
|
||||||
|
|
||||||
def _open_connection(self, raise_on_error=False):
|
def _open_connection(self, raise_on_error=False):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -121,6 +122,11 @@ class PrintingServer(models.Model):
|
|||||||
printer_values["server_id"] = server.id
|
printer_values["server_id"] = server.id
|
||||||
|
|
||||||
updated_printers.append(name)
|
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:
|
if not printer:
|
||||||
printer_values["system_name"] = name
|
printer_values["system_name"] = name
|
||||||
printer.create(printer_values)
|
printer.create(printer_values)
|
||||||
|
|||||||
3
base_report_to_printer/readme/ROADMAP.md
Normal file
3
base_report_to_printer/readme/ROADMAP.md
Normal 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.
|
||||||
2
base_report_to_printer/security/ir.model.access.csv
Normal file
2
base_report_to_printer/security/ir.model.access.csv
Normal 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
|
||||||
|
@@ -128,6 +128,7 @@
|
|||||||
<field eval="1" name="perm_read" />
|
<field eval="1" name="perm_read" />
|
||||||
<field eval="1" name="perm_unlink" />
|
<field eval="1" name="perm_unlink" />
|
||||||
<field eval="1" name="perm_write" />
|
<field eval="1" name="perm_write" />
|
||||||
|
<field eval="1" name="perm_create" />
|
||||||
</record>
|
</record>
|
||||||
<record id="access_wizard_print_attachment_user" model="ir.model.access">
|
<record id="access_wizard_print_attachment_user" model="ir.model.access">
|
||||||
<field name="name">Print Attachment User</field>
|
<field name="name">Print Attachment User</field>
|
||||||
|
|||||||
@@ -8,10 +8,11 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
:Author: David Goodger (goodger@python.org)
|
:Author: David Goodger (goodger@python.org)
|
||||||
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
|
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
|
||||||
:Copyright: This stylesheet has been placed in the public domain.
|
:Copyright: This stylesheet has been placed in the public domain.
|
||||||
|
|
||||||
Default cascading style sheet for the HTML output of Docutils.
|
Default cascading style sheet for the HTML output of Docutils.
|
||||||
|
Despite the name, some widely supported CSS2 features are used.
|
||||||
|
|
||||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||||
customize this style sheet.
|
customize this style sheet.
|
||||||
@@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
|||||||
margin-left: 2em ;
|
margin-left: 2em ;
|
||||||
margin-right: 2em }
|
margin-right: 2em }
|
||||||
|
|
||||||
pre.code .ln { color: grey; } /* line numbers */
|
pre.code .ln { color: gray; } /* line numbers */
|
||||||
pre.code, code { background-color: #eeeeee }
|
pre.code, code { background-color: #eeeeee }
|
||||||
pre.code .comment, code .comment { color: #5C6576 }
|
pre.code .comment, code .comment { color: #5C6576 }
|
||||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||||
@@ -300,7 +301,7 @@ span.option {
|
|||||||
span.pre {
|
span.pre {
|
||||||
white-space: pre }
|
white-space: pre }
|
||||||
|
|
||||||
span.problematic {
|
span.problematic, pre.problematic {
|
||||||
color: red }
|
color: red }
|
||||||
|
|
||||||
span.section-subtitle {
|
span.section-subtitle {
|
||||||
@@ -398,16 +399,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="#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="#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="#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="#known-issues-roadmap" id="toc-entry-4">Known issues / Roadmap</a></li>
|
||||||
<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="#changelog" id="toc-entry-5">Changelog</a><ul>
|
||||||
<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="#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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-7">Bug Tracker</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-8">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-9">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-10">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-11">Maintainers</a></li>
|
<li><a class="reference internal" href="#maintainers" id="toc-entry-12">Maintainers</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -438,9 +440,8 @@ this will not be displayed by CUPS web interface or in Odoo. To see this
|
|||||||
information, you need to change the configuration of your CUPS server
|
information, you need to change the configuration of your CUPS server
|
||||||
and set the JobPrivateValue directive to “none” (or some other list of
|
and set the JobPrivateValue directive to “none” (or some other list of
|
||||||
values which does not include “job-name”) , and reload the server. See
|
values which does not include “job-name”) , and reload the server. See
|
||||||
cupsd.conf(5)
|
cupsd.conf(5) <<a class="reference external" href="https://www.cups.org/doc/man-cupsd.conf.html">https://www.cups.org/doc/man-cupsd.conf.html</a>> for
|
||||||
<<a class="reference external" href="https://www.cups.org/doc/man-cupsd.conf.html>">https://www.cups.org/doc/man-cupsd.conf.html\></a>
|
details.</p>
|
||||||
for details.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="usage">
|
<div class="section" id="usage">
|
||||||
<h1><a class="toc-backref" href="#toc-entry-3">Usage</a></h1>
|
<h1><a class="toc-backref" href="#toc-entry-3">Usage</a></h1>
|
||||||
@@ -462,23 +463,31 @@ preferences.</li>
|
|||||||
<p>When no tray is configured for a report and a user, the default tray
|
<p>When no tray is configured for a report and a user, the default tray
|
||||||
setup on the CUPS server is used.</p>
|
setup on the CUPS server is used.</p>
|
||||||
</div>
|
</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 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.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="section" id="changelog">
|
<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">
|
<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">
|
<ul class="simple">
|
||||||
<li>[RELEASE] Port from V12.</li>
|
<li>[RELEASE] Port from V12.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="section-2">
|
<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">
|
<ul class="simple">
|
||||||
<li>[RELEASE] Port from V11.</li>
|
<li>[RELEASE] Port from V11.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="bug-tracker">
|
<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>.
|
<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.
|
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
|
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||||
@@ -486,9 +495,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>
|
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="credits">
|
<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">
|
<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">
|
<ul class="simple">
|
||||||
<li>Agile Business Group & Domsense</li>
|
<li>Agile Business Group & Domsense</li>
|
||||||
<li>Pegueroles SCP</li>
|
<li>Pegueroles SCP</li>
|
||||||
@@ -499,7 +508,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="contributors">
|
<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">
|
<ul class="simple">
|
||||||
<li>Ferran Pegueroles <<a class="reference external" href="mailto:ferran@pegueroles.com">ferran@pegueroles.com</a>></li>
|
<li>Ferran Pegueroles <<a class="reference external" href="mailto:ferran@pegueroles.com">ferran@pegueroles.com</a>></li>
|
||||||
<li>Albert Cervera i Areny <<a class="reference external" href="mailto:albert@nan-tic.com">albert@nan-tic.com</a>></li>
|
<li>Albert Cervera i Areny <<a class="reference external" href="mailto:albert@nan-tic.com">albert@nan-tic.com</a>></li>
|
||||||
@@ -521,9 +530,11 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="maintainers">
|
<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>
|
<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" /></a>
|
<a class="reference external image-reference" href="https://odoo-community.org">
|
||||||
|
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
|
||||||
|
</a>
|
||||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
mission is to support the collaborative development of Odoo features and
|
mission is to support the collaborative development of Odoo features and
|
||||||
promote its widespread use.</p>
|
promote its widespread use.</p>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/** @odoo-module */
|
/** @odoo-module */
|
||||||
|
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";
|
||||||
|
|
||||||
@@ -9,19 +10,72 @@ async function cupsReportActionHandler(action, options, env) {
|
|||||||
const print_action = await orm.call(
|
const print_action = await orm.call(
|
||||||
"ir.actions.report",
|
"ir.actions.report",
|
||||||
"print_action_for_report_name",
|
"print_action_for_report_name",
|
||||||
[action.report_name]
|
[action.report_name],
|
||||||
|
{context: {force_print_to_client: action.context.force_print_to_client}}
|
||||||
|
);
|
||||||
|
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 (print_action && print_action.action === "server") {
|
|
||||||
const result = await orm.call("ir.actions.report", "print_document", [
|
|
||||||
action.id,
|
|
||||||
action.context.active_ids,
|
|
||||||
action.data,
|
|
||||||
]);
|
|
||||||
if (result) {
|
if (result) {
|
||||||
env.services.notification.add(_t("Successfully sent to printer!"));
|
env.services.notification.add(_t("Successfully sent to printer!"), {
|
||||||
} else {
|
type: "success",
|
||||||
env.services.notification.add(_t("Could not sent to printer!"));
|
});
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
env.services.notification.add(_t("Could not sent to printer!"), {
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
printer_exception = true;
|
||||||
|
}
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ model = "odoo.addons.base.models.ir_actions_report.IrActionsReport"
|
|||||||
class TestIrActionsReportXml(TransactionCase):
|
class TestIrActionsReportXml(TransactionCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.Model = self.env["ir.actions.report"]
|
self.Model = self.env["ir.actions.report"].with_context(
|
||||||
|
skip_printer_exception=True
|
||||||
|
)
|
||||||
self.vals = {}
|
self.vals = {}
|
||||||
|
|
||||||
self.report = self.env["ir.actions.report"].search([], limit=1)
|
self.report = self.Model.search([], limit=1)
|
||||||
self.server = self.env["printing.server"].create({})
|
self.server = self.env["printing.server"].create({})
|
||||||
|
|
||||||
def new_action(self):
|
def new_action(self):
|
||||||
@@ -153,7 +155,7 @@ class TestIrActionsReportXml(TransactionCase):
|
|||||||
self.env.user.printing_action = "client"
|
self.env.user.printing_action = "client"
|
||||||
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.report_id = self.env["ir.actions.report"].search(
|
printing_action.report_id = self.Model.search(
|
||||||
[("id", "!=", report.id)], limit=1
|
[("id", "!=", report.id)], limit=1
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -213,7 +215,7 @@ class TestIrActionsReportXml(TransactionCase):
|
|||||||
"""
|
"""
|
||||||
It should return the correct tray
|
It should return the correct tray
|
||||||
"""
|
"""
|
||||||
report = self.env["ir.actions.report"].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"}
|
||||||
)
|
)
|
||||||
@@ -266,7 +268,7 @@ class TestIrActionsReportXml(TransactionCase):
|
|||||||
self.assertEqual("Action tray", report.behaviour()["tray"])
|
self.assertEqual("Action tray", report.behaviour()["tray"])
|
||||||
|
|
||||||
def test_onchange_printer_tray_id_empty(self):
|
def test_onchange_printer_tray_id_empty(self):
|
||||||
action = self.env["ir.actions.report"].new({"printer_tray_id": False})
|
action = self.Model.new({"printer_tray_id": False})
|
||||||
action.onchange_printing_printer_id()
|
action.onchange_printing_printer_id()
|
||||||
self.assertFalse(action.printer_tray_id)
|
self.assertFalse(action.printer_tray_id)
|
||||||
|
|
||||||
@@ -289,7 +291,25 @@ class TestIrActionsReportXml(TransactionCase):
|
|||||||
{"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
|
{"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
action = self.env["ir.actions.report"].new({"printer_tray_id": tray.id})
|
action = self.Model.new({"printer_tray_id": tray.id})
|
||||||
self.assertEqual(action.printer_tray_id, tray)
|
self.assertEqual(action.printer_tray_id, tray)
|
||||||
action.onchange_printing_printer_id()
|
action.onchange_printing_printer_id()
|
||||||
self.assertFalse(action.printer_tray_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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class TestPrintingPrinter(TransactionCase):
|
|||||||
self.server = self.env["printing.server"].create({})
|
self.server = self.env["printing.server"].create({})
|
||||||
self.printer = self.env["printing.printer"].create(
|
self.printer = self.env["printing.printer"].create(
|
||||||
{
|
{
|
||||||
"name": "Printer",
|
"name": "",
|
||||||
"server_id": self.server.id,
|
"server_id": self.server.id,
|
||||||
"system_name": "Sys Name",
|
"system_name": "Sys Name",
|
||||||
"default": True,
|
"default": True,
|
||||||
@@ -105,10 +105,11 @@ class TestPrintingPrinter(TransactionCase):
|
|||||||
Check that the update_printers method calls _prepare_update_from_cups
|
Check that the update_printers method calls _prepare_update_from_cups
|
||||||
"""
|
"""
|
||||||
self.mock_cups_ppd(cups, file_name=False)
|
self.mock_cups_ppd(cups, file_name=False)
|
||||||
|
|
||||||
self.assertEqual(self.printer.name, "Printer")
|
|
||||||
self.ServerModel.update_printers()
|
self.ServerModel.update_printers()
|
||||||
self.assertEqual(self.printer.name, "info")
|
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)
|
@mock.patch("%s.cups" % server_model)
|
||||||
def test_prepare_update_from_cups_no_ppd(self, cups):
|
def test_prepare_update_from_cups_no_ppd(self, cups):
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ from odoo.tests import common
|
|||||||
class TestReport(common.HttpCase):
|
class TestReport(common.HttpCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.Model = self.env["ir.actions.report"]
|
self.Model = self.env["ir.actions.report"].with_context(
|
||||||
|
skip_printer_exception=True
|
||||||
|
)
|
||||||
self.server = self.env["printing.server"].create({})
|
self.server = self.env["printing.server"].create({})
|
||||||
self.report_vals = {
|
self.report_vals = {
|
||||||
"name": "Test Report",
|
"name": "Test Report",
|
||||||
|
|||||||
@@ -78,6 +78,7 @@
|
|||||||
<field name="location" />
|
<field name="location" />
|
||||||
<field name="status" />
|
<field name="status" />
|
||||||
<field name="status_message" />
|
<field name="status_message" />
|
||||||
|
<field name="multi_thread" />
|
||||||
</group>
|
</group>
|
||||||
<group string="Trays" name="trays">
|
<group string="Trays" name="trays">
|
||||||
<field name="tray_ids" nolabel="1" colspan="2">
|
<field name="tray_ids" nolabel="1" colspan="2">
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
<field name="user" />
|
<field name="user" />
|
||||||
<field name="password" />
|
<field name="password" />
|
||||||
<field name="encryption_policy" />
|
<field name="encryption_policy" />
|
||||||
|
<field name="multi_thread" />
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
<separator name="printers" string="Printers" />
|
<separator name="printers" string="Printers" />
|
||||||
|
|||||||
Reference in New Issue
Block a user