diff --git a/app/modules_app/invoices/upload.php b/app/modules_app/invoices/upload.php index 2dcddc6..bdcab14 100644 --- a/app/modules_app/invoices/upload.php +++ b/app/modules_app/invoices/upload.php @@ -80,29 +80,29 @@ if (move_uploaded_file($_FILES['invoice']['tmp_name'], $targetFile)) { json_success(['id' => $db->lastInsertId()], 'تم رفع الفاتورة ولكن فشل استخراج البيانات تلقائياً'); } - // 5.5 Duplicate Prevention Check - $supplierTin = $extracted['supplier']['tin'] ?? ''; - $invoiceNum = $extracted['invoice_number'] ?? ''; - $invoiceDate = $extracted['invoice_date'] ?? ''; - - // Only hash if we have the critical data to avoid false duplicate collisions - $invoiceHash = null; - if ($supplierTin && $invoiceNum && $invoiceDate) { - $rawHashString = $companyId . '_' . $supplierTin . '_' . $invoiceNum . '_' . $invoiceDate; - $invoiceHash = hash('sha256', strtolower($rawHashString)); - - $checkStmt = $db->prepare("SELECT id FROM invoices WHERE company_id = ? AND invoice_hash = ? AND deleted_at IS NULL"); - $checkStmt->execute([$companyId, $invoiceHash]); - if ($checkStmt->fetch()) { - json_error('هذه الفاتورة تم رفعها مسبقاً لهذه الشركة (رقم الفاتورة مكرر لنفس المورد والتاريخ).', 409); - } - } - // 6. Save Extracted Data with Encryption try { $db->beginTransaction(); - $invoiceId = bin2hex(random_bytes(16)); // Generate UUID (simple version for now or use a lib) + // 5.5 Duplicate Prevention Check (Now inside transaction for safety) + $supplierTin = $extracted['supplier']['tin'] ?? ''; + $invoiceNum = $extracted['invoice_number'] ?? ''; + $invoiceDate = $extracted['invoice_date'] ?? ''; + + $invoiceHash = null; + if ($supplierTin && $invoiceNum && $invoiceDate) { + $rawHashString = $companyId . '_' . $supplierTin . '_' . $invoiceNum . '_' . $invoiceDate; + $invoiceHash = hash('sha256', strtolower($rawHashString)); + + $checkStmt = $db->prepare("SELECT id FROM invoices WHERE company_id = ? AND invoice_hash = ? AND deleted_at IS NULL"); + $checkStmt->execute([$companyId, $invoiceHash]); + if ($checkStmt->fetch()) { + $db->rollBack(); + json_error('هذه الفاتورة تم رفعها مسبقاً لهذه الشركة (رقم الفاتورة مكرر لنفس المورد والتاريخ).', 409); + } + } + + $invoiceId = bin2hex(random_bytes(16)); // Generate UUID // Let's use a standard UUID format if possible, but MySQL CHAR(36) accepts anything. // Actually, let's just use the DB's UUID() function but FETCH it back or generate it here. // I'll use a better UUID generator logic. diff --git a/scripts/migrate_phase2.php b/scripts/migrate_phase2.php index 4b99b66..2f20297 100644 --- a/scripts/migrate_phase2.php +++ b/scripts/migrate_phase2.php @@ -23,6 +23,9 @@ $migrations = [ // 2. Add validation_warnings for AI Pre-Audit 'add_validation_warnings' => "ALTER TABLE invoices ADD COLUMN validation_warnings JSON NULL", + // 2.5 Add deleted_at for soft delete (Missing in Phase 1 for this table) + 'add_invoices_soft_delete' => "ALTER TABLE invoices ADD COLUMN deleted_at DATETIME NULL DEFAULT NULL", + // 3. Create Unique Index to prevent duplicates within the same tenant & company // Using a regular index for now, application logic will handle uniqueness to allow nulls 'add_hash_index' => "CREATE INDEX idx_invoice_hash ON invoices(tenant_id, company_id, invoice_hash)",