diff --git a/db/migrations/20260502_stock_items_schema_sync.php b/db/migrations/20260502_stock_items_schema_sync.php new file mode 100644 index 0000000..1a0ddb5 --- /dev/null +++ b/db/migrations/20260502_stock_items_schema_sync.php @@ -0,0 +1,130 @@ + 'INT NULL AFTER unit_id', + 'min_stock_level' => 'DECIMAL(15,2) DEFAULT 0.00 AFTER stock_quantity', + 'expiry_date' => 'DATE DEFAULT NULL AFTER min_stock_level', + 'image_path' => 'VARCHAR(255) DEFAULT NULL AFTER expiry_date', + 'vat_rate' => 'DECIMAL(5,2) DEFAULT 0.00 AFTER image_path', + 'is_promotion' => 'TINYINT(1) DEFAULT 0 AFTER vat_rate', + 'promotion_start' => 'DATE DEFAULT NULL AFTER is_promotion', + 'promotion_end' => 'DATE DEFAULT NULL AFTER promotion_start', + 'promotion_percent' => 'DECIMAL(5,2) DEFAULT 0.00 AFTER promotion_end', + 'outlet_id' => 'INT(11) DEFAULT 1 AFTER promotion_percent', + ]; + + foreach ($columns as $column => $definition) { + if (stock_items_schema_sync_20260502_column_exists($pdo, 'stock_items', $column)) { + continue; + } + + $statement = sprintf( + 'ALTER TABLE stock_items ADD COLUMN %s %s', + $column, + $definition + ); + + stock_items_schema_sync_20260502_exec($pdo, $statement); + } + + stock_items_schema_sync_20260502_modify_decimal_column($pdo, 'purchase_price', 'DECIMAL(15,3) DEFAULT 0.000'); + stock_items_schema_sync_20260502_modify_decimal_column($pdo, 'sale_price', 'DECIMAL(15,3) DEFAULT 0.000'); + } + + function stock_items_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 stock_items_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 stock_items_schema_sync_20260502_exec(PDO $pdo, string $statement): void + { + try { + $pdo->exec($statement); + } catch (PDOException $exception) { + if (stock_items_schema_sync_20260502_is_ignorable($exception)) { + return; + } + + throw $exception; + } + } + + function stock_items_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 stock_items_schema_sync_20260502_modify_decimal_column(PDO $pdo, string $column, string $definition): void + { + if (!stock_items_schema_sync_20260502_column_exists($pdo, 'stock_items', $column)) { + return; + } + + $statement = sprintf( + 'ALTER TABLE stock_items MODIFY COLUMN %s %s', + $column, + $definition + ); + + $pdo->exec($statement); + } +} + +stock_items_schema_sync_20260502_run(); + +return true;