edit schema2

This commit is contained in:
Flatlogic Bot 2026-05-02 03:41:43 +00:00
parent af8d134f1e
commit baf6cef6ff
3 changed files with 262 additions and 8 deletions

View File

@ -0,0 +1,216 @@
<?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',
'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;

View File

@ -82,6 +82,27 @@ if (!function_exists('db_table_exists')) {
}
}
if (!function_exists('db_column_exists')) {
function db_column_exists(string $tableName, string $columnName): bool {
static $cache = [];
$cacheKey = strtolower($tableName . '.' . $columnName);
if (array_key_exists($cacheKey, $cache)) {
return $cache[$cacheKey];
}
try {
$stmt = db()->prepare("SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1");
$stmt->execute([$tableName, $columnName]);
$cache[$cacheKey] = (bool)$stmt->fetchColumn();
} catch (Throwable $e) {
$cache[$cacheKey] = false;
}
return $cache[$cacheKey];
}
}
if (!function_exists('runtime_debug_can_render_details')) {
function runtime_debug_can_render_details(): bool {
if (PHP_SAPI === 'cli') {
@ -454,15 +475,31 @@ function can(string $permission): bool {
}
function getPurchaseAlerts() {
if (!can('dashboard_view')) return [];
if (!can('dashboard_view') || !db_table_exists('purchases')) return [];
$db = db();
$stmt = $db->query("SELECT p.id, p.due_date, p.total_with_vat, s.name as supplier_name
FROM purchases p
LEFT JOIN suppliers s ON p.supplier_id = s.id
WHERE p.status != 'paid'
AND p.due_date IS NOT NULL
AND p.due_date <= DATE_ADD(CURDATE(), INTERVAL 7 DAY)
ORDER BY p.due_date ASC");
$hasSupplierJoin = db_table_exists('suppliers') && db_column_exists('purchases', 'supplier_id');
$dueDateExpression = db_column_exists('purchases', 'due_date') ? 'p.due_date' : 'NULL';
$statusPredicate = db_column_exists('purchases', 'status') ? "WHERE p.status != 'paid'" : 'WHERE 1=1';
$dueDatePredicate = db_column_exists('purchases', 'due_date')
? ' AND p.due_date IS NOT NULL AND p.due_date <= DATE_ADD(CURDATE(), INTERVAL 7 DAY)'
: '';
$totalExpression = db_column_exists('purchases', 'total_with_vat')
? 'p.total_with_vat'
: (db_column_exists('purchases', 'total_amount') ? 'COALESCE(p.total_amount, 0)' : '0');
$supplierExpression = $hasSupplierJoin ? 's.name' : 'NULL';
$joinClause = $hasSupplierJoin ? ' LEFT JOIN suppliers s ON p.supplier_id = s.id' : '';
$orderBy = db_column_exists('purchases', 'due_date') ? ' ORDER BY p.due_date ASC' : ' ORDER BY p.id DESC';
$sql = "SELECT p.id, {$dueDateExpression} AS due_date, {$totalExpression} AS total_with_vat, {$supplierExpression} AS supplier_name"
. ' FROM purchases p'
. $joinClause
. ' '
. $statusPredicate
. $dueDatePredicate
. $orderBy;
$stmt = $db->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

View File

@ -150,3 +150,4 @@
2026-05-01 19:32:47 - POST: {"username":"admin","password":"admin","login":"1"}
2026-05-02 02:56:23 - POST: {"username":"admin","password":"admin","login":"1"}
2026-05-02 03:00:39 - POST: {"username":"admin","password":"admin","login":"1"}
2026-05-02 03:32:58 - POST: {"username":"admin","password":"admin","login":"1"}