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.
- printer_core/__init__.py +1 -0
- printer_core/algorithms/__init__.py +3 -0
- printer_core/algorithms/products_modifiers_multiprinter.py +59 -0
- printer_core/algorithms/products_modifiers_multiprinter_family.py +53 -0
- printer_core/algorithms/products_modifiers_unique_ticket.py +30 -0
- printer_core/builders/__init__.py +4 -0
- printer_core/builders/client_ticket_builder.py +97 -0
- printer_core/builders/header_builder.py +26 -0
- printer_core/builders/kitchen_ticket_payload_builder.py +99 -0
- printer_core/builders/printer_products_grouper.py +19 -0
- printer_core/clock.py +6 -0
- printer_core/context/__init__.py +1 -0
- printer_core/context/ticket_context_builder.py +55 -0
- printer_core/dispatchers/__init__.py +2 -0
- printer_core/dispatchers/kitchen_ticket_dispatcher.py +34 -0
- printer_core/dispatchers/payload_dispatcher.py +18 -0
- printer_core/entities/__init__.py +53 -0
- printer_core/entities/print_context.py +15 -0
- printer_core/entities/print_models.py +230 -0
- printer_core/formatters/__init__.py +1 -0
- printer_core/formatters/discount_formatter.py +17 -0
- printer_core/ports.py +45 -0
- printer_core/selectors/__init__.py +1 -0
- printer_core/selectors/family_product_selector.py +33 -0
- printer_core/services/__init__.py +12 -0
- printer_core/services/flows/__init__.py +5 -0
- printer_core/services/flows/base.py +26 -0
- printer_core/services/flows/client.py +97 -0
- printer_core/services/flows/kitchen.py +113 -0
- printer_core/services/march_printer_service.py +61 -0
- printer_core/services/multi_printer_family_service.py +102 -0
- printer_core/services/multi_printer_service.py +49 -0
- printer_core/services/order_print_service.py +98 -0
- printer_core/services/print_policy.py +72 -0
- printer_core/services/unique_ticket_service.py +52 -0
- printer_core/tests/__init__.py +0 -0
- printer_core/tests/unitaries/__init__.py +0 -0
- printer_core/tests/unitaries/helpers.py +188 -0
- printer_core/tests/unitaries/test_cases_output.py +189 -0
- printer_core/tests/unitaries/test_client_ticket_builder.py +78 -0
- printer_core/tests/unitaries/test_discount_formatter.py +37 -0
- printer_core/tests/unitaries/test_family_product_selector.py +28 -0
- printer_core/tests/unitaries/test_flows.py +141 -0
- printer_core/tests/unitaries/test_header_builder_foodeo_printers.py +87 -0
- printer_core/tests/unitaries/test_kitchen_ticket_dispatcher.py +61 -0
- printer_core/tests/unitaries/test_kitchen_ticket_payload_builder.py +113 -0
- printer_core/tests/unitaries/test_march_printer_service.py +31 -0
- printer_core/tests/unitaries/test_multi_printer_family_service.py +53 -0
- printer_core/tests/unitaries/test_multi_printer_service.py +51 -0
- printer_core/tests/unitaries/test_order_print_service.py +144 -0
- printer_core/tests/unitaries/test_payload_dispatcher.py +61 -0
- printer_core/tests/unitaries/test_print_policy.py +45 -0
- printer_core/tests/unitaries/test_printer_helpers.py +71 -0
- printer_core/tests/unitaries/test_printer_products_grouper.py +20 -0
- printer_core/tests/unitaries/test_products_modifiers.py +102 -0
- printer_core/tests/unitaries/test_ticket_context_builder.py +71 -0
- printer_core/tests/unitaries/test_unique_ticket_service.py +24 -0
- printer_core/utils/__init__.py +1 -0
- printer_core/utils/printer_helpers.py +42 -0
- printer_core-0.0.1.dist-info/METADATA +24 -0
- printer_core-0.0.1.dist-info/RECORD +62 -0
- printer_core-0.0.1.dist-info/WHEEL +4 -0
printer_core/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# printer_core package
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from printer_core.entities import KitchenProductLine, PrintProductData, PrintRequestData, PrinterData
|
|
4
|
+
from printer_core.formatters.discount_formatter import DiscountFormatter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ProductsModifiersMultiPrinter:
|
|
8
|
+
def __init__(self, formatter: DiscountFormatter):
|
|
9
|
+
self._formatter = formatter
|
|
10
|
+
|
|
11
|
+
def build(
|
|
12
|
+
self,
|
|
13
|
+
request: PrintRequestData,
|
|
14
|
+
products: list[PrintProductData],
|
|
15
|
+
kitchen_ticket_settings: Any,
|
|
16
|
+
main_printer: PrinterData | dict[str, Any],
|
|
17
|
+
) -> list[KitchenProductLine]:
|
|
18
|
+
list_products_own_printer: list[KitchenProductLine] = []
|
|
19
|
+
fallback_printer = self._resolve_printer(main_printer)
|
|
20
|
+
for product in products:
|
|
21
|
+
printers = list(product.printer or [])
|
|
22
|
+
if not printers:
|
|
23
|
+
printers = [fallback_printer]
|
|
24
|
+
for actual_printer in printers:
|
|
25
|
+
printer_payload = self._resolve_printer(actual_printer)
|
|
26
|
+
name = product.product_name
|
|
27
|
+
if kitchen_ticket_settings.show_price_per_unit and not request.all_discounted:
|
|
28
|
+
name += self._formatter.format(product, request.currency or "")
|
|
29
|
+
line = KitchenProductLine(
|
|
30
|
+
name=name,
|
|
31
|
+
qty=int(product.qty),
|
|
32
|
+
option=product.option,
|
|
33
|
+
printer_r=printer_payload.name,
|
|
34
|
+
printer_w=printer_payload.printer_width,
|
|
35
|
+
details=product.details or "",
|
|
36
|
+
order=product.order or "",
|
|
37
|
+
tpv=printer_payload.tpv,
|
|
38
|
+
order_readable=product.order_readable or "",
|
|
39
|
+
)
|
|
40
|
+
if kitchen_ticket_settings.show_price_per_unit:
|
|
41
|
+
line = line.model_copy(update={
|
|
42
|
+
"price": product.total_price,
|
|
43
|
+
"unitary_price": product.product_price,
|
|
44
|
+
})
|
|
45
|
+
list_products_own_printer.append(line)
|
|
46
|
+
return list_products_own_printer
|
|
47
|
+
|
|
48
|
+
def _resolve_printer(self, printer: PrinterData | dict[str, Any]) -> PrinterData:
|
|
49
|
+
if isinstance(printer, PrinterData):
|
|
50
|
+
return printer
|
|
51
|
+
if isinstance(printer, dict):
|
|
52
|
+
return PrinterData(**printer)
|
|
53
|
+
if hasattr(printer, "model_dump"):
|
|
54
|
+
return PrinterData(**printer.model_dump(mode="json", exclude_none=True))
|
|
55
|
+
return PrinterData(
|
|
56
|
+
name=printer.name,
|
|
57
|
+
printer_width=printer.printer_width,
|
|
58
|
+
tpv=getattr(printer, "tpv", None),
|
|
59
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from printer_core.entities import KitchenProductLine, PrintProductData, PrintRequestData, PrinterData
|
|
4
|
+
from printer_core.formatters.discount_formatter import DiscountFormatter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ProductsModifiersMultiprinterFamily:
|
|
8
|
+
def __init__(self, formatter: DiscountFormatter):
|
|
9
|
+
self._formatter = formatter
|
|
10
|
+
|
|
11
|
+
def append_product(self,
|
|
12
|
+
list_products_own_printer: list[KitchenProductLine],
|
|
13
|
+
product: PrintProductData,
|
|
14
|
+
request: PrintRequestData,
|
|
15
|
+
kitchen_ticket_settings: Any,
|
|
16
|
+
) -> None:
|
|
17
|
+
for actual_printer in product.printer:
|
|
18
|
+
printer_payload = self._resolve_printer(actual_printer)
|
|
19
|
+
name_with_discount: str = product.product_name
|
|
20
|
+
if not request.all_discounted:
|
|
21
|
+
name_with_discount += self._formatter.format(product, request.currency or "")
|
|
22
|
+
line = KitchenProductLine(
|
|
23
|
+
name=name_with_discount,
|
|
24
|
+
qty=int(product.qty),
|
|
25
|
+
option=product.option,
|
|
26
|
+
printer_r=printer_payload.name,
|
|
27
|
+
printer_w=printer_payload.printer_width,
|
|
28
|
+
details=product.details or "",
|
|
29
|
+
order=product.order or "",
|
|
30
|
+
tpv=printer_payload.tpv,
|
|
31
|
+
order_readable=product.order_readable or "",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if kitchen_ticket_settings.show_price_per_unit:
|
|
35
|
+
line = line.model_copy(update={
|
|
36
|
+
"price": product.total_price,
|
|
37
|
+
"unitary_price": product.product_price,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
list_products_own_printer.append(line)
|
|
41
|
+
|
|
42
|
+
def _resolve_printer(self, printer: PrinterData | dict[str, Any]) -> PrinterData:
|
|
43
|
+
if isinstance(printer, PrinterData):
|
|
44
|
+
return printer
|
|
45
|
+
if isinstance(printer, dict):
|
|
46
|
+
return PrinterData(**printer)
|
|
47
|
+
if hasattr(printer, "model_dump"):
|
|
48
|
+
return PrinterData(**printer.model_dump(mode="json", exclude_none=True))
|
|
49
|
+
return PrinterData(
|
|
50
|
+
name=printer.name,
|
|
51
|
+
printer_width=printer.printer_width,
|
|
52
|
+
tpv=getattr(printer, "tpv", None),
|
|
53
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from printer_core.entities import ClientTicketProduct, PrintProductData, PrintRequestData
|
|
2
|
+
from printer_core.formatters.discount_formatter import DiscountFormatter
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ProductsModifiersUniqueTicket:
|
|
6
|
+
def __init__(self, formatter: DiscountFormatter, domicile_service_label: str = "Domicile Service"):
|
|
7
|
+
self._formatter = formatter
|
|
8
|
+
self._domicile_service_label = domicile_service_label
|
|
9
|
+
|
|
10
|
+
def build(self, request: PrintRequestData, products: list[PrintProductData]) -> list[ClientTicketProduct]:
|
|
11
|
+
list_products_in_same_printers: list[ClientTicketProduct] = []
|
|
12
|
+
for product in products:
|
|
13
|
+
with_discount = ""
|
|
14
|
+
if not request.all_discounted:
|
|
15
|
+
with_discount = self._formatter.format(product, request.currency or "")
|
|
16
|
+
product_name = product.product_name + with_discount
|
|
17
|
+
list_products_in_same_printers.append(ClientTicketProduct(
|
|
18
|
+
name=product_name,
|
|
19
|
+
qty=int(product.qty),
|
|
20
|
+
option=product.option,
|
|
21
|
+
price=str(product.total_price),
|
|
22
|
+
unitary_price=str(product.product_price),
|
|
23
|
+
))
|
|
24
|
+
if request.price_domicile_service:
|
|
25
|
+
list_products_in_same_printers.append(ClientTicketProduct(
|
|
26
|
+
name=self._domicile_service_label,
|
|
27
|
+
qty=1,
|
|
28
|
+
price=str(request.price_domicile_service),
|
|
29
|
+
))
|
|
30
|
+
return list_products_in_same_printers
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
from printer_core.entities import (
|
|
5
|
+
ClientTicketData,
|
|
6
|
+
ClientTicketPayload,
|
|
7
|
+
ClientTicketProduct,
|
|
8
|
+
PrintProductData,
|
|
9
|
+
PrintRequestData,
|
|
10
|
+
PrinterData,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ClientTicketBuilder:
|
|
15
|
+
def __init__(self,
|
|
16
|
+
currency_symbol_resolver: Callable[[str], str] | None = None,
|
|
17
|
+
domicile_service_label: str = "Domicile Service",
|
|
18
|
+
):
|
|
19
|
+
self._currency_symbol_resolver = currency_symbol_resolver or (lambda _: "")
|
|
20
|
+
self._domicile_service_label = domicile_service_label
|
|
21
|
+
|
|
22
|
+
def build(self,
|
|
23
|
+
request: PrintRequestData,
|
|
24
|
+
products: list[PrintProductData],
|
|
25
|
+
printer: PrinterData | dict[str, Any],
|
|
26
|
+
ticket_settings: Any,
|
|
27
|
+
tpv_key: str,
|
|
28
|
+
) -> list[ClientTicketPayload]:
|
|
29
|
+
ticket_text = self._get_attr(ticket_settings, "ticket_text", "")
|
|
30
|
+
qty_client_ticket = self._get_attr(ticket_settings, "qty_client_ticket", 1)
|
|
31
|
+
client_request = ClientTicketData(
|
|
32
|
+
**request.model_dump(exclude={"products", "ticket_text"}),
|
|
33
|
+
ticket_text=ticket_text,
|
|
34
|
+
products=self._build_products(request, products),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if client_request.type in ["domicilio", "recoger"]:
|
|
38
|
+
client_request = client_request.model_copy(update={"details": client_request.details or ""})
|
|
39
|
+
elif client_request.details:
|
|
40
|
+
client_request = client_request.model_copy(update={"details": None})
|
|
41
|
+
|
|
42
|
+
printer_name, printer_width = self._resolve_printer(printer)
|
|
43
|
+
payload = ClientTicketPayload(
|
|
44
|
+
data=client_request,
|
|
45
|
+
currency=client_request.currency or "",
|
|
46
|
+
model=2,
|
|
47
|
+
company_key=tpv_key,
|
|
48
|
+
printer=printer_name,
|
|
49
|
+
printer_qty=qty_client_ticket,
|
|
50
|
+
printer_width=printer_width,
|
|
51
|
+
)
|
|
52
|
+
return [payload]
|
|
53
|
+
|
|
54
|
+
def _build_products(
|
|
55
|
+
self,
|
|
56
|
+
request: PrintRequestData,
|
|
57
|
+
products: list[PrintProductData],
|
|
58
|
+
) -> list[ClientTicketProduct]:
|
|
59
|
+
list_products: list[ClientTicketProduct] = []
|
|
60
|
+
currency_symbol = self._currency_symbol_resolver(request.currency or "")
|
|
61
|
+
for product in products:
|
|
62
|
+
with_discount = ""
|
|
63
|
+
if not request.all_discounted:
|
|
64
|
+
with_discount = self._format_discount(product, currency_symbol)
|
|
65
|
+
product_name = product.product_name + with_discount
|
|
66
|
+
list_products.append(ClientTicketProduct(
|
|
67
|
+
name=product_name,
|
|
68
|
+
qty=int(product.qty),
|
|
69
|
+
option=product.option,
|
|
70
|
+
price=str(product.total_price),
|
|
71
|
+
unitary_price=str(product.product_price),
|
|
72
|
+
))
|
|
73
|
+
if request.price_domicile_service:
|
|
74
|
+
list_products.append(ClientTicketProduct(
|
|
75
|
+
name=self._domicile_service_label,
|
|
76
|
+
qty=1,
|
|
77
|
+
price=str(request.price_domicile_service),
|
|
78
|
+
))
|
|
79
|
+
return list_products
|
|
80
|
+
|
|
81
|
+
def _format_discount(self, product: PrintProductData, currency_symbol: str) -> str:
|
|
82
|
+
if product.discount_type == "number":
|
|
83
|
+
return f" (Dto {product.total_price}{currency_symbol})"
|
|
84
|
+
if product.discount_type == "percent" and product.discount:
|
|
85
|
+
total = Decimal(str(product.importe)) - Decimal(str(product.total_price))
|
|
86
|
+
return f" (Dto {product.discount.value}% {total}{currency_symbol})"
|
|
87
|
+
return ""
|
|
88
|
+
|
|
89
|
+
def _get_attr(self, obj: Any, attr: str, default: Any) -> Any:
|
|
90
|
+
if isinstance(obj, dict):
|
|
91
|
+
return obj.get(attr, default)
|
|
92
|
+
return getattr(obj, attr, default)
|
|
93
|
+
|
|
94
|
+
def _resolve_printer(self, printer: PrinterData | dict[str, Any]) -> tuple[str, Any]:
|
|
95
|
+
if isinstance(printer, dict):
|
|
96
|
+
return printer["name"], printer["printer_width"]
|
|
97
|
+
return printer.name, printer.printer_width
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from printer_core.entities import HeaderData, PrintRequestData
|
|
4
|
+
from printer_core.ports import HeaderDataProvider
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HeaderBuilder:
|
|
8
|
+
def __init__(self, provider: HeaderDataProvider, show_person_name: bool):
|
|
9
|
+
self._provider = provider
|
|
10
|
+
self._show_person_name = show_person_name
|
|
11
|
+
|
|
12
|
+
def build_header(self, request: PrintRequestData, worker: Any | None = None) -> HeaderData:
|
|
13
|
+
header_data: dict[str, str] = self._provider.get_company_header()
|
|
14
|
+
if self._show_person_name and worker:
|
|
15
|
+
header_data.update(self._provider.get_worker_header(worker))
|
|
16
|
+
header_data.update(self._client_header(request))
|
|
17
|
+
return HeaderData(**header_data)
|
|
18
|
+
|
|
19
|
+
def _client_header(self, request: PrintRequestData) -> dict[str, str]:
|
|
20
|
+
request_type = request.type
|
|
21
|
+
from_client = request.from_client
|
|
22
|
+
if request_type == "recoger":
|
|
23
|
+
return self._provider.get_pickup_client_header(request.client, from_client)
|
|
24
|
+
if request_type == "domicilio":
|
|
25
|
+
return self._provider.get_domicile_client_header(request.address, from_client)
|
|
26
|
+
return {}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Callable
|
|
3
|
+
|
|
4
|
+
from printer_core.clock import SystemClock
|
|
5
|
+
from printer_core.entities import (
|
|
6
|
+
KitchenProductLine,
|
|
7
|
+
KitchenTicketData,
|
|
8
|
+
KitchenTicketPayload,
|
|
9
|
+
PrintRequestData,
|
|
10
|
+
PrintSettingsData,
|
|
11
|
+
ProductsGroup,
|
|
12
|
+
)
|
|
13
|
+
from printer_core.ports import Clock
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class KitchenTicketPayloadBuilder:
|
|
17
|
+
def __init__(self, clock: Clock | None = None):
|
|
18
|
+
self._clock = clock or SystemClock()
|
|
19
|
+
|
|
20
|
+
def build(self,
|
|
21
|
+
request: PrintRequestData,
|
|
22
|
+
printer_name: str,
|
|
23
|
+
tpv_key: str,
|
|
24
|
+
printer_width: str,
|
|
25
|
+
products: list[KitchenProductLine],
|
|
26
|
+
ticket_settings: Any,
|
|
27
|
+
kitchen_ticket_settings: Any,
|
|
28
|
+
group_products_func: Callable[[list[KitchenProductLine]], list[ProductsGroup]],
|
|
29
|
+
) -> KitchenTicketPayload:
|
|
30
|
+
data = KitchenTicketData(
|
|
31
|
+
id=request.id,
|
|
32
|
+
table_name=request.table_name,
|
|
33
|
+
credit_card=request.credit_card,
|
|
34
|
+
serial=request.serial,
|
|
35
|
+
details=request.details or "",
|
|
36
|
+
type=request.type,
|
|
37
|
+
discount=request.discount,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
data = data.model_copy(update={"created_at": self._resolve_created_at(request)})
|
|
41
|
+
|
|
42
|
+
if kitchen_ticket_settings.show_total_price:
|
|
43
|
+
data = data.model_copy(update={
|
|
44
|
+
"total": str(request.total),
|
|
45
|
+
"request_price": request.request_price,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if request.type == "local" and kitchen_ticket_settings.show_command_guests:
|
|
49
|
+
data = data.model_copy(update={"command_guests": request.command_guests})
|
|
50
|
+
|
|
51
|
+
if request.headerData:
|
|
52
|
+
data = data.model_copy(update={"headerData": request.headerData})
|
|
53
|
+
|
|
54
|
+
model = 1
|
|
55
|
+
if request.type != "local":
|
|
56
|
+
model = 2
|
|
57
|
+
|
|
58
|
+
data = self._set_products(data, request, products, group_products_func)
|
|
59
|
+
print_settings = self._set_print_settings(request)
|
|
60
|
+
|
|
61
|
+
return KitchenTicketPayload(
|
|
62
|
+
data=data,
|
|
63
|
+
model=model,
|
|
64
|
+
printer_qty=ticket_settings.qty_kitchen_ticket,
|
|
65
|
+
printer=printer_name,
|
|
66
|
+
company_key=tpv_key,
|
|
67
|
+
printer_width=printer_width,
|
|
68
|
+
currency=request.currency or "",
|
|
69
|
+
PrintSettings=print_settings,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def _resolve_created_at(self, request: PrintRequestData) -> str:
|
|
73
|
+
if request.type in ["domicilio", "recoger"] and request.shift_time and request.delivery_date:
|
|
74
|
+
shift_time = datetime.strptime(request.shift_time, "%H:%M").time()
|
|
75
|
+
delivery_date = datetime.strptime(request.delivery_date, "%Y-%m-%d").date()
|
|
76
|
+
return datetime.combine(delivery_date, shift_time).strftime("%Y-%m-%d %H:%M")
|
|
77
|
+
return self._clock.now_iso()
|
|
78
|
+
|
|
79
|
+
def _set_products(self,
|
|
80
|
+
data: KitchenTicketData,
|
|
81
|
+
request: PrintRequestData,
|
|
82
|
+
products: list[KitchenProductLine],
|
|
83
|
+
group_products_func: Callable[[list[KitchenProductLine]], list[ProductsGroup]],
|
|
84
|
+
) -> KitchenTicketData:
|
|
85
|
+
group_by_orders = request.group_by_orders
|
|
86
|
+
if group_by_orders and request.type == "local":
|
|
87
|
+
grouped = group_products_func(products)
|
|
88
|
+
if grouped and not isinstance(grouped[0], ProductsGroup):
|
|
89
|
+
grouped = [ProductsGroup(group="Sin orden", products=grouped)]
|
|
90
|
+
return data.model_copy(update={"products_groups": grouped})
|
|
91
|
+
return data.model_copy(update={"products": products})
|
|
92
|
+
|
|
93
|
+
def _set_print_settings(self, request: PrintRequestData) -> PrintSettingsData | None:
|
|
94
|
+
print_settings = request.PrintSettings
|
|
95
|
+
if not print_settings:
|
|
96
|
+
return None
|
|
97
|
+
if request.type == "local" and print_settings.show_local_header is not None:
|
|
98
|
+
return print_settings.model_copy(update={"show_header": print_settings.show_local_header})
|
|
99
|
+
return print_settings
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from printer_core.entities import KitchenProductLine, PrinterProductGroup
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PrinterProductsGrouper:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def group_by_printer_key(products: list[KitchenProductLine]) -> list[PrinterProductGroup]:
|
|
7
|
+
grouped: dict[str, PrinterProductGroup] = {}
|
|
8
|
+
for item in products:
|
|
9
|
+
key = f"{item.printer_r}%%{item.tpv or ''}%%{item.printer_w}"
|
|
10
|
+
if key not in grouped:
|
|
11
|
+
grouped[key] = PrinterProductGroup(
|
|
12
|
+
printer_name=item.printer_r,
|
|
13
|
+
tpv_key=item.tpv or "",
|
|
14
|
+
printer_width=item.printer_w,
|
|
15
|
+
products=[item],
|
|
16
|
+
)
|
|
17
|
+
else:
|
|
18
|
+
grouped[key].products.append(item)
|
|
19
|
+
return list(grouped.values())
|
printer_core/clock.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .ticket_context_builder import TicketContextBuilder
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from printer_core.entities import PrintRequestData, PrintSettingsData
|
|
4
|
+
from printer_core.ports import HeaderProvider, PrintSettingsBuilder
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TicketContextBuilder:
|
|
8
|
+
def __init__(self, header_provider: HeaderProvider, print_settings_builder: PrintSettingsBuilder):
|
|
9
|
+
self._header_provider = header_provider
|
|
10
|
+
self._print_settings_builder = print_settings_builder
|
|
11
|
+
|
|
12
|
+
def apply_kitchen_context(self,
|
|
13
|
+
request: PrintRequestData,
|
|
14
|
+
ticket_settings: Any,
|
|
15
|
+
kitchen_ticket_settings: Any,
|
|
16
|
+
header_mode: str,
|
|
17
|
+
include_print_by: bool = True,
|
|
18
|
+
worker: Any | None = None,
|
|
19
|
+
) -> PrintRequestData:
|
|
20
|
+
updates: dict[str, Any] = {
|
|
21
|
+
"order_by": ticket_settings.order_by if ticket_settings.order_by else "name",
|
|
22
|
+
"group_by_orders": kitchen_ticket_settings.group_by_orders,
|
|
23
|
+
"PrintSettings": self._print_settings_builder.build(kitchen_ticket_settings),
|
|
24
|
+
}
|
|
25
|
+
if include_print_by:
|
|
26
|
+
updates["print_by"] = kitchen_ticket_settings.print_by
|
|
27
|
+
if self._should_add_header(kitchen_ticket_settings, header_mode):
|
|
28
|
+
header_data = self._header_provider.build_header(request, worker=worker)
|
|
29
|
+
updates["headerData"] = header_data
|
|
30
|
+
return request.model_copy(update=updates)
|
|
31
|
+
|
|
32
|
+
def apply_client_context(self,
|
|
33
|
+
request: PrintRequestData,
|
|
34
|
+
kitchen_ticket_settings: Any,
|
|
35
|
+
include_header: bool = True,
|
|
36
|
+
details_font_none: bool = True,
|
|
37
|
+
worker: Any | None = None,
|
|
38
|
+
) -> PrintRequestData:
|
|
39
|
+
print_settings: PrintSettingsData = self._print_settings_builder.build(kitchen_ticket_settings)
|
|
40
|
+
if details_font_none:
|
|
41
|
+
print_settings = print_settings.model_copy(update={"details_font": None})
|
|
42
|
+
updates: dict[str, Any] = {"PrintSettings": print_settings}
|
|
43
|
+
if include_header:
|
|
44
|
+
header_data = self._header_provider.build_header(request, worker=worker)
|
|
45
|
+
updates["headerData"] = header_data
|
|
46
|
+
return request.model_copy(update=updates)
|
|
47
|
+
|
|
48
|
+
def _should_add_header(self, kitchen_ticket_settings: Any, header_mode: str) -> bool:
|
|
49
|
+
match header_mode:
|
|
50
|
+
case "local":
|
|
51
|
+
return kitchen_ticket_settings.show_local_header
|
|
52
|
+
case "domicile":
|
|
53
|
+
return kitchen_ticket_settings.show_domicile_header
|
|
54
|
+
case _:
|
|
55
|
+
return False
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
|
|
3
|
+
from printer_core.builders.kitchen_ticket_payload_builder import KitchenTicketPayloadBuilder
|
|
4
|
+
from printer_core.entities import KitchenProductLine, KitchenTicketPayload, PrintRequestData, PrinterProductGroup, ProductsGroup
|
|
5
|
+
from printer_core.ports import PrinterGateway
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KitchenTicketDispatcher:
|
|
9
|
+
def __init__(self, gateway: PrinterGateway, builder: KitchenTicketPayloadBuilder | None = None):
|
|
10
|
+
self._builder = builder or KitchenTicketPayloadBuilder()
|
|
11
|
+
self._gateway = gateway
|
|
12
|
+
|
|
13
|
+
def dispatch(self,
|
|
14
|
+
request: PrintRequestData,
|
|
15
|
+
grouped_products: list[PrinterProductGroup],
|
|
16
|
+
ticket_settings: Any,
|
|
17
|
+
kitchen_ticket_settings: Any,
|
|
18
|
+
group_products_func: Callable[[list[KitchenProductLine]], list[ProductsGroup]],
|
|
19
|
+
) -> list[KitchenTicketPayload]:
|
|
20
|
+
final_request: list[KitchenTicketPayload] = []
|
|
21
|
+
for group in grouped_products:
|
|
22
|
+
payload = self._builder.build(
|
|
23
|
+
request=request,
|
|
24
|
+
printer_name=group.printer_name,
|
|
25
|
+
tpv_key=group.tpv_key,
|
|
26
|
+
printer_width=group.printer_width,
|
|
27
|
+
products=group.products,
|
|
28
|
+
ticket_settings=ticket_settings,
|
|
29
|
+
kitchen_ticket_settings=kitchen_ticket_settings,
|
|
30
|
+
group_products_func=group_products_func,
|
|
31
|
+
)
|
|
32
|
+
final_request.append(payload)
|
|
33
|
+
self._gateway.send_data(group.tpv_key, [payload.model_dump(mode="json", exclude_none=True)])
|
|
34
|
+
return final_request
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from printer_core.ports import PrinterGateway
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PayloadDispatcher:
|
|
7
|
+
def __init__(self, gateway: PrinterGateway):
|
|
8
|
+
self._gateway = gateway
|
|
9
|
+
|
|
10
|
+
def send(self, tpv_key: str, payload: Any) -> None:
|
|
11
|
+
if hasattr(payload, "model_dump"):
|
|
12
|
+
payload = payload.model_dump(mode="json", exclude_none=True)
|
|
13
|
+
if isinstance(payload, list):
|
|
14
|
+
payload = [
|
|
15
|
+
item.model_dump(mode="json", exclude_none=True) if hasattr(item, "model_dump") else item
|
|
16
|
+
for item in payload
|
|
17
|
+
]
|
|
18
|
+
self._gateway.send_data(tpv_key, payload)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from .print_context import PrintContext
|
|
2
|
+
from .print_models import (
|
|
3
|
+
ClientTicketData,
|
|
4
|
+
ClientTicketPayload,
|
|
5
|
+
ClientTicketProduct,
|
|
6
|
+
CompanyHeaderData,
|
|
7
|
+
DiscountData,
|
|
8
|
+
FamilyData,
|
|
9
|
+
FiskalyData,
|
|
10
|
+
HeaderData,
|
|
11
|
+
IvaData,
|
|
12
|
+
KitchenProductLine,
|
|
13
|
+
KitchenTicketData,
|
|
14
|
+
KitchenTicketPayload,
|
|
15
|
+
MarchCommandData,
|
|
16
|
+
PrintSettingsData,
|
|
17
|
+
PrintChannel,
|
|
18
|
+
PrintJob,
|
|
19
|
+
PrintResult,
|
|
20
|
+
PrintProductData,
|
|
21
|
+
PrintRequestData,
|
|
22
|
+
PrinterData,
|
|
23
|
+
PrinterProductGroup,
|
|
24
|
+
ProductsGroup,
|
|
25
|
+
WorkerData,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ClientTicketPayload",
|
|
30
|
+
"ClientTicketProduct",
|
|
31
|
+
"ClientTicketData",
|
|
32
|
+
"CompanyHeaderData",
|
|
33
|
+
"DiscountData",
|
|
34
|
+
"FamilyData",
|
|
35
|
+
"FiskalyData",
|
|
36
|
+
"HeaderData",
|
|
37
|
+
"IvaData",
|
|
38
|
+
"KitchenProductLine",
|
|
39
|
+
"KitchenTicketData",
|
|
40
|
+
"KitchenTicketPayload",
|
|
41
|
+
"MarchCommandData",
|
|
42
|
+
"PrintContext",
|
|
43
|
+
"PrintSettingsData",
|
|
44
|
+
"PrintChannel",
|
|
45
|
+
"PrintJob",
|
|
46
|
+
"PrintResult",
|
|
47
|
+
"PrintProductData",
|
|
48
|
+
"PrintRequestData",
|
|
49
|
+
"PrinterData",
|
|
50
|
+
"PrinterProductGroup",
|
|
51
|
+
"ProductsGroup",
|
|
52
|
+
"WorkerData",
|
|
53
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from printer_core.entities.print_models import PrinterData, WorkerData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class PrintContext:
|
|
11
|
+
ticket_settings: Any
|
|
12
|
+
kitchen_settings: Any
|
|
13
|
+
global_settings: Any
|
|
14
|
+
main_printer: PrinterData
|
|
15
|
+
worker: WorkerData | None = None
|