123 lines
3.9 KiB
PHP
123 lines
3.9 KiB
PHP
<?php
|
|
/**
|
|
* Bulk Excel Import Endpoint
|
|
* POST /v1/excel/import
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Core\Database;
|
|
use App\Core\Encryption;
|
|
use App\Middleware\AuthMiddleware;
|
|
use App\Middleware\QuotaMiddleware;
|
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
|
|
|
$decoded = AuthMiddleware::check();
|
|
$tenantId = $decoded['tenant_id'];
|
|
|
|
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
|
json_error('الملف مطلوب', 422);
|
|
}
|
|
|
|
$companyId = input('company_id');
|
|
if (!$companyId) {
|
|
json_error('يجب تحديد الشركة', 422);
|
|
}
|
|
|
|
$file = $_FILES['file'];
|
|
$tmpPath = $file['tmp_name'];
|
|
|
|
try {
|
|
$spreadsheet = IOFactory::load($tmpPath);
|
|
$worksheet = $spreadsheet->getActiveSheet();
|
|
$rows = $worksheet->toArray();
|
|
|
|
if (count($rows) < 2) {
|
|
json_error('الملف فارغ أو لا يحتوي على بيانات', 422);
|
|
}
|
|
|
|
$header = array_shift($rows);
|
|
$mapping = mapColumns($header);
|
|
|
|
$db = Database::getInstance();
|
|
$db->beginTransaction();
|
|
|
|
$importedCount = 0;
|
|
$errors = [];
|
|
|
|
foreach ($rows as $index => $row) {
|
|
if (empty(array_filter($row))) continue; // Skip empty rows
|
|
|
|
try {
|
|
// Check quota for each invoice (preventive)
|
|
QuotaMiddleware::checkInvoiceQuota($tenantId);
|
|
|
|
$invoiceData = [
|
|
'id' => Database::generateUuid(),
|
|
'tenant_id' => $tenantId,
|
|
'company_id' => $companyId,
|
|
'invoice_number' => $row[$mapping['number']] ?? 'EXT-' . time() . '-' . $index,
|
|
'invoice_date' => formatDate($row[$mapping['date']] ?? date('Y-m-d')),
|
|
'customer_name' => Encryption::encrypt($row[$mapping['customer']] ?? 'عميل عام'),
|
|
'grand_total' => (float)($row[$mapping['total']] ?? 0),
|
|
'tax_amount' => (float)($row[$mapping['tax']] ?? 0),
|
|
'status' => 'extracted', // Ready for review/approval
|
|
'created_at' => date('Y-m-d H:i:s')
|
|
];
|
|
|
|
$stmt = $db->prepare("
|
|
INSERT INTO invoices (id, tenant_id, company_id, invoice_number, invoice_date, customer_name, grand_total, tax_amount, status, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
");
|
|
$stmt->execute(array_values($invoiceData));
|
|
$importedCount++;
|
|
|
|
} catch (\Exception $e) {
|
|
$errors[] = "السطر " . ($index + 2) . ": " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
$db->commit();
|
|
|
|
json_success([
|
|
'imported_count' => $importedCount,
|
|
'errors' => $errors
|
|
], "تم استيراد $importedCount فاتورة بنجاح");
|
|
|
|
} catch (\Exception $e) {
|
|
if (isset($db)) $db->rollBack();
|
|
json_error('فشل معالجة ملف الاكسل: ' . $e->getMessage(), 500);
|
|
}
|
|
|
|
/**
|
|
* Intelligent Column Mapping
|
|
*/
|
|
function mapColumns(array $header): array {
|
|
$map = [
|
|
'number' => 0,
|
|
'date' => 1,
|
|
'customer' => 2,
|
|
'total' => 3,
|
|
'tax' => 4
|
|
];
|
|
|
|
foreach ($header as $i => $col) {
|
|
$col = mb_strtolower(trim((string)$col));
|
|
if (str_contains($col, 'رقم') || str_contains($col, 'number')) $map['number'] = $i;
|
|
if (str_contains($col, 'تاريخ') || str_contains($col, 'date')) $map['date'] = $i;
|
|
if (str_contains($col, 'عميل') || str_contains($col, 'customer') || str_contains($col, 'اسم')) $map['customer'] = $i;
|
|
if (str_contains($col, 'اجمالي') || str_contains($col, 'total') || str_contains($col, 'المجموع')) $map['total'] = $i;
|
|
if (str_contains($col, 'ضريبة') || str_contains($col, 'tax')) $map['tax'] = $i;
|
|
}
|
|
|
|
return $map;
|
|
}
|
|
|
|
function formatDate($val): string {
|
|
if (is_numeric($val)) {
|
|
return date('Y-m-d', \PhpOffice\PhpSpreadsheet\Shared\Date::excelToTimestamp($val));
|
|
}
|
|
$ts = strtotime((string)$val);
|
|
return $ts ? date('Y-m-d', $ts) : date('Y-m-d');
|
|
}
|