mirror of
https://github.com/OCA/report-print-send.git
synced 2025-02-16 07:11:31 +02:00
[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:
committed by
Dave Lasley
parent
47f4270216
commit
3f3d06a004
@@ -10,6 +10,8 @@ python:
|
|||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
sources:
|
||||||
|
- pov-wkhtmltopdf
|
||||||
packages:
|
packages:
|
||||||
- expect-dev # provides unbuffer utility
|
- expect-dev # provides unbuffer utility
|
||||||
- python-lxml # because pip installation is slow
|
- python-lxml # because pip installation is slow
|
||||||
@@ -18,6 +20,11 @@ addons:
|
|||||||
- python-yaml
|
- python-yaml
|
||||||
- cups
|
- cups
|
||||||
- libcups2-dev
|
- libcups2-dev
|
||||||
|
- wkhtmltopdf # only add if needed and check the before_install section below
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- "export DISPLAY=:99.0"
|
||||||
|
- "sh -e /etc/init.d/xvfb start"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
|
|||||||
@@ -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
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -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'],
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
<!-- printing.action -->
|
<!-- printing.action -->
|
||||||
<record model="printing.action" id="printing_action_1">
|
<record model="printing.action" id="printing_action_1">
|
||||||
<field name="name">Send to Printer</field>
|
<field name="name">Send to Printer</field>
|
||||||
<field name="type">server</field>
|
<field name="action_type">server</field>
|
||||||
</record>
|
</record>
|
||||||
<record model="printing.action" id="printing_action_2">
|
<record model="printing.action" id="printing_action_2">
|
||||||
<field name="name">Send to Client</field>
|
<field name="name">Send to Client</field>
|
||||||
<field name="type">client</field>
|
<field name="action_type">client</field>
|
||||||
</record>
|
</record>
|
||||||
<!-- properties -->
|
<!-- properties -->
|
||||||
<record forcecreate="True" id="property_printing_action_id" model="ir.property">
|
<record forcecreate="True" id="property_printing_action_id" model="ir.property">
|
||||||
@@ -18,15 +18,15 @@
|
|||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record forcecreate="True" id="ir_cron_update_printers" model="ir.cron">
|
<record forcecreate="True" id="ir_cron_update_printers" model="ir.cron">
|
||||||
<field name="name">Update Printers Status</field>
|
<field name="name">Update Printers Jobs</field>
|
||||||
<field eval="True" name="active"/>
|
<field eval="True" name="active"/>
|
||||||
<field name="user_id" ref="base.user_root"/>
|
<field name="user_id" ref="base.user_root"/>
|
||||||
<field name="interval_number">1</field>
|
<field name="interval_number">1</field>
|
||||||
<field name="interval_type">minutes</field>
|
<field name="interval_type">minutes</field>
|
||||||
<field name="numbercall">-1</field>
|
<field name="numbercall">-1</field>
|
||||||
<field eval="False" name="doall"/>
|
<field eval="False" name="doall"/>
|
||||||
<field eval="'printing.printer'" name="model"/>
|
<field eval="'printing.server'" name="model"/>
|
||||||
<field eval="'update_printers_status'" name="function"/>
|
<field eval="'action_update_jobs'" name="function"/>
|
||||||
<field eval="'()'" name="args"/>
|
<field eval="'()'" name="args"/>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
})
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
)
|
)
|
||||||
|
|||||||
107
base_report_to_printer/models/printing_job.py
Normal file
107
base_report_to_printer/models/printing_job.py
Normal 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
|
||||||
@@ -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,15 +27,24 @@ 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(
|
||||||
|
selection=[
|
||||||
|
('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')
|
||||||
@@ -55,26 +53,6 @@ class PrintingPrinter(models.Model):
|
|||||||
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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
'action': self.action,
|
||||||
'printer': self.printer_id,
|
'printer': self.printer_id,
|
||||||
}
|
}
|
||||||
|
|||||||
224
base_report_to_printer/models/printing_server.py
Normal file
224
base_report_to_printer/models/printing_server.py
Normal 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
|
||||||
@@ -2,23 +2,17 @@
|
|||||||
# 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:
|
|
||||||
context = self.env['res.users'].context_get()
|
|
||||||
local_context = context.copy()
|
|
||||||
local_context['must_skip_send_to_printer'] = True
|
|
||||||
for rec_id in self.with_context(local_context):
|
|
||||||
document = rec_id.get_pdf(report_name, html=html, data=data)
|
|
||||||
report = self._get_report_from_name(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']
|
||||||
@@ -26,10 +20,7 @@ class Report(models.Model):
|
|||||||
raise exceptions.Warning(
|
raise exceptions.Warning(
|
||||||
_('No printer configured to print this report.')
|
_('No printer configured to print this report.')
|
||||||
)
|
)
|
||||||
res.append(
|
return printer.print_document(report, document, report.report_type)
|
||||||
printer.print_document(report, document, report.report_type)
|
|
||||||
)
|
|
||||||
return all(res)
|
|
||||||
|
|
||||||
@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)
|
|
||||||
|
|||||||
@@ -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']
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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){
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
66
base_report_to_printer/tests/test_printing_job.py
Normal file
66
base_report_to_printer/tests/test_printing_job.py
Normal 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,
|
||||||
|
)
|
||||||
@@ -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,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(), {})
|
||||||
213
base_report_to_printer/tests/test_printing_server.py
Normal file
213
base_report_to_printer/tests/test_printing_server.py
Normal 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')
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
44
base_report_to_printer/views/printing_job.xml
Normal file
44
base_report_to_printer/views/printing_job.xml
Normal 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>
|
||||||
@@ -13,6 +13,11 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form string="Printers">
|
<form string="Printers">
|
||||||
<sheet>
|
<sheet>
|
||||||
|
<header>
|
||||||
|
<button name="enable" type="object" string="Enable" attrs="{'invisible': [('status', 'in', ('available', 'printing'))]}"/>
|
||||||
|
<button name="disable" type="object" string="Disable" attrs="{'invisible': [('status', '=', 'unavailable')]}"/>
|
||||||
|
<button name="action_cancel_all_jobs" type="object" string="Cancel all running jobs"/>
|
||||||
|
</header>
|
||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<h1>
|
<h1>
|
||||||
<label for="name"/>
|
<label for="name"/>
|
||||||
@@ -33,6 +38,10 @@
|
|||||||
<field name="status"/>
|
<field name="status"/>
|
||||||
<field name="status_message"/>
|
<field name="status_message"/>
|
||||||
</group>
|
</group>
|
||||||
|
<group>
|
||||||
|
<separator string="Jobs" colspan="2"/>
|
||||||
|
<field name="job_ids" nolabel="1"/>
|
||||||
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
@@ -70,10 +79,10 @@
|
|||||||
<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"
|
||||||
|
sequence="20"
|
||||||
id="printing_printer_menu"
|
id="printing_printer_menu"
|
||||||
parent="printing_menu"
|
parent="printing_menu"
|
||||||
action="printing_printer_action"/>
|
action="printing_printer_action"/>
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|
||||||
|
|||||||
70
base_report_to_printer/views/printing_server.xml
Normal file
70
base_report_to_printer/views/printing_server.xml
Normal 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>
|
||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?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>
|
||||||
@@ -23,6 +22,5 @@
|
|||||||
<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>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user