prepare( 'SELECT id, invoice_number, order_id, customer_id, status, currency, vat_rate, apply_vat, payment_method, constant_symbol, bank_name, bank_swift, bank_iban, bank_account, issue_date, due_date, tax_date, paid_date, issued_by, notes FROM invoices WHERE id = ?' ); $stmt->execute([$id]); $invoice = $stmt->fetch(); if (!$invoice) { header('Content-Type: application/json; charset=utf-8'); errorResponse('Faktura nebyla nalezena', 404); } // Polozky $stmt = $pdo->prepare( 'SELECT id, invoice_id, description, quantity, unit, unit_price, vat_rate, position FROM invoice_items WHERE invoice_id = ? ORDER BY position' ); $stmt->execute([$id]); $items = $stmt->fetchAll(); // Zakaznik $customer = null; if ($invoice['customer_id']) { $stmt = $pdo->prepare( 'SELECT id, name, street, city, postal_code, country, company_id, vat_id, custom_fields FROM customers WHERE id = ?' ); $stmt->execute([$invoice['customer_id']]); $customer = $stmt->fetch(); } // Firemni udaje $stmt = $pdo->query( 'SELECT id, company_name, company_id, vat_id, street, city, postal_code, country, custom_fields, logo_data, default_currency, default_vat_rate FROM company_settings LIMIT 1' ); $settings = $stmt->fetch(); // Logo $logoBase64 = ''; $logoMime = 'image/png'; if (!empty($settings['logo_data'])) { $logoBase64 = base64_encode($settings['logo_data']); $finfo = new finfo(FILEINFO_MIME_TYPE); $detected = $finfo->buffer($settings['logo_data']); $allowedMimes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']; if ($detected && in_array($detected, $allowedMimes)) { $logoMime = $detected; } } // Helpery $esc = function ($str) { return htmlspecialchars($str ?? '', ENT_QUOTES, 'UTF-8'); }; $formatNum = function ($number, $decimals = 2) { return number_format((float)$number, $decimals, ',', "\xC2\xA0"); }; $formatDate = function ($dateStr) { if (!$dateStr) { return ''; } $d = strtotime($dateStr); return $d !== false ? date('d.m.Y', $d) : $dateStr; }; // Preklady statickych textu $translations = [ 'cs' => [ 'title' => 'Faktura', 'heading' => 'FAKTURA - DAŇOVÝ DOKLAD č.', 'supplier' => 'Dodavatel', 'customer' => 'Odběratel', 'bank' => 'Banka:', 'swift' => 'SWIFT:', 'iban' => 'IBAN:', 'account_no' => 'Číslo účtu:', 'var_symbol' => 'Variabilní s.:', 'const_symbol' => 'Konstantní s.:', 'order_no' => 'Objednávka č.:', 'issue_date' => 'Datum vystavení:', 'due_date' => 'Datum splatnosti:', 'tax_date' => 'Datum uskutečnění plnění:', 'payment_method' => 'Forma úhrady:', 'billing' => 'Fakturujeme Vám za:', 'col_no' => 'Č.', 'col_desc' => 'Popis', 'col_qty' => 'Množství', 'col_unit_price' => 'Jedn. cena', 'col_price' => 'Cena', 'col_vat_pct' => '%DPH', 'col_vat' => 'DPH', 'col_total' => 'Celkem', 'subtotal' => 'Mezisoučet:', 'vat_label' => 'DPH', 'total' => 'Celkem k úhradě', 'amounts_in' => 'Částky jsou uvedeny v', 'notes' => 'Poznámky', 'issued_by' => 'Vystavil:', 'notice' => 'Dovolujeme si Vás upozornit, že v případě nedodržení data splatnosti' . ' uvedeného na faktuře Vám budeme účtovat úrok z prodlení v dohodnuté, resp.' . ' zákonné výši a smluvní pokutu (byla-li sjednána).', 'vat_recap' => 'Rekapitulace DPH v Kč:', 'vat_base' => 'Základ v Kč', 'vat_rate' => 'Sazba', 'vat_amount' => 'DPH v Kč', 'vat_with_total' => 'Celkem s DPH v Kč', 'received_by' => 'Převzal:', 'stamp' => 'Razítko:', 'ico' => 'IČ: ', 'dic' => 'DIČ: ', ], 'en' => [ 'title' => 'Invoice', 'heading' => 'INVOICE - TAX DOCUMENT No.', 'supplier' => 'Supplier', 'customer' => 'Customer', 'bank' => 'Bank:', 'swift' => 'SWIFT:', 'iban' => 'IBAN:', 'account_no' => 'Account No.:', 'var_symbol' => 'Variable symbol:', 'const_symbol' => 'Constant symbol:', 'order_no' => 'Order No.:', 'issue_date' => 'Issue date:', 'due_date' => 'Due date:', 'tax_date' => 'Tax point date:', 'payment_method' => 'Payment method:', 'billing' => 'We invoice you for:', 'col_no' => 'No.', 'col_desc' => 'Description', 'col_qty' => 'Quantity', 'col_unit_price' => 'Unit price', 'col_price' => 'Price', 'col_vat_pct' => 'VAT%', 'col_vat' => 'VAT', 'col_total' => 'Total', 'subtotal' => 'Subtotal:', 'vat_label' => 'VAT', 'total' => 'Total to pay', 'amounts_in' => 'Amounts are in', 'notes' => 'Notes', 'issued_by' => 'Issued by:', 'notice' => 'Please note that in case of late payment, we will charge default interest' . ' at the agreed or statutory rate and a contractual penalty (if agreed).', 'vat_recap' => 'VAT recapitulation in CZK:', 'vat_base' => 'Tax base in CZK', 'vat_rate' => 'Rate', 'vat_amount' => 'VAT in CZK', 'vat_with_total' => 'Total incl. VAT in CZK', 'received_by' => 'Received by:', 'stamp' => 'Stamp:', 'ico' => 'Reg. No.: ', 'dic' => 'Tax ID: ', ], ]; $t = $translations[$lang]; $currency = $invoice['currency'] ?? 'CZK'; $applyVat = !empty($invoice['apply_vat']); // Vypocty $vatSummary = []; $subtotal = 0; foreach ($items as $item) { $lineSubtotal = (float)$item['quantity'] * (float)$item['unit_price']; $subtotal += $lineSubtotal; $rate = (float)$item['vat_rate']; if (!isset($vatSummary[(string)$rate])) { $vatSummary[(string)$rate] = ['base' => 0, 'vat' => 0]; } $vatSummary[(string)$rate]['base'] += $lineSubtotal; if ($applyVat) { $vatSummary[(string)$rate]['vat'] += $lineSubtotal * $rate / 100; } } $totalVat = 0; foreach ($vatSummary as $data) { $totalVat += $data['vat']; } $totalToPay = $subtotal + $totalVat; // Rekapitulace DPH - vzdy v CZK (cesky danovy doklad) $isForeign = strtoupper($currency) !== 'CZK'; $cnbRate = 1.0; if ($isForeign) { $cnb = CnbRates::getInstance(); $issueDate = $invoice['issue_date'] ?? ''; // Kurz CNB k datu vystaveni $cnbRate = $cnb->toCzk(1.0, $currency, $issueDate); } $vatRates = [21, 12, 0]; $vatRecap = []; foreach ($vatRates as $rate) { $key = (string)(float)$rate; $base = $vatSummary[$key]['base'] ?? 0; $vat = $vatSummary[$key]['vat'] ?? 0; $vatRecap[] = [ 'rate' => $rate, 'base' => round($base * $cnbRate, 2), 'vat' => round($vat * $cnbRate, 2), 'total' => round(($base + $vat) * $cnbRate, 2), ]; } // QR kod - SPAYD $spaydParts = [ 'SPD*1.0', 'ACC:' . str_replace(' ', '', $invoice['bank_iban']), 'AM:' . number_format($totalToPay, 2, '.', ''), 'CC:' . $currency, 'X-VS:' . $invoice['invoice_number'], 'X-KS:' . ($invoice['constant_symbol'] ?: '0308'), 'MSG:' . $t['title'] . ' ' . $invoice['invoice_number'], ]; $spaydString = implode('*', $spaydParts); $qrSvg = ''; try { $qrOptions = new QROptions([ 'outputType' => QRCode::OUTPUT_MARKUP_SVG, 'eccLevel' => QRCode::ECC_M, 'scale' => 3, 'addQuietzone' => true, 'svgUseCssProperties' => false, ]); $qrCode = new QRCode($qrOptions); $qrSvg = $qrCode->render($spaydString); } catch (Exception $e) { error_log('QR code generation failed: ' . $e->getMessage()); } // Sanitizace HTML z Rich editoru $cleanQuillHtml = function (string $html): string { $allowedTags = '