mirror of
https://github.com/OCA/report-print-send.git
synced 2025-02-16 07:11:31 +02:00
[FIX] .travis.yml: Update definition
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
from . import ir_actions_report
|
||||
from . import printing_action
|
||||
from . import printing_job
|
||||
|
||||
@@ -5,35 +5,33 @@
|
||||
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, exceptions, fields, models, _
|
||||
from odoo import _, api, exceptions, fields, models
|
||||
|
||||
|
||||
class IrActionsReport(models.Model):
|
||||
_inherit = 'ir.actions.report'
|
||||
_inherit = "ir.actions.report"
|
||||
|
||||
property_printing_action_id = fields.Many2one(
|
||||
comodel_name='printing.action',
|
||||
string='Default Behaviour',
|
||||
comodel_name="printing.action",
|
||||
string="Default Behaviour",
|
||||
company_dependent=True,
|
||||
)
|
||||
printing_printer_id = fields.Many2one(
|
||||
comodel_name='printing.printer',
|
||||
string='Default Printer'
|
||||
comodel_name="printing.printer", string="Default Printer"
|
||||
)
|
||||
printer_tray_id = fields.Many2one(
|
||||
comodel_name='printing.tray',
|
||||
string='Paper Source',
|
||||
comodel_name="printing.tray",
|
||||
string="Paper Source",
|
||||
domain="[('printer_id', '=', printing_printer_id)]",
|
||||
)
|
||||
printing_action_ids = fields.One2many(
|
||||
comodel_name='printing.report.xml.action',
|
||||
inverse_name='report_id',
|
||||
string='Actions',
|
||||
help='This field allows configuring action and printer on a per '
|
||||
'user basis'
|
||||
comodel_name="printing.report.xml.action",
|
||||
inverse_name="report_id",
|
||||
string="Actions",
|
||||
help="This field allows configuring action and printer on a per " "user basis",
|
||||
)
|
||||
|
||||
@api.onchange('printing_printer_id')
|
||||
@api.onchange("printing_printer_id")
|
||||
def onchange_printing_printer_id(self):
|
||||
""" Reset the tray when the printer is changed """
|
||||
self.printer_tray_id = False
|
||||
@@ -49,68 +47,69 @@ class IrActionsReport(models.Model):
|
||||
return {}
|
||||
result = report.behaviour()
|
||||
serializable_result = {
|
||||
'action': result['action'],
|
||||
'printer_name': result['printer'].name,
|
||||
"action": result["action"],
|
||||
"printer_name": result["printer"].name,
|
||||
}
|
||||
return serializable_result
|
||||
|
||||
def _get_user_default_print_behaviour(self):
|
||||
printer_obj = self.env['printing.printer']
|
||||
printer_obj = self.env["printing.printer"]
|
||||
user = self.env.user
|
||||
return dict(
|
||||
action=user.printing_action or 'client',
|
||||
action=user.printing_action or "client",
|
||||
printer=user.printing_printer_id or printer_obj.get_default(),
|
||||
tray=str(user.printer_tray_id.system_name) if
|
||||
user.printer_tray_id else False
|
||||
tray=str(user.printer_tray_id.system_name)
|
||||
if user.printer_tray_id
|
||||
else False,
|
||||
)
|
||||
|
||||
def _get_report_default_print_behaviour(self):
|
||||
result = {}
|
||||
report_action = self.property_printing_action_id
|
||||
if report_action and report_action.action_type != 'user_default':
|
||||
result['action'] = report_action.action_type
|
||||
if report_action and report_action.action_type != "user_default":
|
||||
result["action"] = report_action.action_type
|
||||
if self.printing_printer_id:
|
||||
result['printer'] = self.printing_printer_id
|
||||
result["printer"] = self.printing_printer_id
|
||||
if self.printer_tray_id:
|
||||
result['tray'] = self.printer_tray_id.system_name
|
||||
result["tray"] = self.printer_tray_id.system_name
|
||||
return result
|
||||
|
||||
def behaviour(self):
|
||||
self.ensure_one()
|
||||
printing_act_obj = self.env['printing.report.xml.action']
|
||||
printing_act_obj = self.env["printing.report.xml.action"]
|
||||
|
||||
result = self._get_user_default_print_behaviour()
|
||||
result.update(self._get_report_default_print_behaviour())
|
||||
|
||||
# Retrieve report-user specific values
|
||||
print_action = printing_act_obj.search([
|
||||
('report_id', '=', self.id),
|
||||
('user_id', '=', self.env.uid),
|
||||
('action', '!=', 'user_default'),
|
||||
], limit=1)
|
||||
print_action = printing_act_obj.search(
|
||||
[
|
||||
("report_id", "=", self.id),
|
||||
("user_id", "=", self.env.uid),
|
||||
("action", "!=", "user_default"),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if print_action:
|
||||
# For some reason design takes report defaults over
|
||||
# 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})
|
||||
return result
|
||||
|
||||
def print_document(self, record_ids, data=None):
|
||||
""" Print a document, do not return the document file """
|
||||
document, doc_format = self.with_context(
|
||||
must_skip_send_to_printer=True).render_qweb_pdf(
|
||||
record_ids, data=data)
|
||||
must_skip_send_to_printer=True
|
||||
).render_qweb_pdf(record_ids, data=data)
|
||||
behaviour = self.behaviour()
|
||||
printer = behaviour.pop('printer', None)
|
||||
printer = behaviour.pop("printer", None)
|
||||
|
||||
if not printer:
|
||||
raise exceptions.Warning(
|
||||
_('No printer configured to print this report.')
|
||||
)
|
||||
raise exceptions.Warning(_("No printer configured to print this report."))
|
||||
# TODO should we use doc_format instead of report_type
|
||||
return printer.print_document(self, document,
|
||||
doc_format=self.report_type,
|
||||
**behaviour)
|
||||
return printer.print_document(
|
||||
self, document, doc_format=self.report_type, **behaviour
|
||||
)
|
||||
|
||||
def _can_print_report(self, behaviour, printer, document):
|
||||
"""Predicate that decide if report can be sent to printer
|
||||
@@ -118,16 +117,16 @@ class IrActionsReport(models.Model):
|
||||
If you want to prevent `render_qweb_pdf` to send report you can set
|
||||
the `must_skip_send_to_printer` key to True in the context
|
||||
"""
|
||||
if self.env.context.get('must_skip_send_to_printer'):
|
||||
if self.env.context.get("must_skip_send_to_printer"):
|
||||
return False
|
||||
if behaviour['action'] == 'server' and printer and document:
|
||||
if behaviour["action"] == "server" and printer and document:
|
||||
return True
|
||||
return False
|
||||
|
||||
def report_action(self, docids, data=None, config=True):
|
||||
res = super().report_action(docids, data=data, config=config)
|
||||
if not res.get('id'):
|
||||
res['id'] = self.id
|
||||
if not res.get("id"):
|
||||
res["id"] = self.id
|
||||
return res
|
||||
|
||||
def render_qweb_pdf(self, res_ids=None, data=None):
|
||||
@@ -137,14 +136,16 @@ class IrActionsReport(models.Model):
|
||||
generated document as well.
|
||||
"""
|
||||
document, doc_format = super(IrActionsReport, self).render_qweb_pdf(
|
||||
res_ids=res_ids, data=data)
|
||||
res_ids=res_ids, data=data
|
||||
)
|
||||
|
||||
behaviour = self.behaviour()
|
||||
printer = behaviour.pop('printer', None)
|
||||
printer = behaviour.pop("printer", None)
|
||||
can_print_report = self._can_print_report(behaviour, printer, document)
|
||||
|
||||
if can_print_report:
|
||||
printer.print_document(self, document, doc_format=self.report_type,
|
||||
**behaviour)
|
||||
printer.print_document(
|
||||
self, document, doc_format=self.report_type, **behaviour
|
||||
)
|
||||
|
||||
return document, doc_format
|
||||
|
||||
@@ -5,24 +5,22 @@
|
||||
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PrintingAction(models.Model):
|
||||
_name = 'printing.action'
|
||||
_description = 'Print Job Action'
|
||||
_name = "printing.action"
|
||||
_description = "Print Job Action"
|
||||
|
||||
@api.model
|
||||
def _available_action_types(self):
|
||||
return [
|
||||
('server', 'Send to Printer'),
|
||||
('client', 'Send to Client'),
|
||||
('user_default', "Use user's defaults"),
|
||||
("server", "Send to Printer"),
|
||||
("client", "Send to Client"),
|
||||
("user_default", "Use user's defaults"),
|
||||
]
|
||||
|
||||
name = fields.Char(required=True)
|
||||
action_type = fields.Selection(
|
||||
selection=_available_action_types,
|
||||
string='Type',
|
||||
required=True,
|
||||
selection=_available_action_types, string="Type", required=True
|
||||
)
|
||||
|
||||
@@ -2,87 +2,110 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
from odoo import models, fields
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PrintingJob(models.Model):
|
||||
_name = 'printing.job'
|
||||
_description = 'Printing Job'
|
||||
_order = 'job_id_cups DESC'
|
||||
_name = "printing.job"
|
||||
_description = "Printing Job"
|
||||
_order = "job_id_cups DESC"
|
||||
|
||||
name = fields.Char(help='Job name.')
|
||||
name = fields.Char(help="Job name.")
|
||||
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(
|
||||
string='Job ID', required=True,
|
||||
help='CUPS id for this job.')
|
||||
string="Job ID", required=True, help="CUPS id for this job."
|
||||
)
|
||||
server_id = fields.Many2one(
|
||||
comodel_name='printing.server', string='Server',
|
||||
related='printer_id.server_id', store=True,
|
||||
help='Server which hosts this job.')
|
||||
comodel_name="printing.server",
|
||||
string="Server",
|
||||
related="printer_id.server_id",
|
||||
store=True,
|
||||
help="Server which hosts this job.",
|
||||
)
|
||||
printer_id = fields.Many2one(
|
||||
comodel_name='printing.printer', string='Printer', required=True,
|
||||
ondelete='cascade', help='Printer used for this job.')
|
||||
comodel_name="printing.printer",
|
||||
string="Printer",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
help="Printer used for this job.",
|
||||
)
|
||||
job_media_progress = fields.Integer(
|
||||
string='Media Progress', required=True,
|
||||
help='Percentage of progress for this job.')
|
||||
string="Media Progress",
|
||||
required=True,
|
||||
help="Percentage of progress for this job.",
|
||||
)
|
||||
time_at_creation = fields.Datetime(
|
||||
required=True, help='Date and time of creation for this job.')
|
||||
time_at_processing = fields.Datetime(
|
||||
help='Date and time of process for this job.')
|
||||
required=True, help="Date and time of creation for this job."
|
||||
)
|
||||
time_at_processing = fields.Datetime(help="Date and time of process for this job.")
|
||||
time_at_completed = fields.Datetime(
|
||||
help='Date and time of completion for this job.')
|
||||
job_state = fields.Selection(selection=[
|
||||
('pending', 'Pending'),
|
||||
('pending held', 'Pending Held'),
|
||||
('processing', 'Processing'),
|
||||
('processing stopped', 'Processing Stopped'),
|
||||
('canceled', 'Canceled'),
|
||||
('aborted', 'Aborted'),
|
||||
('completed', 'Completed'),
|
||||
('unknown', 'Unknown'),
|
||||
], string='State', help='Current state of the job.')
|
||||
job_state_reason = fields.Selection(selection=[
|
||||
('none', 'No reason'),
|
||||
('aborted-by-system', 'Aborted by the system'),
|
||||
('compression-error', 'Error in the compressed data'),
|
||||
('document-access-error', 'The URI cannot be accessed'),
|
||||
('document-format-error', 'Error in the document'),
|
||||
('job-canceled-at-device', 'Cancelled at the device'),
|
||||
('job-canceled-by-operator', 'Cancelled by the printer operator'),
|
||||
('job-canceled-by-user', 'Cancelled by the user'),
|
||||
('job-completed-successfully', 'Completed successfully'),
|
||||
('job-completed-with-errors', 'Completed with some errors'),
|
||||
('job-completed(with-warnings', 'Completed with some warnings'),
|
||||
('job-data-insufficient', 'No data has been received'),
|
||||
('job-hold-until-specified', 'Currently held'),
|
||||
('job-incoming', 'Files are currently being received'),
|
||||
('job-interpreting', 'Currently being interpreted'),
|
||||
('job-outgoing', 'Currently being sent to the printer'),
|
||||
('job-printing', 'Currently printing'),
|
||||
('job-queued', 'Queued for printing'),
|
||||
('job-queued-for-marker', 'Printer needs ink/marker/toner'),
|
||||
('job-restartable', 'Can be restarted'),
|
||||
('job-transforming', 'Being transformed into a different format'),
|
||||
('printer-stopped', 'Printer is stopped'),
|
||||
('printer-stopped-partly',
|
||||
'Printer state reason set to \'stopped-partly\''),
|
||||
('processing-to-stop-point',
|
||||
'Cancelled, but printing already processed pages'),
|
||||
('queued-in-device', 'Queued at the output device'),
|
||||
('resources-are-not-ready',
|
||||
'Resources not available to print the job'),
|
||||
('service-off-line', 'Held because the printer is offline'),
|
||||
('submission-interrupted', 'Files were not received in full'),
|
||||
('unsupported-compression', 'Compressed using an unknown algorithm'),
|
||||
('unsupported-document-format', 'Unsupported format'),
|
||||
], string='State Reason', help='Reason for the current job state.')
|
||||
help="Date and time of completion for this job."
|
||||
)
|
||||
job_state = fields.Selection(
|
||||
selection=[
|
||||
("pending", "Pending"),
|
||||
("pending held", "Pending Held"),
|
||||
("processing", "Processing"),
|
||||
("processing stopped", "Processing Stopped"),
|
||||
("canceled", "Canceled"),
|
||||
("aborted", "Aborted"),
|
||||
("completed", "Completed"),
|
||||
("unknown", "Unknown"),
|
||||
],
|
||||
string="State",
|
||||
help="Current state of the job.",
|
||||
)
|
||||
job_state_reason = fields.Selection(
|
||||
selection=[
|
||||
("none", "No reason"),
|
||||
("aborted-by-system", "Aborted by the system"),
|
||||
("compression-error", "Error in the compressed data"),
|
||||
("document-access-error", "The URI cannot be accessed"),
|
||||
("document-format-error", "Error in the document"),
|
||||
("job-canceled-at-device", "Cancelled at the device"),
|
||||
("job-canceled-by-operator", "Cancelled by the printer operator"),
|
||||
("job-canceled-by-user", "Cancelled by the user"),
|
||||
("job-completed-successfully", "Completed successfully"),
|
||||
("job-completed-with-errors", "Completed with some errors"),
|
||||
("job-completed(with-warnings", "Completed with some warnings"),
|
||||
("job-data-insufficient", "No data has been received"),
|
||||
("job-hold-until-specified", "Currently held"),
|
||||
("job-incoming", "Files are currently being received"),
|
||||
("job-interpreting", "Currently being interpreted"),
|
||||
("job-outgoing", "Currently being sent to the printer"),
|
||||
("job-printing", "Currently printing"),
|
||||
("job-queued", "Queued for printing"),
|
||||
("job-queued-for-marker", "Printer needs ink/marker/toner"),
|
||||
("job-restartable", "Can be restarted"),
|
||||
("job-transforming", "Being transformed into a different format"),
|
||||
("printer-stopped", "Printer is stopped"),
|
||||
("printer-stopped-partly", "Printer state reason set to 'stopped-partly'"),
|
||||
(
|
||||
"processing-to-stop-point",
|
||||
"Cancelled, but printing already processed pages",
|
||||
),
|
||||
("queued-in-device", "Queued at the output device"),
|
||||
("resources-are-not-ready", "Resources not available to print the job"),
|
||||
("service-off-line", "Held because the printer is offline"),
|
||||
("submission-interrupted", "Files were not received in full"),
|
||||
("unsupported-compression", "Compressed using an unknown algorithm"),
|
||||
("unsupported-document-format", "Unsupported format"),
|
||||
],
|
||||
string="State Reason",
|
||||
help="Reason for the current job state.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
('job_id_cups_unique', 'UNIQUE(job_id_cups, server_id)',
|
||||
'The id of the job must be unique per server !'),
|
||||
(
|
||||
"job_id_cups_unique",
|
||||
"UNIQUE(job_id_cups, server_id)",
|
||||
"The id of the job must be unique per server !",
|
||||
)
|
||||
]
|
||||
|
||||
def action_cancel(self):
|
||||
@@ -98,7 +121,6 @@ class PrintingJob(models.Model):
|
||||
connection.cancelJob(job.job_id_cups, purge_job=purge_job)
|
||||
|
||||
# Update jobs' states info Odoo
|
||||
self.mapped('server_id').update_jobs(
|
||||
which='all', first_job_id=job.job_id_cups)
|
||||
self.mapped("server_id").update_jobs(which="all", first_job_id=job.job_id_cups)
|
||||
|
||||
return True
|
||||
|
||||
@@ -11,15 +11,14 @@ import logging
|
||||
import os
|
||||
from tempfile import mkstemp
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import cups
|
||||
except ImportError:
|
||||
_logger.debug('Cannot `import cups`.')
|
||||
_logger.debug("Cannot `import cups`.")
|
||||
|
||||
|
||||
class PrintingPrinter(models.Model):
|
||||
@@ -27,63 +26,65 @@ class PrintingPrinter(models.Model):
|
||||
Printers
|
||||
"""
|
||||
|
||||
_name = 'printing.printer'
|
||||
_description = 'Printer'
|
||||
_order = 'name'
|
||||
_name = "printing.printer"
|
||||
_description = "Printer"
|
||||
_order = "name"
|
||||
|
||||
name = fields.Char(required=True, index=True)
|
||||
server_id = fields.Many2one(
|
||||
comodel_name='printing.server', string='Server', required=True,
|
||||
help='Server used to access this printer.')
|
||||
comodel_name="printing.server",
|
||||
string="Server",
|
||||
required=True,
|
||||
help="Server used to access this printer.",
|
||||
)
|
||||
job_ids = fields.One2many(
|
||||
comodel_name='printing.job', inverse_name='printer_id', string='Jobs',
|
||||
help='Jobs printed on this printer.')
|
||||
comodel_name="printing.job",
|
||||
inverse_name="printer_id",
|
||||
string="Jobs",
|
||||
help="Jobs printed on this printer.",
|
||||
)
|
||||
system_name = fields.Char(required=True, index=True)
|
||||
default = fields.Boolean(readonly=True)
|
||||
status = fields.Selection(
|
||||
selection=[
|
||||
('unavailable', 'Unavailable'),
|
||||
('printing', 'Printing'),
|
||||
('unknown', 'Unknown'),
|
||||
('available', 'Available'),
|
||||
('error', 'Error'),
|
||||
('server-error', 'Server Error'),
|
||||
("unavailable", "Unavailable"),
|
||||
("printing", "Printing"),
|
||||
("unknown", "Unknown"),
|
||||
("available", "Available"),
|
||||
("error", "Error"),
|
||||
("server-error", "Server Error"),
|
||||
],
|
||||
required=True,
|
||||
readonly=True,
|
||||
default='unknown')
|
||||
default="unknown",
|
||||
)
|
||||
status_message = fields.Char(readonly=True)
|
||||
model = fields.Char(readonly=True)
|
||||
location = fields.Char(readonly=True)
|
||||
uri = fields.Char(string='URI', readonly=True)
|
||||
tray_ids = fields.One2many(comodel_name='printing.tray',
|
||||
inverse_name='printer_id',
|
||||
string='Paper Sources')
|
||||
uri = fields.Char(string="URI", readonly=True)
|
||||
tray_ids = fields.One2many(
|
||||
comodel_name="printing.tray", inverse_name="printer_id", string="Paper Sources"
|
||||
)
|
||||
|
||||
def _prepare_update_from_cups(self, cups_connection, cups_printer):
|
||||
mapping = {
|
||||
3: 'available',
|
||||
4: 'printing',
|
||||
5: 'error'
|
||||
}
|
||||
mapping = {3: "available", 4: "printing", 5: "error"}
|
||||
vals = {
|
||||
'name': 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),
|
||||
'status': mapping.get(cups_printer.get(
|
||||
'printer-state'), 'unknown'),
|
||||
'status_message': cups_printer.get('printer-state-message', ''),
|
||||
"name": 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),
|
||||
"status": mapping.get(cups_printer.get("printer-state"), "unknown"),
|
||||
"status_message": cups_printer.get("printer-state-message", ""),
|
||||
}
|
||||
printer_uri = cups_printer['printer-uri-supported']
|
||||
printer_system_name = printer_uri[printer_uri.rfind('/') + 1:]
|
||||
printer_uri = cups_printer["printer-uri-supported"]
|
||||
printer_system_name = printer_uri[printer_uri.rfind("/") + 1 :]
|
||||
ppd_info = cups_connection.getPPD3(printer_system_name)
|
||||
ppd_path = ppd_info[2]
|
||||
if not ppd_path:
|
||||
return vals
|
||||
|
||||
ppd = cups.PPD(ppd_path)
|
||||
option = ppd.findOption('InputSlot')
|
||||
option = ppd.findOption("InputSlot")
|
||||
try:
|
||||
os.unlink(ppd_path)
|
||||
except OSError as err:
|
||||
@@ -94,25 +95,29 @@ class PrintingPrinter(models.Model):
|
||||
if not option:
|
||||
return vals
|
||||
|
||||
vals['tray_ids'] = []
|
||||
vals["tray_ids"] = []
|
||||
cups_trays = {
|
||||
tray_option['choice']: tray_option['text']
|
||||
for tray_option in option.choices
|
||||
tray_option["choice"]: tray_option["text"] for tray_option in option.choices
|
||||
}
|
||||
|
||||
# Add new trays
|
||||
vals['tray_ids'].extend([
|
||||
(0, 0, {'name': text, 'system_name': choice})
|
||||
for choice, text in cups_trays.items()
|
||||
if choice not in self.tray_ids.mapped('system_name')
|
||||
])
|
||||
vals["tray_ids"].extend(
|
||||
[
|
||||
(0, 0, {"name": text, "system_name": choice})
|
||||
for choice, text in cups_trays.items()
|
||||
if choice not in self.tray_ids.mapped("system_name")
|
||||
]
|
||||
)
|
||||
|
||||
# Remove deleted trays
|
||||
vals['tray_ids'].extend([
|
||||
(2, tray.id)
|
||||
for tray in self.tray_ids.filtered(
|
||||
lambda record: record.system_name not in cups_trays.keys())
|
||||
])
|
||||
vals["tray_ids"].extend(
|
||||
[
|
||||
(2, tray.id)
|
||||
for tray in self.tray_ids.filtered(
|
||||
lambda record: record.system_name not in cups_trays.keys()
|
||||
)
|
||||
]
|
||||
)
|
||||
return vals
|
||||
|
||||
def print_document(self, report, content, **print_opts):
|
||||
@@ -126,12 +131,11 @@ class PrintingPrinter(models.Model):
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
return self.print_file(
|
||||
file_name, report=report, **print_opts)
|
||||
return self.print_file(file_name, report=report, **print_opts)
|
||||
|
||||
@staticmethod
|
||||
def _set_option_doc_format(report, value):
|
||||
return {'raw': 'True'} if value == 'raw' else {}
|
||||
return {"raw": "True"} if value == "raw" else {}
|
||||
|
||||
# Backwards compatibility of builtin used as kwarg
|
||||
_set_option_format = _set_option_doc_format
|
||||
@@ -139,7 +143,7 @@ class PrintingPrinter(models.Model):
|
||||
def _set_option_tray(self, report, value):
|
||||
"""Note we use self here as some older PPD use tray
|
||||
rather than InputSlot so we may need to query printer in override"""
|
||||
return {'InputSlot': str(value)} if value else {}
|
||||
return {"InputSlot": str(value)} if value else {}
|
||||
|
||||
@staticmethod
|
||||
def _set_option_noop(report, value):
|
||||
@@ -152,8 +156,7 @@ class PrintingPrinter(models.Model):
|
||||
options = {}
|
||||
for option, value in print_opts.items():
|
||||
try:
|
||||
options.update(getattr(
|
||||
self, '_set_option_%s' % option)(report, value))
|
||||
options.update(getattr(self, "_set_option_%s" % option)(report, value))
|
||||
except AttributeError:
|
||||
options[option] = str(value)
|
||||
return options
|
||||
@@ -165,33 +168,30 @@ class PrintingPrinter(models.Model):
|
||||
options = self.print_options(report=report, **print_opts)
|
||||
|
||||
_logger.debug(
|
||||
'Sending job to CUPS printer %s on %s'
|
||||
% (self.system_name, self.server_id.address))
|
||||
connection.printFile(self.system_name,
|
||||
file_name,
|
||||
file_name,
|
||||
options=options)
|
||||
_logger.info("Printing job: '%s' on %s" % (
|
||||
file_name,
|
||||
self.server_id.address,
|
||||
))
|
||||
"Sending job to CUPS printer %s on %s"
|
||||
% (self.system_name, self.server_id.address)
|
||||
)
|
||||
connection.printFile(self.system_name, file_name, file_name, options=options)
|
||||
_logger.info(
|
||||
"Printing job: '{}' on {}".format(file_name, self.server_id.address)
|
||||
)
|
||||
return True
|
||||
|
||||
def set_default(self):
|
||||
if not self:
|
||||
return
|
||||
self.ensure_one()
|
||||
default_printers = self.search([('default', '=', True)])
|
||||
default_printers = self.search([("default", "=", True)])
|
||||
default_printers.unset_default()
|
||||
self.write({'default': True})
|
||||
self.write({"default": True})
|
||||
return True
|
||||
|
||||
def unset_default(self):
|
||||
self.write({'default': False})
|
||||
self.write({"default": False})
|
||||
return True
|
||||
|
||||
def get_default(self):
|
||||
return self.search([('default', '=', True)], limit=1)
|
||||
return self.search([("default", "=", True)], limit=1)
|
||||
|
||||
def action_cancel_all_jobs(self):
|
||||
self.ensure_one()
|
||||
@@ -200,11 +200,10 @@ class PrintingPrinter(models.Model):
|
||||
def cancel_all_jobs(self, purge_jobs=False):
|
||||
for printer in self:
|
||||
connection = printer.server_id._open_connection()
|
||||
connection.cancelAllJobs(
|
||||
name=printer.system_name, purge_jobs=purge_jobs)
|
||||
connection.cancelAllJobs(name=printer.system_name, purge_jobs=purge_jobs)
|
||||
|
||||
# Update jobs' states into Odoo
|
||||
self.mapped('server_id').update_jobs(which='completed')
|
||||
self.mapped("server_id").update_jobs(which="completed")
|
||||
|
||||
return True
|
||||
|
||||
@@ -214,7 +213,7 @@ class PrintingPrinter(models.Model):
|
||||
connection.enablePrinter(printer.system_name)
|
||||
|
||||
# Update printers' stats into Odoo
|
||||
self.mapped('server_id').update_printers()
|
||||
self.mapped("server_id").update_printers()
|
||||
|
||||
return True
|
||||
|
||||
@@ -224,6 +223,6 @@ class PrintingPrinter(models.Model):
|
||||
connection.disablePrinter(printer.system_name)
|
||||
|
||||
# Update printers' stats into Odoo
|
||||
self.mapped('server_id').update_printers()
|
||||
self.mapped("server_id").update_printers()
|
||||
|
||||
return True
|
||||
|
||||
@@ -5,35 +5,35 @@
|
||||
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PrintingReportXmlAction(models.Model):
|
||||
_name = 'printing.report.xml.action'
|
||||
_description = 'Printing Report Printing Actions'
|
||||
_name = "printing.report.xml.action"
|
||||
_description = "Printing Report Printing Actions"
|
||||
|
||||
report_id = fields.Many2one(comodel_name='ir.actions.report',
|
||||
string='Report',
|
||||
required=True,
|
||||
ondelete='cascade')
|
||||
user_id = fields.Many2one(comodel_name='res.users',
|
||||
string='User',
|
||||
required=True,
|
||||
ondelete='cascade')
|
||||
report_id = fields.Many2one(
|
||||
comodel_name="ir.actions.report",
|
||||
string="Report",
|
||||
required=True,
|
||||
ondelete="cascade",
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
comodel_name="res.users", string="User", required=True, ondelete="cascade"
|
||||
)
|
||||
action = fields.Selection(
|
||||
selection=lambda s: s.env['printing.action']._available_action_types(),
|
||||
selection=lambda s: s.env["printing.action"]._available_action_types(),
|
||||
required=True,
|
||||
)
|
||||
printer_id = fields.Many2one(comodel_name='printing.printer',
|
||||
string='Printer')
|
||||
printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer")
|
||||
|
||||
printer_tray_id = fields.Many2one(
|
||||
comodel_name='printing.tray',
|
||||
string='Paper Source',
|
||||
comodel_name="printing.tray",
|
||||
string="Paper Source",
|
||||
domain="[('printer_id', '=', printer_id)]",
|
||||
)
|
||||
|
||||
@api.onchange('printer_id')
|
||||
@api.onchange("printer_id")
|
||||
def onchange_printer_id(self):
|
||||
""" Reset the tray when the printer is changed """
|
||||
self.printer_tray_id = False
|
||||
@@ -42,7 +42,7 @@ class PrintingReportXmlAction(models.Model):
|
||||
if not self:
|
||||
return {}
|
||||
return {
|
||||
'action': self.action,
|
||||
'printer': self.printer_id,
|
||||
'tray': self.printer_tray_id.system_name
|
||||
"action": self.action,
|
||||
"printer": self.printer_id,
|
||||
"tray": self.printer_tray_id.system_name,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from odoo import models, fields, exceptions, _
|
||||
|
||||
from odoo import _, exceptions, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -11,26 +12,25 @@ _logger = logging.getLogger(__name__)
|
||||
try:
|
||||
import cups
|
||||
except ImportError:
|
||||
_logger.debug('Cannot `import cups`.')
|
||||
_logger.debug("Cannot `import cups`.")
|
||||
|
||||
|
||||
class PrintingServer(models.Model):
|
||||
_name = 'printing.server'
|
||||
_description = 'Printing server'
|
||||
_name = "printing.server"
|
||||
_description = "Printing server"
|
||||
|
||||
name = fields.Char(
|
||||
default='Localhost', required=True, help='Name of the server.')
|
||||
name = fields.Char(default="Localhost", required=True, help="Name of the server.")
|
||||
address = fields.Char(
|
||||
default='localhost', required=True,
|
||||
help='IP address or hostname of the server')
|
||||
port = fields.Integer(
|
||||
default=631, required=True, help='Port of the server.')
|
||||
active = fields.Boolean(
|
||||
default=True, help='If checked, this server is useable.')
|
||||
default="localhost", required=True, help="IP address or hostname of the server"
|
||||
)
|
||||
port = fields.Integer(default=631, required=True, help="Port of the server.")
|
||||
active = fields.Boolean(default=True, help="If checked, this server is useable.")
|
||||
printer_ids = fields.One2many(
|
||||
comodel_name='printing.printer', inverse_name='server_id',
|
||||
string='Printers List',
|
||||
help='List of printers available on this server.')
|
||||
comodel_name="printing.printer",
|
||||
inverse_name="server_id",
|
||||
string="Printers List",
|
||||
help="List of printers available on this server.",
|
||||
)
|
||||
|
||||
def _open_connection(self, raise_on_error=False):
|
||||
self.ensure_one()
|
||||
@@ -38,10 +38,11 @@ class PrintingServer(models.Model):
|
||||
try:
|
||||
connection = cups.Connection(host=self.address, port=self.port)
|
||||
except Exception:
|
||||
message = _("Failed to connect to the CUPS server on %s:%s. "
|
||||
"Check that the CUPS server is running and that "
|
||||
"you can reach it from the Odoo server.") % (
|
||||
self.address, self.port)
|
||||
message = _(
|
||||
"Failed to connect to the CUPS server on %s:%s. "
|
||||
"Check that the CUPS server is running and that "
|
||||
"you can reach it from the Odoo server."
|
||||
) % (self.address, self.port)
|
||||
_logger.warning(message)
|
||||
if raise_on_error:
|
||||
raise exceptions.UserError(message)
|
||||
@@ -63,28 +64,25 @@ class PrintingServer(models.Model):
|
||||
for server in servers:
|
||||
connection = server._open_connection(raise_on_error=raise_on_error)
|
||||
if not connection:
|
||||
server.printer_ids.write({'status': 'server-error'})
|
||||
server.printer_ids.write({"status": "server-error"})
|
||||
res = False
|
||||
continue
|
||||
|
||||
# Update Printers
|
||||
printers = connection.getPrinters()
|
||||
existing_printers = dict([
|
||||
(printer.system_name, printer)
|
||||
for printer in server.printer_ids
|
||||
])
|
||||
existing_printers = {
|
||||
printer.system_name: printer for printer in server.printer_ids
|
||||
}
|
||||
updated_printers = []
|
||||
for name, printer_info in printers.items():
|
||||
printer = self.env['printing.printer']
|
||||
printer = self.env["printing.printer"]
|
||||
if name in existing_printers:
|
||||
printer = existing_printers[name]
|
||||
|
||||
printer_values = printer._prepare_update_from_cups(
|
||||
connection, printer_info)
|
||||
printer_values.update(
|
||||
system_name=name,
|
||||
server_id=server.id,
|
||||
connection, printer_info
|
||||
)
|
||||
printer_values.update(system_name=name, server_id=server.id)
|
||||
updated_printers.append(name)
|
||||
if not printer:
|
||||
printer.create(printer_values)
|
||||
@@ -93,8 +91,8 @@ class PrintingServer(models.Model):
|
||||
|
||||
# Set printers not found as unavailable
|
||||
server.printer_ids.filtered(
|
||||
lambda record: record.system_name not in updated_printers)\
|
||||
.write({'status': 'unavailable'})
|
||||
lambda record: record.system_name not in updated_printers
|
||||
).write({"status": "unavailable"})
|
||||
|
||||
return res
|
||||
|
||||
@@ -103,18 +101,18 @@ class PrintingServer(models.Model):
|
||||
self = self.search([])
|
||||
return self.update_jobs()
|
||||
|
||||
def update_jobs(self, which='all', first_job_id=-1):
|
||||
job_obj = self.env['printing.job']
|
||||
printer_obj = self.env['printing.printer']
|
||||
def update_jobs(self, which="all", first_job_id=-1):
|
||||
job_obj = self.env["printing.job"]
|
||||
printer_obj = self.env["printing.printer"]
|
||||
|
||||
mapping = {
|
||||
3: 'pending',
|
||||
4: 'pending held',
|
||||
5: 'processing',
|
||||
6: 'processing stopped',
|
||||
7: 'canceled',
|
||||
8: 'aborted',
|
||||
9: 'completed',
|
||||
3: "pending",
|
||||
4: "pending held",
|
||||
5: "processing",
|
||||
6: "processing stopped",
|
||||
7: "canceled",
|
||||
8: "aborted",
|
||||
9: "completed",
|
||||
}
|
||||
|
||||
# Update printers list, to ensure that jobs printers will be in Odoo
|
||||
@@ -127,86 +125,91 @@ class PrintingServer(models.Model):
|
||||
|
||||
# Retrieve asked job data
|
||||
jobs_data = connection.getJobs(
|
||||
which_jobs=which, first_job_id=first_job_id,
|
||||
which_jobs=which,
|
||||
first_job_id=first_job_id,
|
||||
requested_attributes=[
|
||||
'job-name',
|
||||
'job-id',
|
||||
'printer-uri',
|
||||
'job-media-progress',
|
||||
'time-at-creation',
|
||||
'job-state',
|
||||
'job-state-reasons',
|
||||
'time-at-processing',
|
||||
'time-at-completed',
|
||||
])
|
||||
"job-name",
|
||||
"job-id",
|
||||
"printer-uri",
|
||||
"job-media-progress",
|
||||
"time-at-creation",
|
||||
"job-state",
|
||||
"job-state-reasons",
|
||||
"time-at-processing",
|
||||
"time-at-completed",
|
||||
],
|
||||
)
|
||||
|
||||
# Retrieve known uncompleted jobs data to update them
|
||||
if which == 'not-completed':
|
||||
oldest_uncompleted_job = job_obj.search([
|
||||
('job_state', 'not in', (
|
||||
'canceled',
|
||||
'aborted',
|
||||
'completed',
|
||||
)),
|
||||
], limit=1, order='job_id_cups')
|
||||
if which == "not-completed":
|
||||
oldest_uncompleted_job = job_obj.search(
|
||||
[("job_state", "not in", ("canceled", "aborted", "completed"))],
|
||||
limit=1,
|
||||
order="job_id_cups",
|
||||
)
|
||||
if oldest_uncompleted_job:
|
||||
jobs_data.update(connection.getJobs(
|
||||
which_jobs='completed',
|
||||
first_job_id=oldest_uncompleted_job.job_id_cups,
|
||||
requested_attributes=[
|
||||
'job-name',
|
||||
'job-id',
|
||||
'printer-uri',
|
||||
'job-media-progress',
|
||||
'time-at-creation',
|
||||
'job-state',
|
||||
'job-state-reasons',
|
||||
'time-at-processing',
|
||||
'time-at-completed',
|
||||
]))
|
||||
jobs_data.update(
|
||||
connection.getJobs(
|
||||
which_jobs="completed",
|
||||
first_job_id=oldest_uncompleted_job.job_id_cups,
|
||||
requested_attributes=[
|
||||
"job-name",
|
||||
"job-id",
|
||||
"printer-uri",
|
||||
"job-media-progress",
|
||||
"time-at-creation",
|
||||
"job-state",
|
||||
"job-state-reasons",
|
||||
"time-at-processing",
|
||||
"time-at-completed",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
all_cups_job_ids = set()
|
||||
for cups_job_id, job_data in jobs_data.items():
|
||||
all_cups_job_ids.add(cups_job_id)
|
||||
jobs = job_obj.with_context(active_test=False).search([
|
||||
('job_id_cups', '=', cups_job_id),
|
||||
('server_id', '=', server.id),
|
||||
])
|
||||
jobs = job_obj.with_context(active_test=False).search(
|
||||
[("job_id_cups", "=", cups_job_id), ("server_id", "=", server.id)]
|
||||
)
|
||||
job_values = {
|
||||
'name': job_data.get('job-name', ''),
|
||||
'active': True,
|
||||
'job_id_cups': cups_job_id,
|
||||
'job_media_progress': job_data.get(
|
||||
'job-media-progress', False),
|
||||
'job_state': mapping.get(
|
||||
job_data.get('job-state'), 'unknown'),
|
||||
'job_state_reason': job_data.get('job-state-reasons', ''),
|
||||
'time_at_creation': fields.Datetime.to_string(
|
||||
datetime.fromtimestamp(job_data.get(
|
||||
'time-at-creation', False))),
|
||||
'time_at_processing': job_data.get(
|
||||
'time-at-processing', False) and
|
||||
fields.Datetime.to_string(datetime.fromtimestamp(
|
||||
job_data.get('time-at-processing', False))),
|
||||
'time_at_completed': job_data.get(
|
||||
'time-at-completed', False) and
|
||||
fields.Datetime.to_string(datetime.fromtimestamp(
|
||||
job_data.get('time-at-completed', False))),
|
||||
"name": job_data.get("job-name", ""),
|
||||
"active": True,
|
||||
"job_id_cups": cups_job_id,
|
||||
"job_media_progress": job_data.get("job-media-progress", False),
|
||||
"job_state": mapping.get(job_data.get("job-state"), "unknown"),
|
||||
"job_state_reason": job_data.get("job-state-reasons", ""),
|
||||
"time_at_creation": fields.Datetime.to_string(
|
||||
datetime.fromtimestamp(job_data.get("time-at-creation", False))
|
||||
),
|
||||
"time_at_processing": job_data.get("time-at-processing", False)
|
||||
and fields.Datetime.to_string(
|
||||
datetime.fromtimestamp(
|
||||
job_data.get("time-at-processing", False)
|
||||
)
|
||||
),
|
||||
"time_at_completed": job_data.get("time-at-completed", False)
|
||||
and fields.Datetime.to_string(
|
||||
datetime.fromtimestamp(job_data.get("time-at-completed", False))
|
||||
),
|
||||
}
|
||||
|
||||
# Search for the printer in Odoo
|
||||
printer_uri = job_data['printer-uri']
|
||||
printer_system_name = printer_uri[printer_uri.rfind('/') + 1:]
|
||||
printer = printer_obj.search([
|
||||
('server_id', '=', server.id),
|
||||
('system_name', '=', printer_system_name),
|
||||
], limit=1)
|
||||
printer_uri = job_data["printer-uri"]
|
||||
printer_system_name = printer_uri[printer_uri.rfind("/") + 1 :]
|
||||
printer = printer_obj.search(
|
||||
[
|
||||
("server_id", "=", server.id),
|
||||
("system_name", "=", printer_system_name),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
# CUPS retains jobs for disconnected printers and also may
|
||||
# leak jobs data for unshared printers, therefore we just
|
||||
# discard here if not printer found
|
||||
if not printer:
|
||||
continue
|
||||
job_values['printer_id'] = printer.id
|
||||
job_values["printer_id"] = printer.id
|
||||
|
||||
if jobs:
|
||||
jobs.write(job_values)
|
||||
@@ -214,10 +217,10 @@ class PrintingServer(models.Model):
|
||||
job_obj.create(job_values)
|
||||
|
||||
# Deactive purged jobs
|
||||
if which == 'all' and first_job_id == -1:
|
||||
purged_jobs = job_obj.search([
|
||||
('job_id_cups', 'not in', list(all_cups_job_ids)),
|
||||
])
|
||||
purged_jobs.write({'active': False})
|
||||
if which == "all" and first_job_id == -1:
|
||||
purged_jobs = job_obj.search(
|
||||
[("job_id_cups", "not in", list(all_cups_job_ids))]
|
||||
)
|
||||
purged_jobs.write({"active": False})
|
||||
|
||||
return True
|
||||
|
||||
@@ -5,17 +5,17 @@ from odoo import fields, models
|
||||
|
||||
|
||||
class PrinterTray(models.Model):
|
||||
_name = 'printing.tray'
|
||||
_description = 'Printer Tray'
|
||||
_name = "printing.tray"
|
||||
_description = "Printer Tray"
|
||||
|
||||
_order = 'name asc'
|
||||
_order = "name asc"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
system_name = fields.Char(required=True, readonly=True)
|
||||
printer_id = fields.Many2one(
|
||||
comodel_name='printing.printer',
|
||||
string='Printer',
|
||||
comodel_name="printing.printer",
|
||||
string="Printer",
|
||||
required=True,
|
||||
readonly=True,
|
||||
ondelete='cascade',
|
||||
ondelete="cascade",
|
||||
)
|
||||
|
||||
@@ -5,46 +5,38 @@
|
||||
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = 'res.users'
|
||||
_inherit = "res.users"
|
||||
|
||||
@api.model
|
||||
def _user_available_action_types(self):
|
||||
return [
|
||||
(code, string)
|
||||
for code, string
|
||||
in self.env['printing.action']._available_action_types()
|
||||
if code != 'user_default'
|
||||
for code, string in self.env["printing.action"]._available_action_types()
|
||||
if code != "user_default"
|
||||
]
|
||||
|
||||
printing_action = fields.Selection(
|
||||
selection=_user_available_action_types,
|
||||
printing_action = fields.Selection(selection=_user_available_action_types)
|
||||
printing_printer_id = fields.Many2one(
|
||||
comodel_name="printing.printer", string="Default Printer"
|
||||
)
|
||||
printing_printer_id = fields.Many2one(comodel_name='printing.printer',
|
||||
string='Default Printer')
|
||||
|
||||
@api.model
|
||||
def _register_hook(self):
|
||||
super()._register_hook()
|
||||
self.SELF_WRITEABLE_FIELDS.extend([
|
||||
'printing_action',
|
||||
'printing_printer_id',
|
||||
])
|
||||
self.SELF_READABLE_FIELDS.extend([
|
||||
'printing_action',
|
||||
'printing_printer_id',
|
||||
])
|
||||
self.SELF_WRITEABLE_FIELDS.extend(["printing_action", "printing_printer_id"])
|
||||
self.SELF_READABLE_FIELDS.extend(["printing_action", "printing_printer_id"])
|
||||
|
||||
printer_tray_id = fields.Many2one(
|
||||
comodel_name='printing.tray',
|
||||
string='Default Printer Paper Source',
|
||||
comodel_name="printing.tray",
|
||||
string="Default Printer Paper Source",
|
||||
domain="[('printer_id', '=', printing_printer_id)]",
|
||||
)
|
||||
|
||||
@api.onchange('printing_printer_id')
|
||||
@api.onchange("printing_printer_id")
|
||||
def onchange_printing_printer_id(self):
|
||||
""" Reset the tray when the printer is changed """
|
||||
self.printer_tray_id = False
|
||||
|
||||
Reference in New Issue
Block a user