[FIX] .travis.yml: Update definition

This commit is contained in:
Pedro M. Baeza
2019-10-01 15:26:55 +02:00
committed by Carlos Roca
parent b1103c7b6d
commit 226d4b2c31
25 changed files with 995 additions and 1043 deletions

View File

@@ -6,30 +6,28 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': "Report to printer", "name": "Report to printer",
'version': '13.0.1.0.0', "version": "13.0.1.0.0",
'category': 'Generic Modules/Base', "category": "Generic Modules/Base",
'author': "Agile Business Group & Domsense, Pegueroles SCP, NaN," "author": "Agile Business Group & Domsense, Pegueroles SCP, NaN,"
" LasLabs, Camptocamp, Odoo Community Association (OCA)," " LasLabs, Camptocamp, Odoo Community Association (OCA),"
" Open for Small Business Ltd", " Open for Small Business Ltd",
'website': 'http://www.agilebg.com', "website": "http://www.agilebg.com",
'license': 'AGPL-3', "license": "AGPL-3",
"depends": ['web'], "depends": ["web"],
'data': [ "data": [
'data/printing_data.xml', "data/printing_data.xml",
'security/security.xml', "security/security.xml",
'views/assets.xml', "views/assets.xml",
'views/printing_printer.xml', "views/printing_printer.xml",
'views/printing_server.xml', "views/printing_server.xml",
'views/printing_job.xml', "views/printing_job.xml",
'views/printing_report.xml', "views/printing_report.xml",
'views/res_users.xml', "views/res_users.xml",
'views/ir_actions_report.xml', "views/ir_actions_report.xml",
'wizards/printing_printer_update_wizard_view.xml', "wizards/printing_printer_update_wizard_view.xml",
], ],
'installable': True, "installable": True,
'application': False, "application": False,
'external_dependencies': { "external_dependencies": {"python": ["pycups"]},
'python': ['pycups'],
},
} }

View File

@@ -1,4 +1,3 @@
from . import ir_actions_report from . import ir_actions_report
from . import printing_action from . import printing_action
from . import printing_job from . import printing_job

View File

@@ -5,35 +5,33 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, exceptions, fields, models, _ from odoo import _, api, exceptions, fields, models
class IrActionsReport(models.Model): class IrActionsReport(models.Model):
_inherit = 'ir.actions.report' _inherit = "ir.actions.report"
property_printing_action_id = fields.Many2one( property_printing_action_id = fields.Many2one(
comodel_name='printing.action', comodel_name="printing.action",
string='Default Behaviour', string="Default Behaviour",
company_dependent=True, company_dependent=True,
) )
printing_printer_id = fields.Many2one( printing_printer_id = fields.Many2one(
comodel_name='printing.printer', comodel_name="printing.printer", string="Default Printer"
string='Default Printer'
) )
printer_tray_id = fields.Many2one( printer_tray_id = fields.Many2one(
comodel_name='printing.tray', comodel_name="printing.tray",
string='Paper Source', string="Paper Source",
domain="[('printer_id', '=', printing_printer_id)]", domain="[('printer_id', '=', printing_printer_id)]",
) )
printing_action_ids = fields.One2many( printing_action_ids = fields.One2many(
comodel_name='printing.report.xml.action', comodel_name="printing.report.xml.action",
inverse_name='report_id', inverse_name="report_id",
string='Actions', string="Actions",
help='This field allows configuring action and printer on a per ' help="This field allows configuring action and printer on a per " "user basis",
'user basis'
) )
@api.onchange('printing_printer_id') @api.onchange("printing_printer_id")
def onchange_printing_printer_id(self): def onchange_printing_printer_id(self):
""" Reset the tray when the printer is changed """ """ Reset the tray when the printer is changed """
self.printer_tray_id = False self.printer_tray_id = False
@@ -49,68 +47,69 @@ class IrActionsReport(models.Model):
return {} return {}
result = report.behaviour() result = report.behaviour()
serializable_result = { serializable_result = {
'action': result['action'], "action": result["action"],
'printer_name': result['printer'].name, "printer_name": result["printer"].name,
} }
return serializable_result return serializable_result
def _get_user_default_print_behaviour(self): def _get_user_default_print_behaviour(self):
printer_obj = self.env['printing.printer'] printer_obj = self.env["printing.printer"]
user = self.env.user user = self.env.user
return dict( return dict(
action=user.printing_action or 'client', action=user.printing_action or "client",
printer=user.printing_printer_id or printer_obj.get_default(), printer=user.printing_printer_id or printer_obj.get_default(),
tray=str(user.printer_tray_id.system_name) if tray=str(user.printer_tray_id.system_name)
user.printer_tray_id else False if user.printer_tray_id
else False,
) )
def _get_report_default_print_behaviour(self): def _get_report_default_print_behaviour(self):
result = {} result = {}
report_action = self.property_printing_action_id report_action = self.property_printing_action_id
if report_action and report_action.action_type != 'user_default': if report_action and report_action.action_type != "user_default":
result['action'] = report_action.action_type result["action"] = report_action.action_type
if self.printing_printer_id: if self.printing_printer_id:
result['printer'] = self.printing_printer_id result["printer"] = self.printing_printer_id
if self.printer_tray_id: if self.printer_tray_id:
result['tray'] = self.printer_tray_id.system_name result["tray"] = self.printer_tray_id.system_name
return result return result
def behaviour(self): def behaviour(self):
self.ensure_one() 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 = self._get_user_default_print_behaviour()
result.update(self._get_report_default_print_behaviour()) result.update(self._get_report_default_print_behaviour())
# Retrieve report-user specific values # Retrieve report-user specific values
print_action = printing_act_obj.search([ print_action = printing_act_obj.search(
('report_id', '=', self.id), [
('user_id', '=', self.env.uid), ("report_id", "=", self.id),
('action', '!=', 'user_default'), ("user_id", "=", self.env.uid),
], limit=1) ("action", "!=", "user_default"),
],
limit=1,
)
if print_action: if print_action:
# 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 result.update({k: v for k, v in print_action.behaviour().items() if v})
print_action.behaviour().items() if v})
return result return result
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 """
document, doc_format = self.with_context( document, doc_format = self.with_context(
must_skip_send_to_printer=True).render_qweb_pdf( must_skip_send_to_printer=True
record_ids, data=data) ).render_qweb_pdf(record_ids, data=data)
behaviour = self.behaviour() behaviour = self.behaviour()
printer = behaviour.pop('printer', None) printer = behaviour.pop("printer", None)
if not printer: if not printer:
raise exceptions.Warning( raise exceptions.Warning(_("No printer configured to print this report."))
_('No printer configured to print this report.')
)
# TODO should we use doc_format instead of report_type # TODO should we use doc_format instead of report_type
return printer.print_document(self, document, return printer.print_document(
doc_format=self.report_type, self, document, doc_format=self.report_type, **behaviour
**behaviour) )
def _can_print_report(self, behaviour, printer, document): def _can_print_report(self, behaviour, printer, document):
"""Predicate that decide if report can be sent to printer """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 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 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 return False
if behaviour['action'] == 'server' and printer and document: if behaviour["action"] == "server" and printer and document:
return True return True
return False return False
def report_action(self, docids, data=None, config=True): def report_action(self, docids, data=None, config=True):
res = super().report_action(docids, data=data, config=config) res = super().report_action(docids, data=data, config=config)
if not res.get('id'): if not res.get("id"):
res['id'] = self.id res["id"] = self.id
return res return res
def render_qweb_pdf(self, res_ids=None, data=None): def render_qweb_pdf(self, res_ids=None, data=None):
@@ -137,14 +136,16 @@ class IrActionsReport(models.Model):
generated document as well. generated document as well.
""" """
document, doc_format = super(IrActionsReport, self).render_qweb_pdf( document, doc_format = super(IrActionsReport, self).render_qweb_pdf(
res_ids=res_ids, data=data) res_ids=res_ids, data=data
)
behaviour = self.behaviour() behaviour = self.behaviour()
printer = behaviour.pop('printer', None) printer = behaviour.pop("printer", None)
can_print_report = self._can_print_report(behaviour, printer, document) can_print_report = self._can_print_report(behaviour, printer, document)
if can_print_report: if can_print_report:
printer.print_document(self, document, doc_format=self.report_type, printer.print_document(
**behaviour) self, document, doc_format=self.report_type, **behaviour
)
return document, doc_format return document, doc_format

View File

@@ -5,24 +5,22 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api from odoo import api, fields, models
class PrintingAction(models.Model): class PrintingAction(models.Model):
_name = 'printing.action' _name = "printing.action"
_description = 'Print Job Action' _description = "Print Job Action"
@api.model @api.model
def _available_action_types(self): def _available_action_types(self):
return [ return [
('server', 'Send to Printer'), ("server", "Send to Printer"),
('client', 'Send to Client'), ("client", "Send to Client"),
('user_default', "Use user's defaults"), ("user_default", "Use user's defaults"),
] ]
name = fields.Char(required=True) name = fields.Char(required=True)
action_type = fields.Selection( action_type = fields.Selection(
selection=_available_action_types, selection=_available_action_types, string="Type", required=True
string='Type',
required=True,
) )

View File

@@ -2,87 +2,110 @@
# 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 logging import logging
from odoo import models, fields
from odoo import fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class PrintingJob(models.Model): class PrintingJob(models.Model):
_name = 'printing.job' _name = "printing.job"
_description = 'Printing Job' _description = "Printing Job"
_order = 'job_id_cups DESC' _order = "job_id_cups DESC"
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, string="Job ID", required=True, help="CUPS id for this job."
help='CUPS id for this job.') )
server_id = fields.Many2one( server_id = fields.Many2one(
comodel_name='printing.server', string='Server', comodel_name="printing.server",
related='printer_id.server_id', store=True, string="Server",
help='Server which hosts this job.') related="printer_id.server_id",
store=True,
help="Server which hosts this job.",
)
printer_id = fields.Many2one( printer_id = fields.Many2one(
comodel_name='printing.printer', string='Printer', required=True, comodel_name="printing.printer",
ondelete='cascade', help='Printer used for this job.') string="Printer",
required=True,
ondelete="cascade",
help="Printer used for this job.",
)
job_media_progress = fields.Integer( job_media_progress = fields.Integer(
string='Media Progress', required=True, string="Media Progress",
help='Percentage of progress for this job.') required=True,
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.') 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_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.') help="Date and time of completion for this job."
job_state = fields.Selection(selection=[ )
('pending', 'Pending'), job_state = fields.Selection(
('pending held', 'Pending Held'), selection=[
('processing', 'Processing'), ("pending", "Pending"),
('processing stopped', 'Processing Stopped'), ("pending held", "Pending Held"),
('canceled', 'Canceled'), ("processing", "Processing"),
('aborted', 'Aborted'), ("processing stopped", "Processing Stopped"),
('completed', 'Completed'), ("canceled", "Canceled"),
('unknown', 'Unknown'), ("aborted", "Aborted"),
], string='State', help='Current state of the job.') ("completed", "Completed"),
job_state_reason = fields.Selection(selection=[ ("unknown", "Unknown"),
('none', 'No reason'), ],
('aborted-by-system', 'Aborted by the system'), string="State",
('compression-error', 'Error in the compressed data'), help="Current state of the job.",
('document-access-error', 'The URI cannot be accessed'), )
('document-format-error', 'Error in the document'), job_state_reason = fields.Selection(
('job-canceled-at-device', 'Cancelled at the device'), selection=[
('job-canceled-by-operator', 'Cancelled by the printer operator'), ("none", "No reason"),
('job-canceled-by-user', 'Cancelled by the user'), ("aborted-by-system", "Aborted by the system"),
('job-completed-successfully', 'Completed successfully'), ("compression-error", "Error in the compressed data"),
('job-completed-with-errors', 'Completed with some errors'), ("document-access-error", "The URI cannot be accessed"),
('job-completed(with-warnings', 'Completed with some warnings'), ("document-format-error", "Error in the document"),
('job-data-insufficient', 'No data has been received'), ("job-canceled-at-device", "Cancelled at the device"),
('job-hold-until-specified', 'Currently held'), ("job-canceled-by-operator", "Cancelled by the printer operator"),
('job-incoming', 'Files are currently being received'), ("job-canceled-by-user", "Cancelled by the user"),
('job-interpreting', 'Currently being interpreted'), ("job-completed-successfully", "Completed successfully"),
('job-outgoing', 'Currently being sent to the printer'), ("job-completed-with-errors", "Completed with some errors"),
('job-printing', 'Currently printing'), ("job-completed(with-warnings", "Completed with some warnings"),
('job-queued', 'Queued for printing'), ("job-data-insufficient", "No data has been received"),
('job-queued-for-marker', 'Printer needs ink/marker/toner'), ("job-hold-until-specified", "Currently held"),
('job-restartable', 'Can be restarted'), ("job-incoming", "Files are currently being received"),
('job-transforming', 'Being transformed into a different format'), ("job-interpreting", "Currently being interpreted"),
('printer-stopped', 'Printer is stopped'), ("job-outgoing", "Currently being sent to the printer"),
('printer-stopped-partly', ("job-printing", "Currently printing"),
'Printer state reason set to \'stopped-partly\''), ("job-queued", "Queued for printing"),
('processing-to-stop-point', ("job-queued-for-marker", "Printer needs ink/marker/toner"),
'Cancelled, but printing already processed pages'), ("job-restartable", "Can be restarted"),
('queued-in-device', 'Queued at the output device'), ("job-transforming", "Being transformed into a different format"),
('resources-are-not-ready', ("printer-stopped", "Printer is stopped"),
'Resources not available to print the job'), ("printer-stopped-partly", "Printer state reason set to 'stopped-partly'"),
('service-off-line', 'Held because the printer is offline'), (
('submission-interrupted', 'Files were not received in full'), "processing-to-stop-point",
('unsupported-compression', 'Compressed using an unknown algorithm'), "Cancelled, but printing already processed pages",
('unsupported-document-format', 'Unsupported format'), ),
], string='State Reason', help='Reason for the current job state.') ("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 = [ _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): def action_cancel(self):
@@ -98,7 +121,6 @@ class PrintingJob(models.Model):
connection.cancelJob(job.job_id_cups, purge_job=purge_job) connection.cancelJob(job.job_id_cups, purge_job=purge_job)
# Update jobs' states info Odoo # Update jobs' states info Odoo
self.mapped('server_id').update_jobs( self.mapped("server_id").update_jobs(which="all", first_job_id=job.job_id_cups)
which='all', first_job_id=job.job_id_cups)
return True return True

View File

@@ -11,15 +11,14 @@ import logging
import os import os
from tempfile import mkstemp from tempfile import mkstemp
from odoo import models, fields from odoo import fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
try: try:
import cups import cups
except ImportError: except ImportError:
_logger.debug('Cannot `import cups`.') _logger.debug("Cannot `import cups`.")
class PrintingPrinter(models.Model): class PrintingPrinter(models.Model):
@@ -27,63 +26,65 @@ class PrintingPrinter(models.Model):
Printers Printers
""" """
_name = 'printing.printer' _name = "printing.printer"
_description = 'Printer' _description = "Printer"
_order = 'name' _order = "name"
name = fields.Char(required=True, index=True) name = fields.Char(required=True, index=True)
server_id = fields.Many2one( server_id = fields.Many2one(
comodel_name='printing.server', string='Server', required=True, comodel_name="printing.server",
help='Server used to access this printer.') string="Server",
required=True,
help="Server used to access this printer.",
)
job_ids = fields.One2many( job_ids = fields.One2many(
comodel_name='printing.job', inverse_name='printer_id', string='Jobs', comodel_name="printing.job",
help='Jobs printed on this printer.') inverse_name="printer_id",
string="Jobs",
help="Jobs printed on this printer.",
)
system_name = fields.Char(required=True, index=True) system_name = fields.Char(required=True, index=True)
default = fields.Boolean(readonly=True) default = fields.Boolean(readonly=True)
status = fields.Selection( status = fields.Selection(
selection=[ selection=[
('unavailable', 'Unavailable'), ("unavailable", "Unavailable"),
('printing', 'Printing'), ("printing", "Printing"),
('unknown', 'Unknown'), ("unknown", "Unknown"),
('available', 'Available'), ("available", "Available"),
('error', 'Error'), ("error", "Error"),
('server-error', 'Server Error'), ("server-error", "Server Error"),
], ],
required=True, required=True,
readonly=True, readonly=True,
default='unknown') default="unknown",
)
status_message = fields.Char(readonly=True) status_message = fields.Char(readonly=True)
model = fields.Char(readonly=True) model = fields.Char(readonly=True)
location = fields.Char(readonly=True) location = fields.Char(readonly=True)
uri = fields.Char(string='URI', readonly=True) uri = fields.Char(string="URI", readonly=True)
tray_ids = fields.One2many(comodel_name='printing.tray', tray_ids = fields.One2many(
inverse_name='printer_id', comodel_name="printing.tray", inverse_name="printer_id", string="Paper Sources"
string='Paper Sources') )
def _prepare_update_from_cups(self, cups_connection, cups_printer): def _prepare_update_from_cups(self, cups_connection, cups_printer):
mapping = { mapping = {3: "available", 4: "printing", 5: "error"}
3: 'available',
4: 'printing',
5: 'error'
}
vals = { vals = {
'name': cups_printer['printer-info'], "name": 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),
'status': mapping.get(cups_printer.get( "status": mapping.get(cups_printer.get("printer-state"), "unknown"),
'printer-state'), 'unknown'), "status_message": cups_printer.get("printer-state-message", ""),
'status_message': cups_printer.get('printer-state-message', ''),
} }
printer_uri = cups_printer['printer-uri-supported'] printer_uri = cups_printer["printer-uri-supported"]
printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] printer_system_name = printer_uri[printer_uri.rfind("/") + 1 :]
ppd_info = cups_connection.getPPD3(printer_system_name) ppd_info = cups_connection.getPPD3(printer_system_name)
ppd_path = ppd_info[2] ppd_path = ppd_info[2]
if not ppd_path: if not ppd_path:
return vals return vals
ppd = cups.PPD(ppd_path) ppd = cups.PPD(ppd_path)
option = ppd.findOption('InputSlot') option = ppd.findOption("InputSlot")
try: try:
os.unlink(ppd_path) os.unlink(ppd_path)
except OSError as err: except OSError as err:
@@ -94,25 +95,29 @@ class PrintingPrinter(models.Model):
if not option: if not option:
return vals return vals
vals['tray_ids'] = [] vals["tray_ids"] = []
cups_trays = { cups_trays = {
tray_option['choice']: tray_option['text'] tray_option["choice"]: tray_option["text"] for tray_option in option.choices
for tray_option in option.choices
} }
# Add new trays # Add new trays
vals['tray_ids'].extend([ vals["tray_ids"].extend(
(0, 0, {'name': text, 'system_name': choice}) [
for choice, text in cups_trays.items() (0, 0, {"name": text, "system_name": choice})
if choice not in self.tray_ids.mapped('system_name') for choice, text in cups_trays.items()
]) if choice not in self.tray_ids.mapped("system_name")
]
)
# Remove deleted trays # Remove deleted trays
vals['tray_ids'].extend([ vals["tray_ids"].extend(
(2, tray.id) [
for tray in self.tray_ids.filtered( (2, tray.id)
lambda record: record.system_name not in cups_trays.keys()) for tray in self.tray_ids.filtered(
]) lambda record: record.system_name not in cups_trays.keys()
)
]
)
return vals return vals
def print_document(self, report, content, **print_opts): def print_document(self, report, content, **print_opts):
@@ -126,12 +131,11 @@ class PrintingPrinter(models.Model):
finally: finally:
os.close(fd) os.close(fd)
return self.print_file( return self.print_file(file_name, report=report, **print_opts)
file_name, report=report, **print_opts)
@staticmethod @staticmethod
def _set_option_doc_format(report, value): 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 # Backwards compatibility of builtin used as kwarg
_set_option_format = _set_option_doc_format _set_option_format = _set_option_doc_format
@@ -139,7 +143,7 @@ class PrintingPrinter(models.Model):
def _set_option_tray(self, report, value): def _set_option_tray(self, report, value):
"""Note we use self here as some older PPD use tray """Note we use self here as some older PPD use tray
rather than InputSlot so we may need to query printer in override""" 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 @staticmethod
def _set_option_noop(report, value): def _set_option_noop(report, value):
@@ -152,8 +156,7 @@ class PrintingPrinter(models.Model):
options = {} options = {}
for option, value in print_opts.items(): for option, value in print_opts.items():
try: try:
options.update(getattr( options.update(getattr(self, "_set_option_%s" % option)(report, value))
self, '_set_option_%s' % option)(report, value))
except AttributeError: except AttributeError:
options[option] = str(value) options[option] = str(value)
return options return options
@@ -165,33 +168,30 @@ class PrintingPrinter(models.Model):
options = self.print_options(report=report, **print_opts) options = self.print_options(report=report, **print_opts)
_logger.debug( _logger.debug(
'Sending job to CUPS printer %s on %s' "Sending job to CUPS printer %s on %s"
% (self.system_name, self.server_id.address)) % (self.system_name, self.server_id.address)
connection.printFile(self.system_name, )
file_name, connection.printFile(self.system_name, file_name, file_name, options=options)
file_name, _logger.info(
options=options) "Printing job: '{}' on {}".format(file_name, self.server_id.address)
_logger.info("Printing job: '%s' on %s" % ( )
file_name,
self.server_id.address,
))
return True return True
def set_default(self): def set_default(self):
if not self: if not self:
return return
self.ensure_one() self.ensure_one()
default_printers = self.search([('default', '=', True)]) default_printers = self.search([("default", "=", True)])
default_printers.unset_default() default_printers.unset_default()
self.write({'default': True}) self.write({"default": True})
return True return True
def unset_default(self): def unset_default(self):
self.write({'default': False}) self.write({"default": False})
return True return True
def get_default(self): def get_default(self):
return self.search([('default', '=', True)], limit=1) return self.search([("default", "=", True)], limit=1)
def action_cancel_all_jobs(self): def action_cancel_all_jobs(self):
self.ensure_one() self.ensure_one()
@@ -200,11 +200,10 @@ class PrintingPrinter(models.Model):
def cancel_all_jobs(self, purge_jobs=False): def cancel_all_jobs(self, purge_jobs=False):
for printer in self: for printer in self:
connection = printer.server_id._open_connection() connection = printer.server_id._open_connection()
connection.cancelAllJobs( connection.cancelAllJobs(name=printer.system_name, purge_jobs=purge_jobs)
name=printer.system_name, purge_jobs=purge_jobs)
# Update jobs' states into Odoo # Update jobs' states into Odoo
self.mapped('server_id').update_jobs(which='completed') self.mapped("server_id").update_jobs(which="completed")
return True return True
@@ -214,7 +213,7 @@ class PrintingPrinter(models.Model):
connection.enablePrinter(printer.system_name) connection.enablePrinter(printer.system_name)
# Update printers' stats into Odoo # Update printers' stats into Odoo
self.mapped('server_id').update_printers() self.mapped("server_id").update_printers()
return True return True
@@ -224,6 +223,6 @@ class PrintingPrinter(models.Model):
connection.disablePrinter(printer.system_name) connection.disablePrinter(printer.system_name)
# Update printers' stats into Odoo # Update printers' stats into Odoo
self.mapped('server_id').update_printers() self.mapped("server_id").update_printers()
return True return True

View File

@@ -5,35 +5,35 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api from odoo import api, fields, models
class PrintingReportXmlAction(models.Model): class PrintingReportXmlAction(models.Model):
_name = 'printing.report.xml.action' _name = "printing.report.xml.action"
_description = 'Printing Report Printing Actions' _description = "Printing Report Printing Actions"
report_id = fields.Many2one(comodel_name='ir.actions.report', report_id = fields.Many2one(
string='Report', comodel_name="ir.actions.report",
required=True, string="Report",
ondelete='cascade') required=True,
user_id = fields.Many2one(comodel_name='res.users', ondelete="cascade",
string='User', )
required=True, user_id = fields.Many2one(
ondelete='cascade') comodel_name="res.users", string="User", required=True, ondelete="cascade"
)
action = fields.Selection( action = fields.Selection(
selection=lambda s: s.env['printing.action']._available_action_types(), selection=lambda s: s.env["printing.action"]._available_action_types(),
required=True, required=True,
) )
printer_id = fields.Many2one(comodel_name='printing.printer', printer_id = fields.Many2one(comodel_name="printing.printer", string="Printer")
string='Printer')
printer_tray_id = fields.Many2one( printer_tray_id = fields.Many2one(
comodel_name='printing.tray', comodel_name="printing.tray",
string='Paper Source', string="Paper Source",
domain="[('printer_id', '=', printer_id)]", domain="[('printer_id', '=', printer_id)]",
) )
@api.onchange('printer_id') @api.onchange("printer_id")
def onchange_printer_id(self): def onchange_printer_id(self):
""" Reset the tray when the printer is changed """ """ Reset the tray when the printer is changed """
self.printer_tray_id = False self.printer_tray_id = False
@@ -42,7 +42,7 @@ class PrintingReportXmlAction(models.Model):
if not self: if not self:
return {} return {}
return { return {
'action': self.action, "action": self.action,
'printer': self.printer_id, "printer": self.printer_id,
'tray': self.printer_tray_id.system_name "tray": self.printer_tray_id.system_name,
} }

View File

@@ -3,7 +3,8 @@
import logging import logging
from datetime import datetime from datetime import datetime
from odoo import models, fields, exceptions, _
from odoo import _, exceptions, fields, models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -11,26 +12,25 @@ _logger = logging.getLogger(__name__)
try: try:
import cups import cups
except ImportError: except ImportError:
_logger.debug('Cannot `import cups`.') _logger.debug("Cannot `import cups`.")
class PrintingServer(models.Model): class PrintingServer(models.Model):
_name = 'printing.server' _name = "printing.server"
_description = 'Printing server' _description = "Printing server"
name = fields.Char( name = fields.Char(default="Localhost", required=True, help="Name of the server.")
default='Localhost', required=True, help='Name of the server.')
address = fields.Char( address = fields.Char(
default='localhost', required=True, default="localhost", required=True, help="IP address or hostname of the server"
help='IP address or hostname of the server') )
port = fields.Integer( port = fields.Integer(default=631, required=True, help="Port of the server.")
default=631, required=True, help='Port of the server.') active = fields.Boolean(default=True, help="If checked, this server is useable.")
active = fields.Boolean(
default=True, help='If checked, this server is useable.')
printer_ids = fields.One2many( printer_ids = fields.One2many(
comodel_name='printing.printer', inverse_name='server_id', comodel_name="printing.printer",
string='Printers List', inverse_name="server_id",
help='List of printers available on this server.') string="Printers List",
help="List of printers available on this server.",
)
def _open_connection(self, raise_on_error=False): def _open_connection(self, raise_on_error=False):
self.ensure_one() self.ensure_one()
@@ -38,10 +38,11 @@ class PrintingServer(models.Model):
try: try:
connection = cups.Connection(host=self.address, port=self.port) connection = cups.Connection(host=self.address, port=self.port)
except Exception: except Exception:
message = _("Failed to connect to the CUPS server on %s:%s. " message = _(
"Check that the CUPS server is running and that " "Failed to connect to the CUPS server on %s:%s. "
"you can reach it from the Odoo server.") % ( "Check that the CUPS server is running and that "
self.address, self.port) "you can reach it from the Odoo server."
) % (self.address, self.port)
_logger.warning(message) _logger.warning(message)
if raise_on_error: if raise_on_error:
raise exceptions.UserError(message) raise exceptions.UserError(message)
@@ -63,28 +64,25 @@ class PrintingServer(models.Model):
for server in servers: for server in servers:
connection = server._open_connection(raise_on_error=raise_on_error) connection = server._open_connection(raise_on_error=raise_on_error)
if not connection: if not connection:
server.printer_ids.write({'status': 'server-error'}) server.printer_ids.write({"status": "server-error"})
res = False res = False
continue continue
# Update Printers # Update Printers
printers = connection.getPrinters() printers = connection.getPrinters()
existing_printers = dict([ existing_printers = {
(printer.system_name, printer) printer.system_name: printer for printer in server.printer_ids
for printer in server.printer_ids }
])
updated_printers = [] updated_printers = []
for name, printer_info in printers.items(): for name, printer_info in printers.items():
printer = self.env['printing.printer'] printer = self.env["printing.printer"]
if name in existing_printers: if name in existing_printers:
printer = existing_printers[name] printer = existing_printers[name]
printer_values = printer._prepare_update_from_cups( printer_values = printer._prepare_update_from_cups(
connection, printer_info) connection, printer_info
printer_values.update(
system_name=name,
server_id=server.id,
) )
printer_values.update(system_name=name, server_id=server.id)
updated_printers.append(name) updated_printers.append(name)
if not printer: if not printer:
printer.create(printer_values) printer.create(printer_values)
@@ -93,8 +91,8 @@ class PrintingServer(models.Model):
# Set printers not found as unavailable # Set printers not found as unavailable
server.printer_ids.filtered( server.printer_ids.filtered(
lambda record: record.system_name not in updated_printers)\ lambda record: record.system_name not in updated_printers
.write({'status': 'unavailable'}) ).write({"status": "unavailable"})
return res return res
@@ -103,18 +101,18 @@ class PrintingServer(models.Model):
self = self.search([]) self = self.search([])
return self.update_jobs() return self.update_jobs()
def update_jobs(self, which='all', first_job_id=-1): def update_jobs(self, which="all", first_job_id=-1):
job_obj = self.env['printing.job'] job_obj = self.env["printing.job"]
printer_obj = self.env['printing.printer'] printer_obj = self.env["printing.printer"]
mapping = { mapping = {
3: 'pending', 3: "pending",
4: 'pending held', 4: "pending held",
5: 'processing', 5: "processing",
6: 'processing stopped', 6: "processing stopped",
7: 'canceled', 7: "canceled",
8: 'aborted', 8: "aborted",
9: 'completed', 9: "completed",
} }
# Update printers list, to ensure that jobs printers will be in Odoo # Update printers list, to ensure that jobs printers will be in Odoo
@@ -127,86 +125,91 @@ class PrintingServer(models.Model):
# Retrieve asked job data # Retrieve asked job data
jobs_data = connection.getJobs( jobs_data = connection.getJobs(
which_jobs=which, first_job_id=first_job_id, which_jobs=which,
first_job_id=first_job_id,
requested_attributes=[ requested_attributes=[
'job-name', "job-name",
'job-id', "job-id",
'printer-uri', "printer-uri",
'job-media-progress', "job-media-progress",
'time-at-creation', "time-at-creation",
'job-state', "job-state",
'job-state-reasons', "job-state-reasons",
'time-at-processing', "time-at-processing",
'time-at-completed', "time-at-completed",
]) ],
)
# Retrieve known uncompleted jobs data to update them # Retrieve known uncompleted jobs data to update them
if which == 'not-completed': if which == "not-completed":
oldest_uncompleted_job = job_obj.search([ oldest_uncompleted_job = job_obj.search(
('job_state', 'not in', ( [("job_state", "not in", ("canceled", "aborted", "completed"))],
'canceled', limit=1,
'aborted', order="job_id_cups",
'completed', )
)),
], limit=1, order='job_id_cups')
if oldest_uncompleted_job: if oldest_uncompleted_job:
jobs_data.update(connection.getJobs( jobs_data.update(
which_jobs='completed', connection.getJobs(
first_job_id=oldest_uncompleted_job.job_id_cups, which_jobs="completed",
requested_attributes=[ first_job_id=oldest_uncompleted_job.job_id_cups,
'job-name', requested_attributes=[
'job-id', "job-name",
'printer-uri', "job-id",
'job-media-progress', "printer-uri",
'time-at-creation', "job-media-progress",
'job-state', "time-at-creation",
'job-state-reasons', "job-state",
'time-at-processing', "job-state-reasons",
'time-at-completed', "time-at-processing",
])) "time-at-completed",
],
)
)
all_cups_job_ids = set() all_cups_job_ids = set()
for cups_job_id, job_data in jobs_data.items(): for cups_job_id, job_data in jobs_data.items():
all_cups_job_ids.add(cups_job_id) all_cups_job_ids.add(cups_job_id)
jobs = job_obj.with_context(active_test=False).search([ jobs = job_obj.with_context(active_test=False).search(
('job_id_cups', '=', cups_job_id), [("job_id_cups", "=", cups_job_id), ("server_id", "=", server.id)]
('server_id', '=', server.id), )
])
job_values = { job_values = {
'name': job_data.get('job-name', ''), "name": job_data.get("job-name", ""),
'active': True, "active": True,
'job_id_cups': cups_job_id, "job_id_cups": cups_job_id,
'job_media_progress': job_data.get( "job_media_progress": job_data.get("job-media-progress", False),
'job-media-progress', False), "job_state": mapping.get(job_data.get("job-state"), "unknown"),
'job_state': mapping.get( "job_state_reason": job_data.get("job-state-reasons", ""),
job_data.get('job-state'), 'unknown'), "time_at_creation": fields.Datetime.to_string(
'job_state_reason': job_data.get('job-state-reasons', ''), datetime.fromtimestamp(job_data.get("time-at-creation", False))
'time_at_creation': fields.Datetime.to_string( ),
datetime.fromtimestamp(job_data.get( "time_at_processing": job_data.get("time-at-processing", False)
'time-at-creation', False))), and fields.Datetime.to_string(
'time_at_processing': job_data.get( datetime.fromtimestamp(
'time-at-processing', False) and job_data.get("time-at-processing", False)
fields.Datetime.to_string(datetime.fromtimestamp( )
job_data.get('time-at-processing', False))), ),
'time_at_completed': job_data.get( "time_at_completed": job_data.get("time-at-completed", False)
'time-at-completed', False) and and fields.Datetime.to_string(
fields.Datetime.to_string(datetime.fromtimestamp( datetime.fromtimestamp(job_data.get("time-at-completed", False))
job_data.get('time-at-completed', False))), ),
} }
# Search for the printer in Odoo # Search for the printer in Odoo
printer_uri = job_data['printer-uri'] printer_uri = job_data["printer-uri"]
printer_system_name = printer_uri[printer_uri.rfind('/') + 1:] printer_system_name = printer_uri[printer_uri.rfind("/") + 1 :]
printer = printer_obj.search([ printer = printer_obj.search(
('server_id', '=', server.id), [
('system_name', '=', printer_system_name), ("server_id", "=", server.id),
], limit=1) ("system_name", "=", printer_system_name),
],
limit=1,
)
# CUPS retains jobs for disconnected printers and also may # CUPS retains jobs for disconnected printers and also may
# leak jobs data for unshared printers, therefore we just # leak jobs data for unshared printers, therefore we just
# discard here if not printer found # discard here if not printer found
if not printer: if not printer:
continue continue
job_values['printer_id'] = printer.id job_values["printer_id"] = printer.id
if jobs: if jobs:
jobs.write(job_values) jobs.write(job_values)
@@ -214,10 +217,10 @@ class PrintingServer(models.Model):
job_obj.create(job_values) job_obj.create(job_values)
# Deactive purged jobs # Deactive purged jobs
if which == 'all' and first_job_id == -1: if which == "all" and first_job_id == -1:
purged_jobs = job_obj.search([ purged_jobs = job_obj.search(
('job_id_cups', 'not in', list(all_cups_job_ids)), [("job_id_cups", "not in", list(all_cups_job_ids))]
]) )
purged_jobs.write({'active': False}) purged_jobs.write({"active": False})
return True return True

View File

@@ -5,17 +5,17 @@ from odoo import fields, models
class PrinterTray(models.Model): class PrinterTray(models.Model):
_name = 'printing.tray' _name = "printing.tray"
_description = 'Printer Tray' _description = "Printer Tray"
_order = 'name asc' _order = "name asc"
name = fields.Char(required=True) name = fields.Char(required=True)
system_name = fields.Char(required=True, readonly=True) system_name = fields.Char(required=True, readonly=True)
printer_id = fields.Many2one( printer_id = fields.Many2one(
comodel_name='printing.printer', comodel_name="printing.printer",
string='Printer', string="Printer",
required=True, required=True,
readonly=True, readonly=True,
ondelete='cascade', ondelete="cascade",
) )

View File

@@ -5,46 +5,38 @@
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api from odoo import api, fields, models
class ResUsers(models.Model): class ResUsers(models.Model):
_inherit = 'res.users' _inherit = "res.users"
@api.model @api.model
def _user_available_action_types(self): def _user_available_action_types(self):
return [ return [
(code, string) (code, string)
for code, string for code, string in self.env["printing.action"]._available_action_types()
in self.env['printing.action']._available_action_types() if code != "user_default"
if code != 'user_default'
] ]
printing_action = fields.Selection( printing_action = fields.Selection(selection=_user_available_action_types)
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 @api.model
def _register_hook(self): def _register_hook(self):
super()._register_hook() super()._register_hook()
self.SELF_WRITEABLE_FIELDS.extend([ self.SELF_WRITEABLE_FIELDS.extend(["printing_action", "printing_printer_id"])
'printing_action', self.SELF_READABLE_FIELDS.extend(["printing_action", "printing_printer_id"])
'printing_printer_id',
])
self.SELF_READABLE_FIELDS.extend([
'printing_action',
'printing_printer_id',
])
printer_tray_id = fields.Many2one( printer_tray_id = fields.Many2one(
comodel_name='printing.tray', comodel_name="printing.tray",
string='Default Printer Paper Source', string="Default Printer Paper Source",
domain="[('printer_id', '=', printing_printer_id)]", domain="[('printer_id', '=', printing_printer_id)]",
) )
@api.onchange('printing_printer_id') @api.onchange("printing_printer_id")
def onchange_printing_printer_id(self): def onchange_printing_printer_id(self):
""" Reset the tray when the printer is changed """ """ Reset the tray when the printer is changed """
self.printer_tray_id = False self.printer_tray_id = False

View File

@@ -32,8 +32,8 @@ odoo.define('base_report_to_printer.print', function (require) {
}, function () { }, function () {
self.do_notify(_t('Report'), self.do_notify(_t('Report'),
_.str.sprintf( _.str.sprintf(
_t('Error when sending the document\ _t('Error when sending the document ' +
to the printer '), 'to the printer '),
print_action.printer_name print_action.printer_name
) )
); );
@@ -48,4 +48,3 @@ odoo.define('base_report_to_printer.print', function (require) {
}, },
}); });
}); });

View File

@@ -8,79 +8,74 @@ from odoo.tests.common import TransactionCase
class TestIrActionsReportXml(TransactionCase): class TestIrActionsReportXml(TransactionCase):
def setUp(self): def setUp(self):
super(TestIrActionsReportXml, self).setUp() super(TestIrActionsReportXml, self).setUp()
self.Model = self.env['ir.actions.report'] self.Model = self.env["ir.actions.report"]
self.vals = {} self.vals = {}
self.report = self.env['ir.actions.report'].search([], limit=1) self.report = self.env["ir.actions.report"].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):
return self.env['printing.action'].create({ return self.env["printing.action"].create(
'name': 'Printing Action', {"name": "Printing Action", "action_type": "server"}
'action_type': 'server', )
})
def new_printing_action(self): def new_printing_action(self):
return self.env['printing.report.xml.action'].create({ return self.env["printing.report.xml.action"].create(
'report_id': self.report.id, {
'user_id': self.env.ref('base.user_demo').id, "report_id": self.report.id,
'action': 'server', "user_id": self.env.ref("base.user_demo").id,
}) "action": "server",
}
)
def new_printer(self): def new_printer(self):
return self.env['printing.printer'].create({ return self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': self.server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": self.server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
}
)
def new_tray(self, vals=None, defaults=None): def new_tray(self, vals=None, defaults=None):
values = dict(defaults) values = dict(defaults)
if vals is not None: if vals is not None:
values.update(vals) values.update(vals)
return self.env['printing.tray'].create(values) return self.env["printing.tray"].create(values)
def test_print_action_for_report_name_gets_report(self): def test_print_action_for_report_name_gets_report(self):
""" It should get report by name """ """ It should get report by name """
with mock.patch.object(self.Model, '_get_report_from_name') as mk: with mock.patch.object(self.Model, "_get_report_from_name") as mk:
expect = 'test' expect = "test"
self.Model.print_action_for_report_name(expect) self.Model.print_action_for_report_name(expect)
mk.assert_called_once_with( mk.assert_called_once_with(expect)
expect
)
def test_print_action_for_report_name_returns_if_no_report(self): def test_print_action_for_report_name_returns_if_no_report(self):
""" It should return empty dict when no matching report """ """ It should return empty dict when no matching report """
with mock.patch.object(self.Model, '_get_report_from_name') as mk: with mock.patch.object(self.Model, "_get_report_from_name") as mk:
expect = 'test' expect = "test"
mk.return_value = False mk.return_value = False
res = self.Model.print_action_for_report_name(expect) res = self.Model.print_action_for_report_name(expect)
self.assertDictEqual( self.assertDictEqual({}, res)
{}, res,
)
def test_print_action_for_report_name_returns_if_report(self): def test_print_action_for_report_name_returns_if_report(self):
""" It should return correct serializable result for behaviour """ """ It should return correct serializable result for behaviour """
with mock.patch.object(self.Model, '_get_report_from_name') as mk: with mock.patch.object(self.Model, "_get_report_from_name") as mk:
res = self.Model.print_action_for_report_name('test') res = self.Model.print_action_for_report_name("test")
behaviour = mk().behaviour() behaviour = mk().behaviour()
expect = { expect = {
'action': behaviour['action'], "action": behaviour["action"],
'printer_name': behaviour['printer'].name, "printer_name": behaviour["printer"].name,
} }
self.assertDictEqual( self.assertDictEqual(expect, res, "Expect {}, Got {}".format(expect, res))
expect, res,
'Expect %s, Got %s' % (expect, res),
)
def test_behaviour_default_values(self): def test_behaviour_default_values(self):
""" It should return the default action and printer """ """ It should return the default action and printer """
@@ -89,81 +84,81 @@ class TestIrActionsReportXml(TransactionCase):
self.env.user.printing_printer_id = False self.env.user.printing_printer_id = False
report.property_printing_action_id = False report.property_printing_action_id = False
report.printing_printer_id = False report.printing_printer_id = False
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': self.env['printing.printer'], {
'tray': False, "action": "client",
}, "printer": self.env["printing.printer"],
"tray": False,
},
) )
def test_behaviour_user_values(self): def test_behaviour_user_values(self):
""" It should return the action and printer from user """ """ It should return the action and printer from user """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client' self.env.user.printing_action = "client"
self.env.user.printing_printer_id = self.new_printer() self.env.user.printing_printer_id = self.new_printer()
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': self.env.user.printing_printer_id, {
'tray': False, "action": "client",
}, "printer": self.env.user.printing_printer_id,
"tray": False,
},
) )
def test_behaviour_report_values(self): def test_behaviour_report_values(self):
""" It should return the action and printer from report """ """ It should return the action and printer from report """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client' self.env.user.printing_action = "client"
report.property_printing_action_id = self.new_action() report.property_printing_action_id = self.new_action()
report.printing_printer_id = self.new_printer() report.printing_printer_id = self.new_printer()
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': report.property_printing_action_id.action_type, report.behaviour(),
'printer': report.printing_printer_id, {
'tray': False, "action": report.property_printing_action_id.action_type,
}, "printer": report.printing_printer_id,
"tray": False,
},
) )
def test_behaviour_user_action(self): def test_behaviour_user_action(self):
""" It should return the action and printer from user action""" """ It should return the action and printer from user action"""
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client' self.env.user.printing_action = "client"
report.property_printing_action_id.action_type = 'user_default' report.property_printing_action_id.action_type = "user_default"
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': report.printing_printer_id, {"action": "client", "printer": report.printing_printer_id, "tray": False},
'tray': False,
},
) )
def test_behaviour_printing_action_on_wrong_user(self): def test_behaviour_printing_action_on_wrong_user(self):
""" It should return the action and printer ignoring printing action """ It should return the action and printer ignoring printing action
""" """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
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['res.users'].search([ printing_action.user_id = self.env["res.users"].search(
('id', '!=', self.env.user.id), [("id", "!=", self.env.user.id)], limit=1
], limit=1) )
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': report.printing_printer_id, {"action": "client", "printer": report.printing_printer_id, "tray": False},
'tray': False,
},
) )
def test_behaviour_printing_action_on_wrong_report(self): def test_behaviour_printing_action_on_wrong_report(self):
""" It should return the action and printer ignoring printing action """ It should return the action and printer ignoring printing action
""" """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
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.env["ir.actions.report"].search(
('id', '!=', report.id), [("id", "!=", report.id)], limit=1
], limit=1) )
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': report.printing_printer_id, {"action": "client", "printer": report.printing_printer_id, "tray": False},
'tray': False,
},
) )
def test_behaviour_printing_action_with_no_printer(self): def test_behaviour_printing_action_with_no_printer(self):
@@ -171,29 +166,33 @@ class TestIrActionsReportXml(TransactionCase):
other other
""" """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
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 = report printing_action.report_id = report
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': printing_action.action, report.behaviour(),
'printer': report.printing_printer_id, {
'tray': False, "action": printing_action.action,
}, "printer": report.printing_printer_id,
"tray": False,
},
) )
def test_behaviour_printing_action_with_printer(self): def test_behaviour_printing_action_with_printer(self):
""" It should return the action and printer from printing action """ """ It should return the action and printer from printing action """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
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.printer_id = self.new_printer() printing_action.printer_id = self.new_printer()
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': printing_action.action, report.behaviour(),
'printer': printing_action.printer_id, {
'tray': False, "action": printing_action.action,
}, "printer": printing_action.printer_id,
"tray": False,
},
) )
def test_behaviour_printing_action_user_defaults(self): def test_behaviour_printing_action_user_defaults(self):
@@ -201,106 +200,96 @@ class TestIrActionsReportXml(TransactionCase):
action action
""" """
report = self.Model.search([], limit=1) report = self.Model.search([], limit=1)
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.action = 'user_default' printing_action.action = "user_default"
self.assertEqual(report.behaviour(), { self.assertEqual(
'action': 'client', report.behaviour(),
'printer': report.printing_printer_id, {"action": "client", "printer": report.printing_printer_id, "tray": False},
'tray': False,
},
) )
def test_print_tray_behaviour(self): def test_print_tray_behaviour(self):
""" """
It should return the correct tray It should return the correct tray
""" """
report = self.env['ir.actions.report'].search([], limit=1) report = self.env["ir.actions.report"].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, {"user_id": self.env.user.id, "report_id": report.id, "action": "server"}
'report_id': report.id, )
'action': 'server',
})
printer = self.new_printer() printer = self.new_printer()
tray_vals = { tray_vals = {"name": "Tray", "system_name": "Tray", "printer_id": printer.id}
'name': 'Tray', user_tray = self.new_tray({"system_name": "User tray"}, tray_vals)
'system_name': 'Tray', report_tray = self.new_tray({"system_name": "Report tray"}, tray_vals)
'printer_id': printer.id, action_tray = self.new_tray({"system_name": "Action tray"}, tray_vals)
}
user_tray = self.new_tray({'system_name': 'User tray'}, tray_vals)
report_tray = self.new_tray({'system_name': 'Report tray'}, tray_vals)
action_tray = self.new_tray({'system_name': 'Action tray'}, tray_vals)
# No report passed # No report passed
self.env.user.printer_tray_id = False self.env.user.printer_tray_id = False
options = printer.print_options() options = printer.print_options()
self.assertFalse('InputSlot' in options) self.assertFalse("InputSlot" in options)
# No tray defined # No tray defined
self.env.user.printer_tray_id = False self.env.user.printer_tray_id = False
report.printer_tray_id = False report.printer_tray_id = False
action.printer_tray_id = False action.printer_tray_id = False
options = report.behaviour() options = report.behaviour()
self.assertTrue('tray' in options) self.assertTrue("tray" in options)
# Only user tray is defined # Only user tray is defined
self.env.user.printer_tray_id = user_tray self.env.user.printer_tray_id = user_tray
report.printer_tray_id = False report.printer_tray_id = False
action.printer_tray_id = False action.printer_tray_id = False
self.assertEqual('User tray', report.behaviour()['tray']) self.assertEqual("User tray", report.behaviour()["tray"])
# Only report tray is defined # Only report tray is defined
self.env.user.printer_tray_id = False self.env.user.printer_tray_id = False
report.printer_tray_id = report_tray report.printer_tray_id = report_tray
action.printer_tray_id = False action.printer_tray_id = False
self.assertEqual('Report tray', report.behaviour()['tray']) self.assertEqual("Report tray", report.behaviour()["tray"])
# Only action tray is defined # Only action tray is defined
self.env.user.printer_tray_id = False self.env.user.printer_tray_id = False
report.printer_tray_id = False report.printer_tray_id = False
action.printer_tray_id = action_tray action.printer_tray_id = action_tray
self.assertEqual('Action tray', report.behaviour()['tray']) self.assertEqual("Action tray", report.behaviour()["tray"])
# User and report tray defined # User and report tray defined
self.env.user.printer_tray_id = user_tray self.env.user.printer_tray_id = user_tray
report.printer_tray_id = report_tray report.printer_tray_id = report_tray
action.printer_tray_id = False action.printer_tray_id = False
self.assertEqual('Report tray', report.behaviour()['tray']) self.assertEqual("Report tray", report.behaviour()["tray"])
# All trays are defined # All trays are defined
self.env.user.printer_tray_id = user_tray self.env.user.printer_tray_id = user_tray
report.printer_tray_id = report_tray report.printer_tray_id = report_tray
action.printer_tray_id = action_tray action.printer_tray_id = action_tray
self.assertEqual('Action tray', report.behaviour()['tray']) self.assertEqual("Action tray", report.behaviour()["tray"])
def test_onchange_printer_tray_id_empty(self): def test_onchange_printer_tray_id_empty(self):
action = self.env['ir.actions.report'].new( action = self.env["ir.actions.report"].new({"printer_tray_id": False})
{'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)
def test_onchange_printer_tray_id_not_empty(self): def test_onchange_printer_tray_id_not_empty(self):
server = self.env['printing.server'].create({}) server = self.env["printing.server"].create({})
printer = self.env['printing.printer'].create({ printer = self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
tray = self.env['printing.tray'].create({ }
'name': 'Tray', )
'system_name': 'TrayName', tray = self.env["printing.tray"].create(
'printer_id': printer.id, {"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
}) )
action = self.env['ir.actions.report'].new( action = self.env["ir.actions.report"].new({"printer_tray_id": tray.id})
{'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)

View File

@@ -6,60 +6,58 @@ import mock
from odoo import fields from odoo import fields
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_server"
model = 'odoo.addons.base_report_to_printer.models.printing_server'
class TestPrintingJob(TransactionCase): class TestPrintingJob(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingJob, self).setUp() super(TestPrintingJob, self).setUp()
self.Model = self.env['printing.server'] self.Model = self.env["printing.server"]
self.server = self.Model.create({}) self.server = self.Model.create({})
self.printer_vals = { self.printer_vals = {
'name': 'Printer', "name": "Printer",
'server_id': self.server.id, "server_id": self.server.id,
'system_name': 'Sys Name', "system_name": "Sys Name",
'default': True, "default": True,
'status': 'unknown', "status": "unknown",
'status_message': 'Msg', "status_message": "Msg",
'model': 'res.users', "model": "res.users",
'location': 'Location', "location": "Location",
'uri': 'URI', "uri": "URI",
} }
self.job_vals = { self.job_vals = {
'server_id': self.server.id, "server_id": self.server.id,
'job_id_cups': 1, "job_id_cups": 1,
'job_media_progress': 0, "job_media_progress": 0,
'time_at_creation': fields.Datetime.now(), "time_at_creation": fields.Datetime.now(),
} }
def new_printer(self): def new_printer(self):
return self.env['printing.printer'].create(self.printer_vals) return self.env["printing.printer"].create(self.printer_vals)
def new_job(self, printer, vals=None): def new_job(self, printer, vals=None):
values = self.job_vals values = self.job_vals
if vals is not None: if vals is not None:
values.update(vals) values.update(vals)
values['printer_id'] = printer.id values["printer_id"] = printer.id
return self.env['printing.job'].create(values) return self.env["printing.job"].create(values)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_cancel_job_error(self, cups): def test_cancel_job_error(self, cups):
""" It should catch any exception from CUPS and update status """ """ It should catch any exception from CUPS and update status """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
printer = self.new_printer() printer = self.new_printer()
job = self.new_job(printer, {'job_id_cups': 2}) job = self.new_job(printer, {"job_id_cups": 2})
job.action_cancel() job.action_cancel()
cups.Connection.side_effect = None cups.Connection.side_effect = None
self.assertEqual(cups.Connection().cancelJob.call_count, 0) self.assertEqual(cups.Connection().cancelJob.call_count, 0)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_cancel_job(self, cups): def test_cancel_job(self, cups):
""" It should catch any exception from CUPS and update status """ """ It should catch any exception from CUPS and update status """
printer = self.new_printer() printer = self.new_printer()
job = self.new_job(printer) job = self.new_job(printer)
job.cancel() job.cancel()
cups.Connection().cancelJob.assert_called_once_with( cups.Connection().cancelJob.assert_called_once_with(
job.job_id_cups, purge_job=False, job.job_id_cups, purge_job=False
) )

View File

@@ -2,35 +2,34 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import tempfile import tempfile
import mock import mock
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_printer"
model = 'odoo.addons.base_report_to_printer.models.printing_printer' server_model = "odoo.addons.base_report_to_printer.models.printing_server"
server_model = 'odoo.addons.base_report_to_printer.models.printing_server'
class TestPrintingPrinter(TransactionCase): class TestPrintingPrinter(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingPrinter, self).setUp() super(TestPrintingPrinter, self).setUp()
self.Model = self.env['printing.printer'] self.Model = self.env["printing.printer"]
self.ServerModel = self.env['printing.server'] self.ServerModel = self.env["printing.server"]
self.server = self.env['printing.server'].create({}) self.server = self.env["printing.server"].create({})
self.printer_vals = { self.printer_vals = {
'name': 'Printer', "name": "Printer",
'server_id': self.server.id, "server_id": self.server.id,
'system_name': 'Sys Name', "system_name": "Sys Name",
'default': True, "default": True,
'status': 'unknown', "status": "unknown",
'status_message': 'Msg', "status_message": "Msg",
'model': 'res.users', "model": "res.users",
'location': 'Location', "location": "Location",
'uri': 'URI', "uri": "URI",
} }
self.report = self.env['ir.actions.report'].search([], limit=1) self.report = self.env["ir.actions.report"].search([], limit=1)
def new_record(self): def new_record(self):
return self.Model.create(self.printer_vals) return self.Model.create(self.printer_vals)
@@ -39,94 +38,88 @@ class TestPrintingPrinter(TransactionCase):
""" """
It should put the value in InputSlot It should put the value in InputSlot
""" """
self.assertEqual(self.Model._set_option_tray(None, 'Test Tray'), self.assertEqual(
{'InputSlot': 'Test Tray'}) self.Model._set_option_tray(None, "Test Tray"), {"InputSlot": "Test Tray"}
self.assertEqual(self.Model._set_option_tray(None, False), )
{}) self.assertEqual(self.Model._set_option_tray(None, False), {})
def test_option_noops(self): def test_option_noops(self):
""" """
Noops should return an empty dict Noops should return an empty dict
""" """
self.assertEqual(self.Model._set_option_action(None, 'printer'), {}) self.assertEqual(self.Model._set_option_action(None, "printer"), {})
self.assertEqual(self.Model._set_option_printer(None, self.Model), {}) self.assertEqual(self.Model._set_option_printer(None, self.Model), {})
def test_option_doc_format(self): def test_option_doc_format(self):
""" """
Raw documents should set raw boolean. Raw documents should set raw boolean.
""" """
self.assertEqual(self.Model._set_option_doc_format(None, 'raw'), self.assertEqual(
{'raw': 'True'}) self.Model._set_option_doc_format(None, "raw"), {"raw": "True"}
)
# Deprecate _set_option_format in v12. # Deprecate _set_option_format in v12.
self.assertEqual(self.Model._set_option_format(None, 'raw'), self.assertEqual(self.Model._set_option_format(None, "raw"), {"raw": "True"})
{'raw': 'True'})
self.assertEqual(self.Model._set_option_doc_format(None, 'pdf'), {}) self.assertEqual(self.Model._set_option_doc_format(None, "pdf"), {})
# Deprecate _set_option_format in v12. # Deprecate _set_option_format in v12.
self.assertEqual(self.Model._set_option_format(None, 'pdf'), {}) self.assertEqual(self.Model._set_option_format(None, "pdf"), {})
def test_print_options(self): def test_print_options(self):
""" It should generate the right options dictionnary """ """ It should generate the right options dictionnary """
# TODO: None here used as report - tests here should be merged # TODO: None here used as report - tests here should be merged
# with tests in test_printing_printer_tray from when modules merged # with tests in test_printing_printer_tray from when modules merged
report = self.env['ir.actions.report'].search([], limit=1) report = self.env["ir.actions.report"].search([], limit=1)
self.assertEqual(self.Model.print_options( self.assertEqual(self.Model.print_options(doc_format="raw"), {"raw": "True"})
doc_format='raw'), {'raw': 'True'} self.assertEqual(
self.Model.print_options(report, doc_format="pdf", copies=2),
{"copies": "2"},
) )
self.assertEqual(self.Model.print_options( self.assertEqual(
report, doc_format='pdf', copies=2), {'copies': '2'} self.Model.print_options(report, doc_format="raw", copies=2),
{"raw": "True", "copies": "2"},
) )
self.assertEqual(self.Model.print_options( self.assertTrue("InputSlot" in self.Model.print_options(report, tray="Test"))
report, doc_format='raw', copies=2),
{'raw': 'True', 'copies': '2'}
)
self.assertTrue('InputSlot' in self.Model.print_options(report,
tray='Test'))
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_print_report(self, cups): def test_print_report(self, cups):
""" It should print a report through CUPS """ """ It should print a report through CUPS """
fd, file_name = tempfile.mkstemp() fd, file_name = tempfile.mkstemp()
with mock.patch('%s.mkstemp' % model) as mkstemp: with mock.patch("%s.mkstemp" % model) as mkstemp:
mkstemp.return_value = fd, file_name mkstemp.return_value = fd, file_name
printer = self.new_record() printer = self.new_record()
printer.print_document(self.report, b'content to print', printer.print_document(self.report, b"content to print", doc_format="pdf")
doc_format='pdf')
cups.Connection().printFile.assert_called_once_with( cups.Connection().printFile.assert_called_once_with(
printer.system_name, printer.system_name, file_name, file_name, options={}
file_name, )
file_name,
options={})
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_print_report_error(self, cups): def test_print_report_error(self, cups):
""" It should print a report through CUPS """ """ It should print a report through CUPS """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
fd, file_name = tempfile.mkstemp() fd, file_name = tempfile.mkstemp()
with mock.patch('%s.mkstemp' % model) as mkstemp: with mock.patch("%s.mkstemp" % model) as mkstemp:
mkstemp.return_value = fd, file_name mkstemp.return_value = fd, file_name
printer = self.new_record() printer = self.new_record()
with self.assertRaises(UserError): with self.assertRaises(UserError):
printer.print_document( printer.print_document(
self.report, b'content to print', doc_format='pdf') self.report, b"content to print", doc_format="pdf"
)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_print_file(self, cups): def test_print_file(self, cups):
""" It should print a file through CUPS """ """ It should print a file through CUPS """
file_name = 'file_name' file_name = "file_name"
printer = self.new_record() printer = self.new_record()
printer.print_file(file_name, 'pdf') printer.print_file(file_name, "pdf")
cups.Connection().printFile.assert_called_once_with( cups.Connection().printFile.assert_called_once_with(
printer.system_name, printer.system_name, file_name, file_name, options={}
file_name, )
file_name,
options={})
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_print_file_error(self, cups): def test_print_file_error(self, cups):
""" It should print a file through CUPS """ """ It should print a file through CUPS """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
file_name = 'file_name' file_name = "file_name"
printer = self.new_record() printer = self.new_record()
with self.assertRaises(UserError): with self.assertRaises(UserError):
printer.print_file(file_name) printer.print_file(file_name)
@@ -150,38 +143,34 @@ class TestPrintingPrinter(TransactionCase):
printer.unset_default() printer.unset_default()
self.assertFalse(printer.default) self.assertFalse(printer.default)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_cancel_all_jobs(self, cups): def test_cancel_all_jobs(self, cups):
""" It should cancel all jobs """ """ It should cancel all jobs """
printer = self.new_record() printer = self.new_record()
printer.action_cancel_all_jobs() printer.action_cancel_all_jobs()
cups.Connection().cancelAllJobs.assert_called_once_with( cups.Connection().cancelAllJobs.assert_called_once_with(
name=printer.system_name, name=printer.system_name, purge_jobs=False
purge_jobs=False,
) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_cancel_and_purge_all_jobs(self, cups): def test_cancel_and_purge_all_jobs(self, cups):
""" It should cancel all jobs """ """ It should cancel all jobs """
printer = self.new_record() printer = self.new_record()
printer.cancel_all_jobs(purge_jobs=True) printer.cancel_all_jobs(purge_jobs=True)
cups.Connection().cancelAllJobs.assert_called_once_with( cups.Connection().cancelAllJobs.assert_called_once_with(
name=printer.system_name, name=printer.system_name, purge_jobs=True
purge_jobs=True,
) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_enable_printer(self, cups): def test_enable_printer(self, cups):
""" It should enable the printer """ """ It should enable the printer """
printer = self.new_record() printer = self.new_record()
printer.enable() printer.enable()
cups.Connection().enablePrinter.assert_called_once_with( cups.Connection().enablePrinter.assert_called_once_with(printer.system_name)
printer.system_name)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_disable_printer(self, cups): def test_disable_printer(self, cups):
""" It should disable the printer """ """ It should disable the printer """
printer = self.new_record() printer = self.new_record()
printer.disable() printer.disable()
cups.Connection().disablePrinter.assert_called_once_with( cups.Connection().disablePrinter.assert_called_once_with(printer.system_name)
printer.system_name)

View File

@@ -2,13 +2,14 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import errno import errno
import mock
import tempfile import tempfile
import mock
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_printer"
model = 'odoo.addons.base_report_to_printer.models.printing_printer' server_model = "odoo.addons.base_report_to_printer.models.printing_server"
server_model = 'odoo.addons.base_report_to_printer.models.printing_server'
ppd_header = '*PPD-Adobe: "4.3"' ppd_header = '*PPD-Adobe: "4.3"'
ppd_input_slot_header = """ ppd_input_slot_header = """
@@ -33,34 +34,35 @@ ppd_input_slot_footer = """
class TestPrintingPrinter(TransactionCase): class TestPrintingPrinter(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingPrinter, self).setUp() super(TestPrintingPrinter, self).setUp()
self.Model = self.env['printing.printer'] self.Model = self.env["printing.printer"]
self.ServerModel = self.env['printing.server'] self.ServerModel = self.env["printing.server"]
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', {
'server_id': self.server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": self.server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
}
)
self.tray_vals = { self.tray_vals = {
'name': 'Tray', "name": "Tray",
'system_name': 'TrayName', "system_name": "TrayName",
'printer_id': self.printer.id, "printer_id": self.printer.id,
} }
def new_tray(self, vals=None): def new_tray(self, vals=None):
values = self.tray_vals values = self.tray_vals
if vals is not None: if vals is not None:
values.update(vals) values.update(vals)
return self.env['printing.tray'].create(values) return self.env["printing.tray"].create(values)
def build_ppd(self, input_slots=None): def build_ppd(self, input_slots=None):
""" """
@@ -71,8 +73,7 @@ class TestPrintingPrinter(TransactionCase):
if input_slots is not None: if input_slots is not None:
for input_slot in input_slots: for input_slot in input_slots:
ppd_contents += ppd_input_slot_body.format( ppd_contents += ppd_input_slot_body.format(
name=input_slot['name'], name=input_slot["name"], text=input_slot["text"]
text=input_slot['text'],
) )
ppd_contents += ppd_input_slot_footer ppd_contents += ppd_input_slot_footer
@@ -88,29 +89,29 @@ class TestPrintingPrinter(TransactionCase):
if file_name: if file_name:
ppd_contents = self.build_ppd(input_slots=input_slots) ppd_contents = self.build_ppd(input_slots=input_slots)
with open(file_name, 'w') as fp: with open(file_name, "w") as fp:
fp.write(ppd_contents) fp.write(ppd_contents)
cups.Connection().getPPD3.return_value = (200, 0, file_name) cups.Connection().getPPD3.return_value = (200, 0, file_name)
cups.Connection().getPrinters.return_value = { cups.Connection().getPrinters.return_value = {
self.printer.system_name: { self.printer.system_name: {
'printer-info': 'info', "printer-info": "info",
'printer-uri-supported': 'uri', "printer-uri-supported": "uri",
}, }
} }
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_update_printers(self, cups): def test_update_printers(self, cups):
""" """
Check that the update_printers method calls _prepare_update_from_cups Check that the update_printers method calls _prepare_update_from_cups
""" """
self.mock_cups_ppd(cups, file_name=False) self.mock_cups_ppd(cups, file_name=False)
self.assertEqual(self.printer.name, 'Printer') 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")
@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):
""" """
Check that the tray_ids field has no value when no PPD is available Check that the tray_ids field has no value when no PPD is available
@@ -121,9 +122,9 @@ class TestPrintingPrinter(TransactionCase):
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertFalse('tray_ids' in vals) self.assertFalse("tray_ids" in vals)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups_empty_ppd(self, cups): def test_prepare_update_from_cups_empty_ppd(self, cups):
""" """
Check that the tray_ids field has no value when the PPD file has Check that the tray_ids field has no value when the PPD file has
@@ -132,23 +133,23 @@ class TestPrintingPrinter(TransactionCase):
fd, file_name = tempfile.mkstemp() fd, file_name = tempfile.mkstemp()
self.mock_cups_ppd(cups, file_name=file_name) self.mock_cups_ppd(cups, file_name=file_name)
# Replace the ppd file's contents by an empty file # Replace the ppd file's contents by an empty file
with open(file_name, 'w') as fp: with open(file_name, "w") as fp:
fp.write(ppd_header) fp.write(ppd_header)
connection = cups.Connection() connection = cups.Connection()
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertFalse('tray_ids' in vals) self.assertFalse("tray_ids" in vals)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
@mock.patch('os.unlink') @mock.patch("os.unlink")
def test_prepare_update_from_cups_unlink_error(self, os_unlink, cups): def test_prepare_update_from_cups_unlink_error(self, os_unlink, cups):
""" """
When OSError other than ENOENT is encountered, the exception is raised When OSError other than ENOENT is encountered, the exception is raised
""" """
# Break os.unlink # Break os.unlink
os_unlink.side_effect = OSError(errno.EIO, 'Error') os_unlink.side_effect = OSError(errno.EIO, "Error")
self.mock_cups_ppd(cups) self.mock_cups_ppd(cups)
@@ -158,17 +159,16 @@ class TestPrintingPrinter(TransactionCase):
with self.assertRaises(OSError): with self.assertRaises(OSError):
self.printer._prepare_update_from_cups(connection, cups_printer) self.printer._prepare_update_from_cups(connection, cups_printer)
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
@mock.patch('os.unlink') @mock.patch("os.unlink")
def test_prepare_update_from_cups_unlink_error_enoent( def test_prepare_update_from_cups_unlink_error_enoent(self, os_unlink, cups):
self, os_unlink, cups):
""" """
When a ENOENT error is encountered, the file has already been unlinked When a ENOENT error is encountered, the file has already been unlinked
This is not an issue, as we were trying to delete the file. This is not an issue, as we were trying to delete the file.
The update can continue. The update can continue.
""" """
# Break os.unlink # Break os.unlink
os_unlink.side_effect = OSError(errno.ENOENT, 'Error') os_unlink.side_effect = OSError(errno.ENOENT, "Error")
self.mock_cups_ppd(cups) self.mock_cups_ppd(cups)
@@ -176,12 +176,12 @@ class TestPrintingPrinter(TransactionCase):
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertEqual(vals['tray_ids'], [(0, 0, { self.assertEqual(
'name': 'Auto (Default)', vals["tray_ids"],
'system_name': 'Auto', [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
})]) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups(self, cups): def test_prepare_update_from_cups(self, cups):
""" """
Check the return value when adding a single tray Check the return value when adding a single tray
@@ -192,55 +192,51 @@ class TestPrintingPrinter(TransactionCase):
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertEqual(vals['tray_ids'], [(0, 0, { self.assertEqual(
'name': 'Auto (Default)', vals["tray_ids"],
'system_name': 'Auto', [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
})]) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups_with_multiple_trays(self, cups): def test_prepare_update_from_cups_with_multiple_trays(self, cups):
""" """
Check the return value when adding multiple trays at once Check the return value when adding multiple trays at once
""" """
self.mock_cups_ppd(cups, input_slots=[ self.mock_cups_ppd(cups, input_slots=[{"name": "Tray1", "text": "Tray 1"}])
{'name': 'Tray1', 'text': 'Tray 1'},
])
connection = cups.Connection() connection = cups.Connection()
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertItemsEqual(vals['tray_ids'], [(0, 0, { self.assertItemsEqual(
'name': 'Auto (Default)', vals["tray_ids"],
'system_name': 'Auto', [
}), (0, 0, { (0, 0, {"name": "Auto (Default)", "system_name": "Auto"}),
'name': 'Tray 1', (0, 0, {"name": "Tray 1", "system_name": "Tray1"}),
'system_name': 'Tray1', ],
})]) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups_already_known_trays(self, cups): def test_prepare_update_from_cups_already_known_trays(self, cups):
""" """
Check that calling the method twice doesn't create the trays multiple Check that calling the method twice doesn't create the trays multiple
times times
""" """
self.mock_cups_ppd(cups, input_slots=[ self.mock_cups_ppd(cups, input_slots=[{"name": "Tray1", "text": "Tray 1"}])
{'name': 'Tray1', 'text': 'Tray 1'},
])
connection = cups.Connection() connection = cups.Connection()
cups_printer = connection.getPrinters()[self.printer.system_name] cups_printer = connection.getPrinters()[self.printer.system_name]
# Create a tray which is in the PPD file # Create a tray which is in the PPD file
self.new_tray({'system_name': 'Tray1'}) self.new_tray({"system_name": "Tray1"})
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertEqual(vals['tray_ids'], [(0, 0, { self.assertEqual(
'name': 'Auto (Default)', vals["tray_ids"],
'system_name': 'Auto', [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
})]) )
@mock.patch('%s.cups' % server_model) @mock.patch("%s.cups" % server_model)
def test_prepare_update_from_cups_unknown_trays(self, cups): def test_prepare_update_from_cups_unknown_trays(self, cups):
""" """
Check that trays which are not in the PPD file are removed from Odoo Check that trays which are not in the PPD file are removed from Odoo
@@ -254,7 +250,7 @@ class TestPrintingPrinter(TransactionCase):
tray = self.new_tray() tray = self.new_tray()
vals = self.printer._prepare_update_from_cups(connection, cups_printer) vals = self.printer._prepare_update_from_cups(connection, cups_printer)
self.assertEqual(vals['tray_ids'], [(0, 0, { self.assertEqual(
'name': 'Auto (Default)', vals["tray_ids"],
'system_name': 'Auto', [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"}), (2, tray.id)],
}), (2, tray.id)]) )

View File

@@ -3,11 +3,10 @@
import mock import mock
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_server"
model = 'odoo.addons.base_report_to_printer.models.printing_server'
class StopTest(Exception): class StopTest(Exception):
@@ -15,90 +14,75 @@ class StopTest(Exception):
class TestPrintingPrinterWizard(TransactionCase): class TestPrintingPrinterWizard(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingPrinterWizard, self).setUp() super(TestPrintingPrinterWizard, self).setUp()
self.Model = self.env['printing.printer.update.wizard'] self.Model = self.env["printing.printer.update.wizard"]
self.server = self.env['printing.server'].create({}) self.server = self.env["printing.server"].create({})
self.printer_vals = { self.printer_vals = {
'printer-info': 'Info', "printer-info": "Info",
'printer-make-and-model': 'Make and Model', "printer-make-and-model": "Make and Model",
'printer-location': "location", "printer-location": "location",
'device-uri': 'URI', "device-uri": "URI",
'printer-uri-supported': 'uri' "printer-uri-supported": "uri",
} }
def _record_vals(self, sys_name='sys_name'): def _record_vals(self, sys_name="sys_name"):
return { return {
'name': self.printer_vals['printer-info'], "name": self.printer_vals["printer-info"],
'server_id': self.server.id, "server_id": self.server.id,
'system_name': sys_name, "system_name": sys_name,
'model': self.printer_vals['printer-make-and-model'], "model": self.printer_vals["printer-make-and-model"],
'location': self.printer_vals['printer-location'], "location": self.printer_vals["printer-location"],
'uri': self.printer_vals['device-uri'], "uri": self.printer_vals["device-uri"],
} }
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_action_ok_inits_connection(self, cups): def test_action_ok_inits_connection(self, cups):
""" It should initialize CUPS connection """ """ It should initialize CUPS connection """
self.Model.action_ok() self.Model.action_ok()
cups.Connection.assert_called_once_with( cups.Connection.assert_called_once_with(
host=self.server.address, port=self.server.port, host=self.server.address, port=self.server.port
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_action_ok_gets_printers(self, cups): def test_action_ok_gets_printers(self, cups):
""" It should get printers from CUPS """ """ It should get printers from CUPS """
cups.Connection().getPrinters.return_value = { cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
'sys_name': self.printer_vals, cups.Connection().getPPD3.return_value = (200, 0, "")
}
cups.Connection().getPPD3.return_value = (200, 0, '')
self.Model.action_ok() self.Model.action_ok()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_action_ok_raises_warning_on_error(self, cups): def test_action_ok_raises_warning_on_error(self, cups):
""" It should raise Warning on any error """ """ It should raise Warning on any error """
cups.Connection.side_effect = StopTest cups.Connection.side_effect = StopTest
with self.assertRaises(UserError): with self.assertRaises(UserError):
self.Model.action_ok() self.Model.action_ok()
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_action_ok_creates_new_printer(self, cups): def test_action_ok_creates_new_printer(self, cups):
""" It should create new printer w/ proper vals """ """ It should create new printer w/ proper vals """
cups.Connection().getPrinters.return_value = { cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
'sys_name': self.printer_vals, cups.Connection().getPPD3.return_value = (200, 0, "")
}
cups.Connection().getPPD3.return_value = (200, 0, '')
self.Model.action_ok() self.Model.action_ok()
rec_id = self.env['printing.printer'].search([ rec_id = self.env["printing.printer"].search(
('system_name', '=', 'sys_name') [("system_name", "=", "sys_name")], limit=1
],
limit=1,
) )
self.assertTrue(rec_id) self.assertTrue(rec_id)
for key, val in self._record_vals().items(): for key, val in self._record_vals().items():
if rec_id._fields[key].type == 'many2one': if rec_id._fields[key].type == "many2one":
val = self.env[rec_id._fields[key].comodel_name].browse(val) val = self.env[rec_id._fields[key].comodel_name].browse(val)
self.assertEqual( self.assertEqual(val, rec_id[key])
val, rec_id[key],
)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_action_ok_skips_existing_printer(self, cups): def test_action_ok_skips_existing_printer(self, cups):
""" It should not recreate existing printers """ """ It should not recreate existing printers """
cups.Connection().getPrinters.return_value = { cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
'sys_name': self.printer_vals, cups.Connection().getPPD3.return_value = (200, 0, "")
} self.env["printing.printer"].create(self._record_vals())
cups.Connection().getPPD3.return_value = (200, 0, '')
self.env['printing.printer'].create(
self._record_vals()
)
self.Model.action_ok() self.Model.action_ok()
res_ids = self.env['printing.printer'].search([ res_ids = self.env["printing.printer"].search(
('system_name', '=', 'sys_name') [("system_name", "=", "sys_name")]
])
self.assertEqual(
1, len(res_ids),
) )
self.assertEqual(1, len(res_ids))

View File

@@ -5,18 +5,17 @@ from odoo.tests.common import TransactionCase
class TestPrintingReportXmlAction(TransactionCase): class TestPrintingReportXmlAction(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingReportXmlAction, self).setUp() super(TestPrintingReportXmlAction, self).setUp()
self.Model = self.env['printing.report.xml.action'] self.Model = self.env["printing.report.xml.action"]
self.report = self.env['ir.actions.report'].search([], limit=1) self.report = self.env["ir.actions.report"].search([], limit=1)
self.server = self.env['printing.server'].create({}) self.server = self.env["printing.server"].create({})
self.report_vals = { self.report_vals = {
'report_id': self.report.id, "report_id": self.report.id,
'user_id': self.env.ref('base.user_demo').id, "user_id": self.env.ref("base.user_demo").id,
'action': 'server', "action": "server",
} }
def new_record(self, vals=None): def new_record(self, vals=None):
@@ -27,65 +26,73 @@ class TestPrintingReportXmlAction(TransactionCase):
return self.Model.create(values) return self.Model.create(values)
def new_printer(self): def new_printer(self):
return self.env['printing.printer'].create({ return self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': self.server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": self.server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
}
)
def test_behaviour(self): def test_behaviour(self):
""" It should return some action's data, unless called on empty """ It should return some action's data, unless called on empty
recordset recordset
""" """
xml_action = self.new_record() xml_action = self.new_record()
self.assertEqual(xml_action.behaviour(), { self.assertEqual(
'action': xml_action.action, xml_action.behaviour(),
'printer': xml_action.printer_id, {
'tray': False, "action": xml_action.action,
}) "printer": xml_action.printer_id,
"tray": False,
},
)
xml_action = self.new_record({'printer_id': self.new_printer().id}) xml_action = self.new_record({"printer_id": self.new_printer().id})
self.assertEqual(xml_action.behaviour(), { self.assertEqual(
'action': xml_action.action, xml_action.behaviour(),
'printer': xml_action.printer_id, {
'tray': False, "action": xml_action.action,
}) "printer": xml_action.printer_id,
"tray": False,
},
)
self.assertEqual(self.Model.behaviour(), {}) self.assertEqual(self.Model.behaviour(), {})
def test_onchange_printer_tray_id_empty(self): def test_onchange_printer_tray_id_empty(self):
action = self.env['printing.report.xml.action'].new( action = self.env["printing.report.xml.action"].new({"printer_tray_id": False})
{'printer_tray_id': False})
action.onchange_printer_id() action.onchange_printer_id()
self.assertFalse(action.printer_tray_id) self.assertFalse(action.printer_tray_id)
def test_onchange_printer_tray_id_not_empty(self): def test_onchange_printer_tray_id_not_empty(self):
server = self.env['printing.server'].create({}) server = self.env["printing.server"].create({})
printer = self.env['printing.printer'].create({ printer = self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
tray = self.env['printing.tray'].create({ }
'name': 'Tray', )
'system_name': 'TrayName', tray = self.env["printing.tray"].create(
'printer_id': printer.id, {"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
}) )
action = self.env['printing.report.xml.action'].new( action = self.env["printing.report.xml.action"].new(
{'printer_tray_id': tray.id}) {"printer_tray_id": tray.id}
)
self.assertEqual(action.printer_tray_id, tray) self.assertEqual(action.printer_tray_id, tray)
action.onchange_printer_id() action.onchange_printer_id()
self.assertFalse(action.printer_tray_id) self.assertFalse(action.printer_tray_id)

View File

@@ -6,207 +6,189 @@ import mock
from odoo import fields from odoo import fields
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_server"
model = 'odoo.addons.base_report_to_printer.models.printing_server'
class TestPrintingServer(TransactionCase): class TestPrintingServer(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingServer, self).setUp() super(TestPrintingServer, self).setUp()
self.Model = self.env['printing.server'] self.Model = self.env["printing.server"]
self.server = self.Model.create({}) self.server = self.Model.create({})
self.printer_vals = { self.printer_vals = {
'name': 'Printer', "name": "Printer",
'server_id': self.server.id, "server_id": self.server.id,
'system_name': 'Sys Name', "system_name": "Sys Name",
'default': True, "default": True,
'status': 'unknown', "status": "unknown",
'status_message': 'Msg', "status_message": "Msg",
'model': 'res.users', "model": "res.users",
'location': 'Location', "location": "Location",
'uri': 'URI', "uri": "URI",
} }
self.job_vals = { self.job_vals = {
'server_id': self.server.id, "server_id": self.server.id,
'job_id_cups': 1, "job_id_cups": 1,
'job_media_progress': 0, "job_media_progress": 0,
'time_at_creation': fields.Datetime.now(), "time_at_creation": fields.Datetime.now(),
} }
def new_printer(self): def new_printer(self):
return self.env['printing.printer'].create(self.printer_vals) return self.env["printing.printer"].create(self.printer_vals)
def new_job(self, printer, vals=None): def new_job(self, printer, vals=None):
values = self.job_vals values = self.job_vals
if vals is not None: if vals is not None:
values.update(vals) values.update(vals)
values['printer_id'] = printer.id values["printer_id"] = printer.id
return self.env['printing.job'].create(values) return self.env["printing.job"].create(values)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_error(self, cups): def test_update_printers_error(self, cups):
""" It should catch any exception from CUPS and update status """ """ It should catch any exception from CUPS and update status """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
rec_id = self.new_printer() rec_id = self.new_printer()
self.Model.update_printers() self.Model.update_printers()
self.assertEqual( self.assertEqual("server-error", rec_id.status)
'server-error', rec_id.status,
)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_inits_cups(self, cups): def test_update_printers_inits_cups(self, cups):
""" It should init CUPS connection """ """ It should init CUPS connection """
self.new_printer() self.new_printer()
self.Model.update_printers() self.Model.update_printers()
cups.Connection.assert_called_once_with( cups.Connection.assert_called_once_with(
host=self.server.address, port=self.server.port, host=self.server.address, port=self.server.port
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_gets_all_printers(self, cups): def test_update_printers_gets_all_printers(self, cups):
""" It should get all printers from CUPS server """ """ It should get all printers from CUPS server """
self.new_printer() self.new_printer()
self.Model.update_printers() self.Model.update_printers()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_search(self, cups): def test_update_printers_search(self, cups):
""" It should search all when no domain """ """ It should search all when no domain """
with mock.patch.object(self.Model, 'search') as search: with mock.patch.object(self.Model, "search") as search:
self.Model.update_printers() self.Model.update_printers()
search.assert_called_once_with([]) search.assert_called_once_with([])
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_search_domain(self, cups): def test_update_printers_search_domain(self, cups):
""" It should use specific domain for search """ """ It should use specific domain for search """
with mock.patch.object(self.Model, 'search') as search: with mock.patch.object(self.Model, "search") as search:
expect = [('id', '>', 0)] expect = [("id", ">", 0)]
self.Model.update_printers(expect) self.Model.update_printers(expect)
search.assert_called_once_with(expect) search.assert_called_once_with(expect)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_printers_update_unavailable(self, cups): def test_update_printers_update_unavailable(self, cups):
""" It should update status when printer is unavailable """ """ It should update status when printer is unavailable """
rec_id = self.new_printer() rec_id = self.new_printer()
cups.Connection().getPrinters().get.return_value = False cups.Connection().getPrinters().get.return_value = False
self.Model.action_update_printers() self.Model.action_update_printers()
self.assertEqual( self.assertEqual("unavailable", rec_id.status)
'unavailable', rec_id.status,
)
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_jobs_cron(self, cups): def test_update_jobs_cron(self, cups):
""" It should get all jobs from CUPS server """ """ It should get all jobs from CUPS server """
self.new_printer() self.new_printer()
self.Model.action_update_jobs() self.Model.action_update_jobs()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
cups.Connection().getJobs.assert_called_once_with( cups.Connection().getJobs.assert_called_once_with(
which_jobs='all', which_jobs="all",
first_job_id=-1, first_job_id=-1,
requested_attributes=[ requested_attributes=[
'job-name', "job-name",
'job-id', "job-id",
'printer-uri', "printer-uri",
'job-media-progress', "job-media-progress",
'time-at-creation', "time-at-creation",
'job-state', "job-state",
'job-state-reasons', "job-state-reasons",
'time-at-processing', "time-at-processing",
'time-at-completed', "time-at-completed",
], ],
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_jobs_button(self, cups): def test_update_jobs_button(self, cups):
""" It should get all jobs from CUPS server """ """ It should get all jobs from CUPS server """
self.new_printer() self.new_printer()
self.server.action_update_jobs() self.server.action_update_jobs()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().getPrinters.assert_called_once_with()
cups.Connection().getJobs.assert_called_once_with( cups.Connection().getJobs.assert_called_once_with(
which_jobs='all', which_jobs="all",
first_job_id=-1, first_job_id=-1,
requested_attributes=[ requested_attributes=[
'job-name', "job-name",
'job-id', "job-id",
'printer-uri', "printer-uri",
'job-media-progress', "job-media-progress",
'time-at-creation', "time-at-creation",
'job-state', "job-state",
'job-state-reasons', "job-state-reasons",
'time-at-processing', "time-at-processing",
'time-at-completed', "time-at-completed",
], ],
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_jobs_error(self, cups): def test_update_jobs_error(self, cups):
""" It should catch any exception from CUPS and update status """ """ It should catch any exception from CUPS and update status """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
self.new_printer() self.new_printer()
self.server.update_jobs() self.server.update_jobs()
cups.Connection.assert_called_with( cups.Connection.assert_called_with(
host=self.server.address, port=self.server.port, host=self.server.address, port=self.server.port
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_jobs_uncompleted(self, cups): def test_update_jobs_uncompleted(self, cups):
""" """
It should search which jobs have been completed since last update It should search which jobs have been completed since last update
""" """
printer = self.new_printer() printer = self.new_printer()
self.new_job(printer, vals={'job_state': 'completed'}) self.new_job(printer, vals={"job_state": "completed"})
self.new_job(printer, vals={ self.new_job(printer, vals={"job_id_cups": 2, "job_state": "processing"})
'job_id_cups': 2, self.server.update_jobs(which="not-completed")
'job_state': 'processing',
})
self.server.update_jobs(which='not-completed')
cups.Connection().getJobs.assert_any_call( cups.Connection().getJobs.assert_any_call(
which_jobs='completed', first_job_id=2, which_jobs="completed",
first_job_id=2,
requested_attributes=[ requested_attributes=[
'job-name', "job-name",
'job-id', "job-id",
'printer-uri', "printer-uri",
'job-media-progress', "job-media-progress",
'time-at-creation', "time-at-creation",
'job-state', "job-state",
'job-state-reasons', "job-state-reasons",
'time-at-processing', "time-at-processing",
'time-at-completed', "time-at-completed",
], ],
) )
@mock.patch('%s.cups' % model) @mock.patch("%s.cups" % model)
def test_update_jobs(self, cups): def test_update_jobs(self, cups):
""" """
It should update all jobs, known or not It should update all jobs, known or not
""" """
printer = self.new_printer() printer = self.new_printer()
printer_uri = 'hostname:port/' + printer.system_name printer_uri = "hostname:port/" + printer.system_name
cups.Connection().getJobs.return_value = { cups.Connection().getJobs.return_value = {
1: { 1: {"printer-uri": printer_uri},
'printer-uri': printer_uri, 2: {"printer-uri": printer_uri, "job-state": 9},
}, 4: {"printer-uri": printer_uri, "job-state": 5},
2: {
'printer-uri': printer_uri,
'job-state': 9,
},
4: {
'printer-uri': printer_uri,
'job-state': 5,
},
} }
self.new_job(printer, vals={'job_state': 'completed'}) self.new_job(printer, vals={"job_state": "completed"})
completed_job = self.new_job(printer, vals={ completed_job = self.new_job(
'job_id_cups': 2, printer, vals={"job_id_cups": 2, "job_state": "processing"}
'job_state': 'processing', )
}) purged_job = self.new_job(
purged_job = self.new_job(printer, vals={ printer, vals={"job_id_cups": 3, "job_state": "processing"}
'job_id_cups': 3, )
'job_state': 'processing',
})
self.server.update_jobs() self.server.update_jobs()
new_job = self.env['printing.job'].search([('job_id_cups', '=', 4)]) new_job = self.env["printing.job"].search([("job_id_cups", "=", 4)])
self.assertEqual(completed_job.job_state, 'completed') self.assertEqual(completed_job.job_state, "completed")
self.assertEqual(purged_job.active, False) self.assertEqual(purged_job.active, False)
self.assertEqual(new_job.job_state, 'processing') self.assertEqual(new_job.job_state, "processing")

View File

@@ -3,49 +3,47 @@
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
model = "odoo.addons.base_report_to_printer.models.printing_server"
model = 'odoo.addons.base_report_to_printer.models.printing_server'
class TestPrintingTray(TransactionCase): class TestPrintingTray(TransactionCase):
def setUp(self): def setUp(self):
super(TestPrintingTray, self).setUp() super(TestPrintingTray, self).setUp()
self.Model = self.env['printing.tray'] self.Model = self.env["printing.tray"]
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', {
'server_id': self.server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": self.server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
}
)
self.tray_vals = { self.tray_vals = {
'name': 'Tray', "name": "Tray",
'system_name': 'TrayName', "system_name": "TrayName",
'printer_id': self.printer.id, "printer_id": self.printer.id,
} }
def new_tray(self): def new_tray(self):
return self.env['printing.tray'].create(self.tray_vals) return self.env["printing.tray"].create(self.tray_vals)
def test_report_behaviour(self): def test_report_behaviour(self):
""" It should add the selected tray in the report data """ """ It should add the selected tray in the report data """
ir_report = self.env['ir.actions.report'].search([], limit=1) ir_report = self.env["ir.actions.report"].search([], limit=1)
report = self.env['printing.report.xml.action'].create({ report = self.env["printing.report.xml.action"].create(
'user_id': self.env.user.id, {"user_id": self.env.user.id, "report_id": ir_report.id, "action": "server"}
'report_id': ir_report.id, )
'action': 'server',
})
report.printer_tray_id = False report.printer_tray_id = False
behaviour = report.behaviour() behaviour = report.behaviour()
self.assertEqual(behaviour['tray'], False) self.assertEqual(behaviour["tray"], False)
# Check that we have te right value # Check that we have te right value
report.printer_tray_id = self.new_tray() report.printer_tray_id = self.new_tray()
behaviour = report.behaviour() behaviour = report.behaviour()
self.assertEqual(behaviour['tray'], report.printer_tray_id.system_name) self.assertEqual(behaviour["tray"], report.printer_tray_id.system_name)

View File

@@ -3,8 +3,9 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import mock import mock
from odoo.tests import common
from odoo import exceptions from odoo import exceptions
from odoo.tests import common
@common.at_install(False) @common.at_install(False)
@@ -12,118 +13,124 @@ from odoo import exceptions
class TestReport(common.HttpCase): class TestReport(common.HttpCase):
def setUp(self): def setUp(self):
super(TestReport, self).setUp() super(TestReport, self).setUp()
self.Model = self.env['ir.actions.report'] self.Model = self.env["ir.actions.report"]
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",
'model': 'ir.actions.report', "model": "ir.actions.report",
'report_name': 'Test Report', "report_name": "Test Report",
} }
self.report_imd = self.env["ir.model.data"].create({ self.report_imd = self.env["ir.model.data"].create(
"name": "test", {"name": "test", "module": "base_report_to_printer", "model": "ir.ui.view"}
"module": "base_report_to_printer", )
"model": "ir.ui.view", self.report_view = self.env["ir.ui.view"].create(
}) {
self.report_view = self.env["ir.ui.view"].create({ "name": "Test",
"name": "Test", "type": "qweb",
"type": "qweb", "xml_id": "base_report_to_printer.test",
"xml_id": "base_report_to_printer.test", "model_data_id": self.report_imd.id,
"model_data_id": self.report_imd.id, "arch": """<t t-name="base_report_to_printer.test">
"arch": """<t t-name="base_report_to_printer.test">
<div>Test</div> <div>Test</div>
</t>""", </t>""",
}) }
)
self.report_imd.res_id = self.report_view.id self.report_imd.res_id = self.report_view.id
self.report = self.Model.create({ self.report = self.Model.create(
"name": "Test", {
"report_type": "qweb-pdf", "name": "Test",
"model": "res.partner", "report_type": "qweb-pdf",
"report_name": "base_report_to_printer.test", "model": "res.partner",
}) "report_name": "base_report_to_printer.test",
}
)
self.partners = self.env["res.partner"] self.partners = self.env["res.partner"]
for n in range(5): for n in range(5):
self.partners += self.env["res.partner"].create({ self.partners += self.env["res.partner"].create({"name": "Test %d" % n})
"name": "Test %d" % n,
})
def new_record(self): def new_record(self):
return self.Model.create(self.report_vals) return self.Model.create(self.report_vals)
def new_printer(self): def new_printer(self):
return self.env['printing.printer'].create({ return self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': self.server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": self.server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
}
)
def test_can_print_report_context_skip(self): def test_can_print_report_context_skip(self):
""" It should return False based on context """ """ It should return False based on context """
rec_id = self.new_record().with_context( rec_id = self.new_record().with_context(must_skip_send_to_printer=True)
must_skip_send_to_printer=True res = rec_id._can_print_report({"action": "server"}, True, True)
)
res = rec_id._can_print_report(
{'action': 'server'}, True, True
)
self.assertFalse(res) self.assertFalse(res)
def test_can_print_report_true(self): def test_can_print_report_true(self):
""" It should return True when server print allowed """ """ It should return True when server print allowed """
res = self.new_record()._can_print_report( res = self.new_record()._can_print_report({"action": "server"}, True, True)
{'action': 'server'}, True, True
)
self.assertTrue(res) self.assertTrue(res)
def test_can_print_report_false(self): def test_can_print_report_false(self):
""" It should return False when server print not allowed """ """ It should return False when server print not allowed """
res = self.new_record()._can_print_report( res = self.new_record()._can_print_report({"action": "server"}, True, False)
{'action': 'server'}, True, False
)
self.assertFalse(res) self.assertFalse(res)
def test_render_qweb_pdf_not_printable(self): def test_render_qweb_pdf_not_printable(self):
""" It should print the report, only if it is printable """ It should print the report, only if it is printable
""" """
with mock.patch('odoo.addons.base_report_to_printer.models.' with mock.patch(
'printing_printer.PrintingPrinter.' "odoo.addons.base_report_to_printer.models."
'print_document') as print_document: "printing_printer.PrintingPrinter."
"print_document"
) as print_document:
self.report.render_qweb_pdf(self.partners.ids) self.report.render_qweb_pdf(self.partners.ids)
print_document.assert_not_called() print_document.assert_not_called()
def test_render_qweb_pdf_printable(self): def test_render_qweb_pdf_printable(self):
""" It should print the report, only if it is printable """ It should print the report, only if it is printable
""" """
with mock.patch('odoo.addons.base_report_to_printer.models.' with mock.patch(
'printing_printer.PrintingPrinter.' "odoo.addons.base_report_to_printer.models."
'print_document') as print_document: "printing_printer.PrintingPrinter."
self.report.property_printing_action_id.action_type = 'server' "print_document"
) as print_document:
self.report.property_printing_action_id.action_type = "server"
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
document = self.report.render_qweb_pdf(self.partners.ids) document = self.report.render_qweb_pdf(self.partners.ids)
print_document.assert_called_once_with( print_document.assert_called_once_with(
self.report, document[0], self.report,
action='server', doc_format='qweb-pdf', tray=False) document[0],
action="server",
doc_format="qweb-pdf",
tray=False,
)
def test_print_document_not_printable(self): def test_print_document_not_printable(self):
""" It should print the report, regardless of the defined behaviour """ """ It should print the report, regardless of the defined behaviour """
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
with mock.patch('odoo.addons.base_report_to_printer.models.' with mock.patch(
'printing_printer.PrintingPrinter.' "odoo.addons.base_report_to_printer.models."
'print_document') as print_document: "printing_printer.PrintingPrinter."
"print_document"
) as print_document:
self.report.print_document(self.partners.ids) self.report.print_document(self.partners.ids)
print_document.assert_called_once() print_document.assert_called_once()
def test_print_document_printable(self): def test_print_document_printable(self):
""" It should print the report, regardless of the defined behaviour """ """ It should print the report, regardless of the defined behaviour """
self.report.property_printing_action_id.action_type = 'server' self.report.property_printing_action_id.action_type = "server"
self.report.printing_printer_id = self.new_printer() self.report.printing_printer_id = self.new_printer()
with mock.patch('odoo.addons.base_report_to_printer.models.' with mock.patch(
'printing_printer.PrintingPrinter.' "odoo.addons.base_report_to_printer.models."
'print_document') as print_document: "printing_printer.PrintingPrinter."
"print_document"
) as print_document:
self.report.print_document(self.partners.ids) self.report.print_document(self.partners.ids)
print_document.assert_called_once() print_document.assert_called_once()

View File

@@ -7,54 +7,49 @@ from odoo.tests import common
@common.at_install(False) @common.at_install(False)
@common.post_install(True) @common.post_install(True)
class TestResUsers(common.TransactionCase): class TestResUsers(common.TransactionCase):
def setUp(self): def setUp(self):
super(TestResUsers, self).setUp() super(TestResUsers, self).setUp()
self.user_vals = {'name': 'Test', self.user_vals = {"name": "Test", "login": "login"}
'login': 'login',
}
def new_record(self): def new_record(self):
return self.env['res.users'].create(self.user_vals) return self.env["res.users"].create(self.user_vals)
def test_available_action_types_excludes_user_default(self): def test_available_action_types_excludes_user_default(self):
""" It should not contain `user_default` in avail actions """ """ It should not contain `user_default` in avail actions """
self.user_vals['printing_action'] = 'user_default' self.user_vals["printing_action"] = "user_default"
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
self.new_record() self.new_record()
def test_available_action_types_includes_something_else(self): def test_available_action_types_includes_something_else(self):
""" It should still contain other valid keys """ """ It should still contain other valid keys """
self.user_vals['printing_action'] = 'server' self.user_vals["printing_action"] = "server"
self.assertTrue(self.new_record()) self.assertTrue(self.new_record())
def test_onchange_printer_tray_id_empty(self): def test_onchange_printer_tray_id_empty(self):
user = self.env['res.users'].new( user = self.env["res.users"].new({"printer_tray_id": False})
{'printer_tray_id': False})
user.onchange_printing_printer_id() user.onchange_printing_printer_id()
self.assertFalse(user.printer_tray_id) self.assertFalse(user.printer_tray_id)
def test_onchange_printer_tray_id_not_empty(self): def test_onchange_printer_tray_id_not_empty(self):
server = self.env['printing.server'].create({}) server = self.env["printing.server"].create({})
printer = self.env['printing.printer'].create({ printer = self.env["printing.printer"].create(
'name': 'Printer', {
'server_id': server.id, "name": "Printer",
'system_name': 'Sys Name', "server_id": server.id,
'default': True, "system_name": "Sys Name",
'status': 'unknown', "default": True,
'status_message': 'Msg', "status": "unknown",
'model': 'res.users', "status_message": "Msg",
'location': 'Location', "model": "res.users",
'uri': 'URI', "location": "Location",
}) "uri": "URI",
tray = self.env['printing.tray'].create({ }
'name': 'Tray', )
'system_name': 'TrayName', tray = self.env["printing.tray"].create(
'printer_id': printer.id, {"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
}) )
user = self.env['res.users'].new( user = self.env["res.users"].new({"printer_tray_id": tray.id})
{'printer_tray_id': tray.id})
self.assertEqual(user.printer_tray_id, tray) self.assertEqual(user.printer_tray_id, tray)
user.onchange_printing_printer_id() user.onchange_printing_printer_id()
self.assertFalse(user.printer_tray_id) self.assertFalse(user.printer_tray_id)

View File

@@ -6,4 +6,3 @@
</xpath> </xpath>
</template> </template>
</odoo> </odoo>

View File

@@ -1,2 +1 @@
from . import printing_printer_update_wizard from . import printing_printer_update_wizard

View File

@@ -8,22 +8,20 @@ import logging
from odoo import models from odoo import models
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class PrintingPrinterUpdateWizard(models.TransientModel): class PrintingPrinterUpdateWizard(models.TransientModel):
_name = 'printing.printer.update.wizard' _name = "printing.printer.update.wizard"
_description = 'Printing Printer Update Wizard' _description = "Printing Printer Update Wizard"
def action_ok(self): def action_ok(self):
self.env['printing.server'].search([]) \ self.env["printing.server"].search([]).update_printers(raise_on_error=True)
.update_printers(raise_on_error=True)
return { return {
'name': 'Printers', "name": "Printers",
'view_mode': 'tree,form', "view_mode": "tree,form",
'res_model': 'printing.printer', "res_model": "printing.printer",
'type': 'ir.actions.act_window', "type": "ir.actions.act_window",
'target': 'current', "target": "current",
} }