Skip to content

Commit

Permalink
pos_customer_display: support ePOS display protocol
Browse files Browse the repository at this point in the history
In addition to the USB LED LCD connected to IoTBox or pywebdriver, this
module now also the Epson ePOS protocol for displays: if you have an
Epson USB display (DM-D30 for example) connected to an Epson IP printer
that support ePOS (TM-m30 for example) and has ePOS print enabled in
it's web configuration interface, Odoo can connect directly to it. The
JS code of the POS will send the IP request to the ePOS printer that
will relay the information to the USB display.

Advantages of ePOS vs IoTBox/pywebdriver:
- supports accentuated caracters
- no need to install/setup IoTBox/pywebdriver

I added a dependency on pos_epson_printer, but it should not be a real
problem because it is a very small module and it won't bother you if you
don't use it.

Remove demo data because there are 2 options now for LCD on pos.config, so we let the user choose.

Use 'x' instead of '*' for multiple quantities.
  • Loading branch information
alexis-via committed Jun 20, 2023
1 parent 5753c05 commit 4a9bf93
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 68 deletions.
3 changes: 1 addition & 2 deletions pos_customer_display/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
"author": "Aurélien DUMAINE,GRAP,Akretion,Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "https://github.com/OCA/pos",
"depends": ["point_of_sale"],
"depends": ["pos_epson_printer"],
"data": [
"views/assets.xml",
"views/view_pos_config.xml",
],
"demo": ["demo/pos_config.xml"],
"installable": True,
}
8 changes: 0 additions & 8 deletions pos_customer_display/demo/pos_config.xml

This file was deleted.

31 changes: 30 additions & 1 deletion pos_customer_display/models/pos_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class PosConfig(models.Model):
string="LED Customer Display", help="Display data on the customer display"
)

epos_customer_display = fields.Boolean(
string="LED Customer Display (Epson ePOS)",
help="Activate if you use an Epson LCD connected via USB "
"(DM-D30, DM-D110 or DM-D210) to your Epson printer defined above "
"that support the ePOS protocol.",
)

customer_display_format = fields.Selection(
selection=_CUSTOMER_DISPLAY_FORMAT_SELECTION,
string="Customer Display Format",
Expand Down Expand Up @@ -74,8 +81,20 @@ def _compute_customer_display_line_length(self):
config.customer_display_format.split("_")[1]
)

@api.constrains("iface_customer_display", "epos_customer_display")
def _check_posbox_or_epos(self):
for config in self:
if config.iface_customer_display and config.epos_customer_display:
raise ValidationError(
_(
"On '%s', you activated the LED Customer Display both "
"via the IoTbox and via Direct Devices. You can only "
"select one of the two options."
)
% config.display_name
)

@api.constrains(
"iface_customer_display",
"customer_display_format",
"customer_display_msg_next_l1",
"customer_display_msg_next_l2",
Expand All @@ -99,3 +118,13 @@ def _check_customer_display_length(self):
)
% (self._fields[field_name].string, len(value), maxsize)
)

@api.onchange("other_devices")
def other_devices_change_customer_display(self):
if not self.other_devices and self.epos_customer_display:
self.epos_customer_display = False

@api.onchange("is_posbox")
def is_posbox_change_customer_display(self):
if not self.is_posbox and self.iface_customer_display:
self.iface_customer_display = False
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ odoo.define("pos_customer_display.customer_display_2_20", function (require) {
"update_discount",
].indexOf(action) !== -1
) {
var second_line = String(qty) + " * " + unit_price_str;
var second_line = String(qty) + " x " + unit_price_str;
if (discount) {
discount_str = " -" + String(discount) + "%";
}
Expand Down
79 changes: 76 additions & 3 deletions pos_customer_display/static/src/js/devices.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,85 @@ odoo.define("pos_customer_display.devices", function (require) {

send_text_customer_display: function (data) {
if (this.customer_display_proxy) {
return this.message("send_text_customer_display", {
text_to_display: JSON.stringify(data),
});
if (this.pos.config.iface_customer_display) {
return this.message("send_text_customer_display", {
text_to_display: JSON.stringify(data),
});
} else if (
this.pos.config.epos_customer_display &&
this.pos.config.epson_printer_ip
) {
return this._epos_send_display_soap_request(
this.pos.config.epson_printer_ip,
data
);
}
}
},

_epos_send_display_soap_request: function (epos_printer_ip, data) {
/* I could use the JS lib 'epos-2.12.0.js' supplied by the module
'pos_epson_printer' in Odoo v14
but this JS lib is not supplied any more in Odoo v15, so this module will be
easier to port to newer versions of Odoo if we don't use that JS lib */
var body_xml =
'<epos-display xmlns="http://www.epson-pos.com/schemas/2012/09/epos-display">';
body_xml += "<reset/>";
body_xml += '<cursor type="none"/>';
body_xml += '<text x="1" y="1" lang="mul">' + data[0] + "</text>";
body_xml += '<text x="1" y="2" lang="mul">' + data[1] + "</text>";
body_xml += "</epos-display>";
// Config params in header
var header_xml =
'<parameter xmlns="http://www.epson-pos.com/schemas/2012/09/epos-display">';
header_xml += "<devid>local_display</devid>";
header_xml += "<timeout>1000</timeout>";
header_xml += "</parameter>";
// Build the full XML
var entire_xml =
'<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">';
entire_xml += "<s:Header>" + header_xml + "</s:Header>";
entire_xml += "<s:Body>" + body_xml + "</s:Body></s:Envelope>";
// Build the URL
var url =
window.location.protocol +
"//" +
epos_printer_ip +
"/cgi-bin/eposDisp/service.cgi";
// Prepare the SOAP request to the ePOS printer
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
// Set HTTP headers
xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xhr.setRequestHeader("SOAPAction", '""');
// Define the callback function
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
// Get XML answer to my SOAP request
var answer = xhr.responseXML;
var xml_response = answer
.getElementsByTagName("response")[0]
.getAttribute("success");
// If it fails, log a warning
if (!/^(1|true)$/.test(xml_response)) {
console.warn(
"pos_customer_display: ePOS local_display error. xml_response = " +
xml_response
);
}
} else {
console.warn(
"pos_customer_display: ePOS local_display error. HTTP error code = " +
xhr.status
);
}
}
};
// Fire SOAP request to Epson ePOS Printer
xhr.send(entire_xml);
},

_prepare_line: function (left_part, right_part) {
if (left_part === false) {
left_part = "";
Expand Down
114 changes: 61 additions & 53 deletions pos_customer_display/views/view_pos_config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<record id="pos_config_view_form" model="ir.ui.view">
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form" />
<field name="inherit_id" ref="pos_epson_printer.pos_iot_config_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='iface_cashdrawer']/.." position="after">
<div class="row" id="iface_customer_display">
Expand All @@ -14,84 +14,92 @@
/>
<field name="iface_customer_display" />
</div>

</xpath>
<xpath expr="//field[@name='epson_printer_ip']/../.." position="inside">
<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_format"
name="epos_customer_display"
attrs="{'invisible': [('epson_printer_ip', 'in', [False, ''])]}"
>
<label
string="Customer Display Format"
for="customer_display_format"
class="col-lg-4 o_light_label"
string="LED Customer Display"
for="epos_customer_display"
class="col-lg-3 o_light_label"
/>
<field name="customer_display_format" />
<field name="epos_customer_display" />
</div>

</xpath>
<div id="barcode_scanner" position="after">
<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_line_length"
id="led_customer_display"
class="col-12 col-lg-6 o_setting_box"
attrs="{'invisible': [('iface_customer_display', '=', False), ('epos_customer_display', '=', False)]}"
>
<div class="o_setting_left_pane">
</div>
<div class="o_setting_right_pane">
<span
class="o_form_label"
>LED Customer Display Configuration</span>
<div class="text-muted">
Configure display and customize messages
</div>

<div class="content-group mt16 row" id="customer_display_format">
<label
string="Line Length"
for="customer_display_line_length"
class="col-lg-4 o_light_label"
/>
string="Customer Display Format"
for="customer_display_format"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_format" />
</div>

<div class="content-group mt16 row" id="customer_display_line_length">
<label
string="Line Length"
for="customer_display_line_length"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_line_length" />
</div>

<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_msg_next_l1"
>
<div class="content-group mt16 row" id="customer_display_msg_next_l1">
<label
string="Next Customer (Line 1)"
for="customer_display_msg_next_l1"
class="col-lg-4 o_light_label"
/>
string="Next Customer (Line 1)"
for="customer_display_msg_next_l1"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_msg_next_l1" />
</div>
<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_msg_next_l2"
>
<div class="content-group mt16 row" id="customer_display_msg_next_l2">
<label
string="Next Customer (Line 2)"
for="customer_display_msg_next_l2"
class="col-lg-4 o_light_label"
/>
string="Next Customer (Line 2)"
for="customer_display_msg_next_l2"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_msg_next_l2" />
</div>

<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_msg_closed_l1"
>
<div class="content-group mt16 row" id="customer_display_msg_closed_l1">
<label
string="PoS Closed (Line 1)"
for="customer_display_msg_closed_l1"
class="col-lg-4 o_light_label"
/>
string="PoS Closed (Line 1)"
for="customer_display_msg_closed_l1"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_msg_closed_l1" />
</div>
<div
class="row"
attrs="{'invisible': [('iface_customer_display', '=', False)]}"
id="customer_display_msg_closed_l2"
>
<div class="content-group mt16 row" id="customer_display_msg_closed_l2">
<label
string="PoS Closed (Line 2)"
for="customer_display_msg_closed_l2"
class="col-lg-4 o_light_label"
/>
string="PoS Closed (Line 2)"
for="customer_display_msg_closed_l2"
class="col-lg-4 o_light_label"
/>
<field name="customer_display_msg_closed_l2" />
</div>
</div>
</div>
</div>

</xpath>
</field>
</record>

Expand Down

0 comments on commit 4a9bf93

Please sign in to comment.