[ADD] pos_epson_printer: Use Receipt printer without IoT Box

Add possibility to print receipts on an ePOS compatible Epson receipt
printer located in the same local network than the browser running the
pos.

TaskID: 2033442
This commit is contained in:
Antoine Prieels
2019-08-20 12:19:19 +02:00
parent ddeb7b7cb0
commit 8ea5583b93
16 changed files with 236 additions and 15 deletions
@@ -204,6 +204,7 @@ class PosConfig(models.Model):
payment_method_ids = fields.Many2many('pos.payment.method', string='Payment Methods', default=lambda self: self._default_payment_methods())
company_has_template = fields.Boolean(string="Company has chart of accounts", compute="_compute_company_has_template")
current_user_id = fields.Many2one('res.users', string='Current Session Responsible', compute='_compute_current_session_user')
other_devices = fields.Boolean(string="Other Devices", help="Connect devices to your PoS without an IoT Box.")
@api.depends('company_id')
def _compute_company_has_template(self):
+1 -1
View File
@@ -812,7 +812,7 @@ var Chrome = PosBaseWidget.extend(AbstractAction.prototype, {
'name': 'sale_details',
'widget': SaleDetailsButton,
'append': '.pos-rightheader',
'condition': function(){ return this.pos.config.use_proxy; },
'condition': function(){ return this.pos.proxy.printer; },
},{
'name': 'proxy_status',
'widget': ProxyStatusWidget,
@@ -109,6 +109,7 @@ exports.PosModel = Backbone.Model.extend({
return this.connect_to_proxy();
}
return Promise.resolve();
},
// releases ressources holds by the model at the end of life of the posmodel
destroy: function(){
+11 -2
View File
@@ -32,11 +32,20 @@ var PrinterMixin = {
process_next_job();
},
/**
* Generate a jpeg image from a canvas
* @param {DOMElement} canvas
*/
process_canvas: function (canvas) {
return canvas.toDataURL('image/jpeg').replace('data:image/jpeg;base64,','');
},
/**
* Renders the html as an image to print it
* @param {String} receipt: The receipt to be printed, in HTML
*/
htmlToImg: function (receipt) {;
htmlToImg: function (receipt) {
var self = this;
$('.pos-receipts').html(receipt);
$('.pos-receipt').addClass('pos-receipt-print');
var promise = new Promise(function (resolve, reject) {
@@ -52,7 +61,7 @@ var PrinterMixin = {
}
}).then(function (canvas) {
$('.pos-receipts').empty();
resolve(canvas.toDataURL('image/jpeg').replace('data:image/jpeg;base64,',''));
resolve(self.process_canvas(canvas));
});
});
return promise;
@@ -1631,7 +1631,7 @@ var ReceiptScreenWidget = ScreenWidget.extend({
should_close_immediately: function() {
var order = this.pos.get_order();
var invoiced_finalized = order.is_to_invoice() ? order.finalized : true;
return this.pos.config.iface_print_via_proxy && this.pos.config.iface_print_skip_screen && invoiced_finalized;
return this.pos.proxy.printer && this.pos.config.iface_print_skip_screen && invoiced_finalized;
},
lock_screen: function(locked) {
this._locked = locked;
@@ -1680,7 +1680,7 @@ var ReceiptScreenWidget = ScreenWidget.extend({
print: function() {
var self = this;
if (!this.pos.config.iface_print_via_proxy) { // browser (html) printing
if (!this.pos.proxy.printer) { // browser (html) printing
// The problem is that in chrome the print() is asynchronous and doesn't
// execute until all rpc are finished. So it conflicts with the rpc used
+20 -9
View File
@@ -143,20 +143,20 @@
</div>
</div>
</div>
<h2>IoT Box</h2>
<h2>Connected Devices</h2>
<div class="row mt16 o_settings_container" id="posbox_reference">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_posbox"/>
</div>
<div class="o_setting_right_pane">
<label for="is_posbox" string="IoT Box"/>
<label for="is_posbox" string="IoT Box Devices"/>
<div class="text-muted">
Connect devices to your PoS (ticket printer, barcode scanner, scale, etc.)
Connect devices to your PoS through an IoT Box
</div>
<div class="content-group" attrs="{'invisible' : [('is_posbox', '=', False)]}">
<div class="row mt16">
<label string="IP Address" for="proxy_ip" class="col-lg-3 o_light_label"/>
<label string="IoT Box IP Address" for="proxy_ip" class="col-lg-3 o_light_label"/>
<field name="proxy_ip"/>
</div>
<div class="row">
@@ -167,14 +167,14 @@
<label string="Electronic Scale" for="iface_electronic_scale" class="col-lg-3 o_light_label"/>
<field name="iface_electronic_scale"/>
</div>
<div class="row">
<label string="Cashdrawer" for="iface_cashdrawer" class="col-lg-3 o_light_label"/>
<field name="iface_cashdrawer"/>
</div>
<div class="row">
<label string="Receipt Printer" for="iface_print_via_proxy" class="col-lg-3 o_light_label"/>
<field name="iface_print_via_proxy"/>
</div>
<div class="row" attrs="{'invisible': [('iface_print_via_proxy', '=', False)]}">
<label string="Cashdrawer" for="iface_cashdrawer" class="col-lg-3 o_light_label"/>
<field name="iface_cashdrawer"/>
</div>
<div class="row">
<label string="Customer Display" for="iface_customer_facing_display" class="col-lg-3 o_light_label"/>
<field name="iface_customer_facing_display"/>
@@ -183,6 +183,17 @@
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="other_devices">
<div class="o_setting_left_pane">
<field name="other_devices"/>
</div>
<div class="o_setting_right_pane">
<label for="other_devices" string="Other Devices"/>
<div class="text-muted mb16">
Connect devices to your PoS
</div>
</div>
</div>
<div id="barcode_scanner" class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
</div>
@@ -405,7 +416,7 @@
<div class="text-muted">
Print receipts automatically once the payment is registered
</div>
<div class="content-group mt16" attrs="{'invisible' : ['|', ('iface_print_auto', '=', False), ('is_posbox', '=', False)]}">
<div class="content-group mt16" attrs="{'invisible' : ['|', ('iface_print_auto', '=', False), '&amp;', ('is_posbox', '=', False), ('other_devices', '=', False)]}">
<div>
<field name="iface_print_skip_screen" class="oe_inline"/><span class="oe_inline"><b>Skip Preview Screen</b></span>
</div>
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+22
View File
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'pos_epson_printer',
'version': '1.0',
'category': 'Point of Sale',
'sequence': 6,
'summary': 'Epson ePOS Printers in PoS',
'description': """
Use Epson ePOS Printers without IoT Box in the Point of Sale
""",
'depends': ['point_of_sale'],
'data': [
'views/point_of_sale_assets.xml',
'views/pos_config_views.xml',
],
'installable': True,
'auto_install': True,
}
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import pos_config
@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class PosConfig(models.Model):
_inherit = 'pos.config'
epson_printer_ip = fields.Char(string='Epson Printer IP', help="Local IP address of an Epson receipt printer.")
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
odoo.define('pos_epson_printer.pos_epson_printer', function (require) {
"use strict";
var models = require('point_of_sale.models');
var PaymentScreenWidget = require('point_of_sale.screens').PaymentScreenWidget;
var EpsonPrinter = require('pos_epson_printer.Printer');
var posmodel_super = models.PosModel.prototype;
models.PosModel = models.PosModel.extend({
after_load_server_data: function () {
var self = this;
return posmodel_super.after_load_server_data.apply(this, arguments).then(function () {
if (self.config.other_devices && self.config.epson_printer_ip) {
self.proxy.printer = new EpsonPrinter(self.config.epson_printer_ip , self);
}
});
},
});
});
@@ -0,0 +1,98 @@
odoo.define('pos_epson_printer.Printer', function (require) {
"use strict";
var core = require('web.core');
var PrinterMixin = require('point_of_sale.Printer').PrinterMixin;
var _t = core._t;
var EpsonPrinter = core.Class.extend(PrinterMixin, {
init: function (ip, pos) {
PrinterMixin.init.call(this, arguments);
this.pos = pos;
this.ePOSDevice = new epson.ePOSDevice();
var port = window.location.protocol === 'http:' ? '8008' : '8043';
this.ePOSDevice.connect(ip, port, this.callback_connect.bind(this), {eposprint: true});
},
callback_connect: function (resultConnect) {
var deviceId = 'local_printer';
var options = {'crypto' : false, 'buffer' : false};
if ((resultConnect == 'OK') || (resultConnect == 'SSL_CONNECT_OK')) {
this.ePOSDevice.createDevice(deviceId, this.ePOSDevice.DEVICE_TYPE_PRINTER, options, this.callback_createDevice.bind(this));
} else {
this.pos.gui.show_popup('error', {
'title': _t('Connection to the printer failed'),
'body': _t('Please check if the printer is still connected, if the configured IP address is correct and if your printer supports the ePOS protocol.'),
});
}
},
callback_createDevice: function (deviceObj, errorCode) {
var self = this;
if (deviceObj === null) {
this.pos.gui.show_popup('error', {
'title': _t('Connection to the printer failed'),
'body': _t('Please check if the printer is still connected. Error code: ') + errorCode,
});
return;
}
this.printer = deviceObj;
this.printer.onreceive = function(response){
if (!response.success) {
self.pos.gui.show_popup('error', {
'title': _t('Epson ePOS Error'),
'body': _t('An error happened while sending data to the printer. Error code: ') + response.code,
});
}
};
},
/**
* Create the print request for webPRNT from a canvas
*
* @override
*/
process_canvas: function (canvas) {
if (this.printer) {
this.printer.addTextAlign(this.printer.ALIGN_CENTER);
this.printer.addImage(canvas.getContext('2d'), 0, 0, canvas.width, canvas.height);
this.printer.addCut();
}
},
/**
* @override
*/
open_cashbox: function () {
if (this.printer) {
this.printer.addPulse();
this.printer.send();
}
},
/**
* @override
*/
send_printing_job: function () {
if (this.printer) {
this.printer.send();
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* Not applicable to Epson ePOS
* @override
*/
_onIoTActionFail: function () {},
_onIoTActionResult: function () {},
});
return EpsonPrinter;
});
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_epson_printer/static/src/js/pos_epson_printer.js"></script>
<script type="text/javascript" src="/pos_epson_printer/static/src/js/printers.js"></script>
<script type="text/javascript" src="/pos_epson_printer/static/lib/epos-2.12.0.js"></script>
</xpath>
</template>
</odoo>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="pos_iot_config_view_form" model="ir.ui.view">
<field name="name">pos.iot.config.form.view</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@id='other_devices']//div[hasclass('o_setting_right_pane')]" position="inside">
<div class="content-group" attrs="{'invisible' : [('other_devices', '=', False)]}">
<div class="row">
<label string="Epson Receipt Printer IP Address" for="epson_printer_ip" class="col-lg-3 o_light_label"/>
<field name="epson_printer_ip"/>
</div>
<div class="row" attrs="{'invisible': [('epson_printer_ip', 'in', [False, ''])]}">
<label string="Cashdrawer" for="iface_cashdrawer" class="col-lg-3 o_light_label"/>
<field name="iface_cashdrawer"/>
</div>
</div>
<div role="alert" class="alert alert-warning" attrs="{'invisible': ['|', '|', ('iface_print_via_proxy', '!=', True), ('other_devices', '!=', True), ('epson_printer_ip', 'in', [False, ''])]}">
The Epson receipt printer will be used instead of the receipt printer connected to the IoT Box.
</div>
</xpath>
</field>
</record>
</odoo>
@@ -794,7 +794,7 @@ chrome.OrderSelectorWidget.include({
var _super_posmodel = models.PosModel.prototype;
models.PosModel = models.PosModel.extend({
after_load_server_data: function() {
_super_posmodel.after_load_server_data.call(this);
var res = _super_posmodel.after_load_server_data.call(this);
if (this.config.iface_floorplan) {
var self = this;
this.table = null;
@@ -805,6 +805,7 @@ models.PosModel = models.PosModel.extend({
$('.screen').not('.floor-screen').onscroll = function() {self.set_idle_timer()}; // scrolling with arrow keys
$('.screen').not('.floor-screen').onkeypress = function() {self.set_idle_timer()};
}
return res;
},
transfer_order_to_different_table: function () {