odoo-driver-plugin-display-escpos 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo_driver_plugin_display_escpos-1.0.4/PKG-INFO +94 -0
- odoo_driver_plugin_display_escpos-1.0.4/README.md +67 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/__init__.py +17 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_driver.py +175 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_route_odoo.py +22 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_route_website.py +23 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/babel.cfg +2 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/fr/LC_MESSAGES/messages.mo +0 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/fr/LC_MESSAGES/messages.po +120 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/i18n/messages.pot +119 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png +0 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/generic_device.png +0 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png +0 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/templates/driver_display_escpos.html.jinja +117 -0
- odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/usb_devices.py +4 -0
- odoo_driver_plugin_display_escpos-1.0.4/pyproject.toml +49 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: odoo-driver-plugin-display-escpos
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Summary: Plugin for odoo-driver. Support escpos display devices (LCD).
|
|
5
|
+
License: AGPLv3+
|
|
6
|
+
Keywords: Odoo,Point Of Sale,Driver,Device,Display,LCD,ESC/POS,escpos,Aures ®,Bixolon ®,Oxhoo ®
|
|
7
|
+
Author: Sylvain LE GAL
|
|
8
|
+
Author-email: sylvain.legal@grap.coop
|
|
9
|
+
Maintainer: Sylvain LE GAL
|
|
10
|
+
Maintainer-email: sylvain.legal@grap.coop
|
|
11
|
+
Requires-Python: >=3.9,<4.0
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Operating System :: Unix
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Framework :: Odoo
|
|
23
|
+
Requires-Dist: odoo-driver
|
|
24
|
+
Project-URL: Repository, https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# Odoo Driver Plugin - Display ESC/POS
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
This project provide a plugin for ``odoo-driver``
|
|
36
|
+
to communicate with LCD Display with ECS/POS protocol.
|
|
37
|
+
|
|
38
|
+
This plugin requires the following odoo module : [`pos_odoo_driver_display`](https://github.com/grap/odoo-addons-pos)
|
|
39
|
+
|
|
40
|
+
To install this plugin, refer to the main project page : https://gitlab.com/grap-rhone-alpes/odoo-driver/.
|
|
41
|
+
|
|
42
|
+
For developpers, refer to this page : https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-printer-escpos/-/blob/main/DEVELOP.md.
|
|
43
|
+
|
|
44
|
+
## Table of contents
|
|
45
|
+
|
|
46
|
+
1. [Compatible Devices](#compatible-devices)
|
|
47
|
+
2. [Credits](#credits)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
<a name="compatible-devices"></a>
|
|
51
|
+
|
|
52
|
+
## Compatible Devices
|
|
53
|
+
|
|
54
|
+
<table style="width: 100%;">
|
|
55
|
+
<tbody>
|
|
56
|
+
<tr>
|
|
57
|
+
<td>Aures - OCD 300</td>
|
|
58
|
+
<td>
|
|
59
|
+
<img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png" width="200" height="200" />
|
|
60
|
+
</td>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>Oxhoo - AF 240</td>
|
|
64
|
+
<td>
|
|
65
|
+
<img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png" width="200" height="200" />
|
|
66
|
+
</td>
|
|
67
|
+
</tr>
|
|
68
|
+
</tbody>
|
|
69
|
+
</table>
|
|
70
|
+
|
|
71
|
+
<a name="credits"></a>
|
|
72
|
+
|
|
73
|
+
## Credits
|
|
74
|
+
|
|
75
|
+
### Authors
|
|
76
|
+
|
|
77
|
+
* GRAP <https://www.grap.coop>
|
|
78
|
+
|
|
79
|
+
### Contributors
|
|
80
|
+
|
|
81
|
+
* Sylvain LE GAL <https://www.grap.coop/>
|
|
82
|
+
|
|
83
|
+
### Extra authorship
|
|
84
|
+
|
|
85
|
+
Part of the code in this project comes from the following projects, including:
|
|
86
|
+
|
|
87
|
+
* [Pywebdriver](https://github.com/pywebdriver/pywebdriver) (AGPL-3.0).
|
|
88
|
+
|
|
89
|
+
* [pyposdisplay](https://github.com/akretion/pyposdisplay) (AGPL-3.0).
|
|
90
|
+
|
|
91
|
+
### Images
|
|
92
|
+
|
|
93
|
+
* [LCD Customer Display Icon](https://www.flaticon.com/fr/icone-gratuite/lcd_9622586), created by Iconic Panda (Flaticon).
|
|
94
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Odoo Driver Plugin - Display ESC/POS
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
This project provide a plugin for ``odoo-driver``
|
|
10
|
+
to communicate with LCD Display with ECS/POS protocol.
|
|
11
|
+
|
|
12
|
+
This plugin requires the following odoo module : [`pos_odoo_driver_display`](https://github.com/grap/odoo-addons-pos)
|
|
13
|
+
|
|
14
|
+
To install this plugin, refer to the main project page : https://gitlab.com/grap-rhone-alpes/odoo-driver/.
|
|
15
|
+
|
|
16
|
+
For developpers, refer to this page : https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-printer-escpos/-/blob/main/DEVELOP.md.
|
|
17
|
+
|
|
18
|
+
## Table of contents
|
|
19
|
+
|
|
20
|
+
1. [Compatible Devices](#compatible-devices)
|
|
21
|
+
2. [Credits](#credits)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<a name="compatible-devices"></a>
|
|
25
|
+
|
|
26
|
+
## Compatible Devices
|
|
27
|
+
|
|
28
|
+
<table style="width: 100%;">
|
|
29
|
+
<tbody>
|
|
30
|
+
<tr>
|
|
31
|
+
<td>Aures - OCD 300</td>
|
|
32
|
+
<td>
|
|
33
|
+
<img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/aures__ocd_300.png" width="200" height="200" />
|
|
34
|
+
</td>
|
|
35
|
+
</tr>
|
|
36
|
+
<tr>
|
|
37
|
+
<td>Oxhoo - AF 240</td>
|
|
38
|
+
<td>
|
|
39
|
+
<img src="https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos/-/raw/main/odoo_driver_plugin_display_escpos/static/img/oxhoo__af240.png" width="200" height="200" />
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
</tbody>
|
|
43
|
+
</table>
|
|
44
|
+
|
|
45
|
+
<a name="credits"></a>
|
|
46
|
+
|
|
47
|
+
## Credits
|
|
48
|
+
|
|
49
|
+
### Authors
|
|
50
|
+
|
|
51
|
+
* GRAP <https://www.grap.coop>
|
|
52
|
+
|
|
53
|
+
### Contributors
|
|
54
|
+
|
|
55
|
+
* Sylvain LE GAL <https://www.grap.coop/>
|
|
56
|
+
|
|
57
|
+
### Extra authorship
|
|
58
|
+
|
|
59
|
+
Part of the code in this project comes from the following projects, including:
|
|
60
|
+
|
|
61
|
+
* [Pywebdriver](https://github.com/pywebdriver/pywebdriver) (AGPL-3.0).
|
|
62
|
+
|
|
63
|
+
* [pyposdisplay](https://github.com/akretion/pyposdisplay) (AGPL-3.0).
|
|
64
|
+
|
|
65
|
+
### Images
|
|
66
|
+
|
|
67
|
+
* [LCD Customer Display Icon](https://www.flaticon.com/fr/icone-gratuite/lcd_9622586), created by Iconic Panda (Flaticon).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from single_source import get_version
|
|
4
|
+
|
|
5
|
+
from . import usb_devices
|
|
6
|
+
from .display_escpos_driver import DriverDisplayEscPos
|
|
7
|
+
from .display_escpos_route_odoo import driver_routes_odoo
|
|
8
|
+
from .display_escpos_route_website import driver_routes_website
|
|
9
|
+
|
|
10
|
+
__version__ = get_version(__name__, Path(__file__).parent.parent)
|
|
11
|
+
__package_name__ = __name__
|
|
12
|
+
|
|
13
|
+
DRIVER_FUNCTION = "display"
|
|
14
|
+
PLUGIN_NAME = "display_escpos"
|
|
15
|
+
DRIVER_CLASS = DriverDisplayEscPos
|
|
16
|
+
|
|
17
|
+
DRIVER_ROUTES_GROUPS = [driver_routes_odoo, driver_routes_website]
|
odoo_driver_plugin_display_escpos-1.0.4/odoo_driver_plugin_display_escpos/display_escpos_driver.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
from flask_babel import gettext as _
|
|
2
|
+
from loguru import logger
|
|
3
|
+
from odoo_driver.drivers.driver_abstract_with_queue import DriverAbstractWithQueue
|
|
4
|
+
from odoo_driver.tools.tools_serial_code import FF, US
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DriverDisplayEscPos(DriverAbstractWithQueue):
|
|
8
|
+
_use_serial = True
|
|
9
|
+
_driver_function = "display"
|
|
10
|
+
_plugin_name = "display_escpos"
|
|
11
|
+
_default_max_queue_size = 3
|
|
12
|
+
_default_delay = 1
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def name(self):
|
|
16
|
+
return _("LCD Display")
|
|
17
|
+
|
|
18
|
+
@logger.catch
|
|
19
|
+
def configure(self):
|
|
20
|
+
with self._get_serial() as serial:
|
|
21
|
+
# Note: letter looks to be encoded in ISO-8859-1
|
|
22
|
+
# C:43 ; D: 44; T:54 ; U:55 ...
|
|
23
|
+
|
|
24
|
+
logger.debug(
|
|
25
|
+
"Display: Set Mode: ESC/POS with the message STX ENQ 05 C 31 ETX ..."
|
|
26
|
+
)
|
|
27
|
+
serial.write(b"\x02\x05\x43\x31\x03")
|
|
28
|
+
|
|
29
|
+
logger.debug(
|
|
30
|
+
"Display: Set Character: France with the message STX ENQ 05 T 01 ETX ..."
|
|
31
|
+
)
|
|
32
|
+
serial.write(b"\x02\x05\x54\x01\x03")
|
|
33
|
+
|
|
34
|
+
logger.debug(
|
|
35
|
+
"Display: Set Encoding: CP858 with the message STX ENQ 05 U 13 ETX ..."
|
|
36
|
+
)
|
|
37
|
+
serial.write(b"\x02\x05\x55\x13\x03")
|
|
38
|
+
|
|
39
|
+
@logger.catch
|
|
40
|
+
def _process_task_serial(self, serial, task):
|
|
41
|
+
lines_ascii = self._convert_task_to_ascii_lines(task)
|
|
42
|
+
|
|
43
|
+
self._clear_screen(serial)
|
|
44
|
+
|
|
45
|
+
for row, text in enumerate(lines_ascii, 1):
|
|
46
|
+
self._position_cursor(serial, 1, row)
|
|
47
|
+
self._send_message(serial, [text])
|
|
48
|
+
|
|
49
|
+
def _convert_task_to_ascii_lines(self, task):
|
|
50
|
+
lines_ascii = []
|
|
51
|
+
|
|
52
|
+
action = task.data["action"]
|
|
53
|
+
|
|
54
|
+
if action == "display_lines":
|
|
55
|
+
lines_ascii = task.data.get("text_lines", [])
|
|
56
|
+
if type(lines_ascii) is str:
|
|
57
|
+
lines_ascii = lines_ascii.split("\n")
|
|
58
|
+
|
|
59
|
+
elif action in [
|
|
60
|
+
"add_line",
|
|
61
|
+
"update_quantity",
|
|
62
|
+
"update_unit_price",
|
|
63
|
+
"update_discount",
|
|
64
|
+
]:
|
|
65
|
+
# First Line
|
|
66
|
+
left_part = task.data.get("product_name")
|
|
67
|
+
right_part = self._prepare_percent(task.data.get("discount"))
|
|
68
|
+
line_1 = self._prepare_line(left_part, right_part)
|
|
69
|
+
|
|
70
|
+
# Second Line Line
|
|
71
|
+
left_part = (
|
|
72
|
+
self._prepare_qty(task.data.get("quantity"))
|
|
73
|
+
+ "×"
|
|
74
|
+
+ self._prepare_price(task.data.get("unit_price"))
|
|
75
|
+
)
|
|
76
|
+
right_part = self._prepare_price(task.data.get("total"))
|
|
77
|
+
line_2 = self._prepare_line(left_part, right_part)
|
|
78
|
+
|
|
79
|
+
lines_ascii = [line_1, line_2]
|
|
80
|
+
|
|
81
|
+
elif action == "remove_line":
|
|
82
|
+
lines_ascii = [
|
|
83
|
+
_("Deleting Line ..."),
|
|
84
|
+
self._prepare_line(task.data.get("product_name")),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
elif action == "payment_balance":
|
|
88
|
+
total = task.data.get("total")
|
|
89
|
+
total_paid = task.data.get("total_paid")
|
|
90
|
+
total_due = task.data.get("total_due")
|
|
91
|
+
total_to_pay = total - total_paid
|
|
92
|
+
line_1 = self._prepare_line(_("Total:"), self._prepare_price(total))
|
|
93
|
+
line_2 = ""
|
|
94
|
+
if total_paid != 0.0:
|
|
95
|
+
if total_to_pay > 0:
|
|
96
|
+
line_2 = self._prepare_line(
|
|
97
|
+
_("To Pay:"), self._prepare_price(total_to_pay)
|
|
98
|
+
)
|
|
99
|
+
elif total_due < 0:
|
|
100
|
+
line_2 = self._prepare_line(
|
|
101
|
+
_("Change:"), self._prepare_price(-total_due)
|
|
102
|
+
)
|
|
103
|
+
lines_ascii = [line_1, line_2]
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
raise NotImplementedError(
|
|
107
|
+
f"{action} is not a valid action for the Display device."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return lines_ascii
|
|
111
|
+
|
|
112
|
+
def _clear_screen(self, serial):
|
|
113
|
+
self._send_message(serial, [FF], "Clear Screen")
|
|
114
|
+
|
|
115
|
+
def _position_cursor(self, serial, col=1, row=1):
|
|
116
|
+
self._send_message(
|
|
117
|
+
serial, [US, chr(36), chr(col), chr(row)], f"Move to col {col} / row {row}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def _send_message(self, serial, message_list, action_description=""):
|
|
121
|
+
message_list = [x for x in message_list if x]
|
|
122
|
+
if not message_list:
|
|
123
|
+
return
|
|
124
|
+
message = b"".join([x.encode("cp858", errors="ignore") for x in message_list])
|
|
125
|
+
if action_description:
|
|
126
|
+
logger.debug(
|
|
127
|
+
f"Display: {action_description} with the message {message} ..."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
serial.write(message)
|
|
131
|
+
|
|
132
|
+
def _display_float(self, float_repr, reduce_txt=True, suffix=""):
|
|
133
|
+
if reduce_txt:
|
|
134
|
+
while float_repr:
|
|
135
|
+
if float_repr[-1:] == "0":
|
|
136
|
+
float_repr = float_repr[:-1]
|
|
137
|
+
else:
|
|
138
|
+
break
|
|
139
|
+
if float_repr[-1:] == ".":
|
|
140
|
+
float_repr = float_repr[:-1]
|
|
141
|
+
return float_repr.replace(".", ",") + suffix
|
|
142
|
+
|
|
143
|
+
def _prepare_percent(self, percent):
|
|
144
|
+
try:
|
|
145
|
+
percent = float(percent)
|
|
146
|
+
except ValueError:
|
|
147
|
+
return ""
|
|
148
|
+
except TypeError:
|
|
149
|
+
return ""
|
|
150
|
+
if not percent:
|
|
151
|
+
return ""
|
|
152
|
+
return self._display_float("-{:.1f}".format(percent), suffix="%")
|
|
153
|
+
|
|
154
|
+
def _prepare_qty(self, quantity):
|
|
155
|
+
try:
|
|
156
|
+
quantity = float(quantity)
|
|
157
|
+
except ValueError:
|
|
158
|
+
quantity = 0.0
|
|
159
|
+
return self._display_float("{:.3f}".format(quantity))
|
|
160
|
+
|
|
161
|
+
def _prepare_price(self, price):
|
|
162
|
+
try:
|
|
163
|
+
price = float(price)
|
|
164
|
+
except ValueError:
|
|
165
|
+
price = 0.0
|
|
166
|
+
return self._display_float("{:.2f}".format(price), reduce_txt=False, suffix="€")
|
|
167
|
+
|
|
168
|
+
def _prepare_line(self, left_part, right_part=""):
|
|
169
|
+
result = left_part[:20].ljust(20, " ")
|
|
170
|
+
if right_part:
|
|
171
|
+
right_part = right_part[-20:]
|
|
172
|
+
result = result[0 : (20 - 1 - len(right_part))]
|
|
173
|
+
result += " " + right_part
|
|
174
|
+
|
|
175
|
+
return result
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import simplejson
|
|
2
|
+
from flask import Blueprint, jsonify, request
|
|
3
|
+
from loguru import logger
|
|
4
|
+
from odoo_driver.tools.tools_interface import interface
|
|
5
|
+
|
|
6
|
+
driver_routes_odoo = Blueprint(
|
|
7
|
+
"route_display_escpos_odoo", __name__, template_folder="templates"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@driver_routes_odoo.route("/hw_proxy/display_show", methods=["POST", "PUT"])
|
|
12
|
+
@logger.catch
|
|
13
|
+
def display_show():
|
|
14
|
+
driver = interface.drivers.get("display")
|
|
15
|
+
data = request.json.get("params", {}).get("data", {})
|
|
16
|
+
interface.log_data(data, "display", "DEBUG")
|
|
17
|
+
|
|
18
|
+
if type(data) is str:
|
|
19
|
+
data = simplejson.loads(data)
|
|
20
|
+
|
|
21
|
+
driver.add_task(data)
|
|
22
|
+
return jsonify(jsonrpc="2.0", result=True)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from flask import Blueprint, jsonify, render_template
|
|
2
|
+
from loguru import logger
|
|
3
|
+
from odoo_driver.tools.tools_interface import interface
|
|
4
|
+
|
|
5
|
+
driver_routes_website = Blueprint(
|
|
6
|
+
"route_display_escpos_website", __name__, template_folder="templates"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@driver_routes_website.route("/driver/display_escpos.html")
|
|
11
|
+
@logger.catch
|
|
12
|
+
def driver_display_escpos():
|
|
13
|
+
driver = interface.drivers.get("display")
|
|
14
|
+
return render_template("driver_display_escpos.html.jinja", driver=driver)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@driver_routes_website.route("/display_escpos/configure", methods=["POST", "PUT"])
|
|
18
|
+
@logger.catch
|
|
19
|
+
def display_escpos_configure():
|
|
20
|
+
driver = interface.drivers.get("display")
|
|
21
|
+
driver.configure()
|
|
22
|
+
|
|
23
|
+
return jsonify(jsonrpc="2.0", result=True)
|
|
Binary file
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# French translations for PROJECT.
|
|
2
|
+
# Copyright (C) 2026 ORGANIZATION
|
|
3
|
+
# This file is distributed under the same license as the PROJECT project.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
|
|
5
|
+
#
|
|
6
|
+
msgid ""
|
|
7
|
+
msgstr ""
|
|
8
|
+
"Project-Id-Version: PROJECT VERSION\n"
|
|
9
|
+
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
|
10
|
+
"POT-Creation-Date: 2026-04-24 23:49+0200\n"
|
|
11
|
+
"PO-Revision-Date: 2026-04-24 23:44+0200\n"
|
|
12
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
13
|
+
"Language: fr\n"
|
|
14
|
+
"Language-Team: fr <LL@li.org>\n"
|
|
15
|
+
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
|
16
|
+
"MIME-Version: 1.0\n"
|
|
17
|
+
"Content-Type: text/plain; charset=utf-8\n"
|
|
18
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
19
|
+
"Generated-By: Babel 2.18.0\n"
|
|
20
|
+
|
|
21
|
+
#: display_escpos_driver.py:15
|
|
22
|
+
msgid "LCD Display"
|
|
23
|
+
msgstr "Afficheur LCD"
|
|
24
|
+
|
|
25
|
+
#: display_escpos_driver.py:82
|
|
26
|
+
msgid "Deleting Line ..."
|
|
27
|
+
msgstr "Supprimer une ligne ..."
|
|
28
|
+
|
|
29
|
+
#: display_escpos_driver.py:91
|
|
30
|
+
msgid "Total:"
|
|
31
|
+
msgstr "Total :"
|
|
32
|
+
|
|
33
|
+
#: display_escpos_driver.py:96
|
|
34
|
+
msgid "To Pay:"
|
|
35
|
+
msgstr "A payer :"
|
|
36
|
+
|
|
37
|
+
#: display_escpos_driver.py:100
|
|
38
|
+
msgid "Change:"
|
|
39
|
+
msgstr "Rendu :"
|
|
40
|
+
|
|
41
|
+
#: templates/driver_display_escpos.html.jinja:7
|
|
42
|
+
msgid "Test communication with LCD Display"
|
|
43
|
+
msgstr "Test de communication avec l'afficheur LCD"
|
|
44
|
+
|
|
45
|
+
#: templates/driver_display_escpos.html.jinja:8
|
|
46
|
+
msgid "Configure"
|
|
47
|
+
msgstr "Configurer"
|
|
48
|
+
|
|
49
|
+
#: templates/driver_display_escpos.html.jinja:10
|
|
50
|
+
msgid "Set Mode"
|
|
51
|
+
msgstr "Définir le mode"
|
|
52
|
+
|
|
53
|
+
#: templates/driver_display_escpos.html.jinja:14
|
|
54
|
+
msgid "Set Character"
|
|
55
|
+
msgstr "Définir les charactères"
|
|
56
|
+
|
|
57
|
+
#: templates/driver_display_escpos.html.jinja:18
|
|
58
|
+
msgid "Set Encoding"
|
|
59
|
+
msgstr "Définir l'encodage"
|
|
60
|
+
|
|
61
|
+
#: templates/driver_display_escpos.html.jinja:22
|
|
62
|
+
msgid "Configure Display"
|
|
63
|
+
msgstr "Configurer l'afficheur"
|
|
64
|
+
|
|
65
|
+
#: templates/driver_display_escpos.html.jinja:24
|
|
66
|
+
msgid "Send Text"
|
|
67
|
+
msgstr "Envoyer le texte"
|
|
68
|
+
|
|
69
|
+
#: templates/driver_display_escpos.html.jinja:26
|
|
70
|
+
msgid "Line 1"
|
|
71
|
+
msgstr "Ligne 1"
|
|
72
|
+
|
|
73
|
+
#: templates/driver_display_escpos.html.jinja:30
|
|
74
|
+
msgid "Line 2"
|
|
75
|
+
msgstr "Ligne 2"
|
|
76
|
+
|
|
77
|
+
#: templates/driver_display_escpos.html.jinja:34
|
|
78
|
+
msgid "Send text to display"
|
|
79
|
+
msgstr "Envoyer le texte à l'afficheur"
|
|
80
|
+
|
|
81
|
+
#: templates/driver_display_escpos.html.jinja:36
|
|
82
|
+
msgid "Send Product Information"
|
|
83
|
+
msgstr "Envoyer les informations produits"
|
|
84
|
+
|
|
85
|
+
#: templates/driver_display_escpos.html.jinja:38
|
|
86
|
+
msgid "Product Name"
|
|
87
|
+
msgstr "Nom du produit"
|
|
88
|
+
|
|
89
|
+
#: templates/driver_display_escpos.html.jinja:42
|
|
90
|
+
msgid "Quantity"
|
|
91
|
+
msgstr "Quantité"
|
|
92
|
+
|
|
93
|
+
#: templates/driver_display_escpos.html.jinja:44
|
|
94
|
+
msgid "Unit Price"
|
|
95
|
+
msgstr "Prix unitaire"
|
|
96
|
+
|
|
97
|
+
#: templates/driver_display_escpos.html.jinja:49
|
|
98
|
+
msgid "Discount"
|
|
99
|
+
msgstr "Remise"
|
|
100
|
+
|
|
101
|
+
#: templates/driver_display_escpos.html.jinja:52
|
|
102
|
+
msgid "Total"
|
|
103
|
+
msgstr "Total"
|
|
104
|
+
|
|
105
|
+
#: templates/driver_display_escpos.html.jinja:57
|
|
106
|
+
msgid "Action"
|
|
107
|
+
msgstr "Action"
|
|
108
|
+
|
|
109
|
+
#: templates/driver_display_escpos.html.jinja:59
|
|
110
|
+
msgid "Add Line"
|
|
111
|
+
msgstr "Ajouter une ligne"
|
|
112
|
+
|
|
113
|
+
#: templates/driver_display_escpos.html.jinja:60
|
|
114
|
+
msgid "Remove Line"
|
|
115
|
+
msgstr "Supprimer une ligne"
|
|
116
|
+
|
|
117
|
+
#: templates/driver_display_escpos.html.jinja:64
|
|
118
|
+
msgid "Send product information to display"
|
|
119
|
+
msgstr "Envoyer les informations produits à l'afficheur"
|
|
120
|
+
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Translations template for PROJECT.
|
|
2
|
+
# Copyright (C) 2026 ORGANIZATION
|
|
3
|
+
# This file is distributed under the same license as the PROJECT project.
|
|
4
|
+
# FIRST AUTHOR <EMAIL@ADDRESS>, 2026.
|
|
5
|
+
#
|
|
6
|
+
#, fuzzy
|
|
7
|
+
msgid ""
|
|
8
|
+
msgstr ""
|
|
9
|
+
"Project-Id-Version: PROJECT VERSION\n"
|
|
10
|
+
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
|
11
|
+
"POT-Creation-Date: 2026-04-24 23:49+0200\n"
|
|
12
|
+
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
13
|
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
14
|
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
15
|
+
"MIME-Version: 1.0\n"
|
|
16
|
+
"Content-Type: text/plain; charset=utf-8\n"
|
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
18
|
+
"Generated-By: Babel 2.18.0\n"
|
|
19
|
+
|
|
20
|
+
#: display_escpos_driver.py:15
|
|
21
|
+
msgid "LCD Display"
|
|
22
|
+
msgstr ""
|
|
23
|
+
|
|
24
|
+
#: display_escpos_driver.py:82
|
|
25
|
+
msgid "Deleting Line ..."
|
|
26
|
+
msgstr ""
|
|
27
|
+
|
|
28
|
+
#: display_escpos_driver.py:91
|
|
29
|
+
msgid "Total:"
|
|
30
|
+
msgstr ""
|
|
31
|
+
|
|
32
|
+
#: display_escpos_driver.py:96
|
|
33
|
+
msgid "To Pay:"
|
|
34
|
+
msgstr ""
|
|
35
|
+
|
|
36
|
+
#: display_escpos_driver.py:100
|
|
37
|
+
msgid "Change:"
|
|
38
|
+
msgstr ""
|
|
39
|
+
|
|
40
|
+
#: templates/driver_display_escpos.html.jinja:7
|
|
41
|
+
msgid "Test communication with LCD Display"
|
|
42
|
+
msgstr ""
|
|
43
|
+
|
|
44
|
+
#: templates/driver_display_escpos.html.jinja:8
|
|
45
|
+
msgid "Configure"
|
|
46
|
+
msgstr ""
|
|
47
|
+
|
|
48
|
+
#: templates/driver_display_escpos.html.jinja:10
|
|
49
|
+
msgid "Set Mode"
|
|
50
|
+
msgstr ""
|
|
51
|
+
|
|
52
|
+
#: templates/driver_display_escpos.html.jinja:14
|
|
53
|
+
msgid "Set Character"
|
|
54
|
+
msgstr ""
|
|
55
|
+
|
|
56
|
+
#: templates/driver_display_escpos.html.jinja:18
|
|
57
|
+
msgid "Set Encoding"
|
|
58
|
+
msgstr ""
|
|
59
|
+
|
|
60
|
+
#: templates/driver_display_escpos.html.jinja:22
|
|
61
|
+
msgid "Configure Display"
|
|
62
|
+
msgstr ""
|
|
63
|
+
|
|
64
|
+
#: templates/driver_display_escpos.html.jinja:24
|
|
65
|
+
msgid "Send Text"
|
|
66
|
+
msgstr ""
|
|
67
|
+
|
|
68
|
+
#: templates/driver_display_escpos.html.jinja:26
|
|
69
|
+
msgid "Line 1"
|
|
70
|
+
msgstr ""
|
|
71
|
+
|
|
72
|
+
#: templates/driver_display_escpos.html.jinja:30
|
|
73
|
+
msgid "Line 2"
|
|
74
|
+
msgstr ""
|
|
75
|
+
|
|
76
|
+
#: templates/driver_display_escpos.html.jinja:34
|
|
77
|
+
msgid "Send text to display"
|
|
78
|
+
msgstr ""
|
|
79
|
+
|
|
80
|
+
#: templates/driver_display_escpos.html.jinja:36
|
|
81
|
+
msgid "Send Product Information"
|
|
82
|
+
msgstr ""
|
|
83
|
+
|
|
84
|
+
#: templates/driver_display_escpos.html.jinja:38
|
|
85
|
+
msgid "Product Name"
|
|
86
|
+
msgstr ""
|
|
87
|
+
|
|
88
|
+
#: templates/driver_display_escpos.html.jinja:42
|
|
89
|
+
msgid "Quantity"
|
|
90
|
+
msgstr ""
|
|
91
|
+
|
|
92
|
+
#: templates/driver_display_escpos.html.jinja:44
|
|
93
|
+
msgid "Unit Price"
|
|
94
|
+
msgstr ""
|
|
95
|
+
|
|
96
|
+
#: templates/driver_display_escpos.html.jinja:49
|
|
97
|
+
msgid "Discount"
|
|
98
|
+
msgstr ""
|
|
99
|
+
|
|
100
|
+
#: templates/driver_display_escpos.html.jinja:52
|
|
101
|
+
msgid "Total"
|
|
102
|
+
msgstr ""
|
|
103
|
+
|
|
104
|
+
#: templates/driver_display_escpos.html.jinja:57
|
|
105
|
+
msgid "Action"
|
|
106
|
+
msgstr ""
|
|
107
|
+
|
|
108
|
+
#: templates/driver_display_escpos.html.jinja:59
|
|
109
|
+
msgid "Add Line"
|
|
110
|
+
msgstr ""
|
|
111
|
+
|
|
112
|
+
#: templates/driver_display_escpos.html.jinja:60
|
|
113
|
+
msgid "Remove Line"
|
|
114
|
+
msgstr ""
|
|
115
|
+
|
|
116
|
+
#: templates/driver_display_escpos.html.jinja:64
|
|
117
|
+
msgid "Send product information to display"
|
|
118
|
+
msgstr ""
|
|
119
|
+
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
|
|
3
|
+
{% extends "layout.html.jinja" %}
|
|
4
|
+
|
|
5
|
+
{% block content %}
|
|
6
|
+
|
|
7
|
+
<h1>{{_('Test communication with LCD Display')}}</h1>
|
|
8
|
+
<h2>{{_('Configure')}}</h2>
|
|
9
|
+
<div class="input-group">
|
|
10
|
+
<span class="input-group-text">{{_('Set Mode')}}</span>
|
|
11
|
+
<input type="text" class="form-control" value="ESC/POS" readonly/>
|
|
12
|
+
</div>
|
|
13
|
+
<div class="input-group">
|
|
14
|
+
<span class="input-group-text">{{_('Set Character')}}</span>
|
|
15
|
+
<input type="text" class="form-control" value="France" readonly/>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="input-group">
|
|
18
|
+
<span class="input-group-text">{{_('Set Encoding')}}</span>
|
|
19
|
+
<input type="text" class="form-control" value="CP858" readonly/>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="input-group">
|
|
22
|
+
<button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_configure(); return false;">{{_('Configure Display')}}</button>
|
|
23
|
+
</div>
|
|
24
|
+
<h2>{{_('Send Text')}}</h2>
|
|
25
|
+
<div class="input-group">
|
|
26
|
+
<span class="input-group-text">{{_('Line 1')}}</span>
|
|
27
|
+
<input type="text" class="form-control" id="display_line_1">
|
|
28
|
+
</div>
|
|
29
|
+
<div class="input-group">
|
|
30
|
+
<span class="input-group-text">{{_('Line 2')}}</span>
|
|
31
|
+
<input type="text" class="form-control" id="display_line_2">
|
|
32
|
+
</div>
|
|
33
|
+
<div class="input-group">
|
|
34
|
+
<button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_text(); return false;">{{_('Send text to display')}}</button>
|
|
35
|
+
</div>
|
|
36
|
+
<h2>{{_('Send Product Information')}}</h2>
|
|
37
|
+
<div class="input-group">
|
|
38
|
+
<span class="input-group-text">{{_('Product Name')}}</span>
|
|
39
|
+
<input type="text" class="form-control" id="display_product_name" />
|
|
40
|
+
</div>
|
|
41
|
+
<div class="input-group">
|
|
42
|
+
<span class="input-group-text">{{_('Quantity')}}</span>
|
|
43
|
+
<input type="text" class="form-control" id="display_quantity" />
|
|
44
|
+
<span class="input-group-text">{{_('Unit Price')}}</span>
|
|
45
|
+
<input type="text" class="form-control" id="display_unit_price" />
|
|
46
|
+
<span class="input-group-text">€</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="input-group">
|
|
49
|
+
<span class="input-group-text">{{_('Discount')}}</span>
|
|
50
|
+
<input type="text" class="form-control" id="display_discount" />
|
|
51
|
+
<span class="input-group-text">%</span>
|
|
52
|
+
<span class="input-group-text">{{_('Total')}}</span>
|
|
53
|
+
<input type="text" class="form-control" id="display_total" />
|
|
54
|
+
<span class="input-group-text">€</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="input-group">
|
|
57
|
+
<span class="input-group-text">{{_('Action')}}</span>
|
|
58
|
+
<select type="select" class="form-control" id="display_action">
|
|
59
|
+
<option value="add_line" selected>{{_('Add Line')}}</option>
|
|
60
|
+
<option value="remove_line">{{_('Remove Line')}}</option>
|
|
61
|
+
</select>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="input-group">
|
|
64
|
+
<button type="button" class="btn {%if driver.is_connected %}btn-primary{% else %}btn-secondary{% endif %}" onclick="test_display_product(); return false;">{{_('Send product information to display')}}</button>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
|
|
69
|
+
/* ***************************
|
|
70
|
+
Device Display
|
|
71
|
+
******************************/
|
|
72
|
+
$( document ).ready(function() {
|
|
73
|
+
$("#display_line_1").focus();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function test_display_configure() {
|
|
78
|
+
_call_ajax("/display_escpos/configure")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
function test_display_text() {
|
|
83
|
+
const data = {
|
|
84
|
+
params: {
|
|
85
|
+
data: {
|
|
86
|
+
text_lines:
|
|
87
|
+
[
|
|
88
|
+
$("#display_line_1").val(),
|
|
89
|
+
$("#display_line_2").val(),
|
|
90
|
+
],
|
|
91
|
+
action: "display_lines",
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
_call_ajax("/hw_proxy/display_show", "display", data)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function test_display_product() {
|
|
99
|
+
const data = {
|
|
100
|
+
params: {
|
|
101
|
+
data: {
|
|
102
|
+
product_name: $("#display_product_name").val(),
|
|
103
|
+
quantity: $("#display_quantity").val(),
|
|
104
|
+
discount: $("#display_discount").val(),
|
|
105
|
+
unit_price: $("#display_unit_price").val(),
|
|
106
|
+
total: $("#display_total").val(),
|
|
107
|
+
action: $("#display_action").val(),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
_call_ajax("/hw_proxy/display_show", "display", data)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
{% endblock %}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "odoo-driver-plugin-display-escpos"
|
|
3
|
+
version = "1.0.4"
|
|
4
|
+
description = "Plugin for odoo-driver. Support escpos display devices (LCD)."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
|
|
7
|
+
]
|
|
8
|
+
maintainers = [
|
|
9
|
+
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
|
|
10
|
+
]
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "AGPLv3+"
|
|
13
|
+
keywords = ["Odoo", "Point Of Sale", "Driver", "Device", "Display", "LCD", "ESC/POS", "escpos", "Aures ®", "Bixolon ®", "Oxhoo ®"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Operating System :: Unix",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Framework :: Odoo",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
requires-python = ">=3.9,<4.0"
|
|
29
|
+
dependencies = [
|
|
30
|
+
"odoo-driver",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
repository = "https://gitlab.com/grap-rhone-alpes/odoo-driver-plugin-display-escpos"
|
|
36
|
+
|
|
37
|
+
[project.entry-points.'odoo_driver.plugins']
|
|
38
|
+
display_escpos = 'odoo_driver_plugin_display_escpos'
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["poetry-core"]
|
|
42
|
+
build-backend = "poetry.core.masonry.api"
|
|
43
|
+
|
|
44
|
+
[tool.black]
|
|
45
|
+
line-length = 88
|
|
46
|
+
|
|
47
|
+
[tool.isort]
|
|
48
|
+
profile = "black"
|
|
49
|
+
line_length = 88
|