query("SELECT id, company_id, supplier_tin, invoice_number, invoice_date FROM invoices WHERE invoice_hash IS NULL"); $invoices = $stmt->fetchAll(); if (empty($invoices)) { echo "✅ No invoices found needing a hash update.\n"; exit; } echo "📦 Found " . count($invoices) . " invoices to process...\n"; $updated = 0; $skipped = 0; foreach ($invoices as $inv) { // Decrypt supplier_tin if it's encrypted (assuming it is based on upload logic) $rawTin = Encryption::decrypt($inv['supplier_tin'] ?? '') ?: ($inv['supplier_tin'] ?? ''); $invoiceNum = $inv['invoice_number'] ?? ''; $invoiceDate = $inv['invoice_date'] ?? ''; if (empty($rawTin) || empty($invoiceNum) || empty($invoiceDate)) { echo " ⚠️ Skipping invoice [{$inv['id']}]: Missing critical data for hash calculation.\n"; $skipped++; continue; } // Generate Hash $rawHashString = $inv['company_id'] . '_' . $rawTin . '_' . $invoiceNum . '_' . $invoiceDate; $invoiceHash = hash('sha256', strtolower($rawHashString)); try { $updateStmt = $db->prepare("UPDATE invoices SET invoice_hash = ? WHERE id = ?"); $updateStmt->execute([$invoiceHash, $inv['id']]); $updated++; echo " ✅ Updated invoice [{$inv['id']}] | Hash: " . substr($invoiceHash, 0, 8) . "...\n"; } catch (\PDOException $e) { if (str_contains($e->getMessage(), 'Duplicate entry')) { echo " 🚫 Duplicate hash found for invoice [{$inv['id']}]. Skipping update.\n"; $skipped++; } else { echo " ❌ Failed to update [{$inv['id']}]: " . $e->getMessage() . "\n"; $skipped++; } } } echo "\n═══════════════════════════════════════════\n"; echo " Backfill Complete!\n"; echo " ✅ Updated: {$updated} | ⏭️ Skipped/Failed: {$skipped}\n"; echo "═══════════════════════════════════════════\n";