513 lines
22 KiB
PHP
513 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* Export Invoices as Professional Excel (.xlsx) with Formulas
|
|
* GET /v1/invoices/export-excel
|
|
*
|
|
* Generates a real .xlsx file with:
|
|
* - Invoice header info + line items
|
|
* - Excel formulas for subtotals, tax, discount, net
|
|
* - SUM row at the bottom
|
|
* - Professional formatting (colors, borders, Arabic RTL)
|
|
*/
|
|
|
|
use App\Core\Database;
|
|
use App\Core\Encryption;
|
|
use App\Middleware\AuthMiddleware;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
|
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
|
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
|
use PhpOffice\PhpSpreadsheet\Style\Color;
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
|
|
|
// Enable error reporting for debugging
|
|
ini_set('display_errors', '1');
|
|
error_reporting(E_ALL);
|
|
|
|
// Autoload PhpSpreadsheet
|
|
require_once ROOT_PATH . '/vendor/autoload.php';
|
|
|
|
try {
|
|
|
|
// Auth: Support both Bearer header and ?token= query param (for download links)
|
|
$token = $_GET['token'] ?? null;
|
|
if (!$token) {
|
|
$headers = getallheaders();
|
|
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
|
|
if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
|
|
$token = $matches[1];
|
|
}
|
|
}
|
|
if (!$token) json_error('غير مصرح: لا يوجد رمز دخول', 401);
|
|
|
|
$decoded = \App\Core\JWT::decode($token, env('JWT_SECRET', ''));
|
|
if (!$decoded) json_error('غير مصرح: رمز دخول غير صالح', 401);
|
|
|
|
$db = Database::getInstance();
|
|
|
|
$tenantId = $decoded['tenant_id'];
|
|
$role = $decoded['role'];
|
|
$companyId = $_GET['company_id'] ?? null;
|
|
$dateFrom = $_GET['date_from'] ?? null;
|
|
$dateTo = $_GET['date_to'] ?? null;
|
|
$status = $_GET['status'] ?? null;
|
|
$invoiceId = $_GET['invoice_id'] ?? null; // Single invoice export
|
|
|
|
// Build query with filters
|
|
$where = [];
|
|
$params = [];
|
|
|
|
if ($role !== 'super_admin') {
|
|
$where[] = 'i.tenant_id = ?';
|
|
$params[] = $tenantId;
|
|
}
|
|
|
|
if ($invoiceId) {
|
|
$where[] = 'i.id = ?';
|
|
$params[] = $invoiceId;
|
|
}
|
|
|
|
if ($companyId) {
|
|
$where[] = 'i.company_id = ?';
|
|
$params[] = $companyId;
|
|
}
|
|
|
|
if ($dateFrom) {
|
|
$where[] = 'i.invoice_date >= ?';
|
|
$params[] = $dateFrom;
|
|
}
|
|
|
|
if ($dateTo) {
|
|
$where[] = 'i.invoice_date <= ?';
|
|
$params[] = $dateTo;
|
|
}
|
|
|
|
if ($status) {
|
|
$where[] = 'i.status = ?';
|
|
$params[] = $status;
|
|
}
|
|
|
|
$whereClause = $where ? 'WHERE ' . implode(' AND ', $where) : '';
|
|
|
|
$stmt = $db->prepare("
|
|
SELECT i.*, c.name as company_name_raw
|
|
FROM invoices i
|
|
JOIN companies c ON i.company_id = c.id
|
|
$whereClause
|
|
ORDER BY i.invoice_date DESC
|
|
LIMIT 5000
|
|
");
|
|
$stmt->execute($params);
|
|
$invoices = $stmt->fetchAll();
|
|
|
|
if (empty($invoices)) {
|
|
json_error('لا توجد فواتير لتصديرها', 404);
|
|
}
|
|
|
|
// Decrypt helper
|
|
$dec = function($val) {
|
|
if (empty($val)) return '';
|
|
$result = Encryption::decrypt((string)$val);
|
|
return ($result !== false && $result !== null) ? $result : (string)$val;
|
|
};
|
|
|
|
// Robust download helper for QR codes
|
|
$downloadUrl = function($url) {
|
|
$data = @file_get_contents($url);
|
|
if ($data === false && function_exists('curl_init')) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
$data = curl_exec($ch);
|
|
curl_close($ch);
|
|
}
|
|
return $data;
|
|
};
|
|
|
|
// ══════════════════════════════════════════
|
|
// BUILD SPREADSHEET
|
|
// ══════════════════════════════════════════
|
|
|
|
$spreadsheet = new Spreadsheet();
|
|
$spreadsheet->getProperties()
|
|
->setCreator('مُصادَق - Musadaq')
|
|
->setTitle('تقرير الفواتير')
|
|
->setDescription('تقرير فواتير المشتريات - تم إنشاؤه تلقائياً من منصة مُصادَق');
|
|
|
|
// === COLORS ===
|
|
$headerBg = '1C1550'; // Deep violet
|
|
$headerFont = 'FFFFFF'; // White
|
|
$subHeaderBg = 'EDE9FE'; // Light violet
|
|
$subHeaderFont = '5B21B6'; // Violet
|
|
$totalBg = 'D1FAE5'; // Light green
|
|
$totalFont = '065F46'; // Dark green
|
|
$borderColor = 'E2E1F0'; // Light border
|
|
$altRowBg = 'F8F7FD'; // Alternating row
|
|
|
|
$logoPath = ROOT_PATH . '/public/assets/img/logo.jpg';
|
|
if (!file_exists($logoPath)) {
|
|
error_log("Excel Export Error: Logo not found at {$logoPath}");
|
|
}
|
|
|
|
// ══════════════════════════════════════════
|
|
// 1. SUMMARY SHEET (First Sheet)
|
|
// ══════════════════════════════════════════
|
|
|
|
$summarySheet = $spreadsheet->getActiveSheet();
|
|
$summarySheet->setTitle('الملخص الإجمالي');
|
|
$summarySheet->setRightToLeft(true);
|
|
|
|
// --- SUMMARY HEADER ---
|
|
$summarySheet->mergeCells("A1:J1");
|
|
$summarySheet->setCellValue("A1", 'مُـصَـادَق — ملخص الفواتير الإجمالي');
|
|
$summarySheet->getStyle("A1")->applyFromArray([
|
|
'font' => ['bold' => true, 'size' => 16, 'color' => ['argb' => 'FF' . $headerFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $headerBg]],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
|
|
]);
|
|
$summarySheet->getRowDimension(1)->setRowHeight(45);
|
|
|
|
// --- Add Logo ---
|
|
$logoSummary = new Drawing();
|
|
$logoSummary->setName('Musadaq Logo');
|
|
$logoSummary->setPath(ROOT_PATH . '/public/assets/img/logo.jpg');
|
|
$logoSummary->setHeight(38);
|
|
$logoSummary->setCoordinates('A1');
|
|
$logoSummary->setOffsetX(15);
|
|
$logoSummary->setOffsetY(5);
|
|
$logoSummary->setWorksheet($summarySheet);
|
|
|
|
// --- Add Clickable Website Link ---
|
|
$summarySheet->setCellValue("J1", 'musadaq.intaleqapp.com');
|
|
$summarySheet->getCell("J1")->getHyperlink()->setUrl('https://musadaq.intaleqapp.com/');
|
|
$summarySheet->getStyle("J1")->applyFromArray([
|
|
'font' => ['color' => ['argb' => 'FFFFFFFF'], 'underline' => true, 'size' => 9],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER],
|
|
]);
|
|
|
|
// --- Add QR Code to Summary Header ---
|
|
try {
|
|
$summaryUrl = "https://musadaq.intaleqapp.com/";
|
|
$qrApiUrl = "https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=" . urlencode($summaryUrl);
|
|
$qrData = $downloadUrl($qrApiUrl);
|
|
if ($qrData) {
|
|
$tmpQr = tempnam(sys_get_temp_dir(), 'qr_sum_');
|
|
file_put_contents($tmpQr, $qrData);
|
|
$drawingQr = new Drawing();
|
|
$drawingQr->setName('Musadaq QR');
|
|
$drawingQr->setPath($tmpQr);
|
|
$drawingQr->setHeight(38);
|
|
$drawingQr->setCoordinates('I1');
|
|
$drawingQr->setOffsetX(40);
|
|
$drawingQr->setOffsetY(5);
|
|
$drawingQr->setWorksheet($summarySheet);
|
|
}
|
|
} catch(\Exception $e) {}
|
|
|
|
// Summary Meta Info
|
|
$companyNameFilter = 'جميع الشركات';
|
|
if ($companyId) {
|
|
$cStmt = $db->prepare("SELECT name FROM companies WHERE id = ?");
|
|
$cStmt->execute([$companyId]);
|
|
$cName = $cStmt->fetchColumn();
|
|
if ($cName) $companyNameFilter = $dec($cName);
|
|
}
|
|
|
|
$summarySheet->setCellValue("A3", 'الشركة:');
|
|
$summarySheet->setCellValue("B3", $companyNameFilter);
|
|
$summarySheet->setCellValue("D3", 'الفترة:');
|
|
$summarySheet->setCellValue("E3", ($dateFrom ?? '—') . ' إلى ' . ($dateTo ?? '—'));
|
|
$summarySheet->setCellValue("G3", 'عدد الفواتير:');
|
|
$summarySheet->setCellValue("H3", count($invoices));
|
|
|
|
$summarySheet->getStyle("A3:H3")->getFont()->setBold(true);
|
|
|
|
// --- SUMMARY TABLE HEADERS ---
|
|
$row = 5;
|
|
$summaryHeaders = ['#', 'رقم الفاتورة', 'المورّد', 'وصف البند', 'الكمية', 'سعر الوحدة', 'المجموع الجزئي', 'نسبة الضريبة', 'قيمة الضريبة', 'الصافي'];
|
|
$sumCols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
|
|
|
|
foreach ($summaryHeaders as $i => $h) {
|
|
$summarySheet->setCellValue($sumCols[$i] . $row, $h);
|
|
}
|
|
|
|
$summarySheet->getStyle("A{$row}:J{$row}")->applyFromArray([
|
|
'font' => ['bold' => true, 'color' => ['argb' => 'FF' . $headerFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $headerBg]],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
|
|
]);
|
|
|
|
// Summary column widths
|
|
$summarySheet->getColumnDimension('B')->setWidth(18);
|
|
$summarySheet->getColumnDimension('C')->setWidth(25);
|
|
$summarySheet->getColumnDimension('D')->setWidth(35);
|
|
$summarySheet->getColumnDimension('G')->setWidth(14);
|
|
$summarySheet->getColumnDimension('I')->setWidth(14);
|
|
$summarySheet->getColumnDimension('J')->setWidth(16);
|
|
|
|
$row++;
|
|
$summaryStartRow = $row;
|
|
$globalLineCount = 0;
|
|
|
|
// ══════════════════════════════════════════
|
|
// 2. INDIVIDUAL INVOICE SHEETS + POPULATE SUMMARY
|
|
// ══════════════════════════════════════════
|
|
|
|
foreach ($invoices as $invIdx => $inv) {
|
|
// Fetch line items for this invoice
|
|
$stmtLines = $db->prepare("SELECT * FROM invoice_lines WHERE invoice_id = ? ORDER BY line_number ASC");
|
|
$stmtLines->execute([$inv['id']]);
|
|
$lines = $stmtLines->fetchAll();
|
|
|
|
// --- Add to Summary Sheet ---
|
|
if (!empty($lines)) {
|
|
foreach ($lines as $line) {
|
|
$globalLineCount++;
|
|
$summarySheet->setCellValue("A{$row}", $globalLineCount);
|
|
$summarySheet->setCellValue("B{$row}", $inv['invoice_number'] ?? '-');
|
|
$summarySheet->setCellValue("C{$row}", $dec($inv['supplier_name']));
|
|
$summarySheet->setCellValue("D{$row}", $line['description'] ?? 'بدون وصف');
|
|
$summarySheet->setCellValue("E{$row}", (float)$line['quantity']);
|
|
$summarySheet->setCellValue("F{$row}", (float)$line['unit_price']);
|
|
$summarySheet->setCellValue("G{$row}", "=E{$row}*F{$row}");
|
|
$summarySheet->setCellValue("H{$row}", (float)$line['tax_rate']);
|
|
$summarySheet->setCellValue("I{$row}", "=G{$row}*H{$row}");
|
|
$summarySheet->setCellValue("J{$row}", "=G{$row}+I{$row}");
|
|
|
|
if ($globalLineCount % 2 === 0) {
|
|
$summarySheet->getStyle("A{$row}:J{$row}")->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setARGB('FFF8F7FD');
|
|
}
|
|
$row++;
|
|
}
|
|
} else {
|
|
// Fallback if no line items
|
|
$globalLineCount++;
|
|
$summarySheet->setCellValue("A{$row}", $globalLineCount);
|
|
$summarySheet->setCellValue("B{$row}", $inv['invoice_number'] ?? '-');
|
|
$summarySheet->setCellValue("C{$row}", $dec($inv['supplier_name']));
|
|
$summarySheet->setCellValue("D{$row}", 'إجمالي الفاتورة');
|
|
$summarySheet->setCellValue("E{$row}", 1);
|
|
$summarySheet->setCellValue("F{$row}", (float)$inv['subtotal']);
|
|
$summarySheet->setCellValue("G{$row}", "=E{$row}*F{$row}");
|
|
$summarySheet->setCellValue("H{$row}", 0.16);
|
|
$summarySheet->setCellValue("I{$row}", "=G{$row}*H{$row}");
|
|
$summarySheet->setCellValue("J{$row}", "=G{$row}+I{$row}");
|
|
$row++;
|
|
}
|
|
|
|
// --- Create Individual Sheet ---
|
|
$sheet = $spreadsheet->createSheet();
|
|
$invoiceNum = $inv['invoice_number'] ?? ('INV-' . ($invIdx + 1));
|
|
$sheetTitle = mb_substr(preg_replace('/[^a-zA-Z0-9\x{0600}-\x{06FF}\s\-]/u', '', $invoiceNum), 0, 31) ?: ('فاتورة ' . ($invIdx + 1));
|
|
$sheet->setTitle($sheetTitle);
|
|
$sheet->setRightToLeft(true);
|
|
|
|
// ── Column widths ──
|
|
$sheet->getColumnDimension('A')->setWidth(6); // #
|
|
$sheet->getColumnDimension('B')->setWidth(38); // Description
|
|
$sheet->getColumnDimension('C')->setWidth(12); // Quantity
|
|
$sheet->getColumnDimension('D')->setWidth(14); // Unit Price
|
|
$sheet->getColumnDimension('E')->setWidth(16); // Subtotal (formula)
|
|
$sheet->getColumnDimension('F')->setWidth(14); // Tax Rate
|
|
$sheet->getColumnDimension('G')->setWidth(16); // Tax Amount (formula)
|
|
$sheet->getColumnDimension('H')->setWidth(14); // Discount
|
|
$sheet->getColumnDimension('I')->setWidth(18); // Net Total (formula)
|
|
|
|
$invRow = 1;
|
|
|
|
// ── INVOICE HEADER ──────────────────────────
|
|
$sheet->mergeCells("A{$invRow}:I{$invRow}");
|
|
$sheet->setCellValue("A{$invRow}", 'مُـصَـادَق — تقرير فاتورة مشتريات');
|
|
$sheet->getStyle("A{$invRow}")->applyFromArray([
|
|
'font' => ['bold' => true, 'size' => 16, 'color' => ['argb' => 'FF' . $headerFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $headerBg]],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
|
|
]);
|
|
$sheet->getRowDimension($invRow)->setRowHeight(45);
|
|
|
|
// --- Add Logo ---
|
|
$logoInv = new Drawing();
|
|
$logoInv->setName('Musadaq Logo');
|
|
$logoInv->setPath(ROOT_PATH . '/public/assets/img/logo.jpg');
|
|
$logoInv->setHeight(38);
|
|
$logoInv->setCoordinates('A' . $invRow);
|
|
$logoInv->setOffsetX(15);
|
|
$logoInv->setOffsetY(5);
|
|
$logoInv->setWorksheet($sheet);
|
|
|
|
// --- Add Clickable Website Link ---
|
|
$sheet->setCellValue("I" . $invRow, 'musadaq.intaleqapp.com');
|
|
$sheet->getCell("I" . $invRow)->getHyperlink()->setUrl('https://musadaq.intaleqapp.com/');
|
|
$sheet->getStyle("I" . $invRow)->applyFromArray([
|
|
'font' => ['color' => ['argb' => 'FFFFFFFF'], 'underline' => true, 'size' => 9],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_LEFT, 'vertical' => Alignment::VERTICAL_CENTER],
|
|
]);
|
|
|
|
// --- Add Verification QR Code ---
|
|
try {
|
|
$verifyUrl = "https://musadaq.intaleqapp.com/index.php?route=v1/verify&id=" . $inv['id'];
|
|
$qrApiUrl = "https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=" . urlencode($verifyUrl);
|
|
$qrData = $downloadUrl($qrApiUrl);
|
|
if ($qrData) {
|
|
$tmpQr = tempnam(sys_get_temp_dir(), 'qr_inv_');
|
|
file_put_contents($tmpQr, $qrData);
|
|
$drawingQr = new Drawing();
|
|
$drawingQr->setName('Verification QR');
|
|
$drawingQr->setPath($tmpQr);
|
|
$drawingQr->setHeight(38);
|
|
$drawingQr->setCoordinates('H' . $invRow);
|
|
$drawingQr->setOffsetX(40);
|
|
$drawingQr->setOffsetY(5);
|
|
$drawingQr->setWorksheet($sheet);
|
|
}
|
|
} catch(\Exception $e) {}
|
|
|
|
$invRow++;
|
|
|
|
// Invoice meta data
|
|
$metaData = [
|
|
['رقم الفاتورة', $inv['invoice_number'] ?? '-', 'اسم المورّد', $dec($inv['supplier_name'])],
|
|
['تاريخ الفاتورة', $inv['invoice_date'] ?? '-', 'الرقم الضريبي للمورّد', $dec($inv['supplier_tin'])],
|
|
['الشركة', $dec($inv['company_name_raw'] ?? ''), 'العملة', $inv['currency_code'] ?? 'JOD'],
|
|
['نوع الفاتورة', ($inv['invoice_type'] === 'cash' ? 'نقدي' : 'آجل'), 'الحالة', match($inv['status']) {
|
|
'extracted' => 'مستخرجة',
|
|
'approved' => 'معتمدة',
|
|
'submitted' => 'مقدمة لجوفتورة',
|
|
'rejected' => 'مرفوضة',
|
|
default => $inv['status']
|
|
}],
|
|
];
|
|
|
|
foreach ($metaData as $meta) {
|
|
$sheet->setCellValue("A{$invRow}", $meta[0]);
|
|
$sheet->mergeCells("B{$invRow}:C{$invRow}");
|
|
$sheet->setCellValue("B{$invRow}", $meta[1]);
|
|
$sheet->setCellValue("E{$invRow}", $meta[2]);
|
|
$sheet->mergeCells("F{$invRow}:I{$invRow}");
|
|
$sheet->setCellValue("F{$invRow}", $meta[3]);
|
|
|
|
$sheet->getStyle("A{$invRow}:C{$invRow}")->applyFromArray([
|
|
'font' => ['bold' => true, 'size' => 11, 'color' => ['argb' => 'FF' . $subHeaderFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $subHeaderBg]],
|
|
]);
|
|
$sheet->getStyle("E{$invRow}")->applyFromArray([
|
|
'font' => ['bold' => true, 'size' => 11, 'color' => ['argb' => 'FF' . $subHeaderFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $subHeaderBg]],
|
|
]);
|
|
$sheet->getRowDimension($invRow)->setRowHeight(24);
|
|
$invRow++;
|
|
}
|
|
$invRow++;
|
|
|
|
// Items Header
|
|
$headers = ['#', 'وصف البند', 'الكمية', 'سعر الوحدة', 'المجموع الجزئي', 'نسبة الضريبة', 'قيمة الضريبة', 'الخصم', 'الصافي'];
|
|
$cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'];
|
|
foreach ($headers as $i => $h) $sheet->setCellValue($cols[$i] . $invRow, $h);
|
|
|
|
$sheet->getStyle("A{$invRow}:I{$invRow}")->applyFromArray([
|
|
'font' => ['bold' => true, 'color' => ['argb' => 'FF' . $headerFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $headerBg]],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
|
|
]);
|
|
$sheet->getRowDimension($invRow)->setRowHeight(32);
|
|
$invRow++;
|
|
$itemsStart = $invRow;
|
|
|
|
if (!empty($lines)) {
|
|
foreach ($lines as $lIdx => $line) {
|
|
$sheet->setCellValue("A{$invRow}", $lIdx + 1);
|
|
$sheet->setCellValue("B{$invRow}", $line['description'] ?? 'بدون وصف');
|
|
$sheet->setCellValue("C{$invRow}", (float)$line['quantity']);
|
|
$sheet->setCellValue("D{$invRow}", (float)$line['unit_price']);
|
|
$sheet->setCellValue("E{$invRow}", "=C{$invRow}*D{$invRow}");
|
|
$sheet->setCellValue("F{$invRow}", (float)$line['tax_rate']);
|
|
$sheet->getStyle("F{$invRow}")->getNumberFormat()->setFormatCode('0%');
|
|
$sheet->setCellValue("G{$invRow}", "=E{$invRow}*F{$invRow}");
|
|
$sheet->setCellValue("H{$invRow}", (float)$line['discount_amount']);
|
|
$sheet->setCellValue("I{$invRow}", "=E{$invRow}+G{$invRow}-H{$invRow}");
|
|
if ($lIdx % 2 === 1) $sheet->getStyle("A{$invRow}:I{$invRow}")->getFill()->setFillType(Fill::FILL_SOLID)->getStartColor()->setARGB('FFF8F7FD');
|
|
foreach (['D','E','G','H','I'] as $c) $sheet->getStyle("{$c}{$invRow}")->getNumberFormat()->setFormatCode('#,##0.000');
|
|
$invRow++;
|
|
}
|
|
} else {
|
|
$sheet->setCellValue("A{$invRow}", 1);
|
|
$sheet->setCellValue("B{$invRow}", 'إجمالي الفاتورة');
|
|
$sheet->setCellValue("C{$invRow}", 1);
|
|
$sheet->setCellValue("D{$invRow}", (float)$inv['subtotal']);
|
|
$sheet->setCellValue("E{$invRow}", "=C{$invRow}*D{$invRow}");
|
|
$sheet->setCellValue("F{$invRow}", 0.16);
|
|
$sheet->getStyle("F{$invRow}")->getNumberFormat()->setFormatCode('0%');
|
|
$sheet->setCellValue("G{$invRow}", "=E{$invRow}*F{$invRow}");
|
|
$sheet->setCellValue("H{$invRow}", (float)$inv['discount_total']);
|
|
$sheet->setCellValue("I{$invRow}", "=E{$invRow}+G{$invRow}-H{$invRow}");
|
|
foreach (['D','E','G','H','I'] as $c) $sheet->getStyle("{$c}{$invRow}")->getNumberFormat()->setFormatCode('#,##0.000');
|
|
$invRow++;
|
|
}
|
|
|
|
// Totals row for individual sheet
|
|
$lastItemRow = $invRow - 1;
|
|
$sheet->mergeCells("A{$invRow}:B{$invRow}");
|
|
$sheet->setCellValue("A{$invRow}", 'المجموع الكلي');
|
|
$sheet->setCellValue("C{$invRow}", "=SUM(C{$itemsStart}:C{$lastItemRow})");
|
|
$sheet->setCellValue("E{$invRow}", "=SUM(E{$itemsStart}:E{$lastItemRow})");
|
|
$sheet->setCellValue("G{$invRow}", "=SUM(G{$itemsStart}:G{$lastItemRow})");
|
|
$sheet->setCellValue("H{$invRow}", "=SUM(H{$itemsStart}:H{$lastItemRow})");
|
|
$sheet->setCellValue("I{$invRow}", "=SUM(I{$itemsStart}:I{$lastItemRow})");
|
|
$sheet->getStyle("G{$invRow}:I{$invRow}")->applyFromArray([
|
|
'font' => ['bold' => true, 'color' => ['argb' => 'FF' . $totalFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $totalBg]],
|
|
]);
|
|
foreach (['C','E','G','H','I'] as $c) $sheet->getStyle("{$c}{$invRow}")->getNumberFormat()->setFormatCode('#,##0.000');
|
|
$invRow += 2;
|
|
$sheet->setCellValue("A{$invRow}", 'تم إنشاء هذا التقرير تلقائياً من منصة مُصادَق — ' . date('Y-m-d H:i'));
|
|
}
|
|
|
|
// Final Summary Row with totals
|
|
$lastSummaryRow = $row - 1;
|
|
$summarySheet->mergeCells("A{$row}:D{$row}");
|
|
$summarySheet->setCellValue("A{$row}", 'المجموع الكلي النهائي');
|
|
$summarySheet->setCellValue("G{$row}", "=SUM(G{$summaryStartRow}:G{$lastSummaryRow})");
|
|
$summarySheet->setCellValue("I{$row}", "=SUM(I{$summaryStartRow}:I{$lastSummaryRow})");
|
|
$summarySheet->setCellValue("J{$row}", "=SUM(J{$summaryStartRow}:J{$lastSummaryRow})");
|
|
|
|
$summarySheet->getStyle("A{$row}:J{$row}")->applyFromArray([
|
|
'font' => ['bold' => true, 'size' => 13, 'color' => ['argb' => 'FF' . $totalFont]],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['argb' => 'FF' . $totalBg]],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
|
|
]);
|
|
|
|
foreach (['F', 'G', 'I', 'J'] as $c) {
|
|
$summarySheet->getStyle("{$c}{$summaryStartRow}:{$c}{$row}")->getNumberFormat()->setFormatCode('#,##0.000');
|
|
}
|
|
$summarySheet->getStyle("H{$summaryStartRow}:H{$row}")->getNumberFormat()->setFormatCode('0%');
|
|
|
|
// Set first sheet as active
|
|
$spreadsheet->setActiveSheetIndex(0);
|
|
|
|
// ══════════════════════════════════════════
|
|
// SEND FILE
|
|
// ══════════════════════════════════════════
|
|
|
|
$filename = 'musadaq_invoices_' . date('Y-m-d_His') . '.xlsx';
|
|
|
|
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|
header('Cache-Control: max-age=0');
|
|
header('Pragma: public');
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
$writer->save('php://output');
|
|
$spreadsheet->disconnectWorksheets();
|
|
unset($spreadsheet);
|
|
exit;
|
|
|
|
} catch (\Exception $e) {
|
|
if (ob_get_length()) ob_end_clean();
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
file_put_contents(STORAGE_PATH . '/logs/export_errors.log', "[" . date('Y-m-d H:i:s') . "] " . $e->getMessage() . "\n" . $e->getTraceAsString(), FILE_APPEND);
|
|
die("خطأ في التصدير: " . $e->getMessage());
|
|
}
|