printer-core 0.0.1__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 (62) hide show
  1. printer_core/__init__.py +1 -0
  2. printer_core/algorithms/__init__.py +3 -0
  3. printer_core/algorithms/products_modifiers_multiprinter.py +59 -0
  4. printer_core/algorithms/products_modifiers_multiprinter_family.py +53 -0
  5. printer_core/algorithms/products_modifiers_unique_ticket.py +30 -0
  6. printer_core/builders/__init__.py +4 -0
  7. printer_core/builders/client_ticket_builder.py +97 -0
  8. printer_core/builders/header_builder.py +26 -0
  9. printer_core/builders/kitchen_ticket_payload_builder.py +99 -0
  10. printer_core/builders/printer_products_grouper.py +19 -0
  11. printer_core/clock.py +6 -0
  12. printer_core/context/__init__.py +1 -0
  13. printer_core/context/ticket_context_builder.py +55 -0
  14. printer_core/dispatchers/__init__.py +2 -0
  15. printer_core/dispatchers/kitchen_ticket_dispatcher.py +34 -0
  16. printer_core/dispatchers/payload_dispatcher.py +18 -0
  17. printer_core/entities/__init__.py +53 -0
  18. printer_core/entities/print_context.py +15 -0
  19. printer_core/entities/print_models.py +230 -0
  20. printer_core/formatters/__init__.py +1 -0
  21. printer_core/formatters/discount_formatter.py +17 -0
  22. printer_core/ports.py +45 -0
  23. printer_core/selectors/__init__.py +1 -0
  24. printer_core/selectors/family_product_selector.py +33 -0
  25. printer_core/services/__init__.py +12 -0
  26. printer_core/services/flows/__init__.py +5 -0
  27. printer_core/services/flows/base.py +26 -0
  28. printer_core/services/flows/client.py +97 -0
  29. printer_core/services/flows/kitchen.py +113 -0
  30. printer_core/services/march_printer_service.py +61 -0
  31. printer_core/services/multi_printer_family_service.py +102 -0
  32. printer_core/services/multi_printer_service.py +49 -0
  33. printer_core/services/order_print_service.py +98 -0
  34. printer_core/services/print_policy.py +72 -0
  35. printer_core/services/unique_ticket_service.py +52 -0
  36. printer_core/tests/__init__.py +0 -0
  37. printer_core/tests/unitaries/__init__.py +0 -0
  38. printer_core/tests/unitaries/helpers.py +188 -0
  39. printer_core/tests/unitaries/test_cases_output.py +189 -0
  40. printer_core/tests/unitaries/test_client_ticket_builder.py +78 -0
  41. printer_core/tests/unitaries/test_discount_formatter.py +37 -0
  42. printer_core/tests/unitaries/test_family_product_selector.py +28 -0
  43. printer_core/tests/unitaries/test_flows.py +141 -0
  44. printer_core/tests/unitaries/test_header_builder_foodeo_printers.py +87 -0
  45. printer_core/tests/unitaries/test_kitchen_ticket_dispatcher.py +61 -0
  46. printer_core/tests/unitaries/test_kitchen_ticket_payload_builder.py +113 -0
  47. printer_core/tests/unitaries/test_march_printer_service.py +31 -0
  48. printer_core/tests/unitaries/test_multi_printer_family_service.py +53 -0
  49. printer_core/tests/unitaries/test_multi_printer_service.py +51 -0
  50. printer_core/tests/unitaries/test_order_print_service.py +144 -0
  51. printer_core/tests/unitaries/test_payload_dispatcher.py +61 -0
  52. printer_core/tests/unitaries/test_print_policy.py +45 -0
  53. printer_core/tests/unitaries/test_printer_helpers.py +71 -0
  54. printer_core/tests/unitaries/test_printer_products_grouper.py +20 -0
  55. printer_core/tests/unitaries/test_products_modifiers.py +102 -0
  56. printer_core/tests/unitaries/test_ticket_context_builder.py +71 -0
  57. printer_core/tests/unitaries/test_unique_ticket_service.py +24 -0
  58. printer_core/utils/__init__.py +1 -0
  59. printer_core/utils/printer_helpers.py +42 -0
  60. printer_core-0.0.1.dist-info/METADATA +24 -0
  61. printer_core-0.0.1.dist-info/RECORD +62 -0
  62. printer_core-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,189 @@
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+ from types import SimpleNamespace
5
+
6
+ import unittest
7
+ from printer_core.dispatchers.kitchen_ticket_dispatcher import KitchenTicketDispatcher
8
+ from printer_core.entities import HeaderData, KitchenProductLine, PrintChannel, PrinterProductGroup
9
+ from printer_core.services.flows.client import ClientPrintFlow
10
+
11
+ from printer_core.tests.unitaries.helpers import (
12
+ FakeGateway,
13
+ FakeHeaderProvider,
14
+ FakePrintSettingsBuilder,
15
+ make_kitchen_settings,
16
+ make_printer,
17
+ make_product,
18
+ make_request,
19
+ make_ticket_settings,
20
+ )
21
+
22
+
23
+ def _should_dump_cases() -> bool:
24
+ return os.getenv("FOODEO_PRINTERS_DUMP_CASES", "").lower() in {"1", "true", "yes"}
25
+
26
+
27
+ def _dump_case_output(case_id: str, flow: str, input_data: dict[str, object], ws_payload: object) -> None:
28
+ if not _should_dump_cases():
29
+ return
30
+ dump_dir = Path(os.getenv("FOODEO_PRINTERS_DUMP_DIR", "tmp/foodeo_printers_cases"))
31
+ dump_dir.mkdir(parents=True, exist_ok=True)
32
+ path = dump_dir / f"{case_id}_{flow}.json"
33
+ payload = {
34
+ "case_id": case_id,
35
+ "flow": flow,
36
+ "input": input_data,
37
+ "ws_payload": ws_payload,
38
+ }
39
+ with path.open("w", encoding="utf-8") as handle:
40
+ json.dump(payload, handle, ensure_ascii=True, indent=2, sort_keys=True)
41
+
42
+
43
+ CASES_ALL = [
44
+ ("CASE01", "local", "web", PrintChannel.LOCAL),
45
+ ("CASE02", "local", "apk", PrintChannel.LOCAL),
46
+ ("CASE03", "local", "web_sales", PrintChannel.LOCAL),
47
+ ("CASE04", "local", "kiosko", PrintChannel.LOCAL),
48
+ ("CASE05", "barra", "web", PrintChannel.BARRA),
49
+ ("CASE06", "barra", "apk", PrintChannel.BARRA),
50
+ ("CASE07", "barra", "web_sales", PrintChannel.BARRA),
51
+ ("CASE08", "barra", "kiosko", PrintChannel.BARRA),
52
+ ("CASE09", "kiosko", "web", PrintChannel.KIOSKO),
53
+ ("CASE10", "kiosko", "apk", PrintChannel.KIOSKO),
54
+ ("CASE11", "kiosko", "web_sales", PrintChannel.KIOSKO),
55
+ ("CASE12", "kiosko", "kiosko", PrintChannel.KIOSKO),
56
+ ("CASE13", "domicilio", "web", PrintChannel.DOMICILE),
57
+ ("CASE14", "domicilio", "apk", PrintChannel.DOMICILE),
58
+ ("CASE15", "domicilio", "kiosko", PrintChannel.DOMICILE),
59
+ ("CASE16", "domicilio", "web_sales", PrintChannel.DOMICILE),
60
+ ("CASE17", "recoger", "web", PrintChannel.PICKUP),
61
+ ("CASE18", "recoger", "apk", PrintChannel.PICKUP),
62
+ ("CASE19", "recoger", "kiosko", PrintChannel.PICKUP),
63
+ ("CASE20", "recoger", "web_sales", PrintChannel.PICKUP),
64
+ ("CASE21", "local", "web", PrintChannel.LOCAL),
65
+ ("CASE22", "local", "apk", PrintChannel.LOCAL),
66
+ ("CASE23", "local", "web_sales", PrintChannel.LOCAL),
67
+ ("CASE24", "local", "kiosko", PrintChannel.LOCAL),
68
+ ("CASE25", "barra", "web", PrintChannel.BARRA),
69
+ ("CASE26", "barra", "apk", PrintChannel.BARRA),
70
+ ("CASE27", "barra", "web_sales", PrintChannel.BARRA),
71
+ ("CASE28", "barra", "kiosko", PrintChannel.BARRA),
72
+ ("CASE29", "kiosko", "web", PrintChannel.KIOSKO),
73
+ ("CASE30", "kiosko", "apk", PrintChannel.KIOSKO),
74
+ ("CASE31", "kiosko", "web_sales", PrintChannel.KIOSKO),
75
+ ("CASE32", "kiosko", "kiosko", PrintChannel.KIOSKO),
76
+ ("CASE33", "domicilio", "web", PrintChannel.DOMICILE),
77
+ ("CASE34", "domicilio", "apk", PrintChannel.DOMICILE),
78
+ ("CASE35", "domicilio", "web_sales", PrintChannel.DOMICILE),
79
+ ("CASE36", "domicilio", "kiosko", PrintChannel.DOMICILE),
80
+ ("CASE37", "recoger", "web", PrintChannel.PICKUP),
81
+ ("CASE38", "recoger", "apk", PrintChannel.PICKUP),
82
+ ("CASE39", "recoger", "web_sales", PrintChannel.PICKUP),
83
+ ("CASE40", "recoger", "kiosko", PrintChannel.PICKUP),
84
+ ("CASE41", "domicilio", "web", PrintChannel.DOMICILE),
85
+ ("CASE42", "domicilio", "apk", PrintChannel.DOMICILE),
86
+ ("CASE43", "domicilio", "web_sales", PrintChannel.DOMICILE),
87
+ ("CASE44", "domicilio", "kiosko", PrintChannel.DOMICILE),
88
+ ("CASE45", "recoger", "web", PrintChannel.PICKUP),
89
+ ("CASE46", "recoger", "apk", PrintChannel.PICKUP),
90
+ ("CASE47", "recoger", "web_sales", PrintChannel.PICKUP),
91
+ ("CASE48", "recoger", "kiosko", PrintChannel.PICKUP),
92
+ ]
93
+
94
+
95
+ class CasesOutputTest(unittest.TestCase):
96
+ def test_client_flow_outputs_for_cases(self):
97
+ for case_id, request_type, from_client, channel in CASES_ALL:
98
+ with self.subTest(case=case_id):
99
+ gateway = FakeGateway()
100
+ base_request = make_request(request_type=request_type, from_client=from_client)
101
+ products = [make_product()]
102
+ flow = ClientPrintFlow(
103
+ gateway=gateway,
104
+ header_provider=FakeHeaderProvider(),
105
+ print_settings_builder=FakePrintSettingsBuilder(),
106
+ currency_symbol_resolver=lambda _: "",
107
+ domicile_service_label="Service",
108
+ )
109
+ context = SimpleNamespace(
110
+ kitchen_settings=make_kitchen_settings(),
111
+ ticket_settings=make_ticket_settings(),
112
+ main_printer=make_printer(),
113
+ worker=None,
114
+ )
115
+ flow.execute(
116
+ base_request=base_request,
117
+ products=products,
118
+ context=context,
119
+ channel=channel,
120
+ tpv_key="TPV1",
121
+ )
122
+
123
+ payload = gateway.calls[0][1][0]
124
+ _dump_case_output(
125
+ case_id=case_id,
126
+ flow="client",
127
+ input_data={
128
+ "request": base_request.model_dump(mode="json", exclude_none=True),
129
+ "products": [product.model_dump(mode="json", exclude_none=True) for product in products],
130
+ "channel": channel.value,
131
+ "tpv_key": "TPV1",
132
+ "ticket_settings": vars(context.ticket_settings),
133
+ "kitchen_settings": vars(context.kitchen_settings),
134
+ "main_printer": context.main_printer.model_dump(mode="json", exclude_none=True),
135
+ },
136
+ ws_payload=gateway.calls[0][1],
137
+ )
138
+ self.assertEqual(payload["data"]["headerData"]["client_name"], f"{request_type}:{from_client}")
139
+ if channel == PrintChannel.LOCAL:
140
+ self.assertEqual(payload["data"]["PrintSettings"]["show_header"], False)
141
+ else:
142
+ self.assertEqual(payload["data"]["PrintSettings"]["show_header"], True)
143
+
144
+ def test_kitchen_dispatcher_outputs_for_cases(self):
145
+ for case_id, request_type, from_client, channel in CASES_ALL:
146
+ with self.subTest(case=case_id):
147
+ gateway = FakeGateway()
148
+ request = make_request(
149
+ request_type=request_type,
150
+ from_client=from_client,
151
+ headerData=HeaderData(client_name=f"{request_type}:{from_client}"),
152
+ )
153
+ dispatcher = KitchenTicketDispatcher(gateway=gateway)
154
+ grouped_products = [
155
+ PrinterProductGroup(
156
+ printer_name="P1",
157
+ tpv_key="TPV1",
158
+ printer_width="80",
159
+ products=[KitchenProductLine(name="A", qty=1, option=None, printer_r="P1", printer_w="80")],
160
+ )
161
+ ]
162
+ ticket_settings = make_ticket_settings()
163
+ kitchen_ticket_settings = make_kitchen_settings()
164
+
165
+ dispatcher.dispatch(
166
+ request=request,
167
+ grouped_products=grouped_products,
168
+ ticket_settings=ticket_settings,
169
+ kitchen_ticket_settings=kitchen_ticket_settings,
170
+ group_products_func=lambda items: [],
171
+ )
172
+
173
+ payload = gateway.calls[0][1][0]
174
+ _dump_case_output(
175
+ case_id=case_id,
176
+ flow="kitchen",
177
+ input_data={
178
+ "request": request.model_dump(mode="json", exclude_none=True),
179
+ "grouped_products": [group.model_dump(mode="json", exclude_none=True) for group in grouped_products],
180
+ "ticket_settings": vars(ticket_settings),
181
+ "kitchen_ticket_settings": vars(kitchen_ticket_settings),
182
+ },
183
+ ws_payload=gateway.calls[0][1],
184
+ )
185
+ expected_model = 1 if request_type == "local" else 2
186
+ self.assertEqual(payload["model"], expected_model)
187
+ self.assertEqual(payload["data"]["headerData"]["client_name"], f"{request_type}:{from_client}")
188
+ self.assertEqual(payload["printer"], "P1")
189
+ self.assertEqual(payload["company_key"], "TPV1")
@@ -0,0 +1,78 @@
1
+ import unittest
2
+ from printer_core.builders.client_ticket_builder import ClientTicketBuilder
3
+ from printer_core.entities import DiscountData
4
+
5
+ from printer_core.tests.unitaries.helpers import make_printer, make_product, make_request, make_ticket_settings
6
+
7
+
8
+ class ClientTicketBuilderTest(unittest.TestCase):
9
+ def test_build_removes_details_for_local(self):
10
+ builder = ClientTicketBuilder(currency_symbol_resolver=lambda _: "€")
11
+ request = make_request(request_type="local", details="Some details")
12
+ products = [make_product()]
13
+ printer = make_printer()
14
+ ticket_settings = make_ticket_settings(ticket_text="Text", qty_client_ticket=2)
15
+
16
+ payloads = builder.build(
17
+ request=request,
18
+ products=products,
19
+ printer=printer,
20
+ ticket_settings=ticket_settings,
21
+ tpv_key="TPV1",
22
+ )
23
+
24
+ self.assertIsNone(payloads[0].data.details)
25
+ self.assertEqual(payloads[0].printer_qty, 2)
26
+ self.assertEqual(payloads[0].data.ticket_text, "Text")
27
+
28
+ def test_build_keeps_details_for_domicile(self):
29
+ builder = ClientTicketBuilder()
30
+ request = make_request(request_type="domicilio", details=None)
31
+ products = [make_product()]
32
+ payloads = builder.build(
33
+ request=request,
34
+ products=products,
35
+ printer=make_printer(),
36
+ ticket_settings=make_ticket_settings(),
37
+ tpv_key="TPV1",
38
+ )
39
+
40
+ self.assertEqual(payloads[0].data.details, "")
41
+
42
+ def test_build_formats_discount(self):
43
+ builder = ClientTicketBuilder(currency_symbol_resolver=lambda _: "€")
44
+ request = make_request()
45
+ products = [
46
+ make_product(
47
+ total_price="90",
48
+ product_price="100",
49
+ discount_type="percent",
50
+ discount=DiscountData(value="10", type="percent"),
51
+ importe="100",
52
+ )
53
+ ]
54
+ payloads = builder.build(
55
+ request=request,
56
+ products=products,
57
+ printer=make_printer(),
58
+ ticket_settings=make_ticket_settings(),
59
+ tpv_key="TPV1",
60
+ )
61
+
62
+ self.assertIn("Dto 10% 10€", payloads[0].data.products[0].name)
63
+
64
+ def test_build_adds_domicile_service_line(self):
65
+ builder = ClientTicketBuilder(domicile_service_label="Service")
66
+ request = make_request(request_type="domicilio", price_domicile_service="2.00")
67
+ products = [make_product()]
68
+
69
+ payloads = builder.build(
70
+ request=request,
71
+ products=products,
72
+ printer=make_printer(),
73
+ ticket_settings=make_ticket_settings(),
74
+ tpv_key="TPV1",
75
+ )
76
+
77
+ self.assertEqual(payloads[0].data.products[-1].name, "Service")
78
+ self.assertEqual(payloads[0].data.products[-1].price, "2.00")
@@ -0,0 +1,37 @@
1
+ import unittest
2
+ from printer_core.entities import DiscountData
3
+ from printer_core.formatters.discount_formatter import DiscountFormatter
4
+
5
+ from printer_core.tests.unitaries.helpers import make_product
6
+
7
+
8
+ class DiscountFormatterTest(unittest.TestCase):
9
+ def test_format_number_discount(self):
10
+ formatter = DiscountFormatter(currency_symbol_resolver=lambda _: "€")
11
+ product = make_product(total_price="5.00", discount_type="number")
12
+
13
+ result = formatter.format(product, "EUR")
14
+
15
+ self.assertEqual(result, " (Dto 5.00€)")
16
+
17
+ def test_format_percent_discount(self):
18
+ formatter = DiscountFormatter(currency_symbol_resolver=lambda _: "€")
19
+ product = make_product(
20
+ total_price="90",
21
+ product_price="100",
22
+ discount_type="percent",
23
+ discount=DiscountData(value="10", type="percent"),
24
+ importe="100",
25
+ )
26
+
27
+ result = formatter.format(product, "EUR")
28
+
29
+ self.assertEqual(result, " (Dto 10% 10€)")
30
+
31
+ def test_format_without_discount(self):
32
+ formatter = DiscountFormatter(currency_symbol_resolver=lambda _: "€")
33
+ product = make_product(total_price="90", product_price="100")
34
+
35
+ result = formatter.format(product, "EUR")
36
+
37
+ self.assertEqual(result, "")
@@ -0,0 +1,28 @@
1
+ import unittest
2
+ from printer_core.entities import FamilyData
3
+ from printer_core.selectors.family_product_selector import FamilyProductSelector
4
+
5
+ from printer_core.tests.unitaries.helpers import FakeFamilyProvider, make_product
6
+
7
+
8
+ class FamilyProductSelectorTest(unittest.TestCase):
9
+ def test_group_products(self):
10
+ family_1 = FamilyData(id=1, name="Family 1")
11
+ family_2 = FamilyData(id=2, name="Family 2")
12
+ provider = FakeFamilyProvider(
13
+ families=[family_1, family_2],
14
+ family_by_category={10: family_1, 20: family_2},
15
+ )
16
+ selector = FamilyProductSelector(provider=provider)
17
+ products = [
18
+ make_product(name="A", category=10),
19
+ make_product(name="B", category=20),
20
+ make_product(name="C", category=None),
21
+ ]
22
+
23
+ families, products_by_family, products_without_family = selector.group_products(products)
24
+
25
+ self.assertEqual(families, [family_1, family_2])
26
+ self.assertEqual([p.product_name for p in products_by_family[1]], ["A"])
27
+ self.assertEqual([p.product_name for p in products_by_family[2]], ["B"])
28
+ self.assertEqual([p.product_name for p in products_without_family], ["C"])
@@ -0,0 +1,141 @@
1
+ from unittest.mock import patch
2
+
3
+ import unittest
4
+ from printer_core.entities import PrintChannel
5
+ from printer_core.services.flows.client import ClientPrintFlow
6
+ from printer_core.services.flows.kitchen import KitchenPrintFlow
7
+
8
+ from printer_core.tests.unitaries.helpers import (
9
+ FakeGateway,
10
+ FakeHeaderProvider,
11
+ FakePrintSettingsBuilder,
12
+ make_kitchen_settings,
13
+ make_printer,
14
+ make_product,
15
+ make_request,
16
+ make_ticket_settings,
17
+ )
18
+
19
+
20
+ class ClientPrintFlowTest(unittest.TestCase):
21
+ def test_execute_requires_tpv_key(self):
22
+ flow = ClientPrintFlow(
23
+ gateway=FakeGateway(),
24
+ header_provider=FakeHeaderProvider(),
25
+ print_settings_builder=FakePrintSettingsBuilder(),
26
+ currency_symbol_resolver=lambda _: "",
27
+ domicile_service_label="Service",
28
+ )
29
+
30
+ with self.assertRaises(ValueError):
31
+ flow.execute(
32
+ base_request=make_request(),
33
+ products=[make_product()],
34
+ context=type("Ctx", (), {"kitchen_settings": make_kitchen_settings(), "ticket_settings": make_ticket_settings(), "main_printer": make_printer(), "worker": None})(),
35
+ channel=PrintChannel.LOCAL,
36
+ tpv_key="",
37
+ )
38
+
39
+ def test_execute_sends_payload(self):
40
+ gateway = FakeGateway()
41
+ flow = ClientPrintFlow(
42
+ gateway=gateway,
43
+ header_provider=FakeHeaderProvider(),
44
+ print_settings_builder=FakePrintSettingsBuilder(),
45
+ currency_symbol_resolver=lambda _: "",
46
+ domicile_service_label="Service",
47
+ )
48
+ context = type("Ctx", (), {
49
+ "kitchen_settings": make_kitchen_settings(),
50
+ "ticket_settings": make_ticket_settings(),
51
+ "main_printer": make_printer(),
52
+ "worker": None,
53
+ })()
54
+
55
+ flow.execute(
56
+ base_request=make_request(request_type="local", from_client="web"),
57
+ products=[make_product()],
58
+ context=context,
59
+ channel=PrintChannel.LOCAL,
60
+ tpv_key="TPV1",
61
+ )
62
+
63
+ self.assertEqual(len(gateway.calls), 1)
64
+ payload = gateway.calls[0][1][0]
65
+ self.assertEqual(payload["company_key"], "TPV1")
66
+ self.assertEqual(payload["data"]["PrintSettings"]["show_header"], False)
67
+ self.assertEqual(payload["data"]["headerData"]["client_name"], "local:web")
68
+
69
+
70
+ class KitchenPrintFlowTest(unittest.TestCase):
71
+ def test_execute_uses_multi_printer_service(self):
72
+ captured = {}
73
+
74
+ class DummyService:
75
+ def __init__(self, **kwargs):
76
+ pass
77
+
78
+ def build_and_dispatch(self, request, products, main_printer, ticket_settings, kitchen_ticket_settings):
79
+ captured["request"] = request
80
+ return []
81
+
82
+ with patch("printer_core.services.flows.kitchen.MultiPrinterService", DummyService):
83
+ flow = KitchenPrintFlow(
84
+ gateway=FakeGateway(),
85
+ header_provider=FakeHeaderProvider(),
86
+ print_settings_builder=FakePrintSettingsBuilder(),
87
+ family_provider=object(),
88
+ currency_symbol_resolver=lambda _: "",
89
+ )
90
+ context = type("Ctx", (), {
91
+ "ticket_settings": make_ticket_settings(),
92
+ "kitchen_settings": make_kitchen_settings(print_by="categories"),
93
+ "main_printer": make_printer(),
94
+ "worker": None,
95
+ })()
96
+
97
+ flow.execute(
98
+ base_request=make_request(request_type="local"),
99
+ products=[make_product()],
100
+ context=context,
101
+ channel=PrintChannel.LOCAL,
102
+ tpv_key="TPV1",
103
+ )
104
+
105
+ self.assertEqual(captured["request"].print_by, "categories")
106
+
107
+ def test_execute_uses_family_service_and_skips_print_by(self):
108
+ captured = {}
109
+
110
+ class DummyFamilyService:
111
+ def __init__(self, **kwargs):
112
+ pass
113
+
114
+ def build_and_dispatch(self, request, products, main_printer, ticket_settings, kitchen_ticket_settings):
115
+ captured["request"] = request
116
+ return []
117
+
118
+ with patch("printer_core.services.flows.kitchen.MultiPrinterFamilyService", DummyFamilyService):
119
+ flow = KitchenPrintFlow(
120
+ gateway=FakeGateway(),
121
+ header_provider=FakeHeaderProvider(),
122
+ print_settings_builder=FakePrintSettingsBuilder(),
123
+ family_provider=object(),
124
+ currency_symbol_resolver=lambda _: "",
125
+ )
126
+ context = type("Ctx", (), {
127
+ "ticket_settings": make_ticket_settings(),
128
+ "kitchen_settings": make_kitchen_settings(print_by="families"),
129
+ "main_printer": make_printer(),
130
+ "worker": None,
131
+ })()
132
+
133
+ flow.execute(
134
+ base_request=make_request(request_type="barra"),
135
+ products=[make_product()],
136
+ context=context,
137
+ channel=PrintChannel.BARRA,
138
+ tpv_key="TPV1",
139
+ )
140
+
141
+ self.assertIsNone(captured["request"].print_by)
@@ -0,0 +1,87 @@
1
+ import unittest
2
+ from printer_core.builders.header_builder import HeaderBuilder
3
+ from printer_core.entities import PrintRequestData
4
+
5
+
6
+ class FakeHeaderDataProvider:
7
+ def __init__(self):
8
+ self.calls: list[tuple[str, object | None, object | None]] = []
9
+
10
+ def get_company_header(self) -> dict[str, str]:
11
+ return {"company_name": "Company"}
12
+
13
+ def get_worker_header(self, user: object) -> dict[str, str]:
14
+ return {"worker_name": getattr(user, "first_name", "")}
15
+
16
+ def get_pickup_client_header(self, client_id: int | None, from_client: str | None) -> dict[str, str]:
17
+ self.calls.append(("pickup", client_id, from_client))
18
+ return {"client_name": f"pickup:{client_id}:{from_client}"}
19
+
20
+ def get_domicile_client_header(self, address_id: int | None, from_client: str | None) -> dict[str, str]:
21
+ self.calls.append(("domicile", address_id, from_client))
22
+ return {"client_name": f"domicile:{address_id}:{from_client}"}
23
+
24
+
25
+ class FakeUser:
26
+ def __init__(self, first_name: str):
27
+ self.first_name = first_name
28
+
29
+
30
+ class HeaderBuilderTest(unittest.TestCase):
31
+ def test_build_header_with_worker_and_pickup(self):
32
+ provider = FakeHeaderDataProvider()
33
+ builder = HeaderBuilder(provider=provider, show_person_name=True)
34
+ request = PrintRequestData(
35
+ id=1,
36
+ type="recoger",
37
+ serial="S1",
38
+ credit_card=False,
39
+ request_price="10.00",
40
+ from_client="web",
41
+ client=10,
42
+ )
43
+
44
+ header = builder.build_header(request, worker=FakeUser("Ana"))
45
+
46
+ self.assertEqual(header.company_name, "Company")
47
+ self.assertEqual(header.worker_name, "Ana")
48
+ self.assertEqual(header.client_name, "pickup:10:web")
49
+ self.assertEqual(provider.calls, [("pickup", 10, "web")])
50
+
51
+ def test_build_header_domicile_without_worker(self):
52
+ provider = FakeHeaderDataProvider()
53
+ builder = HeaderBuilder(provider=provider, show_person_name=False)
54
+ request = PrintRequestData(
55
+ id=1,
56
+ type="domicilio",
57
+ serial="S1",
58
+ credit_card=False,
59
+ request_price="10.00",
60
+ from_client="apk",
61
+ address=20,
62
+ )
63
+
64
+ header = builder.build_header(request)
65
+
66
+ self.assertEqual(header.company_name, "Company")
67
+ self.assertIsNone(header.worker_name)
68
+ self.assertEqual(header.client_name, "domicile:20:apk")
69
+ self.assertEqual(provider.calls, [("domicile", 20, "apk")])
70
+
71
+ def test_build_header_for_other_types(self):
72
+ provider = FakeHeaderDataProvider()
73
+ builder = HeaderBuilder(provider=provider, show_person_name=False)
74
+ request = PrintRequestData(
75
+ id=1,
76
+ type="local",
77
+ serial="S1",
78
+ credit_card=False,
79
+ request_price="10.00",
80
+ from_client="web",
81
+ )
82
+
83
+ header = builder.build_header(request)
84
+
85
+ self.assertEqual(header.company_name, "Company")
86
+ self.assertIsNone(header.client_name)
87
+ self.assertEqual(provider.calls, [])
@@ -0,0 +1,61 @@
1
+ import unittest
2
+ from printer_core.dispatchers.kitchen_ticket_dispatcher import KitchenTicketDispatcher
3
+ from printer_core.entities import KitchenTicketData, KitchenTicketPayload, PrinterProductGroup
4
+
5
+ from printer_core.tests.unitaries.helpers import FakeGateway
6
+
7
+
8
+ class DummyBuilder:
9
+ def __init__(self):
10
+ self.calls: list[tuple[str, str]] = []
11
+
12
+ def build(self, request, printer_name, tpv_key, printer_width, products, ticket_settings, kitchen_ticket_settings, group_products_func):
13
+ self.calls.append((printer_name, tpv_key))
14
+ return KitchenTicketPayload(
15
+ data=KitchenTicketData(
16
+ id=request.id,
17
+ table_name=request.table_name,
18
+ credit_card=request.credit_card,
19
+ serial=request.serial,
20
+ type=request.type,
21
+ ),
22
+ currency=request.currency or "",
23
+ model=1,
24
+ company_key=tpv_key,
25
+ printer=printer_name,
26
+ printer_qty=1,
27
+ printer_width=printer_width,
28
+ )
29
+
30
+
31
+ class KitchenTicketDispatcherTest(unittest.TestCase):
32
+ def test_dispatch_sends_payloads(self):
33
+ gateway = FakeGateway()
34
+ builder = DummyBuilder()
35
+ dispatcher = KitchenTicketDispatcher(gateway=gateway, builder=builder)
36
+ request = type("Request", (), {
37
+ "id": 1,
38
+ "table_name": "T1",
39
+ "credit_card": False,
40
+ "serial": "S1",
41
+ "type": "local",
42
+ "currency": "EUR",
43
+ })()
44
+
45
+ grouped_products = [
46
+ PrinterProductGroup(printer_name="P1", tpv_key="TPV1", printer_width="80", products=[]),
47
+ PrinterProductGroup(printer_name="P2", tpv_key="TPV2", printer_width="58", products=[]),
48
+ ]
49
+
50
+ dispatcher.dispatch(
51
+ request=request,
52
+ grouped_products=grouped_products,
53
+ ticket_settings=object(),
54
+ kitchen_ticket_settings=object(),
55
+ group_products_func=lambda items: [],
56
+ )
57
+
58
+ self.assertEqual(len(gateway.calls), 2)
59
+ self.assertEqual(builder.calls, [("P1", "TPV1"), ("P2", "TPV2")])
60
+ self.assertEqual(gateway.calls[0][1][0]["printer"], "P1")
61
+ self.assertEqual(gateway.calls[1][1][0]["printer"], "P2")