odoo-addon-base-report-to-printer 18.0.1.1.6__py3-none-any.whl

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.
Files changed (77) hide show
  1. odoo/addons/base_report_to_printer/README.rst +205 -0
  2. odoo/addons/base_report_to_printer/__init__.py +9 -0
  3. odoo/addons/base_report_to_printer/__manifest__.py +39 -0
  4. odoo/addons/base_report_to_printer/data/neutralize.sql +2 -0
  5. odoo/addons/base_report_to_printer/data/printing_data.xml +27 -0
  6. odoo/addons/base_report_to_printer/i18n/am.po +936 -0
  7. odoo/addons/base_report_to_printer/i18n/base_report_to_printer.pot +932 -0
  8. odoo/addons/base_report_to_printer/i18n/bg.po +939 -0
  9. odoo/addons/base_report_to_printer/i18n/ca.po +936 -0
  10. odoo/addons/base_report_to_printer/i18n/de.po +994 -0
  11. odoo/addons/base_report_to_printer/i18n/el_GR.po +937 -0
  12. odoo/addons/base_report_to_printer/i18n/es.po +1004 -0
  13. odoo/addons/base_report_to_printer/i18n/es_AR.po +987 -0
  14. odoo/addons/base_report_to_printer/i18n/es_ES.po +937 -0
  15. odoo/addons/base_report_to_printer/i18n/fi.po +939 -0
  16. odoo/addons/base_report_to_printer/i18n/fr.po +999 -0
  17. odoo/addons/base_report_to_printer/i18n/gl.po +936 -0
  18. odoo/addons/base_report_to_printer/i18n/hr.po +966 -0
  19. odoo/addons/base_report_to_printer/i18n/hr_HR.po +941 -0
  20. odoo/addons/base_report_to_printer/i18n/it.po +985 -0
  21. odoo/addons/base_report_to_printer/i18n/nl.po +939 -0
  22. odoo/addons/base_report_to_printer/i18n/nl_NL.po +950 -0
  23. odoo/addons/base_report_to_printer/i18n/pt.po +936 -0
  24. odoo/addons/base_report_to_printer/i18n/pt_BR.po +940 -0
  25. odoo/addons/base_report_to_printer/i18n/pt_PT.po +937 -0
  26. odoo/addons/base_report_to_printer/i18n/sl.po +943 -0
  27. odoo/addons/base_report_to_printer/i18n/sv.po +983 -0
  28. odoo/addons/base_report_to_printer/i18n/tr.po +936 -0
  29. odoo/addons/base_report_to_printer/i18n/zh_CN.po +958 -0
  30. odoo/addons/base_report_to_printer/models/__init__.py +8 -0
  31. odoo/addons/base_report_to_printer/models/ir_actions_report.py +253 -0
  32. odoo/addons/base_report_to_printer/models/printing_action.py +26 -0
  33. odoo/addons/base_report_to_printer/models/printing_job.py +131 -0
  34. odoo/addons/base_report_to_printer/models/printing_printer.py +268 -0
  35. odoo/addons/base_report_to_printer/models/printing_report_xml_action.py +48 -0
  36. odoo/addons/base_report_to_printer/models/printing_server.py +275 -0
  37. odoo/addons/base_report_to_printer/models/printing_tray.py +21 -0
  38. odoo/addons/base_report_to_printer/models/res_users.py +55 -0
  39. odoo/addons/base_report_to_printer/readme/CONFIGURE.md +13 -0
  40. odoo/addons/base_report_to_printer/readme/CONTRIBUTORS.md +18 -0
  41. odoo/addons/base_report_to_printer/readme/CREDITS.md +1 -0
  42. odoo/addons/base_report_to_printer/readme/DESCRIPTION.md +27 -0
  43. odoo/addons/base_report_to_printer/readme/HISTORY.md +7 -0
  44. odoo/addons/base_report_to_printer/readme/INSTALL.md +10 -0
  45. odoo/addons/base_report_to_printer/readme/ROADMAP.md +3 -0
  46. odoo/addons/base_report_to_printer/readme/USAGE.md +15 -0
  47. odoo/addons/base_report_to_printer/security/ir.model.access.csv +2 -0
  48. odoo/addons/base_report_to_printer/security/security.xml +151 -0
  49. odoo/addons/base_report_to_printer/static/description/icon.png +0 -0
  50. odoo/addons/base_report_to_printer/static/description/index.html +561 -0
  51. odoo/addons/base_report_to_printer/static/src/js/qweb_action_manager.esm.js +92 -0
  52. odoo/addons/base_report_to_printer/tests/__init__.py +13 -0
  53. odoo/addons/base_report_to_printer/tests/test_ir_actions_report.py +350 -0
  54. odoo/addons/base_report_to_printer/tests/test_printing_job.py +70 -0
  55. odoo/addons/base_report_to_printer/tests/test_printing_printer.py +198 -0
  56. odoo/addons/base_report_to_printer/tests/test_printing_printer_tray.py +256 -0
  57. odoo/addons/base_report_to_printer/tests/test_printing_printer_wizard.py +94 -0
  58. odoo/addons/base_report_to_printer/tests/test_printing_report_xml_action.py +98 -0
  59. odoo/addons/base_report_to_printer/tests/test_printing_server.py +219 -0
  60. odoo/addons/base_report_to_printer/tests/test_printing_tray.py +49 -0
  61. odoo/addons/base_report_to_printer/tests/test_report.py +226 -0
  62. odoo/addons/base_report_to_printer/tests/test_res_users.py +53 -0
  63. odoo/addons/base_report_to_printer/views/ir_actions_report.xml +21 -0
  64. odoo/addons/base_report_to_printer/views/printing_job.xml +46 -0
  65. odoo/addons/base_report_to_printer/views/printing_printer.xml +147 -0
  66. odoo/addons/base_report_to_printer/views/printing_report.xml +39 -0
  67. odoo/addons/base_report_to_printer/views/printing_server.xml +79 -0
  68. odoo/addons/base_report_to_printer/views/res_users.xml +33 -0
  69. odoo/addons/base_report_to_printer/wizards/__init__.py +2 -0
  70. odoo/addons/base_report_to_printer/wizards/print_attachment_report.py +80 -0
  71. odoo/addons/base_report_to_printer/wizards/print_attachment_report.xml +56 -0
  72. odoo/addons/base_report_to_printer/wizards/printing_printer_update_wizard.py +27 -0
  73. odoo/addons/base_report_to_printer/wizards/printing_printer_update_wizard_view.xml +37 -0
  74. odoo_addon_base_report_to_printer-18.0.1.1.6.dist-info/METADATA +222 -0
  75. odoo_addon_base_report_to_printer-18.0.1.1.6.dist-info/RECORD +77 -0
  76. odoo_addon_base_report_to_printer-18.0.1.1.6.dist-info/WHEEL +5 -0
  77. odoo_addon_base_report_to_printer-18.0.1.1.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,256 @@
1
+ # Copyright 2016 LasLabs Inc.
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ import errno
5
+ import tempfile
6
+ from unittest import mock
7
+
8
+ from odoo.tests.common import TransactionCase
9
+
10
+ model = "odoo.addons.base_report_to_printer.models.printing_printer"
11
+ server_model = "odoo.addons.base_report_to_printer.models.printing_server"
12
+
13
+ ppd_header = '*PPD-Adobe: "4.3"'
14
+ ppd_input_slot_header = """
15
+ *OpenUI *InputSlot: PickOne
16
+ *DefaultInputSlot: Auto
17
+ *InputSlot Auto/Auto (Default): "
18
+ << /DeferredMediaSelection true /ManualFeed false
19
+ /MediaPosition null /MediaType null >> setpagedevice
20
+ userdict /TSBMediaType 0 put"
21
+ *End
22
+ """
23
+ ppd_input_slot_body = """
24
+ *InputSlot {name}/{text}: "
25
+ << /DeferredMediaSelection true /ManualFeed false
26
+ /MediaPosition null /MediaType null >> setpagedevice
27
+ userdict /TSBMediaType 0 put"
28
+ *End
29
+ """
30
+ ppd_input_slot_footer = """
31
+ *CloseUI: *InputSlot
32
+ """
33
+
34
+
35
+ class TestPrintingPrinter(TransactionCase):
36
+ def setUp(self):
37
+ super().setUp()
38
+ self.Model = self.env["printing.printer"]
39
+ self.ServerModel = self.env["printing.server"]
40
+ self.server = self.env["printing.server"].create({})
41
+ self.printer = self.env["printing.printer"].create(
42
+ {
43
+ "name": "",
44
+ "server_id": self.server.id,
45
+ "system_name": "Sys Name",
46
+ "default": True,
47
+ "status": "unknown",
48
+ "status_message": "Msg",
49
+ "model": "res.users",
50
+ "location": "Location",
51
+ "uri": "URI",
52
+ }
53
+ )
54
+ self.tray_vals = {
55
+ "name": "Tray",
56
+ "system_name": "TrayName",
57
+ "printer_id": self.printer.id,
58
+ }
59
+
60
+ def new_tray(self, vals=None):
61
+ values = self.tray_vals
62
+ if vals is not None:
63
+ values.update(vals)
64
+ return self.env["printing.tray"].create(values)
65
+
66
+ def build_ppd(self, input_slots=None):
67
+ """
68
+ Builds a fake PPD file declaring defined input slots
69
+ """
70
+ ppd_contents = ppd_header
71
+ ppd_contents += ppd_input_slot_header
72
+ if input_slots is not None:
73
+ for input_slot in input_slots:
74
+ ppd_contents += ppd_input_slot_body.format(
75
+ name=input_slot["name"], text=input_slot["text"]
76
+ )
77
+ ppd_contents += ppd_input_slot_footer
78
+
79
+ return ppd_contents
80
+
81
+ def mock_cups_ppd(self, cups, file_name=None, input_slots=None):
82
+ """
83
+ Create a fake PPD file (if needed), then mock the getPPD3 method
84
+ return value to give that file
85
+ """
86
+ if file_name is None:
87
+ fd, file_name = tempfile.mkstemp()
88
+
89
+ if file_name:
90
+ ppd_contents = self.build_ppd(input_slots=input_slots)
91
+ with open(file_name, "w") as fp:
92
+ fp.write(ppd_contents)
93
+
94
+ cups.Connection().getPPD3.return_value = (200, 0, file_name)
95
+ cups.Connection().getPrinters.return_value = {
96
+ self.printer.system_name: {
97
+ "printer-info": "info",
98
+ "printer-uri-supported": "uri",
99
+ }
100
+ }
101
+
102
+ @mock.patch(f"{server_model}.cups")
103
+ def test_update_printers(self, cups):
104
+ """
105
+ Check that the update_printers method calls _prepare_update_from_cups
106
+ """
107
+ self.mock_cups_ppd(cups, file_name=False)
108
+ self.ServerModel.update_printers()
109
+ self.assertEqual(self.printer.name, "info")
110
+ self.printer.name = "My custom name"
111
+ self.ServerModel.update_printers()
112
+ self.assertEqual(self.printer.name, "My custom name")
113
+
114
+ @mock.patch(f"{server_model}.cups")
115
+ def test_prepare_update_from_cups_no_ppd(self, cups):
116
+ """
117
+ Check that the tray_ids field has no value when no PPD is available
118
+ """
119
+ self.mock_cups_ppd(cups, file_name=False)
120
+
121
+ connection = cups.Connection()
122
+ cups_printer = connection.getPrinters()[self.printer.system_name]
123
+
124
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
125
+ self.assertFalse("tray_ids" in vals)
126
+
127
+ @mock.patch(f"{server_model}.cups")
128
+ def test_prepare_update_from_cups_empty_ppd(self, cups):
129
+ """
130
+ Check that the tray_ids field has no value when the PPD file has
131
+ no input slot declared
132
+ """
133
+ fd, file_name = tempfile.mkstemp()
134
+ self.mock_cups_ppd(cups, file_name=file_name)
135
+ # Replace the ppd file's contents by an empty file
136
+ with open(file_name, "w") as fp:
137
+ fp.write(ppd_header)
138
+
139
+ connection = cups.Connection()
140
+ cups_printer = connection.getPrinters()[self.printer.system_name]
141
+
142
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
143
+ self.assertFalse("tray_ids" in vals)
144
+
145
+ @mock.patch(f"{server_model}.cups")
146
+ @mock.patch("os.unlink")
147
+ def test_prepare_update_from_cups_unlink_error(self, os_unlink, cups):
148
+ """
149
+ When OSError other than ENOENT is encountered, the exception is raised
150
+ """
151
+ # Break os.unlink
152
+ os_unlink.side_effect = OSError(errno.EIO, "Error")
153
+
154
+ self.mock_cups_ppd(cups)
155
+
156
+ connection = cups.Connection()
157
+ cups_printer = connection.getPrinters()[self.printer.system_name]
158
+
159
+ with self.assertRaises(OSError):
160
+ self.printer._prepare_update_from_cups(connection, cups_printer)
161
+
162
+ @mock.patch(f"{server_model}.cups")
163
+ @mock.patch("os.unlink")
164
+ def test_prepare_update_from_cups_unlink_error_enoent(self, os_unlink, cups):
165
+ """
166
+ When a ENOENT error is encountered, the file has already been unlinked
167
+ This is not an issue, as we were trying to delete the file.
168
+ The update can continue.
169
+ """
170
+ # Break os.unlink
171
+ os_unlink.side_effect = OSError(errno.ENOENT, "Error")
172
+
173
+ self.mock_cups_ppd(cups)
174
+
175
+ connection = cups.Connection()
176
+ cups_printer = connection.getPrinters()[self.printer.system_name]
177
+
178
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
179
+ self.assertEqual(
180
+ vals["tray_ids"],
181
+ [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
182
+ )
183
+
184
+ @mock.patch(f"{server_model}.cups")
185
+ def test_prepare_update_from_cups(self, cups):
186
+ """
187
+ Check the return value when adding a single tray
188
+ """
189
+ self.mock_cups_ppd(cups)
190
+
191
+ connection = cups.Connection()
192
+ cups_printer = connection.getPrinters()[self.printer.system_name]
193
+
194
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
195
+ self.assertEqual(
196
+ vals["tray_ids"],
197
+ [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
198
+ )
199
+
200
+ @mock.patch(f"{server_model}.cups")
201
+ def test_prepare_update_from_cups_with_multiple_trays(self, cups):
202
+ """
203
+ Check the return value when adding multiple trays at once
204
+ """
205
+ self.mock_cups_ppd(cups, input_slots=[{"name": "Tray1", "text": "Tray 1"}])
206
+
207
+ connection = cups.Connection()
208
+ cups_printer = connection.getPrinters()[self.printer.system_name]
209
+
210
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
211
+ self.assertItemsEqual(
212
+ vals["tray_ids"],
213
+ [
214
+ (0, 0, {"name": "Auto (Default)", "system_name": "Auto"}),
215
+ (0, 0, {"name": "Tray 1", "system_name": "Tray1"}),
216
+ ],
217
+ )
218
+
219
+ @mock.patch(f"{server_model}.cups")
220
+ def test_prepare_update_from_cups_already_known_trays(self, cups):
221
+ """
222
+ Check that calling the method twice doesn't create the trays multiple
223
+ times
224
+ """
225
+ self.mock_cups_ppd(cups, input_slots=[{"name": "Tray1", "text": "Tray 1"}])
226
+
227
+ connection = cups.Connection()
228
+ cups_printer = connection.getPrinters()[self.printer.system_name]
229
+
230
+ # Create a tray which is in the PPD file
231
+ self.new_tray({"system_name": "Tray1"})
232
+
233
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
234
+ self.assertEqual(
235
+ vals["tray_ids"],
236
+ [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"})],
237
+ )
238
+
239
+ @mock.patch(f"{server_model}.cups")
240
+ def test_prepare_update_from_cups_unknown_trays(self, cups):
241
+ """
242
+ Check that trays which are not in the PPD file are removed from Odoo
243
+ """
244
+ self.mock_cups_ppd(cups)
245
+
246
+ connection = cups.Connection()
247
+ cups_printer = connection.getPrinters()[self.printer.system_name]
248
+
249
+ # Create a tray which is absent from the PPD file
250
+ tray = self.new_tray()
251
+
252
+ vals = self.printer._prepare_update_from_cups(connection, cups_printer)
253
+ self.assertEqual(
254
+ vals["tray_ids"],
255
+ [(0, 0, {"name": "Auto (Default)", "system_name": "Auto"}), (2, tray.id)],
256
+ )
@@ -0,0 +1,94 @@
1
+ # Copyright 2016 LasLabs Inc.
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ import logging
5
+ from unittest import mock
6
+
7
+ from odoo.exceptions import UserError
8
+ from odoo.tests.common import TransactionCase
9
+
10
+ model = "odoo.addons.base_report_to_printer.models.printing_server"
11
+
12
+
13
+ class StopTest(Exception):
14
+ pass
15
+
16
+
17
+ class TestPrintingPrinterWizard(TransactionCase):
18
+ def setUp(self):
19
+ super().setUp()
20
+ self.Model = self.env["printing.printer.update.wizard"]
21
+ self.server = self.env["printing.server"].create({})
22
+ self.printer_vals = {
23
+ "printer-info": "Info",
24
+ "printer-make-and-model": "Make and Model",
25
+ "printer-location": "location",
26
+ "device-uri": "URI",
27
+ "printer-uri-supported": "uri",
28
+ }
29
+
30
+ def _record_vals(self, sys_name="sys_name"):
31
+ return {
32
+ "name": self.printer_vals["printer-info"],
33
+ "server_id": self.server.id,
34
+ "system_name": sys_name,
35
+ "model": self.printer_vals["printer-make-and-model"],
36
+ "location": self.printer_vals["printer-location"],
37
+ "uri": self.printer_vals["device-uri"],
38
+ }
39
+
40
+ @mock.patch(f"{model}.cups")
41
+ def test_action_ok_inits_connection(self, cups):
42
+ """It should initialize CUPS connection"""
43
+ self.Model.action_ok()
44
+ cups.Connection.assert_called_once_with(
45
+ host=self.server.address, port=self.server.port
46
+ )
47
+
48
+ @mock.patch(f"{model}.cups")
49
+ def test_action_ok_gets_printers(self, cups):
50
+ """It should get printers from CUPS"""
51
+ cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
52
+ cups.Connection().getPPD3.return_value = (200, 0, "")
53
+ self.Model.action_ok()
54
+ cups.Connection().getPrinters.assert_called_once_with()
55
+
56
+ def test_action_ok_raises_warning_on_error(self):
57
+ """It should raise Warning on any error"""
58
+ with (
59
+ mock.patch(f"{model}.cups") as cups,
60
+ self.assertLogs(level=logging.WARNING) as logs,
61
+ ):
62
+ cups.Connection.side_effect = StopTest
63
+ with self.assertRaises(UserError):
64
+ self.Model.action_ok()
65
+ self.assertEqual(len(logs.records), 1)
66
+ self.assertEqual(logs.records[0].levelno, logging.WARNING)
67
+
68
+ @mock.patch(f"{model}.cups")
69
+ def test_action_ok_creates_new_printer(self, cups):
70
+ """It should create new printer w/ proper vals"""
71
+ cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
72
+ cups.Connection().getPPD3.return_value = (200, 0, "")
73
+ self.Model.action_ok()
74
+ rec_id = self.env["printing.printer"].search(
75
+ [("system_name", "=", "sys_name")], limit=1
76
+ )
77
+ self.assertTrue(rec_id)
78
+ for key, val in self._record_vals().items():
79
+ if rec_id._fields[key].type == "many2one":
80
+ val = self.env[rec_id._fields[key].comodel_name].browse(val)
81
+
82
+ self.assertEqual(val, rec_id[key])
83
+
84
+ @mock.patch(f"{model}.cups")
85
+ def test_action_ok_skips_existing_printer(self, cups):
86
+ """It should not recreate existing printers"""
87
+ cups.Connection().getPrinters.return_value = {"sys_name": self.printer_vals}
88
+ cups.Connection().getPPD3.return_value = (200, 0, "")
89
+ self.env["printing.printer"].create(self._record_vals())
90
+ self.Model.action_ok()
91
+ res_ids = self.env["printing.printer"].search(
92
+ [("system_name", "=", "sys_name")]
93
+ )
94
+ self.assertEqual(1, len(res_ids))
@@ -0,0 +1,98 @@
1
+ # Copyright 2016 SYLEAM
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ from odoo.tests.common import TransactionCase
5
+
6
+
7
+ class TestPrintingReportXmlAction(TransactionCase):
8
+ def setUp(self):
9
+ super().setUp()
10
+ self.Model = self.env["printing.report.xml.action"]
11
+
12
+ self.report = self.env["ir.actions.report"].search([], limit=1)
13
+ self.server = self.env["printing.server"].create({})
14
+
15
+ self.report_vals = {
16
+ "report_id": self.report.id,
17
+ "user_id": self.env.ref("base.user_demo").id,
18
+ "action": "server",
19
+ }
20
+
21
+ def new_record(self, vals=None):
22
+ values = self.report_vals
23
+ if vals is not None:
24
+ values.update(vals)
25
+
26
+ return self.Model.create(values)
27
+
28
+ def new_printer(self):
29
+ return self.env["printing.printer"].create(
30
+ {
31
+ "name": "Printer",
32
+ "server_id": self.server.id,
33
+ "system_name": "Sys Name",
34
+ "default": True,
35
+ "status": "unknown",
36
+ "status_message": "Msg",
37
+ "model": "res.users",
38
+ "location": "Location",
39
+ "uri": "URI",
40
+ }
41
+ )
42
+
43
+ def test_behaviour(self):
44
+ """It should return some action's data, unless called on empty
45
+ recordset
46
+ """
47
+ xml_action = self.new_record()
48
+ self.assertEqual(
49
+ xml_action.behaviour(),
50
+ {
51
+ "action": xml_action.action,
52
+ "printer": xml_action.printer_id,
53
+ "tray": False,
54
+ },
55
+ )
56
+
57
+ xml_action = self.new_record({"printer_id": self.new_printer().id})
58
+ self.assertEqual(
59
+ xml_action.behaviour(),
60
+ {
61
+ "action": xml_action.action,
62
+ "printer": xml_action.printer_id,
63
+ "tray": False,
64
+ },
65
+ )
66
+
67
+ self.assertEqual(self.Model.behaviour(), {})
68
+
69
+ def test_onchange_printer_tray_id_empty(self):
70
+ action = self.env["printing.report.xml.action"].new({"printer_tray_id": False})
71
+ action.onchange_printer_id()
72
+ self.assertFalse(action.printer_tray_id)
73
+
74
+ def test_onchange_printer_tray_id_not_empty(self):
75
+ server = self.env["printing.server"].create({})
76
+ printer = self.env["printing.printer"].create(
77
+ {
78
+ "name": "Printer",
79
+ "server_id": server.id,
80
+ "system_name": "Sys Name",
81
+ "default": True,
82
+ "status": "unknown",
83
+ "status_message": "Msg",
84
+ "model": "res.users",
85
+ "location": "Location",
86
+ "uri": "URI",
87
+ }
88
+ )
89
+ tray = self.env["printing.tray"].create(
90
+ {"name": "Tray", "system_name": "TrayName", "printer_id": printer.id}
91
+ )
92
+
93
+ action = self.env["printing.report.xml.action"].new(
94
+ {"printer_tray_id": tray.id}
95
+ )
96
+ self.assertEqual(action.printer_tray_id, tray)
97
+ action.onchange_printer_id()
98
+ self.assertFalse(action.printer_tray_id)
@@ -0,0 +1,219 @@
1
+ # Copyright 2016 LasLabs Inc.
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3
+
4
+ import logging
5
+ from unittest import mock
6
+
7
+ from odoo import fields
8
+ from odoo.tests.common import TransactionCase
9
+
10
+ model = "odoo.addons.base_report_to_printer.models.printing_server"
11
+ model_base = "odoo.models.BaseModel"
12
+
13
+
14
+ class TestPrintingServer(TransactionCase):
15
+ def setUp(self):
16
+ super().setUp()
17
+ self.Model = self.env["printing.server"]
18
+ self.server = self.Model.create({})
19
+ self.printer_vals = {
20
+ "name": "Printer",
21
+ "server_id": self.server.id,
22
+ "system_name": "Sys Name",
23
+ "default": True,
24
+ "status": "unknown",
25
+ "status_message": "Msg",
26
+ "model": "res.users",
27
+ "location": "Location",
28
+ "uri": "URI",
29
+ }
30
+ self.job_vals = {
31
+ "server_id": self.server.id,
32
+ "job_id_cups": 1,
33
+ "job_media_progress": 0,
34
+ "time_at_creation": fields.Datetime.now(),
35
+ }
36
+
37
+ def new_printer(self):
38
+ return self.env["printing.printer"].create(self.printer_vals)
39
+
40
+ def new_job(self, printer, vals=None):
41
+ values = self.job_vals
42
+ if vals is not None:
43
+ values.update(vals)
44
+ values["printer_id"] = printer.id
45
+ return self.env["printing.job"].create(values)
46
+
47
+ def test_update_printers_error(self):
48
+ """It should catch any exception from CUPS and update status"""
49
+ with (
50
+ mock.patch(f"{model}.cups") as cups,
51
+ self.assertLogs(level=logging.WARNING) as logs,
52
+ ):
53
+ cups.Connection.side_effect = Exception
54
+ rec_id = self.new_printer()
55
+ self.Model.update_printers()
56
+ self.assertEqual("server-error", rec_id.status)
57
+ self.assertEqual(len(logs.records), 1)
58
+ self.assertEqual(logs.records[0].levelno, logging.WARNING)
59
+
60
+ @mock.patch(f"{model}.cups")
61
+ def test_update_printers_inits_cups(self, cups):
62
+ """It should init CUPS connection"""
63
+ self.new_printer()
64
+ self.Model.update_printers()
65
+ cups.Connection.assert_called_once_with(
66
+ host=self.server.address, port=self.server.port
67
+ )
68
+
69
+ @mock.patch(f"{model}.cups")
70
+ def test_update_printers_gets_all_printers(self, cups):
71
+ """It should get all printers from CUPS server"""
72
+ self.new_printer()
73
+ self.Model.update_printers()
74
+ cups.Connection().getPrinters.assert_called_once_with()
75
+
76
+ @mock.patch(f"{model}.cups")
77
+ def test_update_printers_search(self, cups):
78
+ """It should search all when no domain"""
79
+ with mock.patch(f"{model_base}.search") as search:
80
+ self.Model.update_printers()
81
+ search.assert_called_once_with([])
82
+
83
+ @mock.patch(f"{model}.cups")
84
+ def test_update_printers_search_domain(self, cups):
85
+ """It should use specific domain for search"""
86
+ with mock.patch(f"{model_base}.search") as search:
87
+ expect = [("id", ">", 0)]
88
+ self.Model.update_printers(expect)
89
+ search.assert_called_once_with(expect)
90
+
91
+ @mock.patch(f"{model}.cups")
92
+ def test_update_printers_update_unavailable(self, cups):
93
+ """It should update status when printer is unavailable"""
94
+ rec_id = self.new_printer()
95
+ cups.Connection().getPrinters().get.return_value = False
96
+ self.Model.action_update_printers()
97
+ self.assertEqual("unavailable", rec_id.status)
98
+
99
+ @mock.patch(f"{model}.cups")
100
+ def test_update_archived_printers(self, cups):
101
+ """It should update status even if printer is archived"""
102
+ rec_id = self.new_printer()
103
+ rec_id.toggle_active()
104
+ self.server.invalidate_model()
105
+ cups.Connection().getPrinters().get.return_value = False
106
+ self.Model.action_update_printers()
107
+ self.assertEqual(
108
+ "unavailable",
109
+ rec_id.status,
110
+ )
111
+
112
+ @mock.patch(f"{model}.cups")
113
+ def test_update_jobs_cron(self, cups):
114
+ """It should get all jobs from CUPS server"""
115
+ self.new_printer()
116
+ self.Model.action_update_jobs()
117
+ cups.Connection().getPrinters.assert_called_once_with()
118
+ cups.Connection().getJobs.assert_called_once_with(
119
+ which_jobs="all",
120
+ first_job_id=-1,
121
+ requested_attributes=[
122
+ "job-name",
123
+ "job-id",
124
+ "printer-uri",
125
+ "job-media-progress",
126
+ "time-at-creation",
127
+ "job-state",
128
+ "job-state-reasons",
129
+ "time-at-processing",
130
+ "time-at-completed",
131
+ ],
132
+ )
133
+
134
+ @mock.patch(f"{model}.cups")
135
+ def test_update_jobs_button(self, cups):
136
+ """It should get all jobs from CUPS server"""
137
+ self.new_printer()
138
+ self.server.action_update_jobs()
139
+ cups.Connection().getPrinters.assert_called_once_with()
140
+ cups.Connection().getJobs.assert_called_once_with(
141
+ which_jobs="all",
142
+ first_job_id=-1,
143
+ requested_attributes=[
144
+ "job-name",
145
+ "job-id",
146
+ "printer-uri",
147
+ "job-media-progress",
148
+ "time-at-creation",
149
+ "job-state",
150
+ "job-state-reasons",
151
+ "time-at-processing",
152
+ "time-at-completed",
153
+ ],
154
+ )
155
+
156
+ def test_update_jobs_error(self):
157
+ """It should catch any exception from CUPS and update status"""
158
+ with (
159
+ mock.patch(f"{model}.cups") as cups,
160
+ self.assertLogs(level=logging.WARNING) as logs,
161
+ ):
162
+ cups.Connection.side_effect = Exception
163
+ self.new_printer()
164
+ self.server.update_jobs()
165
+ cups.Connection.assert_called_with(
166
+ host=self.server.address, port=self.server.port
167
+ )
168
+ self.assertEqual(len(logs.records), 2)
169
+ self.assertEqual(logs.records[0].levelno, logging.WARNING)
170
+
171
+ @mock.patch(f"{model}.cups")
172
+ def test_update_jobs_uncompleted(self, cups):
173
+ """
174
+ It should search which jobs have been completed since last update
175
+ """
176
+ printer = self.new_printer()
177
+ self.new_job(printer, vals={"job_state": "completed"})
178
+ self.new_job(printer, vals={"job_id_cups": 2, "job_state": "processing"})
179
+ self.server.update_jobs(which="not-completed")
180
+ cups.Connection().getJobs.assert_any_call(
181
+ which_jobs="completed",
182
+ first_job_id=2,
183
+ requested_attributes=[
184
+ "job-name",
185
+ "job-id",
186
+ "printer-uri",
187
+ "job-media-progress",
188
+ "time-at-creation",
189
+ "job-state",
190
+ "job-state-reasons",
191
+ "time-at-processing",
192
+ "time-at-completed",
193
+ ],
194
+ )
195
+
196
+ @mock.patch(f"{model}.cups")
197
+ def test_update_jobs(self, cups):
198
+ """
199
+ It should update all jobs, known or not
200
+ """
201
+ printer = self.new_printer()
202
+ printer_uri = "hostname:port/" + printer.system_name
203
+ cups.Connection().getJobs.return_value = {
204
+ 1: {"printer-uri": printer_uri},
205
+ 2: {"printer-uri": printer_uri, "job-state": 9},
206
+ 4: {"printer-uri": printer_uri, "job-state": 5},
207
+ }
208
+ self.new_job(printer, vals={"job_state": "completed"})
209
+ completed_job = self.new_job(
210
+ printer, vals={"job_id_cups": 2, "job_state": "processing"}
211
+ )
212
+ purged_job = self.new_job(
213
+ printer, vals={"job_id_cups": 3, "job_state": "processing"}
214
+ )
215
+ self.server.update_jobs()
216
+ new_job = self.env["printing.job"].search([("job_id_cups", "=", 4)])
217
+ self.assertEqual(completed_job.job_state, "completed")
218
+ self.assertEqual(purged_job.active, False)
219
+ self.assertEqual(new_job.job_state, "processing")