[10.0][MIG] base_report_to_printer (#68)

* Set api.multi for action called as `object` on view

* Merge syleam printers module into base_report_to_printer (#60)

* [IMP] Updated unit tests

* [FIX] Fixed renamed attributes

* [FIX] Remove deleted fields

* [IMP] Add printing.server and printing.job models

* [IMP] Allow to cancel all jobs, enable, and disable printers

* [IMP] Split the cups part of print_document into a new print_file method

* [IMP] Updated cron job to run the action_update_jobs method

* [ADD] Add a migration script to create a printing server from configuration

* [MIG] Migrate base_report_to_printer to v10.0

Removed deprecated methods on printing.printer (replaced by methods on
        printing.server)

* [IMP] Add wkhtmltopdf in travis configuration file

* [FIX] base_report_to_printer: Fix Update Job Cron
* Fix API issue with Update Job Cron
** Forward Port from 9.0

* [FIX] Fixed the res.users view

The string attribute should not be used as a selector, because it is
translatable.

* [FIX] Fixed the print_document method of report

The new API migration was made to @api.multi because of the "cr, uid,
ids" signature, but "ids" was the ids of the records to print here, not
the report's ids.
Also, the new API version of "get_pdf" get directly the ids of the
records to print in the standard module, not a recordset.

* [FIX] UI is now (un)blocked only when using qweb-pdf reports in standard addons
This commit is contained in:
Sylvain Garancher
2017-04-05 18:06:16 +02:00
committed by trisdoan
parent 21b19339ae
commit cbdf1b13a7
32 changed files with 1482 additions and 417 deletions

View File

@@ -90,6 +90,7 @@ Contributors
* Lionel Sausin <ls@numerigraphe.com> * Lionel Sausin <ls@numerigraphe.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com> * Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Dave Lasley <dave@laslabs.com> * Dave Lasley <dave@laslabs.com>
* Sylvain Garancher <sylvain.garancher@syleam.fr>
Maintainer Maintainer
---------- ----------

View File

@@ -8,7 +8,7 @@
{ {
'name': "Report to printer", 'name': "Report to printer",
'version': '9.0.1.0.0', 'version': '10.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, Odoo Community Association (OCA)", " LasLabs, Odoo Community Association (OCA)",
@@ -20,12 +20,14 @@
'security/security.xml', 'security/security.xml',
'views/assets.xml', 'views/assets.xml',
'views/printing_printer_view.xml', 'views/printing_printer_view.xml',
'views/printing_server.xml',
'views/printing_job.xml',
'views/printing_report_view.xml', 'views/printing_report_view.xml',
'views/res_users_view.xml', 'views/res_users_view.xml',
'views/ir_actions_report_xml_view.xml', 'views/ir_actions_report_xml_view.xml',
'wizards/printing_printer_update_wizard_view.xml', 'wizards/printing_printer_update_wizard_view.xml',
], ],
'installable': False, 'installable': True,
'application': True, 'application': True,
'external_dependencies': { 'external_dependencies': {
'python': ['cups'], 'python': ['cups'],

View File

@@ -1,33 +1,33 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<odoo noupdate="1"> <odoo noupdate="1">
<!-- printing.action -->
<record model="printing.action" id="printing_action_1">
<field name="name">Send to Printer</field>
<field name="type">server</field>
</record>
<record model="printing.action" id="printing_action_2">
<field name="name">Send to Client</field>
<field name="type">client</field>
</record>
<!-- properties -->
<record forcecreate="True" id="property_printing_action_id" model="ir.property">
<field name="name">property_printing_action_id</field>
<field name="fields_id" search="[('model','=','ir.actions.report.xml'),('name','=','property_printing_action_id')]"/>
<field name="value" eval="'printing.action,'+str(printing_action_2)"/>
</record>
<record forcecreate="True" id="ir_cron_update_printers" model="ir.cron"> <!-- printing.action -->
<field name="name">Update Printers Status</field> <record model="printing.action" id="printing_action_1">
<field eval="True" name="active"/> <field name="name">Send to Printer</field>
<field name="user_id" ref="base.user_root"/> <field name="action_type">server</field>
<field name="interval_number">1</field> </record>
<field name="interval_type">minutes</field> <record model="printing.action" id="printing_action_2">
<field name="numbercall">-1</field> <field name="name">Send to Client</field>
<field eval="False" name="doall"/> <field name="action_type">client</field>
<field eval="'printing.printer'" name="model"/> </record>
<field eval="'update_printers_status'" name="function"/> <!-- properties -->
<field eval="'()'" name="args"/> <record forcecreate="True" id="property_printing_action_id" model="ir.property">
</record> <field name="name">property_printing_action_id</field>
<field name="fields_id" search="[('model', '=', 'ir.actions.report.xml'), ('name', '=', 'property_printing_action_id')]"/>
<field name="value" eval="'printing.action,' + str(printing_action_2)"/>
</record>
<record forcecreate="True" id="ir_cron_update_printers" model="ir.cron">
<field name="name">Update Printers Jobs</field>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field eval="'printing.server'" name="model"/>
<field eval="'action_update_jobs'" name="function"/>
<field eval="'()'" name="args"/>
</record>
</odoo> </odoo>

View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import SUPERUSER_ID, api
from openerp.tools.config import config
__name__ = 'Create a printing.server record from previous configuration'
def migrate(cr, v):
with api.Environment.manage():
uid = SUPERUSER_ID
env = api.Environment(cr, uid, {})
env['printing.server'].create({
'name': config.get('cups_host', 'localhost'),
'address': config.get('cups_host', 'localhost'),
'port': config.get('cups_port', 631),
})

View File

@@ -2,7 +2,9 @@
from . import ir_actions_report_xml from . import ir_actions_report_xml
from . import printing_action from . import printing_action
from . import printing_job
from . import printing_printer from . import printing_printer
from . import printing_server
from . import printing_report_xml_action from . import printing_report_xml_action
from . import report from . import report
from . import res_users from . import res_users

View File

@@ -6,7 +6,7 @@
# 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 openerp import models, fields, api from odoo import models, fields, api
class IrActionsReportXml(models.Model): class IrActionsReportXml(models.Model):
@@ -73,8 +73,8 @@ class IrActionsReportXml(models.Model):
# Retrieve report default values # Retrieve report default values
report_action = report.property_printing_action_id report_action = report.property_printing_action_id
if report_action and report_action.type != 'user_default': if report_action and report_action.action_type != 'user_default':
action = report_action.type action = report_action.action_type
if report.printing_printer_id: if report.printing_printer_id:
printer = report.printing_printer_id printer = report.printing_printer_id

View File

@@ -6,23 +6,25 @@
# 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 openerp import models, fields, api from odoo import models, fields, api
@api.model
def _available_action_types(self):
return [('server', 'Send to Printer'),
('client', 'Send to Client'),
('user_default', "Use user's defaults"),
]
class PrintingAction(models.Model): class PrintingAction(models.Model):
_name = 'printing.action' _name = 'printing.action'
_description = 'Print Job Action' _description = 'Print Job Action'
@api.model
def _available_action_types(self):
return [
('server', 'Send to Printer'),
('client', 'Send to Client'),
('user_default', "Use user's defaults"),
]
name = fields.Char(required=True) name = fields.Char(required=True)
type = fields.Selection( action_type = fields.Selection(
lambda s: _available_action_types(s), selection=_available_action_types,
string='Type',
required=True, required=True,
oldname='type'
) )

View File

@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
class PrintingJob(models.Model):
_name = 'printing.job'
_description = 'Printing Job'
_order = 'job_id_cups DESC'
name = fields.Char(help='Job name.')
active = fields.Boolean(
default=True, help='Unchecked if the job is purged from cups.')
job_id_cups = fields.Integer(
string='Job ID', required=True,
help='CUPS id for this job.')
server_id = fields.Many2one(
comodel_name='printing.server', string='Server',
related='printer_id.server_id', store=True,
help='Server which hosts this job.')
printer_id = fields.Many2one(
comodel_name='printing.printer', string='Printer', required=True,
ondelete='cascade', help='Printer used for this job.')
job_media_progress = fields.Integer(
string='Media Progress', required=True,
help='Percentage of progress for this job.')
time_at_creation = fields.Datetime(
required=True, help='Date and time of creation for this job.')
time_at_processing = fields.Datetime(
help='Date and time of process for this job.')
time_at_completed = fields.Datetime(
help='Date and time of completion for this job.')
job_state = fields.Selection(selection=[
('pending', 'Pending'),
('pending held', 'Pending Held'),
('processing', 'Processing'),
('processing stopped', 'Processing Stopped'),
('canceled', 'Canceled'),
('aborted', 'Aborted'),
('completed', 'Completed'),
('unknown', 'Unknown'),
], string='State', help='Current state of the job.')
job_state_reason = fields.Selection(selection=[
('none', 'No reason'),
('aborted-by-system', 'Aborted by the system'),
('compression-error', 'Error in the compressed data'),
('document-access-error', 'The URI cannot be accessed'),
('document-format-error', 'Error in the document'),
('job-canceled-at-device', 'Cancelled at the device'),
('job-canceled-by-operator', 'Cancelled by the printer operator'),
('job-canceled-by-user', 'Cancelled by the user'),
('job-completed-successfully', 'Completed successfully'),
('job-completed-with-errors', 'Completed with some errors'),
('job-completed(with-warnings', 'Completed with some warnings'),
('job-data-insufficient', 'No data has been received'),
('job-hold-until-specified', 'Currently held'),
('job-incoming', 'Files are currently being received'),
('job-interpreting', 'Currently being interpreted'),
('job-outgoing', 'Currently being sent to the printer'),
('job-printing', 'Currently printing'),
('job-queued', 'Queued for printing'),
('job-queued-for-marker', 'Printer needs ink/marker/toner'),
('job-restartable', 'Can be restarted'),
('job-transforming', 'Being transformed into a different format'),
('printer-stopped', 'Printer is stopped'),
('printer-stopped-partly',
'Printer state reason set to \'stopped-partly\''),
('processing-to-stop-point',
'Cancelled, but printing already processed pages'),
('queued-in-device', 'Queued at the output device'),
('resources-are-not-ready',
'Resources not available to print the job'),
('service-off-line', 'Held because the printer is offline'),
('submission-interrupted', 'Files were not received in full'),
('unsupported-compression', 'Compressed using an unknown algorithm'),
('unsupported-document-format', 'Unsupported format'),
], string='State Reason', help='Reason for the current job state.')
_sql_constraints = [
('job_id_cups_unique', 'UNIQUE(job_id_cups, server_id)',
'The id of the job must be unique per server !'),
]
@api.multi
def action_cancel(self):
self.ensure_one()
return self.cancel()
@api.multi
def cancel(self, purge_job=False):
for job in self:
connection = job.server_id._open_connection()
if not connection:
continue
connection.cancelJob(job.job_id_cups, purge_job=purge_job)
# Update jobs' states info Odoo
self.mapped('server_id').update_jobs(
which='all', first_job_id=job.job_id_cups)
return True

View File

@@ -4,6 +4,7 @@
# Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>) # Copyright (C) 2011 Agile Business Group sagl (<http://www.agilebg.com>)
# Copyright (C) 2011 Domsense srl (<http://www.domsense.com>) # Copyright (C) 2011 Domsense srl (<http://www.domsense.com>)
# Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>) # Copyright (C) 2013-2014 Camptocamp (<http://www.camptocamp.com>)
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# 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
@@ -11,24 +12,12 @@ import logging
import os import os
from tempfile import mkstemp from tempfile import mkstemp
from openerp import models, fields, api, _ from odoo import models, fields, api
from openerp.exceptions import UserError
from openerp.tools.config import config
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
try:
import cups
except ImportError:
_logger.debug('Cannot `import cups`.')
CUPS_HOST = config.get('cups_host', 'localhost')
CUPS_PORT = int(config.get('cups_port', 631)) # config.get returns a string
class PrintingPrinter(models.Model): class PrintingPrinter(models.Model):
""" """
Printers Printers
@@ -38,43 +27,32 @@ class PrintingPrinter(models.Model):
_description = 'Printer' _description = 'Printer'
_order = 'name' _order = 'name'
name = fields.Char(required=True, select=True) name = fields.Char(required=True, index=True)
system_name = fields.Char(required=True, select=True) server_id = fields.Many2one(
comodel_name='printing.server', string='Server', required=True,
help='Server used to access this printer.')
job_ids = fields.One2many(
comodel_name='printing.job', inverse_name='printer_id', string='Jobs',
help='Jobs printed on this printer.')
system_name = fields.Char(required=True, index=True)
default = fields.Boolean(readonly=True) default = fields.Boolean(readonly=True)
status = fields.Selection([('unavailable', 'Unavailable'), status = fields.Selection(
('printing', 'Printing'), selection=[
('unknown', 'Unknown'), ('unavailable', 'Unavailable'),
('available', 'Available'), ('printing', 'Printing'),
('error', 'Error'), ('unknown', 'Unknown'),
('server-error', 'Server Error')], ('available', 'Available'),
required=True, ('error', 'Error'),
readonly=True, ('server-error', 'Server Error'),
default='unknown') ],
required=True,
readonly=True,
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)
@api.model
def update_printers_status(self, domain=None):
if domain is None:
domain = []
printer_recs = self.search(domain)
try:
connection = cups.Connection(CUPS_HOST, CUPS_PORT)
printers = connection.getPrinters()
except:
printer_recs.write({'status': 'server-error'})
else:
for printer in printer_recs:
cups_printer = printers.get(printer.system_name)
if cups_printer:
printer.update_from_cups(connection, cups_printer)
else:
# not in cups list
printer.status = 'unavailable'
return True
@api.multi @api.multi
def _prepare_update_from_cups(self, cups_connection, cups_printer): def _prepare_update_from_cups(self, cups_connection, cups_printer):
mapping = { mapping = {
@@ -83,29 +61,18 @@ class PrintingPrinter(models.Model):
5: 'error' 5: 'error'
} }
vals = { vals = {
'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['printer-state'], 'unknown'), 'status': mapping.get(cups_printer.get(
'printer-state'), 'unknown'),
'status_message': cups_printer.get('printer-state-message', ''),
} }
return vals return vals
@api.multi @api.multi
def update_from_cups(self, cups_connection, cups_printer): def print_options(self, report=None, format=None, copies=1):
""" Update a printer from the information returned by cups.
:param cups_connection: connection to CUPS, may be used when the
method is overriden (e.g. in printer_tray)
:param cups_printer: dict of information returned by CUPS for the
current printer
"""
self.ensure_one()
vals = self._prepare_update_from_cups(cups_connection, cups_printer)
if any(self[name] != value for name, value in vals.iteritems()):
self.write(vals)
@api.multi
def print_options(self, report, format, copies=1):
""" Hook to set print options """ """ Hook to set print options """
options = {} options = {}
if format == 'raw': if format == 'raw':
@@ -128,29 +95,28 @@ class PrintingPrinter(models.Model):
finally: finally:
os.close(fd) os.close(fd)
try: return self.print_file(file_name, report=report, copies=copies)
_logger.debug(
'Starting to connect to CUPS on %s:%s'
% (CUPS_HOST, CUPS_PORT))
connection = cups.Connection(CUPS_HOST, CUPS_PORT)
_logger.debug('Connection to CUPS successfull')
except:
raise UserError(
_("Failed to connect to the CUPS server on %s:%s. "
"Check that the CUPS server is running and that "
"you can reach it from the Odoo server.")
% (CUPS_HOST, CUPS_PORT))
options = self.print_options(report, format, copies) @api.multi
def print_file(self, file_name, report=None, copies=1):
""" Print a file """
self.ensure_one()
connection = self.server_id._open_connection(raise_on_error=True)
options = self.print_options(
report=report, format=format, copies=copies)
_logger.debug( _logger.debug(
'Sending job to CUPS printer %s on %s' 'Sending job to CUPS printer %s on %s'
% (self.system_name, CUPS_HOST)) % (self.system_name, self.server_id.address))
connection.printFile(self.system_name, connection.printFile(self.system_name,
file_name, file_name,
file_name, file_name,
options=options) options=options)
_logger.info("Printing job: '%s' on %s" % (file_name, CUPS_HOST)) _logger.info("Printing job: '%s' on %s" % (
file_name,
self.server_id.address,
))
return True return True
@api.multi @api.multi
@@ -166,3 +132,42 @@ class PrintingPrinter(models.Model):
@api.multi @api.multi
def get_default(self): def get_default(self):
return self.search([('default', '=', True)], limit=1) return self.search([('default', '=', True)], limit=1)
@api.multi
def action_cancel_all_jobs(self):
self.ensure_one()
return self.cancel_all_jobs()
@api.multi
def cancel_all_jobs(self, purge_jobs=False):
for printer in self:
connection = printer.server_id._open_connection()
connection.cancelAllJobs(
name=printer.system_name, purge_jobs=purge_jobs)
# Update jobs' states into Odoo
self.mapped('server_id').update_jobs(which='completed')
return True
@api.multi
def enable(self):
for printer in self:
connection = printer.server_id._open_connection()
connection.enablePrinter(printer.system_name)
# Update printers' stats into Odoo
self.mapped('server_id').update_printers()
return True
@api.multi
def disable(self):
for printer in self:
connection = printer.server_id._open_connection()
connection.disablePrinter(printer.system_name)
# Update printers' stats into Odoo
self.mapped('server_id').update_printers()
return True

View File

@@ -6,9 +6,7 @@
# 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 openerp import models, fields, api from odoo import models, fields, api
from .printing_action import _available_action_types
class PrintingReportXmlAction(models.Model): class PrintingReportXmlAction(models.Model):
@@ -24,7 +22,7 @@ class PrintingReportXmlAction(models.Model):
required=True, required=True,
ondelete='cascade') ondelete='cascade')
action = fields.Selection( action = fields.Selection(
lambda s: _available_action_types(s), 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',
@@ -34,6 +32,7 @@ class PrintingReportXmlAction(models.Model):
def behaviour(self): def behaviour(self):
if not self: if not self:
return {} return {}
return {'action': self.action, return {
'printer': self.printer_id, 'action': self.action,
} 'printer': self.printer_id,
}

View File

@@ -0,0 +1,224 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 SYLEAM (<http://www.syleam.fr>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import datetime
from odoo import models, fields, api, exceptions, _
_logger = logging.getLogger(__name__)
try:
import cups
except ImportError:
_logger.debug('Cannot `import cups`.')
class PrintingServer(models.Model):
_name = 'printing.server'
_description = 'Printing server'
name = fields.Char(
default='Localhost', required=True, help='Name of the server.')
address = fields.Char(
default='localhost', required=True,
help='IP address or hostname of the server')
port = fields.Integer(
default=631, required=True, help='Port of the server.')
active = fields.Boolean(
default=True, help='If checked, this server is useable.')
printer_ids = fields.One2many(
comodel_name='printing.printer', inverse_name='server_id',
string='Printers List',
help='List of printers available on this server.')
@api.multi
def _open_connection(self, raise_on_error=False):
self.ensure_one()
connection = False
try:
connection = cups.Connection(host=self.address, port=self.port)
except:
message = _("Failed to connect to the CUPS server on %s:%s. "
"Check that the CUPS server is running and that "
"you can reach it from the Odoo server.") % (
self.address, self.port)
_logger.warning(message)
if raise_on_error:
raise exceptions.UserError(message)
return connection
@api.multi
def action_update_printers(self):
return self.update_printers()
@api.multi
def update_printers(self, domain=None, raise_on_error=False):
if domain is None:
domain = []
servers = self
if not self:
servers = self.search(domain)
res = True
for server in servers:
connection = server._open_connection(raise_on_error=raise_on_error)
if not connection:
server.printer_ids.write({'status': 'server-error'})
res = False
continue
# Update Printers
printers = connection.getPrinters()
existing_printers = dict([
(printer.system_name, printer)
for printer in server.printer_ids
])
updated_printers = []
for name, printer_info in printers.iteritems():
printer = self.env['printing.printer']
if name in existing_printers:
printer = existing_printers[name]
printer_values = printer._prepare_update_from_cups(
connection, printer_info)
printer_values.update(
system_name=name,
server_id=server.id,
)
updated_printers.append(name)
if not printer:
printer.create(printer_values)
else:
printer.write(printer_values)
# Set printers not found as unavailable
server.printer_ids.filtered(
lambda record: record.system_name not in updated_printers)\
.write({'status': 'unavailable'})
return res
@api.model
def action_update_jobs(self):
if not self:
self = self.search([])
return self.update_jobs()
@api.multi
def update_jobs(self, which='all', first_job_id=-1):
job_obj = self.env['printing.job']
printer_obj = self.env['printing.printer']
mapping = {
3: 'pending',
4: 'pending held',
5: 'processing',
6: 'processing stopped',
7: 'canceled',
8: 'aborted',
9: 'completed',
}
# Update printers list, to ensure that jobs printers will be in Odoo
self.update_printers()
for server in self:
connection = server._open_connection()
if not connection:
continue
# Retrieve asked job data
jobs_data = connection.getJobs(
which_jobs=which, first_job_id=first_job_id,
requested_attributes=[
'job-name',
'job-id',
'printer-uri',
'job-media-progress',
'time-at-creation',
'job-state',
'job-state-reasons',
'time-at-processing',
'time-at-completed',
])
# Retrieve known uncompleted jobs data to update them
if which == 'not-completed':
oldest_uncompleted_job = job_obj.search([
('job_state', 'not in', (
'canceled',
'aborted',
'completed',
)),
], limit=1, order='job_id_cups')
if oldest_uncompleted_job:
jobs_data.update(connection.getJobs(
which_jobs='completed',
first_job_id=oldest_uncompleted_job.job_id_cups,
requested_attributes=[
'job-name',
'job-id',
'printer-uri',
'job-media-progress',
'time-at-creation',
'job-state',
'job-state-reasons',
'time-at-processing',
'time-at-completed',
]))
all_cups_job_ids = set()
for cups_job_id, job_data in jobs_data.items():
all_cups_job_ids.add(cups_job_id)
jobs = job_obj.with_context(active_test=False).search([
('job_id_cups', '=', cups_job_id),
('server_id', '=', server.id),
])
job_values = {
'name': job_data.get('job-name', ''),
'active': True,
'job_id_cups': cups_job_id,
'job_media_progress': job_data.get(
'job-media-progress', 0),
'job_state': mapping.get(
job_data.get('job-state'), 'unknown'),
'job_state_reason': job_data.get('job-state-reasons', ''),
'time_at_creation': fields.Datetime.to_string(
datetime.fromtimestamp(job_data.get(
'time-at-creation', 0))),
'time_at_processing': job_data.get(
'time-at-processing', 0) and fields.Datetime.to_string(
datetime.fromtimestamp(job_data.get(
'time-at-processing', 0))),
'time_at_completed': job_data.get(
'time-at-completed', 0) and fields.Datetime.to_string(
datetime.fromtimestamp(job_data.get(
'time-at-completed', 0))),
}
# Search for the printer in Odoo
printer_uri = job_data['printer-uri']
printer_system_name = printer_uri[printer_uri.rfind('/') + 1:]
printer = printer_obj.search([
('server_id', '=', server.id),
('system_name', '=', printer_system_name),
], limit=1)
job_values['printer_id'] = printer.id
if jobs:
jobs.write(job_values)
else:
job_obj.create(job_values)
# Deactive purged jobs
if which == 'all' and first_job_id == -1:
purged_jobs = job_obj.search([
('job_id_cups', 'not in', list(all_cups_job_ids)),
])
purged_jobs.write({'active': False})
return True

View File

@@ -2,34 +2,25 @@
# Copyright (c) 2014 Camptocamp SA # Copyright (c) 2014 Camptocamp SA
# 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 openerp import models, exceptions, _, api from odoo import models, exceptions, _, api
class Report(models.Model): class Report(models.Model):
_inherit = 'report' _inherit = 'report'
@api.multi @api.model
def print_document(self, report_name, html=None, data=None): def print_document(self, record_ids, report_name, html=None, data=None):
""" Print a document, do not return the document file """ """ Print a document, do not return the document file """
res = [] document = self.with_context(must_skip_send_to_printer=True).get_pdf(
context = self.env.context record_ids, report_name, html=html, data=data)
if context is None: report = self._get_report_from_name(report_name)
context = self.env['res.users'].context_get() behaviour = report.behaviour()[report.id]
local_context = context.copy() printer = behaviour['printer']
local_context['must_skip_send_to_printer'] = True if not printer:
for rec_id in self.with_context(local_context): raise exceptions.Warning(
document = rec_id.get_pdf(report_name, html=html, data=data) _('No printer configured to print this report.')
report = self._get_report_from_name(report_name)
behaviour = report.behaviour()[report.id]
printer = behaviour['printer']
if not printer:
raise exceptions.Warning(
_('No printer configured to print this report.')
)
res.append(
printer.print_document(report, document, report.report_type)
) )
return all(res) return printer.print_document(report, document, report.report_type)
@api.multi @api.multi
def _can_print_report(self, behaviour, printer, document): def _can_print_report(self, behaviour, printer, document):
@@ -44,29 +35,22 @@ class Report(models.Model):
return True return True
return False return False
@api.v7 @api.model
def get_pdf(self, cr, uid, ids, report_name, html=None, def get_pdf(self, docids, report_name, html=None, data=None):
data=None, context=None):
""" Generate a PDF and returns it. """ Generate a PDF and returns it.
If the action configured on the report is server, it prints the If the action configured on the report is server, it prints the
generated document as well. generated document as well.
""" """
document = super(Report, self).get_pdf(cr, uid, ids, report_name, document = super(Report, self).get_pdf(
html=html, data=data, docids, report_name, html=html, data=data)
context=context)
report = self._get_report_from_name(cr, uid, report_name) report = self._get_report_from_name(report_name)
behaviour = report.behaviour()[report.id] behaviour = report.behaviour()[report.id]
printer = behaviour['printer'] printer = behaviour['printer']
can_print_report = self._can_print_report(cr, uid, ids, can_print_report = self._can_print_report(behaviour, printer, document)
behaviour, printer, document,
context=context)
if can_print_report: if can_print_report:
printer.print_document(report, document, report.report_type) printer.print_document(report, document, report.report_type)
return document
@api.v8 return document
def get_pdf(self, records, report_name, html=None, data=None):
return self._model.get_pdf(self._cr, self._uid,
records.ids, report_name,
html=html, data=data, context=self._context)

View File

@@ -6,26 +6,23 @@
# 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 openerp import models, fields, api from odoo import models, fields, api
from .printing_action import _available_action_types
class ResUsers(models.Model): class ResUsers(models.Model):
_inherit = 'res.users' _inherit = 'res.users'
@api.model
def _user_available_action_types(self):
return [
(code, string)
for code, string
in self.env['printing.action']._available_action_types()
if code != 'user_default'
]
printing_action = fields.Selection( printing_action = fields.Selection(
lambda s: s._user_available_action_types(), selection=_user_available_action_types,
) )
printing_printer_id = fields.Many2one(comodel_name='printing.printer', printing_printer_id = fields.Many2one(comodel_name='printing.printer',
string='Default Printer') string='Default Printer')
@api.model
def _available_action_types(self):
return _available_action_types(self)
@api.model
def _user_available_action_types(self):
return [(code, string) for code, string
in self._available_action_types()
if code != 'user_default']

View File

@@ -6,11 +6,11 @@
# 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).
import openerp import odoo
from openerp.service.report import self_reports from odoo.service.report import self_reports
original_exp_report = openerp.service.report.exp_report original_exp_report = odoo.service.report.exp_report
def exp_report(db, uid, object, ids, datas=None, context=None): def exp_report(db, uid, object, ids, datas=None, context=None):
@@ -22,15 +22,15 @@ def exp_report(db, uid, object, ids, datas=None, context=None):
return res return res
openerp.service.report.exp_report = exp_report odoo.service.report.exp_report = exp_report
original_exp_report_get = openerp.service.report.exp_report_get original_exp_report_get = odoo.service.report.exp_report_get
def exp_report_get(db, uid, report_id): def exp_report_get(db, uid, report_id):
# First we need to know if the module is installed # First we need to know if the module is installed
registry = openerp.registry(db) registry = odoo.registry(db)
if registry.get('printing.printer'): if registry.get('printing.printer'):
cr = registry.cursor() cr = registry.cursor()
try: try:
@@ -72,4 +72,4 @@ def exp_report_get(db, uid, report_id):
return original_exp_report_get(db, uid, report_id) return original_exp_report_get(db, uid, report_id)
openerp.service.report.exp_report_get = exp_report_get odoo.service.report.exp_report_get = exp_report_get

View File

@@ -8,6 +8,15 @@
<record id="printing_group_user" model="res.groups"> <record id="printing_group_user" model="res.groups">
<field name="name">Printing / Print User</field> <field name="name">Printing / Print User</field>
</record> </record>
<record id="printing_server_group_manager" model="ir.model.access">
<field name="name">Printing Server Manager</field>
<field name="model_id" ref="model_printing_server"/>
<field name="group_id" ref="printing_group_manager"/>
<field eval="1" name="perm_read"/>
<field eval="1" name="perm_unlink"/>
<field eval="1" name="perm_write"/>
<field eval="1" name="perm_create"/>
</record>
<record id="printing_printer_group_manager" model="ir.model.access"> <record id="printing_printer_group_manager" model="ir.model.access">
<field name="name">Printing Printer Manager</field> <field name="name">Printing Printer Manager</field>
<field name="model_id" ref="model_printing_printer"/> <field name="model_id" ref="model_printing_printer"/>
@@ -38,6 +47,15 @@
</data> </data>
<data> <data>
<record id="printing_server_group_user" model="ir.model.access">
<field name="name">Printing Server User</field>
<field name="model_id" ref="model_printing_server"/>
<field name="group_id" ref="printing_group_user"/>
<field eval="1" name="perm_read"/>
<field eval="0" name="perm_unlink"/>
<field eval="0" name="perm_write"/>
<field eval="0" name="perm_create"/>
</record>
<record id="printing_printer_group_user" model="ir.model.access"> <record id="printing_printer_group_user" model="ir.model.access">
<field name="name">Printing Printer User</field> <field name="name">Printing Printer User</field>
<field name="model_id" ref="model_printing_printer"/> <field name="model_id" ref="model_printing_printer"/>
@@ -47,6 +65,15 @@
<field eval="0" name="perm_write"/> <field eval="0" name="perm_write"/>
<field eval="0" name="perm_create"/> <field eval="0" name="perm_create"/>
</record> </record>
<record id="printing_job_group_user" model="ir.model.access">
<field name="name">Printing Job User</field>
<field name="model_id" ref="model_printing_job"/>
<field name="group_id" ref="printing_group_user"/>
<field eval="1" name="perm_read"/>
<field eval="0" name="perm_unlink"/>
<field eval="0" name="perm_write"/>
<field eval="0" name="perm_create"/>
</record>
<record id="printing_action_group_user" model="ir.model.access"> <record id="printing_action_group_user" model="ir.model.access">
<field name="name">Printing Action User</field> <field name="name">Printing Action User</field>
<field name="model_id" ref="model_printing_action"/> <field name="model_id" ref="model_printing_action"/>

View File

@@ -8,13 +8,13 @@ odoo.define('base_report_to_printer.print', function(require) {
ActionManager.include({ ActionManager.include({
ir_actions_report_xml: function(action, options) { ir_actions_report_xml: function(action, options) {
framework.blockUI();
action = _.clone(action); action = _.clone(action);
var _t = core._t; var _t = core._t;
var self = this; var self = this;
var _super = this._super; var _super = this._super;
if ('report_type' in action && action.report_type === 'qweb-pdf') { if ('report_type' in action && action.report_type === 'qweb-pdf') {
framework.blockUI();
new Model('ir.actions.report.xml') new Model('ir.actions.report.xml')
.call('print_action_for_report_name', [action.report_name]) .call('print_action_for_report_name', [action.report_name])
.then(function(print_action){ .then(function(print_action){

View File

@@ -2,8 +2,11 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_printing_job
from . import test_printing_printer from . import test_printing_printer
from . import test_printing_server
from . import test_report from . import test_report
from . import test_res_users from . import test_res_users
from . import test_ir_actions_report_xml from . import test_ir_actions_report_xml
from . import test_printing_printer_wizard from . import test_printing_printer_wizard
from . import test_printing_report_xml_action

View File

@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# Copyright 2016 SYLEAM
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import mock import mock
from openerp.tests.common import TransactionCase from odoo.tests.common import TransactionCase
class TestIrActionsReportXml(TransactionCase): class TestIrActionsReportXml(TransactionCase):
@@ -14,8 +15,34 @@ class TestIrActionsReportXml(TransactionCase):
self.Model = self.env['ir.actions.report.xml'] self.Model = self.env['ir.actions.report.xml']
self.vals = {} self.vals = {}
def new_record(self): self.report = self.env['ir.actions.report.xml'].search([], limit=1)
return self.Model.create(self.vals) self.server = self.env['printing.server'].create({})
def new_action(self):
return self.env['printing.action'].create({
'name': 'Printing Action',
'action_type': 'server',
})
def new_printing_action(self):
return self.env['printing.report.xml.action'].create({
'report_id': self.report.id,
'user_id': self.env.ref('base.user_demo').id,
'action': 'server',
})
def new_printer(self):
return self.env['printing.printer'].create({
'name': 'Printer',
'server_id': self.server.id,
'system_name': 'Sys Name',
'default': True,
'status': 'unknown',
'status_message': 'Msg',
'model': 'res.users',
'location': 'Location',
'uri': 'URI',
})
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 """
@@ -51,3 +78,131 @@ class TestIrActionsReportXml(TransactionCase):
expect, res, expect, res,
'Expect %s, Got %s' % (expect, res), 'Expect %s, Got %s' % (expect, res),
) )
def test_behaviour_default_values(self):
""" It should return the default action and printer """
report = self.Model.search([], limit=1)
self.env.user.printing_action = False
self.env.user.printing_printer_id = False
report.property_printing_action_id = False
report.printing_printer_id = False
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': self.env['printing.printer'],
},
})
def test_behaviour_user_values(self):
""" It should return the action and printer from user """
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
self.env.user.printing_printer_id = self.new_printer()
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': self.env.user.printing_printer_id,
},
})
def test_behaviour_report_values(self):
""" It should return the action and printer from report """
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
report.property_printing_action_id = self.new_action()
report.printing_printer_id = self.new_printer()
self.assertEqual(report.behaviour(), {
report.id: {
'action': report.property_printing_action_id.action_type,
'printer': report.printing_printer_id,
},
})
def test_behaviour_user_action(self):
""" It should return the action and printer from user action"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
report.property_printing_action_id.action_type = 'user_default'
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': report.printing_printer_id,
},
})
def test_behaviour_printing_action_on_wrong_user(self):
""" It should return the action and printer ignoring printing action
"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
printing_action = self.new_printing_action()
printing_action.user_id = self.env['res.users'].search([
('id', '!=', self.env.user.id),
], limit=1)
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': report.printing_printer_id,
},
})
def test_behaviour_printing_action_on_wrong_report(self):
""" It should return the action and printer ignoring printing action
"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
printing_action = self.new_printing_action()
printing_action.user_id = self.env.user
printing_action.report_id = self.env['ir.actions.report.xml'].search([
('id', '!=', report.id),
], limit=1)
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': report.printing_printer_id,
},
})
def test_behaviour_printing_action_with_no_printer(self):
""" It should return the action from printing action and printer from other
"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
printing_action = self.new_printing_action()
printing_action.user_id = self.env.user
printing_action.report_id = report
self.assertEqual(report.behaviour(), {
report.id: {
'action': printing_action.action,
'printer': report.printing_printer_id,
},
})
def test_behaviour_printing_action_with_printer(self):
""" It should return the action and printer from printing action """
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
printing_action = self.new_printing_action()
printing_action.user_id = self.env.user
printing_action.printer_id = self.new_printer()
self.assertEqual(report.behaviour(), {
report.id: {
'action': printing_action.action,
'printer': printing_action.printer_id,
},
})
def test_behaviour_printing_action_user_defaults(self):
""" It should return the action and printer from user with printing action
"""
report = self.Model.search([], limit=1)
self.env.user.printing_action = 'client'
printing_action = self.new_printing_action()
printing_action.user_id = self.env.user
printing_action.action = 'user_default'
self.assertEqual(report.behaviour(), {
report.id: {
'action': 'client',
'printer': report.printing_printer_id,
},
})

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import mock
from odoo import fields
from odoo.tests.common import TransactionCase
model = 'odoo.addons.base_report_to_printer.models.printing_server'
class TestPrintingJob(TransactionCase):
def setUp(self):
super(TestPrintingJob, self).setUp()
self.Model = self.env['printing.server']
self.server = self.Model.create({})
self.printer_vals = {
'name': 'Printer',
'server_id': self.server.id,
'system_name': 'Sys Name',
'default': True,
'status': 'unknown',
'status_message': 'Msg',
'model': 'res.users',
'location': 'Location',
'uri': 'URI',
}
self.job_vals = {
'server_id': self.server.id,
'job_id_cups': 1,
'job_media_progress': 0,
'time_at_creation': fields.Datetime.now(),
}
def new_printer(self):
return self.env['printing.printer'].create(self.printer_vals)
def new_job(self, printer, vals=None):
values = self.job_vals
if vals is not None:
values.update(vals)
values['printer_id'] = printer.id
return self.env['printing.job'].create(values)
@mock.patch('%s.cups' % model)
def test_cancel_job_error(self, cups):
""" It should catch any exception from CUPS and update status """
cups.Connection.side_effect = Exception
printer = self.new_printer()
job = self.new_job(printer, {'job_id_cups': 2})
job.action_cancel()
cups.Connection.side_effect = None
self.assertEquals(cups.Connection().cancelJob.call_count, 0)
@mock.patch('%s.cups' % model)
def test_cancel_job(self, cups):
""" It should catch any exception from CUPS and update status """
printer = self.new_printer()
job = self.new_job(printer)
job.cancel()
cups.Connection().cancelJob.assert_called_once_with(
job.job_id_cups, purge_job=False,
)

View File

@@ -2,17 +2,15 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import tempfile
import mock import mock
from openerp.tests.common import TransactionCase from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
from openerp.addons.base_report_to_printer.models.printing_printer import (
CUPS_HOST,
CUPS_PORT,
)
model = 'openerp.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'
class TestPrintingPrinter(TransactionCase): class TestPrintingPrinter(TransactionCase):
@@ -20,8 +18,11 @@ 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.server = self.env['printing.server'].create({})
self.printer_vals = { self.printer_vals = {
'name': 'Printer', 'name': 'Printer',
'server_id': self.server.id,
'system_name': 'Sys Name', 'system_name': 'Sys Name',
'default': True, 'default': True,
'status': 'unknown', 'status': 'unknown',
@@ -34,74 +35,110 @@ class TestPrintingPrinter(TransactionCase):
def new_record(self): def new_record(self):
return self.Model.create(self.printer_vals) return self.Model.create(self.printer_vals)
@mock.patch('%s.cups' % model) def test_printing_options(self):
def test_update_printers_status_error(self, cups): """ It should generate the right options dictionnary """
""" It should catch any exception from CUPS and update status """ self.assertEquals(self.Model.print_options('report', 'raw'), {
'raw': 'True',
})
self.assertEquals(self.Model.print_options('report', 'pdf', 2), {
'copies': '2',
})
self.assertEquals(self.Model.print_options('report', 'raw', 2), {
'raw': 'True',
'copies': '2',
})
@mock.patch('%s.cups' % server_model)
def test_print_report(self, cups):
""" It should print a report through CUPS """
fd, file_name = tempfile.mkstemp()
with mock.patch('%s.mkstemp' % model) as mkstemp:
mkstemp.return_value = fd, file_name
printer = self.new_record()
printer.print_document('report_name', 'content to print', 'pdf')
cups.Connection().printFile.assert_called_once_with(
printer.system_name,
file_name,
file_name,
options={})
@mock.patch('%s.cups' % server_model)
def test_print_report_error(self, cups):
""" It should print a report through CUPS """
cups.Connection.side_effect = Exception cups.Connection.side_effect = Exception
rec_id = self.new_record() fd, file_name = tempfile.mkstemp()
self.Model.update_printers_status() with mock.patch('%s.mkstemp' % model) as mkstemp:
self.assertEqual( mkstemp.return_value = fd, file_name
'server-error', rec_id.status, printer = self.new_record()
with self.assertRaises(UserError):
printer.print_document(
'report_name', 'content to print', 'pdf')
@mock.patch('%s.cups' % server_model)
def test_print_file(self, cups):
""" It should print a file through CUPS """
file_name = 'file_name'
printer = self.new_record()
printer.print_file(file_name, 'pdf')
cups.Connection().printFile.assert_called_once_with(
printer.system_name,
file_name,
file_name,
options={})
@mock.patch('%s.cups' % server_model)
def test_print_file_error(self, cups):
""" It should print a file through CUPS """
cups.Connection.side_effect = Exception
file_name = 'file_name'
printer = self.new_record()
with self.assertRaises(UserError):
printer.print_file(file_name)
def test_set_default(self):
""" It should set a single record as default """
printer = self.new_record()
self.assertTrue(printer.default)
other_printer = self.new_record()
other_printer.set_default()
self.assertFalse(printer.default)
self.assertTrue(other_printer.default)
# Check that calling the method on an empty recordset does nothing
self.Model.set_default()
self.assertEquals(other_printer, self.Model.get_default())
@mock.patch('%s.cups' % server_model)
def test_cancel_all_jobs(self, cups):
""" It should cancel all jobs """
printer = self.new_record()
printer.action_cancel_all_jobs()
cups.Connection().cancelAllJobs.assert_called_once_with(
name=printer.system_name,
purge_jobs=False,
) )
@mock.patch('%s.cups' % model) @mock.patch('%s.cups' % server_model)
def test_update_printers_status_inits_cups(self, cups): def test_cancel_and_purge_all_jobs(self, cups):
""" It should init CUPS connection """ """ It should cancel all jobs """
self.new_record() printer = self.new_record()
self.Model.update_printers_status() printer.cancel_all_jobs(purge_jobs=True)
cups.Connection.assert_called_once_with( cups.Connection().cancelAllJobs.assert_called_once_with(
CUPS_HOST, CUPS_PORT, name=printer.system_name,
purge_jobs=True,
) )
@mock.patch('%s.cups' % model) @mock.patch('%s.cups' % server_model)
def test_update_printers_status_gets_all_printers(self, cups): def test_enable_printer(self, cups):
""" It should get all printers from CUPS server """ """ It should enable the printer """
self.new_record() printer = self.new_record()
self.Model.update_printers_status() printer.enable()
cups.Connection().getPrinters.assert_called_once_with() cups.Connection().enablePrinter.assert_called_once_with(
printer.system_name)
@mock.patch('%s.cups' % model) @mock.patch('%s.cups' % server_model)
def test_update_printers_status_gets_printer(self, cups): def test_disable_printer(self, cups):
""" It should get printer from CUPS by system_name """ """ It should disable the printer """
rec_id = self.new_record() printer = self.new_record()
self.Model.update_printers_status() printer.disable()
cups.Connection().getPrinters().get.assert_called_once_with( cups.Connection().disablePrinter.assert_called_once_with(
rec_id.system_name, printer.system_name)
)
@mock.patch('%s.cups' % model)
def test_update_printers_status_search(self, cups):
""" It should search all when no domain """
with mock.patch.object(self.Model, 'search') as search:
self.Model.update_printers_status()
search.assert_called_once_with([])
@mock.patch('%s.cups' % model)
def test_update_printers_status_search_domain(self, cups):
""" It should use specific domain for search """
with mock.patch.object(self.Model, 'search') as search:
expect = [('id', '>', 0)]
self.Model.update_printers_status(expect)
search.assert_called_once_with(expect)
@mock.patch('%s.cups' % model)
def test_update_printers_status_update_printer(self, cups):
""" It should update from CUPS when printer identified """
with mock.patch.object(self.Model, 'search') as search:
printer_mk = mock.MagicMock()
search.return_value = [printer_mk]
self.Model.update_printers_status()
printer_mk.update_from_cups.assert_called_once_with(
cups.Connection(),
cups.Connection().getPrinters().get(),
)
@mock.patch('%s.cups' % model)
def test_update_printers_status_update_unavailable(self, cups):
""" It should update status when printer is unavailable """
rec_id = self.new_record()
cups.Connection().getPrinters().get.return_value = False
self.Model.update_printers_status()
self.assertEqual(
'unavailable', rec_id.status,
)

View File

@@ -4,17 +4,11 @@
import mock import mock
from openerp.tests.common import TransactionCase from odoo.tests.common import TransactionCase
from openerp.exceptions import UserError from odoo.exceptions import UserError
from openerp.addons.base_report_to_printer.models.printing_printer import (
CUPS_HOST,
CUPS_PORT,
)
model = '%s.%s' % ('openerp.addons.base_report_to_printer.wizards', model = 'odoo.addons.base_report_to_printer.models.printing_server'
'printing_printer_update_wizard')
class StopTest(Exception): class StopTest(Exception):
@@ -26,6 +20,7 @@ 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.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',
@@ -36,6 +31,7 @@ class TestPrintingPrinterWizard(TransactionCase):
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,
'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'],
@@ -45,12 +41,9 @@ class TestPrintingPrinterWizard(TransactionCase):
@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 """
try: self.Model.action_ok()
self.Model.action_ok()
except:
pass
cups.Connection.assert_called_once_with( cups.Connection.assert_called_once_with(
CUPS_HOST, CUPS_PORT, host=self.server.address, port=self.server.port,
) )
@mock.patch('%s.cups' % model) @mock.patch('%s.cups' % model)
@@ -83,8 +76,11 @@ class TestPrintingPrinterWizard(TransactionCase):
) )
self.assertTrue(rec_id) self.assertTrue(rec_id)
for key, val in self._record_vals().iteritems(): for key, val in self._record_vals().iteritems():
if rec_id._fields[key].type == 'many2one':
val = self.env[rec_id._fields[key].comodel_name].browse(val)
self.assertEqual( self.assertEqual(
val, getattr(rec_id, key), val, rec_id[key],
) )
@mock.patch('%s.cups' % model) @mock.patch('%s.cups' % model)

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Copyright 2016 SYLEAM
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.tests.common import TransactionCase
class TestPrintingReportXmlAction(TransactionCase):
def setUp(self):
super(TestPrintingReportXmlAction, self).setUp()
self.Model = self.env['printing.report.xml.action']
self.report = self.env['ir.actions.report.xml'].search([], limit=1)
self.server = self.env['printing.server'].create({})
self.report_vals = {
'report_id': self.report.id,
'user_id': self.env.ref('base.user_demo').id,
'action': 'server',
}
def new_record(self, vals=None):
values = self.report_vals
if vals is not None:
values.update(vals)
return self.Model.create(values)
def new_printer(self):
return self.env['printing.printer'].create({
'name': 'Printer',
'server_id': self.server.id,
'system_name': 'Sys Name',
'default': True,
'status': 'unknown',
'status_message': 'Msg',
'model': 'res.users',
'location': 'Location',
'uri': 'URI',
})
def test_behaviour(self):
""" It should return some action's data, unless called on empty recordset
"""
xml_action = self.new_record()
self.assertEqual(xml_action.behaviour(), {
'action': xml_action.action,
'printer': xml_action.printer_id,
})
xml_action = self.new_record({'printer_id': self.new_printer().id})
self.assertEqual(xml_action.behaviour(), {
'action': xml_action.action,
'printer': xml_action.printer_id,
})
self.assertEqual(self.Model.behaviour(), {})

View File

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

View File

@@ -2,7 +2,9 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase import mock
from odoo.tests.common import TransactionCase
from odoo import exceptions
class StopTest(Exception): class StopTest(Exception):
@@ -14,11 +16,25 @@ class TestReport(TransactionCase):
def setUp(self): def setUp(self):
super(TestReport, self).setUp() super(TestReport, self).setUp()
self.Model = self.env['report'] self.Model = self.env['report']
self.server = self.env['printing.server'].create({})
self.report_vals = {} self.report_vals = {}
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):
return self.env['printing.printer'].create({
'name': 'Printer',
'server_id': self.server.id,
'system_name': 'Sys Name',
'default': True,
'status': 'unknown',
'status_message': 'Msg',
'model': 'res.users',
'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(
@@ -42,3 +58,72 @@ class TestReport(TransactionCase):
{'action': 'server'}, True, False {'action': 'server'}, True, False
) )
self.assertFalse(res) self.assertFalse(res)
def test_get_pdf_not_printable(self):
""" It should print the report, only if it is printable
"""
with mock.patch('odoo.addons.base_report_to_printer.models.'
'printing_printer.PrintingPrinter.'
'print_document') as print_document:
report = self.env['ir.actions.report.xml'].search([
('report_type', '=', 'qweb-pdf'),
], limit=1)
records = self.env[report.model].search([], limit=5)
self.env['report'].get_pdf(records.ids, report.report_name)
print_document.assert_not_called()
def test_get_pdf_printable(self):
""" It should print the report, only if it is printable
"""
with mock.patch('odoo.addons.base_report_to_printer.models.'
'printing_printer.PrintingPrinter.'
'print_document') as print_document:
report = self.env['ir.actions.report.xml'].search([
('report_type', '=', 'qweb-pdf'),
], limit=1)
report.property_printing_action_id.action_type = 'server'
report.printing_printer_id = self.new_printer()
records = self.env[report.model].search([], limit=5)
document = self.env['report'].get_pdf(
records.ids, report.report_name)
print_document.assert_called_once_with(
report, document, report.report_type)
def test_print_document_not_printable(self):
""" It should print the report, regardless of the defined behaviour """
report = self.env['ir.actions.report.xml'].search([
('report_type', '=', 'qweb-pdf'),
], limit=1)
report.printing_printer_id = self.new_printer()
records = self.env[report.model].search([], limit=5)
with mock.patch('odoo.addons.base_report_to_printer.models.'
'printing_printer.PrintingPrinter.'
'print_document') as print_document:
self.env['report'].print_document(records.ids, report.report_name)
print_document.assert_called_once()
def test_print_document_printable(self):
""" It should print the report, regardless of the defined behaviour """
report = self.env['ir.actions.report.xml'].search([
('report_type', '=', 'qweb-pdf'),
], limit=1)
report.property_printing_action_id.action_type = 'server'
report.printing_printer_id = self.new_printer()
records = self.env[report.model].search([], limit=5)
with mock.patch('odoo.addons.base_report_to_printer.models.'
'printing_printer.PrintingPrinter.'
'print_document') as print_document:
self.env['report'].print_document(records.ids, report.report_name)
print_document.assert_called_once()
def test_print_document_no_printer(self):
""" It should raise an error """
report = self.env['ir.actions.report.xml'].search([
('report_type', '=', 'qweb-pdf'),
], limit=1)
records = self.env[report.model].search([], limit=5)
with self.assertRaises(exceptions.UserError):
self.env['report'].print_document(records.ids, report.report_name)

View File

@@ -2,7 +2,7 @@
# Copyright 2016 LasLabs Inc. # Copyright 2016 LasLabs Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase from odoo.tests.common import TransactionCase
class TestResUsers(TransactionCase): class TestResUsers(TransactionCase):

View File

@@ -0,0 +1,44 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="printing_job_view_form">
<field name="name">printing.job.form</field>
<field name="model">printing.job</field>
<field name="arch" type="xml">
<form string="Job">
<sheet>
<header>
<button name="action_cancel" type="object" string="Cancel" attrs="{'invisible': [('job_state', 'in', ('canceled', 'aborted', 'completed'))]}"/>
<field name="job_state" widget="statusbar"/>
</header>
<group>
<group>
<field name="name"/>
<field name="job_id_cups"/>
<field name="job_media_progress" widget="progressbar"/>
<field name="job_state_reason"/>
</group>
<group>
<field name="time_at_creation"/>
<field name="time_at_processing"/>
<field name="time_at_completed"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="printing_job_view_tree">
<field name="name">printing.job.tree</field>
<field name="model">printing.job</field>
<field name="arch" type="xml">
<tree string="Job">
<field name="name"/>
<field name="job_id_cups"/>
<field name="job_state"/>
</tree>
</field>
</record>
</odoo>

View File

@@ -1,81 +1,90 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<odoo> <odoo>
<menuitem name="Printing" <menuitem name="Printing"
id="printing_menu" id="printing_menu"
parent="base.menu_administration" parent="base.menu_administration"
groups="printing_group_manager" groups="printing_group_manager"
/> />
<record model="ir.ui.view" id="printing_printer_view_form"> <record model="ir.ui.view" id="printing_printer_view_form">
<field name="name">printing.printer.form</field> <field name="name">printing.printer.form</field>
<field name="model">printing.printer</field> <field name="model">printing.printer</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Printers"> <form string="Printers">
<sheet> <sheet>
<div class="oe_title"> <header>
<h1> <button name="enable" type="object" string="Enable" attrs="{'invisible': [('status', 'in', ('available', 'printing'))]}"/>
<label for="name"/> <button name="disable" type="object" string="Disable" attrs="{'invisible': [('status', '=', 'unavailable')]}"/>
<field name="name"/> <button name="action_cancel_all_jobs" type="object" string="Cancel all running jobs"/>
</h1> </header>
</div> <div class="oe_title">
<group> <h1>
<field name="system_name"/> <label for="name"/>
</group> <field name="name"/>
<group col="3" colspan="4"> </h1>
<field name="default"/> </div>
<button name="set_default" string="Set Default" type="object"/> <group>
</group> <field name="system_name"/>
<group> </group>
<field name="uri"/> <group col="3" colspan="4">
<field name="model"/> <field name="default"/>
<field name="location"/> <button name="set_default" string="Set Default" type="object"/>
<field name="status"/> </group>
<field name="status_message"/> <group>
</group> <field name="uri"/>
</sheet> <field name="model"/>
</form> <field name="location"/>
</field> <field name="status"/>
</record> <field name="status_message"/>
</group>
<group>
<separator string="Jobs" colspan="2"/>
<field name="job_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="printing_printer_view_tree"> <record model="ir.ui.view" id="printing_printer_view_tree">
<field name="name">printing.printer.tree</field> <field name="name">printing.printer.tree</field>
<field name="model">printing.printer</field> <field name="model">printing.printer</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Printers" colors="green:status=='available'; orange:status=='printing'; red:1;"> <tree string="Printers" colors="green:status=='available'; orange:status=='printing'; red:1;">
<field name="default"/> <field name="default"/>
<field name="name"/> <field name="name"/>
<field name="system_name"/> <field name="system_name"/>
<field name="status"/> <field name="status"/>
</tree> </tree>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="printing_printer_view_search"> <record model="ir.ui.view" id="printing_printer_view_search">
<field name="name">printing.printer.search</field> <field name="name">printing.printer.search</field>
<field name="model">printing.printer</field> <field name="model">printing.printer</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Printers"> <search string="Printers">
<field name="name"/> <field name="name"/>
<field name="system_name"/> <field name="system_name"/>
<field name="location"/> <field name="location"/>
<field name="status"/> <field name="status"/>
</search> </search>
</field> </field>
</record> </record>
<record model="ir.actions.act_window" id="printing_printer_action"> <record model="ir.actions.act_window" id="printing_printer_action">
<field name="name">Show Printers</field> <field name="name">Show Printers</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">printing.printer</field> <field name="res_model">printing.printer</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="auto_refresh">20</field> </record>
</record>
<menuitem name="Printers" <menuitem name="Printers"
id="printing_printer_menu" sequence="20"
parent="printing_menu" id="printing_printer_menu"
action="printing_printer_action"/> parent="printing_menu"
action="printing_printer_action"/>
</odoo> </odoo>

View File

@@ -28,6 +28,7 @@
<!-- Add a shorcut to "Actions/Report" in the Printing menu --> <!-- Add a shorcut to "Actions/Report" in the Printing menu -->
<menuitem id="printing_report_xml_action_menu" <menuitem id="printing_report_xml_action_menu"
sequence="30"
parent="printing_menu" parent="printing_menu"
action="base.ir_action_report_xml"/> action="base.ir_action_report_xml"/>

View File

@@ -0,0 +1,70 @@
<?xml version="1.0"?>
<odoo>
<record model="ir.ui.view" id="printing_server_view_form">
<field name="name">printing.server.form</field>
<field name="model">printing.server</field>
<field name="arch" type="xml">
<form string="Servers">
<sheet>
<header>
<button name="action_update_printers" type="object" string="Update Printers"/>
<button name="action_update_jobs" type="object" string="Update Jobs"/>
</header>
<div class="oe_title">
<h1>
<field name="name"/>
</h1>
</div>
<group>
<field name="address"/>
<field name="port"/>
</group>
<group>
<separator string="Printers" colspan="2"/>
<field name="printer_ids" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="printing_server_view_tree">
<field name="name">printing.server.tree</field>
<field name="model">printing.server</field>
<field name="arch" type="xml">
<tree string="Servers">
<field name="name"/>
<field name="address"/>
<field name="port"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="printing_server_view_search">
<field name="name">printing.server.search</field>
<field name="model">printing.server</field>
<field name="arch" type="xml">
<search string="Servers">
<field name="name"/>
<field name="address"/>
<field name="port"/>
</search>
</field>
</record>
<record model="ir.actions.act_window" id="printing_server_action">
<field name="name">Servers</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">printing.server</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Servers"
sequence="10"
id="printing_server_menu"
parent="printing_menu"
action="printing_server_action"/>
</odoo>

View File

@@ -6,12 +6,12 @@
<field name="model">res.users</field> <field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form" /> <field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<page string="Preferences"> <xpath expr="//group[@name='preferences']/ancestor::page" position="inside">
<group name="printing"> <group string="Printing" name="printing">
<field name="printing_action"/> <field name="printing_action"/>
<field name="printing_printer_id"/> <field name="printing_printer_id"/>
</group> </group>
</page> </xpath>
</field> </field>
</record> </record>

View File

@@ -7,16 +7,7 @@
import logging import logging
from openerp.exceptions import UserError from odoo import models, api
from openerp import models, api, _
from ..models.printing_printer import CUPS_HOST, CUPS_PORT
try:
import cups
except ImportError:
_logger = logging.getLogger(__name__)
_logger.debug('Cannot `import cups`.')
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -26,40 +17,10 @@ class PrintingPrinterUpdateWizard(models.TransientModel):
_name = 'printing.printer.update.wizard' _name = 'printing.printer.update.wizard'
_description = 'Printing Printer Update Wizard' _description = 'Printing Printer Update Wizard'
@api.model @api.multi
def action_ok(self): def action_ok(self):
# Update Printers self.env['printing.server'].search([]) \
printer_obj = self.env['printing.printer'] .update_printers(raise_on_error=True)
try:
_logger.info('Trying to get list of printers')
connection = cups.Connection(CUPS_HOST, CUPS_PORT)
printers = connection.getPrinters()
_logger.info('Printers found: %s' % ','.join(printers.keys()))
except:
raise UserError(
_('Could not get the list of printers from the CUPS server '
'(%s:%s)') % (CUPS_HOST, CUPS_PORT))
printer_recs = printer_obj.search(
[('system_name', 'in', printers.keys())]
)
for printer in printer_recs:
del printers[printer.system_name]
_logger.info(
'Printer %s was already created' % printer.system_name)
for name, printer in printers.iteritems():
values = {
'name': printer['printer-info'],
'system_name': name,
'model': printer.get('printer-make-and-model', False),
'location': printer.get('printer-location', False),
'uri': printer.get('device-uri', False),
}
printer_obj.create(values)
_logger.info(
'Created new printer %s with URI %s'
% (values['name'], values['uri']))
return { return {
'name': 'Printers', 'name': 'Printers',

View File

@@ -1,28 +1,26 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<openerp> <odoo>
<data>
<record id="printer_update_wizard" model="ir.ui.view"> <record id="printer_update_wizard" model="ir.ui.view">
<field name="name">printing.printer.update.wizard</field> <field name="name">printing.printer.update.wizard</field>
<field name="model">printing.printer.update.wizard</field> <field name="model">printing.printer.update.wizard</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Update Printers from CUPS"> <form string="Update Printers from CUPS">
<label string="This process will create all missing printers from the current CUPS server." colspan="2"/> <label string="This process will create all missing printers from the current CUPS server." colspan="2"/>
<footer> <footer>
<button name="action_ok" string="Ok" type="object" class="oe_highlight"/> <button name="action_ok" string="Ok" type="object" class="oe_highlight"/>
or or
<button string="Cancel" class="oe_link" special="cancel"/> <button string="Cancel" class="oe_link" special="cancel"/>
</footer> </footer>
</form> </form>
</field> </field>
</record> </record>
<record id="action_printer_update_wizard" model="ir.actions.act_window"> <record id="action_printer_update_wizard" model="ir.actions.act_window">
<field name="name">Update Printers from CUPS</field> <field name="name">Update Printers from CUPS</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">printing.printer.update.wizard</field> <field name="res_model">printing.printer.update.wizard</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">form</field> <field name="view_mode">form</field>
<field name="target">new</field> <field name="target">new</field>
</record> </record>
<menuitem action="action_printer_update_wizard" id="menu_printer_update_wizard" parent="printing_menu"/> <menuitem action="action_printer_update_wizard" sequence="40" id="menu_printer_update_wizard" parent="printing_menu"/>
</data> </odoo>
</openerp>