input('nip', '')) ?? ''; if (!preg_match('/^\d{10}$/', $nip)) { return Response::json(['success' => false, 'error' => 'Niepoprawny NIP (musi miec 10 cyfr).'], 422); } try { $data = $this->mfWhitelist->lookupByNip($nip); } catch (Throwable $e) { return Response::json(['success' => false, 'error' => $e->getMessage()], 502); } return Response::json([ 'success' => true, 'data' => [ 'company_name' => $data['name'], 'tax_number' => $data['tax_no'], 'street' => $data['street'], 'postal_code' => $data['postal_code'], 'city' => $data['city'], 'country' => $data['country'], 'regon' => $data['regon'], 'status_vat' => $data['status_vat'], ], ]); } public function create(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); $details = $this->orders->findDetails($orderId); if ($details === null) { return Response::html('Not found', 404); } $order = is_array($details['order'] ?? null) ? $details['order'] : []; if ((int) ($order['invoice_requested'] ?? 0) !== 1) { Flash::set('order.error', 'Faktura nie zostala zazadana dla tego zamowienia.'); return Response::redirect('/orders/' . $orderId); } $configs = array_values(array_filter( $this->invoiceConfigs->listAll(), static fn (array $c): bool => (int) ($c['is_active'] ?? 0) === 1 )); if ($configs === []) { Flash::set('order.error', 'Brak aktywnych konfiguracji faktur. Skonfiguruj w Ustawienia > Ksiegowosc > Faktury.'); return Response::redirect('/orders/' . $orderId); } $items = is_array($details['items'] ?? null) ? $details['items'] : []; $addresses = is_array($details['addresses'] ?? null) ? $details['addresses'] : []; $byType = []; foreach ($addresses as $addr) { $type = (string) ($addr['address_type'] ?? ''); if ($type !== '' && !isset($byType[$type])) { $byType[$type] = $addr; } } $buyerAddress = $byType['invoice'] ?? $byType['customer'] ?? null; $autoTaxNumber = InvoiceService::extractBuyerTaxNumber($order, $buyerAddress); $existingInvoices = $this->invoices->findByOrderId($orderId); $html = $this->template->render('accounting/invoice_form', [ 'title' => 'Wystaw fakture', 'activeMenu' => 'orders', 'activeOrders' => 'list', 'user' => $this->auth->user(), 'csrfToken' => Csrf::token(), 'orderId' => $orderId, 'order' => $order, 'items' => $items, 'configs' => $configs, 'seller' => $this->companySettings->getSettings(), 'buyerAddress' => $buyerAddress, 'autoTaxNumber' => $autoTaxNumber, 'existingInvoices' => $existingInvoices, 'errorMessage' => (string) Flash::get('invoice.error', ''), ], 'layouts/app'); return Response::html($html); } public function store(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); if (!Csrf::validate((string) $request->input('_token', ''))) { Flash::set('order.error', 'Nieprawidlowy token CSRF.'); return Response::redirect('/orders/' . $orderId); } $configId = (int) $request->input('config_id', 0); if ($configId <= 0) { Flash::set('invoice.error', 'Wybierz konfiguracje faktury.'); return Response::redirect('/orders/' . $orderId . '/invoice/create'); } $user = $this->auth->user(); try { $result = $this->invoiceService->issue([ 'order_id' => $orderId, 'config_id' => $configId, 'buyer_tax_number' => (string) $request->input('buyer_tax_number', ''), 'buyer_name' => (string) $request->input('buyer_name', ''), 'buyer_company_name' => (string) $request->input('buyer_company_name', ''), 'buyer_street' => (string) $request->input('buyer_street', ''), 'buyer_city' => (string) $request->input('buyer_city', ''), 'buyer_postal_code' => (string) $request->input('buyer_postal_code', ''), 'buyer_email' => (string) $request->input('buyer_email', ''), 'issue_date_override' => (string) $request->input('issue_date', ''), 'created_by' => is_array($user) ? ($user['id'] ?? null) : null, ]); $userName = is_array($user) ? trim((string) ($user['name'] ?? $user['email'] ?? '')) : ''; $this->orders->recordActivity( $orderId, 'invoice_issued', ($result['mode'] === 'delegated' ? 'Wystawiono fakture (Fakturownia): ' : 'Wystawiono fakture: ') . $result['invoice_number'], [ 'invoice_number' => $result['invoice_number'], 'config_id' => $configId, 'mode' => $result['mode'], 'total_gross' => $result['total_gross'], ], 'user', $userName !== '' ? $userName : null ); Flash::set('order.success', 'Faktura wystawiona: ' . $result['invoice_number']); return Response::redirect('/orders/' . $orderId . '/invoice/' . $result['invoice_id']); } catch (InvoiceIssueException $e) { Flash::set('invoice.error', $e->getMessage()); } catch (Throwable $e) { Flash::set('invoice.error', 'Blad wystawiania faktury: ' . $e->getMessage()); } return Response::redirect('/orders/' . $orderId . '/invoice/create'); } public function show(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); $invoiceId = max(0, (int) $request->input('invoiceId', 0)); $invoice = $this->invoices->findById($invoiceId); if ($invoice === null || (int) ($invoice['order_id'] ?? 0) !== $orderId) { return Response::html('Not found', 404); } $data = $this->buildInvoiceViewData($invoice, $orderId); $html = $this->template->render('accounting/invoice_preview', $data, 'layouts/app'); return Response::html($html); } public function pdf(Request $request): Response { $orderId = max(0, (int) $request->input('id', 0)); $invoiceId = max(0, (int) $request->input('invoiceId', 0)); $invoice = $this->invoices->findById($invoiceId); if ($invoice === null || (int) ($invoice['order_id'] ?? 0) !== $orderId) { return Response::html('Not found', 404); } $externalPdfUrl = trim((string) ($invoice['external_pdf_url'] ?? '')); if ($externalPdfUrl !== '') { return Response::redirect($externalPdfUrl); } $data = $this->buildInvoiceViewData($invoice, $orderId); $html = $this->template->render('accounting/invoice_pdf', $data); $dompdf = new \Dompdf\Dompdf(); $dompdf->loadHtml($html); $dompdf->setPaper('A4'); $dompdf->render(); $filename = str_replace(['/', '\\'], '_', (string) ($invoice['invoice_number'] ?? 'invoice')) . '.pdf'; return new Response($dompdf->output() ?: '', 200, [ 'Content-Type' => 'application/pdf', 'Content-Disposition' => 'attachment; filename="' . $filename . '"', ]); } public function issuedList(Request $request): Response { $filters = [ 'search' => trim((string) $request->input('search', '')), 'config_id' => (int) $request->input('config_id', 0), 'mode' => (string) $request->input('mode', ''), 'date_from' => trim((string) $request->input('date_from', '')), 'date_to' => trim((string) $request->input('date_to', '')), 'page' => max(1, (int) $request->input('page', 1)), 'per_page' => 50, ]; $result = $this->invoices->paginate($filters); $totalPages = max(1, (int) ceil(((int) $result['total']) / max(1, (int) $result['per_page']))); $configs = $this->invoiceConfigs->listAll(); $html = $this->template->render('accounting/invoices_issued_list', [ 'title' => 'Wystawione faktury', 'activeMenu' => 'settings', 'activeSettings' => 'accounting', 'user' => $this->auth->user(), 'csrfToken' => Csrf::token(), 'filters' => $filters, 'invoices' => $result['items'], 'total' => $result['total'], 'page' => $result['page'], 'perPage' => $result['per_page'], 'totalPages' => $totalPages, 'configs' => $configs, ], 'layouts/app'); return Response::html($html); } /** * @param array $invoice * @return array */ private function buildInvoiceViewData(array $invoice, int $orderId): array { $seller = json_decode((string) ($invoice['seller_data_json'] ?? '{}'), true); $buyer = ($invoice['buyer_data_json'] ?? null) !== null ? json_decode((string) $invoice['buyer_data_json'], true) : null; $itemsJson = json_decode((string) ($invoice['items_json'] ?? '{"items":[]}'), true); $items = is_array($itemsJson['items'] ?? null) ? $itemsJson['items'] : (is_array($itemsJson) ? $itemsJson : []); $configName = trim((string) ($invoice['config_name'] ?? '')); $isDelegated = (int) ($invoice['config_is_delegated'] ?? 0) === 1 || trim((string) ($invoice['external_invoice_id'] ?? '')) !== ''; return [ 'title' => 'Faktura ' . ($invoice['invoice_number'] ?? ''), 'activeMenu' => 'orders', 'activeOrders' => 'list', 'user' => $this->auth->user(), 'orderId' => $orderId, 'invoice' => $invoice, 'seller' => is_array($seller) ? $seller : [], 'buyer' => is_array($buyer) ? $buyer : null, 'items' => $items, 'configName' => $configName, 'isDelegated' => $isDelegated, 'integrationName' => trim((string) ($invoice['integration_name'] ?? '')), 'accountPrefix' => trim((string) ($invoice['account_prefix'] ?? '')), ]; } }