input('id', 0)); $details = $this->ordersRepository->findDetails($orderId); if ($details === null) { return Response::html('Not found', 404); } $order = is_array($details['order'] ?? null) ? $details['order'] : []; $addresses = is_array($details['addresses'] ?? null) ? $details['addresses'] : []; $items = is_array($details['items'] ?? null) ? $details['items'] : []; $company = $this->companySettings->getSettings(); $existingPackages = $this->packageRepository->findByOrderId($orderId); $deliveryAddr = null; $customerAddr = null; foreach ($addresses as $addr) { $type = (string) ($addr['address_type'] ?? ''); if ($type === 'delivery') { $deliveryAddr = $addr; } if ($type === 'customer') { $customerAddr = $addr; } } $receiverAddr = $this->buildReceiverAddress($deliveryAddr, $customerAddr); $preferences = is_array($order['preferences_json'] ?? null) ? $order['preferences_json'] : (is_string($order['preferences_json'] ?? null) ? (json_decode($order['preferences_json'], true) ?: []) : []); $deliveryServices = []; $apaczkaServices = []; $deliveryServicesError = ''; $allegroProvider = $this->providerRegistry->get('allegro_wza'); if ($allegroProvider !== null) { try { $deliveryServices = $allegroProvider->getDeliveryServices(); } catch (Throwable $exception) { $deliveryServicesError = $exception->getMessage(); } } $apaczkaProvider = $this->providerRegistry->get('apaczka'); if ($apaczkaProvider !== null) { try { $apaczkaServices = $apaczkaProvider->getDeliveryServices(); } catch (Throwable $exception) { if ($deliveryServicesError === '') { $deliveryServicesError = $exception->getMessage(); } } } $inpostServices = array_values(array_filter( $deliveryServices, static fn(array $svc) => stripos((string) ($svc['carrierId'] ?? ''), 'inpost') !== false )); $flashSuccess = (string) Flash::get('shipment.success', ''); $flashError = (string) Flash::get('shipment.error', ''); $deliveryMapping = null; $deliveryMappingDiagnostic = ''; $orderCarrierName = trim((string) ($order['external_carrier_id'] ?? '')); $source = strtolower(trim((string) ($order['source'] ?? ''))); $sourceIntegrationId = $source === 'shoppro' ? max(0, (int) ($order['integration_id'] ?? 0)) : 0; if ($orderCarrierName !== '' && $this->deliveryMappings !== null && in_array($source, ['allegro', 'shoppro'], true)) { $deliveryMapping = $this->deliveryMappings->findByOrderMethod($source, $sourceIntegrationId, $orderCarrierName); if ($deliveryMapping === null) { $hasMappingsForSource = $this->deliveryMappings->hasMappingsForSource($source, $sourceIntegrationId); if (!$hasMappingsForSource && $source === 'shoppro' && $sourceIntegrationId > 0) { $deliveryMappingDiagnostic = 'Brak mapowan form dostawy dla tej instancji shopPRO (ID integracji: ' . $sourceIntegrationId . ').'; } elseif (!$hasMappingsForSource) { $deliveryMappingDiagnostic = 'Brak skonfigurowanych mapowan form dostawy dla tego zrodla zamowienia.'; } else { $deliveryMappingDiagnostic = 'Brak mapowania dla metody dostawy: ' . $orderCarrierName . '.'; } } } $html = $this->template->render('shipments/prepare', [ 'title' => $this->translator->get('shipments.prepare.title') . ' #' . $orderId, 'activeMenu' => 'orders', 'activeOrders' => 'list', 'user' => $this->auth->user(), 'csrfToken' => Csrf::token(), 'orderId' => $orderId, 'order' => $order, 'items' => $items, 'receiverAddr' => $receiverAddr, 'preferences' => $preferences, 'company' => $company, 'deliveryServices' => $deliveryServices, 'apaczkaServices' => $apaczkaServices, 'deliveryServicesError' => $deliveryServicesError, 'existingPackages' => $existingPackages, 'flashSuccess' => $flashSuccess, 'flashError' => $flashError, 'deliveryMapping' => $deliveryMapping, 'deliveryMappingDiagnostic' => $deliveryMappingDiagnostic, 'inpostServices' => $inpostServices, ], 'layouts/app'); return Response::html($html); } public function create(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); if ($orderId <= 0) { return Response::html('Not found', 404); } $csrfToken = (string) $request->input('_token', ''); if (!Csrf::validate($csrfToken)) { Flash::set('shipment.error', $this->translator->get('auth.errors.csrf_expired')); return Response::redirect('/orders/' . $orderId . '/shipment/prepare'); } $user = $this->auth->user(); $actorName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : null; $actorName = ($actorName !== null && $actorName !== '') ? $actorName : null; try { $providerCode = strtolower(trim((string) $request->input('provider_code', 'allegro_wza'))); if ($providerCode === 'inpost') { $providerCode = 'allegro_wza'; } $provider = $this->providerRegistry->get($providerCode); if ($provider === null) { throw new RuntimeException('Nieznany provider przesylek: ' . $providerCode); } $result = $provider->createShipment($orderId, [ 'provider_code' => $providerCode, 'delivery_method_id' => (string) $request->input('delivery_method_id', ''), 'credentials_id' => (string) $request->input('credentials_id', ''), 'carrier_id' => (string) $request->input('carrier_id', ''), 'package_type' => (string) $request->input('package_type', 'PACKAGE'), 'length_cm' => (string) $request->input('length_cm', ''), 'width_cm' => (string) $request->input('width_cm', ''), 'height_cm' => (string) $request->input('height_cm', ''), 'weight_kg' => (string) $request->input('weight_kg', ''), 'insurance_amount' => (string) $request->input('insurance_amount', '0'), 'insurance_currency' => (string) $request->input('insurance_currency', 'PLN'), 'cod_amount' => (string) $request->input('cod_amount', '0'), 'cod_currency' => (string) $request->input('cod_currency', 'PLN'), 'label_format' => (string) $request->input('label_format', 'PDF'), 'receiver_name' => (string) $request->input('receiver_name', ''), 'receiver_company' => (string) $request->input('receiver_company', ''), 'receiver_street' => (string) $request->input('receiver_street', ''), 'receiver_city' => (string) $request->input('receiver_city', ''), 'receiver_postal_code' => (string) $request->input('receiver_postal_code', ''), 'receiver_country_code' => (string) $request->input('receiver_country_code', 'PL'), 'receiver_phone' => (string) $request->input('receiver_phone', ''), 'receiver_email' => (string) $request->input('receiver_email', ''), 'receiver_point_id' => (string) $request->input('receiver_point_id', ''), 'sender_point_id' => (string) $request->input('sender_point_id', ''), ]); $packageId = (int) ($result['package_id'] ?? 0); $this->ordersRepository->recordActivity( $orderId, 'shipment_created', 'Zlecono utworzenie przesylki (' . $providerCode . ', ID paczki: ' . $packageId . ')', ['package_id' => $packageId, 'command_id' => $result['command_id'] ?? null, 'provider' => $providerCode], 'user', $actorName ); Flash::set('shipment.success', 'Komenda tworzenia przesylki wyslana. Sprawdz status.'); return Response::redirect('/orders/' . $orderId . '/shipment/prepare?check=' . $packageId); } catch (Throwable $exception) { $this->ordersRepository->recordActivity( $orderId, 'shipment_error', 'Blad tworzenia przesylki: ' . $exception->getMessage(), null, 'user', $actorName ); Flash::set('shipment.error', 'Blad tworzenia przesylki: ' . $exception->getMessage()); return Response::redirect('/orders/' . $orderId . '/shipment/prepare'); } } public function checkStatus(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); $packageId = max(0, (int) $request->input('packageId', 0)); if ($orderId <= 0 || $packageId <= 0) { return Response::json(['error' => 'Not found'], 404); } try { $package = $this->packageRepository->findById($packageId); if ($package === null) { return Response::json(['error' => 'Not found'], 404); } $providerCode = strtolower(trim((string) ($package['provider'] ?? 'allegro_wza'))); $provider = $this->providerRegistry->get($providerCode); if ($provider === null) { return Response::json(['status' => 'error', 'error' => 'Brak providera: ' . $providerCode]); } $result = $provider->checkCreationStatus($packageId); if (($result['status'] ?? '') === 'created') { try { $provider->downloadLabel($packageId, $this->storagePath); $result['status'] = 'label_ready'; } catch (Throwable) { // label generation failed, user can retry manually } } return Response::json($result); } catch (Throwable $exception) { return Response::json(['status' => 'error', 'error' => $exception->getMessage()]); } } public function label(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); $packageId = max(0, (int) $request->input('packageId', 0)); if ($orderId <= 0 || $packageId <= 0) { return Response::html('Not found', 404); } $csrfToken = (string) $request->input('_token', ''); if (!Csrf::validate($csrfToken)) { Flash::set('shipment.error', $this->translator->get('auth.errors.csrf_expired')); return Response::redirect('/orders/' . $orderId . '/shipment/prepare'); } $user = $this->auth->user(); $actorName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : null; $actorName = ($actorName !== null && $actorName !== '') ? $actorName : null; try { $package = $this->packageRepository->findById($packageId); if ($package === null) { throw new RuntimeException('Paczka nie znaleziona.'); } $providerCode = strtolower(trim((string) ($package['provider'] ?? 'allegro_wza'))); $provider = $this->providerRegistry->get($providerCode); if ($provider === null) { throw new RuntimeException('Brak providera: ' . $providerCode); } $result = $provider->downloadLabel($packageId, $this->storagePath); $fullPath = (string) ($result['full_path'] ?? ''); if ($fullPath !== '' && file_exists($fullPath)) { $labelFormat = strtoupper(trim((string) ($package['label_format'] ?? 'PDF'))); $contentType = $labelFormat === 'ZPL' ? 'application/octet-stream' : 'application/pdf'; $filename = basename($fullPath); $this->ordersRepository->recordActivity( $orderId, 'shipment_label_downloaded', 'Pobrano etykiete dla przesylki #' . $packageId, ['package_id' => $packageId, 'filename' => $filename, 'provider' => $providerCode], 'user', $actorName ); return new Response( (string) file_get_contents($fullPath), 200, [ 'Content-Type' => $contentType, 'Content-Disposition' => 'attachment; filename="' . $filename . '"', ] ); } Flash::set('shipment.success', 'Etykieta pobrana.'); } catch (Throwable $exception) { $this->ordersRepository->recordActivity( $orderId, 'shipment_error', 'Blad pobierania etykiety (paczka #' . $packageId . '): ' . $exception->getMessage(), null, 'user', $actorName ); Flash::set('shipment.error', 'Blad pobierania etykiety: ' . $exception->getMessage()); } return Response::redirect('/orders/' . $orderId . '/shipment/prepare'); } /** * @param array|null $deliveryAddr * @param array|null $customerAddr * @return array */ private function buildReceiverAddress(?array $deliveryAddr, ?array $customerAddr): array { $delivery = is_array($deliveryAddr) ? $deliveryAddr : []; $customer = is_array($customerAddr) ? $customerAddr : []; if ($delivery === []) { return $customer; } $result = $delivery; $deliveryName = trim((string) ($delivery['name'] ?? '')); $customerName = trim((string) ($customer['name'] ?? '')); if (($this->isPickupPointDelivery($delivery) || $deliveryName === '') && $customerName !== '') { $result['name'] = $customerName; } if (trim((string) ($result['phone'] ?? '')) === '' && trim((string) ($customer['phone'] ?? '')) !== '') { $result['phone'] = $customer['phone']; } if (trim((string) ($result['email'] ?? '')) === '' && trim((string) ($customer['email'] ?? '')) !== '') { $result['email'] = $customer['email']; } return $result; } /** * @param array $deliveryAddr */ private function isPickupPointDelivery(array $deliveryAddr): bool { $parcelId = trim((string) ($deliveryAddr['parcel_external_id'] ?? '')); $parcelName = trim((string) ($deliveryAddr['parcel_name'] ?? '')); return $parcelId !== '' || $parcelName !== ''; } }