service = $service; $this->productRepo = $productRepo; } public function list(): string { return $this->view_list(); } public function view_list(): string { $sortableColumns = [ 'number', 'date_order', 'status', 'summary', 'client', 'order_email', 'client_phone', 'transport', 'payment_method', 'total_orders', 'paid', ]; $statusOptions = ['' => '- status -']; foreach ($this->service->statuses() as $statusId => $statusName) { $statusOptions[(string)$statusId] = (string)$statusName; } $filterDefinitions = [ ['key' => 'number', 'label' => 'Nr zamówienia', 'type' => 'text'], ['key' => 'date_from', 'label' => 'Data od', 'type' => 'date'], ['key' => 'date_to', 'label' => 'Data do', 'type' => 'date'], ['key' => 'status', 'label' => 'Status', 'type' => 'select', 'options' => $statusOptions], ['key' => 'client', 'label' => 'Klient', 'type' => 'text'], ['key' => 'address', 'label' => 'Adres', 'type' => 'text'], ['key' => 'order_email', 'label' => 'Email', 'type' => 'text'], ['key' => 'client_phone', 'label' => 'Telefon', 'type' => 'text'], ['key' => 'transport', 'label' => 'Dostawa', 'type' => 'text'], ['key' => 'payment_method', 'label' => 'Płatność', 'type' => 'text'], ]; $listRequest = \admin\Support\TableListRequestFactory::fromRequest( $filterDefinitions, $sortableColumns, 'date_order' ); $result = $this->service->listForAdmin( $listRequest['filters'], $listRequest['sortColumn'], $listRequest['sortDir'], $listRequest['page'], $listRequest['perPage'] ); $statusData = $this->service->statusData(); $statusesMap = $statusData['names']; $statusColorsMap = $statusData['colors']; $rows = []; $lp = ($listRequest['page'] - 1) * $listRequest['perPage'] + 1; foreach ($result['items'] as $item) { $orderId = (int)($item['id'] ?? 0); $orderNumber = (string)($item['number'] ?? ''); $statusId = (int)($item['status'] ?? 0); $statusLabel = htmlspecialchars((string)($statusesMap[$statusId] ?? ('Status #' . $statusId)), ENT_QUOTES, 'UTF-8'); $statusColor = isset($statusColorsMap[$statusId]) ? $statusColorsMap[$statusId] : ''; if ($statusColor !== '') { $textColor = $this->contrastTextColor($statusColor); $statusHtml = '' . $statusLabel . ''; } else { $statusHtml = $statusLabel; } $rows[] = [ 'lp' => $lp++ . '.', 'date_order' => $this->formatDateTime((string)($item['date_order'] ?? '')), 'number' => '' . htmlspecialchars($orderNumber, ENT_QUOTES, 'UTF-8') . '', 'paid' => ((int)($item['paid'] ?? 0) === 1) ? '' : '', 'status' => $statusHtml, 'summary' => number_format((float)($item['summary'] ?? 0), 2, '.', ' ') . ' zł', 'client' => htmlspecialchars((string)($item['client'] ?? ''), ENT_QUOTES, 'UTF-8') . ' | zamówienia: ' . (int)($item['total_orders'] ?? 0) . '', 'address' => (string)($item['address'] ?? ''), 'order_email' => (string)($item['order_email'] ?? ''), 'client_phone' => (string)($item['client_phone'] ?? ''), 'transport' => $this->sanitizeInlineHtml((string)($item['transport'] ?? '')), 'payment_method' => (string)($item['payment_method'] ?? ''), '_actions' => [ [ 'label' => 'Szczegóły', 'url' => '/admin/shop_order/order_details/order_id=' . $orderId, 'class' => 'btn btn-xs btn-primary', ], [ 'label' => 'Usuń', 'url' => '/admin/shop_order/order_delete/id=' . $orderId, 'class' => 'btn btn-xs btn-danger', 'confirm' => 'Na pewno chcesz usunąć wybrane zamówienie?', 'confirm_ok' => 'Usuń', 'confirm_cancel' => 'Anuluj', ], ], ]; } $total = (int)$result['total']; $totalPages = max(1, (int)ceil($total / $listRequest['perPage'])); $viewModel = new PaginatedTableViewModel( [ ['key' => 'lp', 'label' => 'Lp.', 'class' => 'text-center', 'sortable' => false], ['key' => 'date_order', 'sort_key' => 'date_order', 'label' => 'Data dodania', 'class' => 'text-center', 'sortable' => true], ['key' => 'number', 'sort_key' => 'number', 'label' => 'Nr zamówienia', 'class' => 'text-center', 'sortable' => true, 'raw' => true], ['key' => 'paid', 'sort_key' => 'paid', 'label' => '', 'class' => 'text-center', 'sortable' => true, 'raw' => true], ['key' => 'status', 'sort_key' => 'status', 'label' => 'Status', 'sortable' => true, 'raw' => true], ['key' => 'summary', 'sort_key' => 'summary', 'label' => 'Wartość', 'class' => 'text-right align-middle', 'sortable' => true], ['key' => 'client', 'sort_key' => 'client', 'label' => 'Klient', 'sortable' => true, 'raw' => true], ['key' => 'address', 'label' => 'Adres', 'sortable' => false], ['key' => 'order_email', 'sort_key' => 'order_email', 'label' => 'Email', 'sortable' => true], ['key' => 'client_phone', 'sort_key' => 'client_phone', 'label' => 'Telefon', 'sortable' => true], ['key' => 'transport', 'sort_key' => 'transport', 'label' => 'Dostawa', 'sortable' => true, 'raw' => true], ['key' => 'payment_method', 'sort_key' => 'payment_method', 'label' => 'Płatność', 'sortable' => true], ], $rows, $listRequest['viewFilters'], [ 'column' => $listRequest['sortColumn'], 'dir' => $listRequest['sortDir'], ], [ 'page' => $listRequest['page'], 'per_page' => $listRequest['perPage'], 'total' => $total, 'total_pages' => $totalPages, ], array_merge($listRequest['queryFilters'], [ 'sort' => $listRequest['sortColumn'], 'dir' => $listRequest['sortDir'], 'per_page' => $listRequest['perPage'], ]), $listRequest['perPageOptions'], $sortableColumns, '/admin/shop_order/list/', 'Brak danych w tabeli.' ); return \Shared\Tpl\Tpl::view('shop-order/orders-list', [ 'viewModel' => $viewModel, ]); } public function details(): string { return $this->order_details(); } public function order_details(): string { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); $order = $this->service->details($orderId); $coupon = null; if (!empty($order) && !empty($order['coupon_id'])) { $coupon = ( new \Domain\Coupon\CouponRepository( $GLOBALS['mdb'] ) )->find((int)$order['coupon_id']); } return \Shared\Tpl\Tpl::view('shop-order/order-details', [ 'order' => $order, 'coupon' => $coupon, 'order_statuses' => $this->service->statuses(), 'next_order_id' => $this->service->nextOrderId($orderId), 'prev_order_id' => $this->service->prevOrderId($orderId), ]); } public function edit(): string { return $this->order_edit(); } public function order_edit(): string { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); $transports = ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->allActive(); // Dane transportów do JS (id, cost, delivery_free) $transportsJson = []; if (is_array($transports)) { foreach ($transports as $t) { $transportsJson[] = [ 'id' => (int)$t['id'], 'cost' => (float)$t['cost'], 'delivery_free' => (int)($t['delivery_free'] ?? 0), ]; } } return \Shared\Tpl\Tpl::view('shop-order/order-edit', [ 'order' => $this->service->details($orderId), 'order_statuses' => $this->service->statuses(), 'transport' => $transports, 'payment_methods' => ( new \Domain\PaymentMethod\PaymentMethodRepository( $GLOBALS['mdb'] ) )->allActive(), 'free_delivery' => $this->service->getFreeDeliveryThreshold(), 'transports_json' => json_encode($transportsJson), ]); } public function save(): void { $this->order_save(); } public function order_save(): void { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); // Zapisz produkty PRZED zapisem zamówienia (bo saveOrderByAdmin przelicza summary) $productsData = \Shared\Helpers\Helpers::get('products'); if (is_array($productsData)) { $this->service->saveOrderProducts($orderId, $productsData); } $saved = $this->service->saveOrderByAdmin([ 'order_id' => $orderId, 'client_name' => (string)\Shared\Helpers\Helpers::get('client_name'), 'client_surname' => (string)\Shared\Helpers\Helpers::get('client_surname'), 'client_street' => (string)\Shared\Helpers\Helpers::get('client_street'), 'client_postal_code' => (string)\Shared\Helpers\Helpers::get('client_postal_code'), 'client_city' => (string)\Shared\Helpers\Helpers::get('client_city'), 'client_email' => (string)\Shared\Helpers\Helpers::get('client_email'), 'firm_name' => (string)\Shared\Helpers\Helpers::get('firm_name'), 'firm_street' => (string)\Shared\Helpers\Helpers::get('firm_street'), 'firm_postal_code' => (string)\Shared\Helpers\Helpers::get('firm_postal_code'), 'firm_city' => (string)\Shared\Helpers\Helpers::get('firm_city'), 'firm_nip' => (string)\Shared\Helpers\Helpers::get('firm_nip'), 'transport_id' => (int)\Shared\Helpers\Helpers::get('transport_id'), 'inpost_paczkomat' => (string)\Shared\Helpers\Helpers::get('inpost_paczkomat'), 'payment_method_id' => (int)\Shared\Helpers\Helpers::get('payment_method_id'), ]); if ($saved) { \Shared\Helpers\Helpers::alert('Zamówienie zostało zapisane.'); } header('Location: /admin/shop_order/order_details/order_id=' . $orderId); exit; } public function search_products_ajax(): void { $query = trim((string)\Shared\Helpers\Helpers::get('query')); $langId = trim((string)\Shared\Helpers\Helpers::get('lang_id')); if ($langId === '') { $langId = isset($_SESSION['lang_id']) ? (string)$_SESSION['lang_id'] : 'pl'; } $results = $this->service->searchProducts($query, $langId); header('Content-Type: application/json; charset=utf-8'); echo json_encode(['status' => 'ok', 'products' => $results]); exit; } public function notes_save(): void { $this->service->saveNotes((int)\Shared\Helpers\Helpers::get('order_id'), (string)\Shared\Helpers\Helpers::get('notes')); } public function order_status_change(): void { $response = $this->service->changeStatus( (int)\Shared\Helpers\Helpers::get('order_id'), (int)\Shared\Helpers\Helpers::get('status'), (string)\Shared\Helpers\Helpers::get('email') === 'true' ); echo json_encode($response); exit; } public function order_resend_confirmation_email(): void { $response = $this->service->resendConfirmationEmail((int)\Shared\Helpers\Helpers::get('order_id')); echo json_encode(['result' => $response]); exit; } public function set_order_as_unpaid(): void { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); $this->service->setOrderAsUnpaid($orderId); header('Location: /admin/shop_order/order_details/order_id=' . $orderId); exit; } public function set_order_as_paid(): void { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); $this->service->setOrderAsPaid($orderId, (int)\Shared\Helpers\Helpers::get('send_mail') === 1); header('Location: /admin/shop_order/order_details/order_id=' . $orderId); exit; } public function send_order_to_apilo(): void { $orderId = (int)\Shared\Helpers\Helpers::get('order_id'); if ($this->service->sendOrderToApilo($orderId)) { \Shared\Helpers\Helpers::alert('Zamówienie zostanie wysłane ponownie do apilo.com'); } else { \Shared\Helpers\Helpers::alert('Wystąpił błąd podczas wysyłania zamówienia do apilo.com'); } header('Location: /admin/shop_order/order_details/order_id=' . $orderId); exit; } public function toggle_trustmate_send(): void { echo json_encode($this->service->toggleTrustmateSend((int)\Shared\Helpers\Helpers::get('order_id'))); exit; } public function delete(): void { $this->order_delete(); } public function order_delete(): void { if ($this->service->deleteOrder((int)\Shared\Helpers\Helpers::get('id'))) { \Shared\Helpers\Helpers::alert('Zamówienie zostało usunięte'); } header('Location: /admin/shop_order/list/'); exit; } private function formatDateTime(string $value): string { if ($value === '') { return ''; } $ts = strtotime($value); if ($ts === false) { return $value; } return date('Y-m-d H:i', $ts); } private function contrastTextColor(string $hex): string { $hex = ltrim($hex, '#'); if (strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } if (strlen($hex) !== 6) { return '#fff'; } $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); $luminance = (0.299 * $r + 0.587 * $g + 0.114 * $b) / 255; return $luminance > 0.5 ? '#000' : '#fff'; } private function sanitizeInlineHtml(string $html): string { $html = strip_tags($html, ''); return preg_replace('/<(b|strong|i|em)\s[^>]*>/i', '<$1>', $html); } }