[ 'transaction_no' => 'VARCHAR(50) DEFAULT NULL', 'payment_type' => 'VARCHAR(100) DEFAULT NULL', 'due_date' => 'DATE DEFAULT NULL', 'vat_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'total_with_vat' => 'DECIMAL(15,3) DEFAULT 0.000', 'terms_conditions' => 'TEXT NULL', 'paid_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'status' => "ENUM('paid','unpaid','partially_paid','refunded','cancelled') DEFAULT 'unpaid'", 'register_session_id' => 'INT(11) DEFAULT NULL', 'is_pos' => 'TINYINT(1) DEFAULT 0', 'discount_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'loyalty_points_earned' => 'DECIMAL(15,3) DEFAULT 0.000', 'loyalty_points_redeemed' => 'DECIMAL(15,3) DEFAULT 0.000', 'created_by' => 'INT(11) DEFAULT NULL', 'outlet_id' => 'INT(11) DEFAULT NULL', ], 'purchases' => [ 'payment_type' => 'VARCHAR(100) DEFAULT NULL', 'total_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'vat_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'total_with_vat' => 'DECIMAL(15,3) DEFAULT 0.000', 'terms_conditions' => 'TEXT NULL', 'paid_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'status' => "ENUM('paid','unpaid','partially_paid') DEFAULT 'unpaid'", 'register_session_id' => 'INT(11) DEFAULT NULL', 'due_date' => 'DATE DEFAULT NULL', 'outlet_id' => 'INT(11) DEFAULT 1', ], 'lpos' => [ 'delivery_date' => 'DATE DEFAULT NULL', 'status' => "ENUM('pending','converted','cancelled') DEFAULT 'pending'", 'total_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'vat_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'total_with_vat' => 'DECIMAL(15,3) DEFAULT 0.000', 'terms_conditions' => 'TEXT NULL', 'outlet_id' => 'INT(11) DEFAULT NULL', ], 'quotations' => [ 'status' => "ENUM('pending','converted','expired','cancelled') DEFAULT 'pending'", 'total_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'vat_amount' => 'DECIMAL(15,3) DEFAULT 0.000', 'total_with_vat' => 'DECIMAL(15,3) DEFAULT 0.000', 'terms_conditions' => 'TEXT NULL', 'outlet_id' => 'INT(11) DEFAULT NULL', ], ]; foreach ($columns as $table => $tableColumns) { if (!financial_documents_schema_sync_20260502_table_exists($pdo, $table)) { continue; } foreach ($tableColumns as $column => $definition) { if (financial_documents_schema_sync_20260502_column_exists($pdo, $table, $column)) { continue; } $statement = sprintf( 'ALTER TABLE `%s` ADD COLUMN `%s` %s', str_replace('`', '', $table), str_replace('`', '', $column), $definition ); financial_documents_schema_sync_20260502_exec($pdo, $statement); } } foreach (['invoices', 'purchases', 'lpos', 'quotations'] as $table) { financial_documents_schema_sync_20260502_backfill_total_with_vat($pdo, $table); } foreach (['invoices', 'purchases'] as $table) { financial_documents_schema_sync_20260502_backfill_paid_amount($pdo, $table); } foreach (['purchases', 'lpos', 'quotations'] as $table) { financial_documents_schema_sync_20260502_default_outlet_id($pdo, $table); } } function financial_documents_schema_sync_20260502_table_exists(PDO $pdo, string $table): bool { $stmt = $pdo->prepare( 'SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table LIMIT 1' ); $stmt->execute(['table' => $table]); return (bool) $stmt->fetchColumn(); } function financial_documents_schema_sync_20260502_column_exists(PDO $pdo, string $table, string $column): bool { $stmt = $pdo->prepare( 'SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table AND COLUMN_NAME = :column LIMIT 1' ); $stmt->execute([ 'table' => $table, 'column' => $column, ]); return (bool) $stmt->fetchColumn(); } function financial_documents_schema_sync_20260502_exec(PDO $pdo, string $statement): void { try { $pdo->exec($statement); } catch (PDOException $exception) { if (financial_documents_schema_sync_20260502_is_ignorable($exception)) { return; } throw $exception; } } function financial_documents_schema_sync_20260502_is_ignorable(PDOException $exception): bool { $driverCode = isset($exception->errorInfo[1]) ? (int) $exception->errorInfo[1] : null; $message = strtolower($exception->getMessage()); $ignorableCodes = [1050, 1060, 1061, 1062, 1091, 1826]; $ignorableSnippets = [ 'already exists', 'duplicate column name', 'duplicate key name', 'duplicate entry', 'duplicate foreign key constraint name', 'duplicate key on write or update', 'errno: 121', 'check that column/key exists', ]; if ($driverCode !== null && in_array($driverCode, $ignorableCodes, true)) { return true; } foreach ($ignorableSnippets as $snippet) { if (str_contains($message, $snippet)) { return true; } } return false; } function financial_documents_schema_sync_20260502_backfill_total_with_vat(PDO $pdo, string $table): void { if ( !financial_documents_schema_sync_20260502_table_exists($pdo, $table) || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'total_amount') || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'vat_amount') || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'total_with_vat') ) { return; } $sql = sprintf( 'UPDATE `%1$s` SET `total_with_vat` = COALESCE(`total_amount`, 0) + COALESCE(`vat_amount`, 0) WHERE `total_with_vat` IS NULL OR (`total_with_vat` = 0 AND (COALESCE(`total_amount`, 0) <> 0 OR COALESCE(`vat_amount`, 0) <> 0))', str_replace('`', '', $table) ); $pdo->exec($sql); } function financial_documents_schema_sync_20260502_backfill_paid_amount(PDO $pdo, string $table): void { if ( !financial_documents_schema_sync_20260502_table_exists($pdo, $table) || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'status') || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'paid_amount') || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'total_with_vat') ) { return; } $sql = sprintf( 'UPDATE `%1$s` SET `paid_amount` = COALESCE(`total_with_vat`, 0) WHERE `status` = \'paid\' AND (`paid_amount` IS NULL OR `paid_amount` = 0)', str_replace('`', '', $table) ); $pdo->exec($sql); } function financial_documents_schema_sync_20260502_default_outlet_id(PDO $pdo, string $table): void { if ( !financial_documents_schema_sync_20260502_table_exists($pdo, $table) || !financial_documents_schema_sync_20260502_column_exists($pdo, $table, 'outlet_id') ) { return; } $sql = sprintf( 'UPDATE `%1$s` SET `outlet_id` = 1 WHERE `outlet_id` IS NULL', str_replace('`', '', $table) ); $pdo->exec($sql); } } financial_documents_schema_sync_20260502_run(); return true;