38471-vm/db/migrations/20260502_zz_financial_documents_schema_sync.php
2026-05-03 02:57:40 +00:00

218 lines
8.4 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../config.php';
if (!function_exists('financial_documents_schema_sync_20260502_run')) {
function financial_documents_schema_sync_20260502_run(): void
{
$pdo = db();
$columns = [
'invoices' => [
'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;