feat(19-ui-integration): przycisk Drukuj, bulk print, kolejka wydruku

- Przycisk "Drukuj" w prepare.php i show.php z AJAX + duplikat protection
- Bulk print z listy zamówień (checkboxy + header action)
- Kolejka wydruku w Ustawienia > Drukowanie (filtr statusu, retry)
- POST /api/print/jobs/bulk endpoint (package_ids + order_ids)
- ensureLabel() auto-download przez ShipmentProviderRegistry
- Apaczka carrier_id = nazwa usługi, kolumna Przewoznik
- Tab persistence (localStorage), label file_exists check
- Fix use statement ApaczkaApiClient, redirect po utworzeniu przesyłki
- Phase 17 (receipt duplicate guard) + Phase 18 (print queue backend) docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 21:16:54 +01:00
parent d1a1b79247
commit 02d06298ea
33 changed files with 2623 additions and 117 deletions

View File

@@ -30,7 +30,9 @@ final class OrdersController
private readonly ?ReceiptConfigRepository $receiptConfigRepo = null,
private readonly ?EmailSendingService $emailService = null,
private readonly ?EmailTemplateRepository $emailTemplateRepo = null,
private readonly ?EmailMailboxRepository $emailMailboxRepo = null
private readonly ?EmailMailboxRepository $emailMailboxRepo = null,
private readonly string $storagePath = '',
private readonly ?\App\Modules\Printing\PrintJobRepository $printJobRepo = null
) {
}
@@ -131,6 +133,17 @@ final class OrdersController
'per_page' => (int) ($result['per_page'] ?? 20),
],
'per_page_options' => [20, 50, 100],
'selectable' => true,
'select_name' => 'selected_ids[]',
'select_value_key' => 'id',
'header_actions' => [
[
'type' => 'button',
'label' => 'Drukuj etykiety',
'class' => 'btn btn--secondary js-bulk-print-labels',
'attrs' => ['data-csrf' => Csrf::token()],
],
],
'empty_message' => $this->translator->get('orders.empty'),
'show_actions' => false,
],
@@ -172,6 +185,16 @@ final class OrdersController
? $this->shipmentPackages->findByOrderId($orderId)
: [];
if ($this->storagePath !== '') {
foreach ($packages as &$pkg) {
$lp = trim((string) ($pkg['label_path'] ?? ''));
if ($lp !== '' && !file_exists($this->storagePath . '/' . $lp)) {
$pkg['label_path'] = '';
}
}
unset($pkg);
}
$receipts = $this->receiptRepo !== null
? $this->receiptRepo->findByOrderId($orderId)
: [];
@@ -203,6 +226,7 @@ final class OrdersController
'payments' => $payments,
'shipments' => $shipments,
'packages' => $packages,
'pendingPrintPackageIds' => $this->printJobRepo !== null ? $this->printJobRepo->pendingPackageIds() : [],
'documents' => $documents,
'notes' => $notes,
'history' => $resolvedHistory,
@@ -282,6 +306,7 @@ final class OrdersController
$itemsPreview = is_array($row['items_preview'] ?? null) ? $row['items_preview'] : [];
return [
'id' => (int) ($row['id'] ?? 0),
'order_ref' => '<div class="orders-ref">'
. '<div class="orders-ref__main"><a href="/orders/' . (int) ($row['id'] ?? 0) . '">'
. htmlspecialchars($internalOrderNumber !== '' ? $internalOrderNumber : ('#' . (string) ($row['id'] ?? 0)), ENT_QUOTES, 'UTF-8')