Update: 2026-05-06 01:38:39
This commit is contained in:
153
scripts/migrate_phase3_mobile.php
Normal file
153
scripts/migrate_phase3_mobile.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* Phase 3 Migration: Mobile App Support
|
||||
* Run: php scripts/migrate_phase3_mobile.php
|
||||
*
|
||||
* Adds tables and columns required for:
|
||||
* - Mobile OTP Authentication
|
||||
* - Device Management
|
||||
* - Batch Invoice Upload from Scanner
|
||||
* - Processing Queue for AI extraction
|
||||
* - Notifications System
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../app/bootstrap/init.php';
|
||||
|
||||
use App\Core\Database;
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
echo "═══════════════════════════════════════════\n";
|
||||
echo " مُصادَق — Phase 3 Migration\n";
|
||||
echo " Mobile App + Batch Processing Support\n";
|
||||
echo "═══════════════════════════════════════════\n\n";
|
||||
|
||||
$migrations = [
|
||||
|
||||
// ─── 1. User Device Management ─────────────────────────
|
||||
'create_user_devices' => "
|
||||
CREATE TABLE IF NOT EXISTS user_devices (
|
||||
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
|
||||
user_id CHAR(36) NOT NULL,
|
||||
device_fingerprint VARCHAR(64) NOT NULL,
|
||||
device_name VARCHAR(100) NULL,
|
||||
platform ENUM('android','ios','web') NOT NULL DEFAULT 'android',
|
||||
app_version VARCHAR(20) NULL,
|
||||
push_token TEXT NULL,
|
||||
device_secret VARCHAR(128) NULL,
|
||||
is_trusted BOOLEAN DEFAULT FALSE,
|
||||
last_seen_at DATETIME NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY uq_user_device (user_id, device_fingerprint),
|
||||
INDEX idx_device_fingerprint (device_fingerprint)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
",
|
||||
|
||||
// ─── 2. Users table: Add phone + mobile fields ─────────
|
||||
'add_users_phone' => "ALTER TABLE users ADD COLUMN phone VARCHAR(20) NULL AFTER email",
|
||||
'add_users_phone_hash' => "ALTER TABLE users ADD COLUMN phone_hash VARCHAR(64) NULL AFTER phone",
|
||||
'add_users_pin_hash' => "ALTER TABLE users ADD COLUMN pin_hash VARCHAR(255) NULL AFTER password_hash",
|
||||
'add_users_biometric' => "ALTER TABLE users ADD COLUMN biometric_enabled BOOLEAN DEFAULT FALSE AFTER pin_hash",
|
||||
'add_users_phone_index' => "CREATE INDEX idx_phone_hash ON users(phone_hash)",
|
||||
|
||||
// ─── 3. Invoice Batches (Mobile Scanner) ───────────────
|
||||
'create_invoice_batches' => "
|
||||
CREATE TABLE IF NOT EXISTS invoice_batches (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
tenant_id CHAR(36) NOT NULL,
|
||||
company_id CHAR(36) NOT NULL,
|
||||
uploaded_by CHAR(36) NOT NULL,
|
||||
total_images INT NOT NULL DEFAULT 0,
|
||||
processed_images INT NOT NULL DEFAULT 0,
|
||||
failed_images INT NOT NULL DEFAULT 0,
|
||||
status ENUM('uploading','processing','done','partial_fail','failed') DEFAULT 'uploading',
|
||||
source ENUM('mobile_scan','web_upload','whatsapp') DEFAULT 'mobile_scan',
|
||||
pdf_path VARCHAR(500) NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
completed_at DATETIME NULL,
|
||||
INDEX idx_tenant_status (tenant_id, status),
|
||||
INDEX idx_company (company_id),
|
||||
INDEX idx_uploaded_by (uploaded_by),
|
||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
",
|
||||
|
||||
// ─── 4. Invoice Processing Queue ───────────────────────
|
||||
'create_processing_queue' => "
|
||||
CREATE TABLE IF NOT EXISTS invoice_processing_queue (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
batch_id CHAR(36) NOT NULL,
|
||||
invoice_id CHAR(36) NULL,
|
||||
tenant_id CHAR(36) NOT NULL,
|
||||
company_id CHAR(36) NOT NULL,
|
||||
image_path VARCHAR(500) NOT NULL,
|
||||
image_order INT NOT NULL DEFAULT 0,
|
||||
status ENUM('pending','processing','done','failed') DEFAULT 'pending',
|
||||
attempts INT DEFAULT 0,
|
||||
max_attempts INT DEFAULT 3,
|
||||
error_message TEXT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
processed_at DATETIME NULL,
|
||||
INDEX idx_status_tenant (status, tenant_id),
|
||||
INDEX idx_batch (batch_id),
|
||||
INDEX idx_pending (status, attempts)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
",
|
||||
|
||||
// ─── 5. Add batch_id to invoices table ─────────────────
|
||||
'add_invoices_batch_id' => "ALTER TABLE invoices ADD COLUMN batch_id CHAR(36) NULL AFTER company_id",
|
||||
'add_invoices_batch_index' => "CREATE INDEX idx_batch_id ON invoices(batch_id)",
|
||||
|
||||
// ─── 6. Notifications Table ────────────────────────────
|
||||
'create_notifications' => "
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
|
||||
tenant_id CHAR(36) NOT NULL,
|
||||
user_id CHAR(36) NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
body TEXT NULL,
|
||||
data JSON NULL,
|
||||
is_read BOOLEAN DEFAULT FALSE,
|
||||
read_at DATETIME NULL,
|
||||
push_sent BOOLEAN DEFAULT FALSE,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_unread (user_id, is_read),
|
||||
INDEX idx_tenant (tenant_id),
|
||||
INDEX idx_type (type),
|
||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
||||
",
|
||||
];
|
||||
|
||||
$success = 0;
|
||||
$skipped = 0;
|
||||
$failed = 0;
|
||||
|
||||
foreach ($migrations as $name => $sql) {
|
||||
try {
|
||||
$db->exec($sql);
|
||||
echo " ✅ {$name}\n";
|
||||
$success++;
|
||||
} catch (\PDOException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if (str_contains($msg, 'Duplicate column') || str_contains($msg, 'Duplicate key name') || str_contains($msg, 'already exists')) {
|
||||
echo " ⏭️ {$name} (already exists)\n";
|
||||
$skipped++;
|
||||
} else {
|
||||
echo " ❌ {$name}: {$msg}\n";
|
||||
$failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n═══════════════════════════════════════════\n";
|
||||
echo " Migration Complete!\n";
|
||||
echo " ✅ Success: {$success} | ⏭️ Skipped: {$skipped} | ❌ Failed: {$failed}\n";
|
||||
echo "═══════════════════════════════════════════\n";
|
||||
Reference in New Issue
Block a user