Phase 115 complete (vertical slice "zamowienie z NIP -> faktura PDF"):
- Task 1: InvoiceRepository + InvoiceService (dual-flow orchestrator) +
InvoiceIssueException + FakturowniaApiClient::createInvoice + buildPdfUrl
- Task 2: InvoiceController + OrdersController::toggleInvoiceRequested +
OrdersRepository::setInvoiceRequested + auto-import invoice_requested z
Allegro (invoice.required) i shopPRO (5-key flexible parser) + show.php
(toggle w zakladce Platnosci + warunkowy przycisk Wystaw fakture)
- Task 3: Lista wystawionych /settings/accounting/invoices/issued z filtrami
+ invoice_preview + invoice_pdf Dompdf template + hub link
- Task 3b (dodany): NIP lookup przez MF Biala Lista (publiczne API, bez
rejestracji) — MfWhitelistApiClient w src/Core/Http/ + /api/nip/lookup +
przycisk "Pobierz z GUS" w formularzu
Auto-fixes podczas smoke testu (5):
- GUS endpoint Fakturowni nie istnial (HTML 404 -> "json is not valid");
switch na MF Biala Liste
- PHP 8.5 curl_close() deprecation wycieka HTML przed JSON; usuniete z
MfWhitelistApiClient i FakturowniaApiClient (3 miejsca)
- Fakturownia 422 payment_to_kind_days (nieistniejace pole) -> usuniete
- Generic "error" w 422 -> parser plaskuje errors: {pole: [...]} +
error_log z 1000 znakow raw body
- Fakturownia security odrzuca seller_*/department_id jako "create new
department"; usuniete z payloadu (Fakturownia uzywa danych konta)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
157 lines
7.5 KiB
PHP
157 lines
7.5 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="pl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Faktura <?= $e((string) ($invoice['invoice_number'] ?? '')) ?></title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body { font-family: "DejaVu Sans", "Segoe UI", Arial, sans-serif; font-size: 12px; color: #1a1a1a; padding: 20mm 15mm; }
|
|
.invoice-print { max-width: 740px; margin: 0 auto; }
|
|
.invoice-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; padding-bottom: 12px; border-bottom: 2px solid #333; }
|
|
.invoice-header__seller { flex: 1; }
|
|
.invoice-header__seller strong { font-size: 14px; display: block; margin-bottom: 4px; }
|
|
.invoice-header__title { text-align: right; }
|
|
.invoice-header__title h1 { font-size: 18px; font-weight: 700; margin-bottom: 4px; }
|
|
.invoice-header__title .invoice-number { font-size: 14px; font-weight: 600; }
|
|
.invoice-parties { display: flex; gap: 12px; margin-bottom: 16px; }
|
|
.invoice-party { flex: 1; padding: 8px 12px; border: 1px solid #ccc; }
|
|
.invoice-party strong { display: block; margin-bottom: 4px; font-size: 11px; text-transform: uppercase; color: #666; }
|
|
.invoice-items { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
|
|
.invoice-items th { background: #f0f0f0; text-align: left; padding: 6px 8px; font-size: 11px; border-bottom: 1px solid #999; }
|
|
.invoice-items td { padding: 5px 8px; border-bottom: 1px solid #ddd; }
|
|
.invoice-items .text-right { text-align: right; }
|
|
.invoice-items .text-nowrap { white-space: nowrap; }
|
|
.invoice-items tfoot td { border-top: 1px solid #999; font-weight: 600; padding: 6px 8px; }
|
|
.invoice-items tfoot .total-row td { border-top: 2px solid #333; font-weight: 700; }
|
|
.invoice-meta { margin-top: 16px; font-size: 11px; color: #555; }
|
|
.invoice-meta dt { display: inline; font-weight: 600; }
|
|
.invoice-meta dd { display: inline; margin: 0 16px 0 4px; }
|
|
.invoice-payment { margin-top: 16px; padding: 8px 12px; border: 1px solid #ccc; background: #fafafa; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<?php
|
|
$invoiceData = is_array($invoice ?? null) ? $invoice : [];
|
|
$sellerData = is_array($seller ?? null) ? $seller : [];
|
|
$buyerData = is_array($buyer ?? null) ? $buyer : null;
|
|
$itemsList = is_array($items ?? null) ? $items : [];
|
|
$totalGross = (float) ($invoiceData['total_gross'] ?? 0);
|
|
$totalNet = (float) ($invoiceData['total_net'] ?? 0);
|
|
$totalVat = max(0.0, $totalGross - $totalNet);
|
|
?>
|
|
<div class="invoice-print">
|
|
<div class="invoice-header">
|
|
<div class="invoice-header__seller">
|
|
<strong><?= $e((string) ($sellerData['company_name'] ?? '')) ?></strong>
|
|
<?php if (($sellerData['tax_number'] ?? '') !== ''): ?>
|
|
<div>NIP: <?= $e((string) $sellerData['tax_number']) ?></div>
|
|
<?php endif; ?>
|
|
<div><?= $e((string) ($sellerData['street'] ?? '')) ?></div>
|
|
<div><?= $e((string) ($sellerData['postal_code'] ?? '')) ?> <?= $e((string) ($sellerData['city'] ?? '')) ?></div>
|
|
<?php if (($sellerData['phone'] ?? '') !== ''): ?>
|
|
<div>Tel: <?= $e((string) $sellerData['phone']) ?></div>
|
|
<?php endif; ?>
|
|
<?php if (($sellerData['email'] ?? '') !== ''): ?>
|
|
<div>Email: <?= $e((string) $sellerData['email']) ?></div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="invoice-header__title">
|
|
<h1>FAKTURA VAT</h1>
|
|
<div class="invoice-number"><?= $e((string) ($invoiceData['invoice_number'] ?? '')) ?></div>
|
|
<?php $issueDate = (string) ($invoiceData['issue_date'] ?? ''); ?>
|
|
<div>Data wystawienia: <?= $e(substr($issueDate, 0, 10)) ?></div>
|
|
<?php $saleDate = (string) ($invoiceData['sale_date'] ?? ''); ?>
|
|
<?php if ($saleDate !== ''): ?>
|
|
<div>Data sprzedazy: <?= $e(substr($saleDate, 0, 10)) ?></div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($buyerData !== null): ?>
|
|
<div class="invoice-parties">
|
|
<div class="invoice-party">
|
|
<strong>Sprzedawca</strong>
|
|
<div><?= $e((string) ($sellerData['company_name'] ?? '')) ?></div>
|
|
<?php if (($sellerData['tax_number'] ?? '') !== ''): ?>
|
|
<div>NIP: <?= $e((string) $sellerData['tax_number']) ?></div>
|
|
<?php endif; ?>
|
|
<div><?= $e((string) ($sellerData['street'] ?? '')) ?></div>
|
|
<div><?= $e((string) ($sellerData['postal_code'] ?? '')) ?> <?= $e((string) ($sellerData['city'] ?? '')) ?></div>
|
|
</div>
|
|
<div class="invoice-party">
|
|
<strong>Nabywca</strong>
|
|
<?php if (($buyerData['company_name'] ?? '') !== ''): ?>
|
|
<div><?= $e((string) $buyerData['company_name']) ?></div>
|
|
<?php endif; ?>
|
|
<?php if (($buyerData['name'] ?? '') !== ''): ?>
|
|
<div><?= $e((string) $buyerData['name']) ?></div>
|
|
<?php endif; ?>
|
|
<?php if (($buyerData['tax_number'] ?? '') !== ''): ?>
|
|
<div>NIP: <?= $e((string) $buyerData['tax_number']) ?></div>
|
|
<?php endif; ?>
|
|
<div><?= $e((string) ($buyerData['street'] ?? '')) ?></div>
|
|
<div><?= $e((string) ($buyerData['postal_code'] ?? '')) ?> <?= $e((string) ($buyerData['city'] ?? '')) ?></div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<table class="invoice-items">
|
|
<thead>
|
|
<tr>
|
|
<th>Lp.</th>
|
|
<th>Nazwa</th>
|
|
<th class="text-right">Ilosc</th>
|
|
<th class="text-right">Cena netto</th>
|
|
<th class="text-right">VAT</th>
|
|
<th class="text-right">Cena brutto</th>
|
|
<th class="text-right">Suma brutto</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($itemsList as $idx => $item): ?>
|
|
<tr>
|
|
<td><?= $e((string) ($idx + 1)) ?></td>
|
|
<td><?= $e((string) ($item['name'] ?? '')) ?></td>
|
|
<td class="text-right text-nowrap"><?= $e((string) ($item['quantity'] ?? 0)) ?></td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format((float) ($item['price_net'] ?? 0), 2, '.', ' ')) ?></td>
|
|
<td class="text-right"><?= $e(number_format((float) ($item['vat'] ?? 0), 0, '.', '')) ?>%</td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format((float) ($item['price_gross'] ?? 0), 2, '.', ' ')) ?></td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format((float) ($item['total_gross'] ?? 0), 2, '.', ' ')) ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="6" class="text-right">Razem netto</td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format($totalNet, 2, '.', ' ')) ?></td>
|
|
</tr>
|
|
<tr>
|
|
<td colspan="6" class="text-right">Razem VAT</td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format($totalVat, 2, '.', ' ')) ?></td>
|
|
</tr>
|
|
<tr class="total-row">
|
|
<td colspan="6" class="text-right">Razem brutto</td>
|
|
<td class="text-right text-nowrap"><?= $e(number_format($totalGross, 2, '.', ' ')) ?> PLN</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
|
|
<?php if (($invoiceData['payment_due_date'] ?? null) !== null): ?>
|
|
<div class="invoice-payment">
|
|
<strong>Termin platnosci:</strong> <?= $e(substr((string) $invoiceData['payment_due_date'], 0, 10)) ?>
|
|
<?php if (trim((string) ($sellerData['bank_account'] ?? '')) !== ''): ?>
|
|
| <strong>Konto:</strong> <?= $e((string) $sellerData['bank_account']) ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<dl class="invoice-meta">
|
|
<?php if (($invoiceData['order_reference_value'] ?? null) !== null): ?>
|
|
<dt>Nr referencyjny:</dt><dd><?= $e((string) $invoiceData['order_reference_value']) ?></dd>
|
|
<?php endif; ?>
|
|
<dt>Typ dokumentu:</dt><dd><?= $e((string) ($invoiceData['kind'] ?? 'vat')) ?></dd>
|
|
</dl>
|
|
</div>
|
|
</body>
|
|
</html>
|