update last
This commit is contained in:
parent
baf6cef6ff
commit
18ae044a97
@ -12,7 +12,7 @@
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
-- Auto-generated full schema snapshot for fresh installs.
|
||||
-- Re-run refresh_complete_schema.php after schema changes so new installations stay current.
|
||||
-- Re-run refresh_complete_schema.php after schema or migration changes so new installations stay current.
|
||||
|
||||
--
|
||||
-- Table structure for table `acc_accounts`
|
||||
|
||||
56
db/install_baseline_migrations.php
Normal file
56
db/install_baseline_migrations.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
// Auto-generated install baseline for fresh installs.
|
||||
// Refreshed together with complete_schema.sql by refresh_complete_schema.php.
|
||||
|
||||
return [
|
||||
'20260216_add_credit_limit.sql',
|
||||
'20260216_add_invoices.sql',
|
||||
'20260216_add_invoice_status.sql',
|
||||
'20260216_add_payments_table.sql',
|
||||
'20260216_add_quotations.sql',
|
||||
'20260216_add_stock_tables.sql',
|
||||
'20260216_pos_advanced_features.sql',
|
||||
'20260216_setup_pos_full.sql',
|
||||
'20260216_update_invoices_payment.sql',
|
||||
'20260216_update_precision_3_decimal.sql',
|
||||
'20260216_vat_and_profile.sql',
|
||||
'20260217_accounting_module.sql',
|
||||
'20260217_biometric_attendance.sql',
|
||||
'20260217_biometric_devices.sql',
|
||||
'20260217_biometric_logs_update.sql',
|
||||
'20260217_hr_module.sql',
|
||||
'20260217_hr_payroll_unique.sql',
|
||||
'20260217_purchase_returns.sql',
|
||||
'20260217_sales_returns.sql',
|
||||
'20260217_vat_accounts.sql',
|
||||
'20260218_create_license_table.sql',
|
||||
'20260218_modern_loyalty_system.sql',
|
||||
'20260218_pos_payments.sql',
|
||||
'20260219_add_license_fields.sql',
|
||||
'20260219_add_trial_logic.sql',
|
||||
'20260219_add_vat_to_pos_items.sql',
|
||||
'20260219_fix_vat_columns.sql',
|
||||
'20260220_add_due_date_to_invoices.sql',
|
||||
'20260220_license_server_schema.sql',
|
||||
'20260220_split_customers_suppliers.sql',
|
||||
'20260220_split_invoices_purchases.sql',
|
||||
'20260220_unify_pos_sales.php',
|
||||
'20260220_unify_pos_sales.sql',
|
||||
'20260221_add_theme_to_users.sql',
|
||||
'20260221_fix_invoice_items_columns.sql',
|
||||
'20260221_fix_pos_invoices_columns.sql',
|
||||
'20260221_fix_supplier_foreign_keys.sql',
|
||||
'20260318_add_outlet_id_to_purchases.sql',
|
||||
'20260318_create_outlets_table.sql',
|
||||
'20260318_multi_outlet_schema.sql',
|
||||
'20260318_local_definitions.sql',
|
||||
'20260318_user_outlets_table.sql',
|
||||
'20260502_full_schema_sync.php',
|
||||
'20260502_stock_items_schema_sync.php',
|
||||
'20260502_zzz_payment_methods_schema_sync.php',
|
||||
'20260502_zz_financial_documents_schema_sync.php',
|
||||
'add_outlet_id.sql',
|
||||
'fix_lpo_foreign_key.sql',
|
||||
];
|
||||
177
db/migrations/20260502_zzz_payment_methods_schema_sync.php
Normal file
177
db/migrations/20260502_zzz_payment_methods_schema_sync.php
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../config.php';
|
||||
|
||||
if (!function_exists('payment_methods_schema_sync_20260502_run')) {
|
||||
function payment_methods_schema_sync_20260502_run(): void
|
||||
{
|
||||
$pdo = db();
|
||||
|
||||
payment_methods_schema_sync_20260502_exec(
|
||||
$pdo,
|
||||
"CREATE TABLE IF NOT EXISTS `payment_methods` (
|
||||
"
|
||||
. " `id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
"
|
||||
. " `name_en` varchar(255) DEFAULT NULL,
|
||||
"
|
||||
. " `name_ar` varchar(255) DEFAULT NULL,
|
||||
"
|
||||
. " `created_at` timestamp NULL DEFAULT current_timestamp(),
|
||||
"
|
||||
. " PRIMARY KEY (`id`)
|
||||
"
|
||||
. ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
||||
);
|
||||
|
||||
$columns = [
|
||||
'name_en' => 'VARCHAR(255) DEFAULT NULL',
|
||||
'name_ar' => 'VARCHAR(255) DEFAULT NULL',
|
||||
'created_at' => 'TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP',
|
||||
];
|
||||
|
||||
foreach ($columns as $column => $definition) {
|
||||
if (payment_methods_schema_sync_20260502_column_exists($pdo, 'payment_methods', $column)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
payment_methods_schema_sync_20260502_exec(
|
||||
$pdo,
|
||||
sprintf(
|
||||
'ALTER TABLE `payment_methods` ADD COLUMN `%s` %s',
|
||||
str_replace('`', '', $column),
|
||||
$definition
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
payment_methods_schema_sync_20260502_backfill_legacy_names($pdo);
|
||||
payment_methods_schema_sync_20260502_seed_defaults($pdo);
|
||||
}
|
||||
|
||||
function payment_methods_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 payment_methods_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 payment_methods_schema_sync_20260502_backfill_legacy_names(PDO $pdo): void
|
||||
{
|
||||
if (!payment_methods_schema_sync_20260502_table_exists($pdo, 'payment_methods')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hasLegacyName = payment_methods_schema_sync_20260502_column_exists($pdo, 'payment_methods', 'name');
|
||||
$hasNameEn = payment_methods_schema_sync_20260502_column_exists($pdo, 'payment_methods', 'name_en');
|
||||
$hasNameAr = payment_methods_schema_sync_20260502_column_exists($pdo, 'payment_methods', 'name_ar');
|
||||
|
||||
if ($hasLegacyName && $hasNameEn) {
|
||||
$pdo->exec(
|
||||
"UPDATE `payment_methods`
|
||||
SET `name_en` = CASE
|
||||
WHEN `name_en` IS NULL OR TRIM(`name_en`) = '' THEN `name`
|
||||
ELSE `name_en`
|
||||
END
|
||||
WHERE `name` IS NOT NULL AND TRIM(`name`) <> ''"
|
||||
);
|
||||
}
|
||||
|
||||
if ($hasNameAr) {
|
||||
$sourceExpression = $hasLegacyName ? "COALESCE(NULLIF(`name_en`, ''), `name`)" : '`name_en`';
|
||||
$pdo->exec(
|
||||
"UPDATE `payment_methods`
|
||||
SET `name_ar` = $sourceExpression
|
||||
WHERE (`name_ar` IS NULL OR TRIM(`name_ar`) = '')
|
||||
AND $sourceExpression IS NOT NULL
|
||||
AND TRIM($sourceExpression) <> ''"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function payment_methods_schema_sync_20260502_seed_defaults(PDO $pdo): void
|
||||
{
|
||||
if (!payment_methods_schema_sync_20260502_table_exists($pdo, 'payment_methods')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = (int)$pdo->query('SELECT COUNT(*) FROM `payment_methods`')->fetchColumn();
|
||||
if ($count > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
['Cash', 'كاش'],
|
||||
['Credit Card', 'بطاقة بنكية'],
|
||||
['Bank Transfer', 'تحويل بنكي'],
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO `payment_methods` (`name_en`, `name_ar`) VALUES (?, ?)');
|
||||
foreach ($defaults as [$nameEn, $nameAr]) {
|
||||
$stmt->execute([$nameEn, $nameAr]);
|
||||
}
|
||||
}
|
||||
|
||||
function payment_methods_schema_sync_20260502_exec(PDO $pdo, string $statement): void
|
||||
{
|
||||
try {
|
||||
$pdo->exec($statement);
|
||||
} catch (PDOException $exception) {
|
||||
if (payment_methods_schema_sync_20260502_is_ignorable($exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
function payment_methods_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;
|
||||
}
|
||||
}
|
||||
|
||||
payment_methods_schema_sync_20260502_run();
|
||||
|
||||
return true;
|
||||
@ -29,6 +29,7 @@ class DatabaseInstaller {
|
||||
self::executeSqlFile($seedFile);
|
||||
}
|
||||
|
||||
self::seedInstallMigrationBaseline($pdo, $schemaFile);
|
||||
self::ensureCurrentSchema();
|
||||
|
||||
return true;
|
||||
@ -52,6 +53,57 @@ class DatabaseInstaller {
|
||||
return (int) $stmt->fetchColumn() === 0;
|
||||
}
|
||||
|
||||
private static function seedInstallMigrationBaseline(PDO $pdo, string $schemaFile): void {
|
||||
if (!self::shouldSeedInstallMigrationBaseline($schemaFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$baselineMigrations = self::getInstallBaselineMigrations();
|
||||
if ($baselineMigrations === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::ensureMigrationsTable($pdo);
|
||||
foreach ($baselineMigrations as $migrationName) {
|
||||
self::recordMigration($pdo, $migrationName);
|
||||
}
|
||||
}
|
||||
|
||||
private static function shouldSeedInstallMigrationBaseline(string $schemaFile): bool {
|
||||
$completeSchemaFile = __DIR__ . '/../complete_schema.sql';
|
||||
$schemaRealPath = realpath($schemaFile);
|
||||
$completeSchemaRealPath = realpath($completeSchemaFile);
|
||||
|
||||
if ($schemaRealPath === false || $completeSchemaRealPath === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $schemaRealPath === $completeSchemaRealPath;
|
||||
}
|
||||
|
||||
private static function getInstallBaselineMigrations(): array {
|
||||
$baselineFile = __DIR__ . '/../db/install_baseline_migrations.php';
|
||||
if (!is_file($baselineFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$baseline = require $baselineFile;
|
||||
if (!is_array($baseline)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$normalized = [];
|
||||
foreach ($baseline as $migrationName) {
|
||||
if (!is_string($migrationName) || trim($migrationName) === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalized[basename($migrationName)] = true;
|
||||
}
|
||||
|
||||
return array_keys($normalized);
|
||||
}
|
||||
|
||||
public static function ensureCurrentSchema(): void {
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
|
||||
142
index.php
142
index.php
@ -1973,11 +1973,97 @@ function getPromotionalPrice($item) {
|
||||
}
|
||||
|
||||
if (isset($_POST['add_payment_method'])) {
|
||||
$name = $_POST['name'] ?? '';
|
||||
db()->prepare("INSERT INTO payment_methods (name) VALUES (?)")->execute([$name]);
|
||||
$name_en = trim((string)($_POST['name_en'] ?? ''));
|
||||
$name_ar = trim((string)($_POST['name_ar'] ?? ''));
|
||||
|
||||
if ($name_en === '' && $name_ar === '') {
|
||||
redirectWithMessage("Please enter a payment method name.", "index.php?page=payment_methods");
|
||||
}
|
||||
|
||||
if ($name_en === '') {
|
||||
$name_en = $name_ar;
|
||||
}
|
||||
if ($name_ar === '') {
|
||||
$name_ar = $name_en;
|
||||
}
|
||||
|
||||
if (db_column_exists('payment_methods', 'name_en') || db_column_exists('payment_methods', 'name_ar')) {
|
||||
$columns = [];
|
||||
$placeholders = [];
|
||||
$values = [];
|
||||
|
||||
if (db_column_exists('payment_methods', 'name_en')) {
|
||||
$columns[] = 'name_en';
|
||||
$placeholders[] = '?';
|
||||
$values[] = $name_en;
|
||||
}
|
||||
if (db_column_exists('payment_methods', 'name_ar')) {
|
||||
$columns[] = 'name_ar';
|
||||
$placeholders[] = '?';
|
||||
$values[] = $name_ar;
|
||||
}
|
||||
|
||||
db()->prepare("INSERT INTO payment_methods (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $placeholders) . ")")->execute($values);
|
||||
} elseif (db_column_exists('payment_methods', 'name')) {
|
||||
db()->prepare("INSERT INTO payment_methods (`name`) VALUES (?)")->execute([$name_en]);
|
||||
} else {
|
||||
throw new RuntimeException('payment_methods table is missing a usable name column.');
|
||||
}
|
||||
|
||||
redirectWithMessage("Payment method added!", "index.php?page=payment_methods");
|
||||
}
|
||||
|
||||
if (isset($_POST['edit_payment_method'])) {
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$name_en = trim((string)($_POST['name_en'] ?? ''));
|
||||
$name_ar = trim((string)($_POST['name_ar'] ?? ''));
|
||||
|
||||
if ($id <= 0) {
|
||||
redirectWithMessage("Invalid payment method.", "index.php?page=payment_methods");
|
||||
}
|
||||
if ($name_en === '' && $name_ar === '') {
|
||||
redirectWithMessage("Please enter a payment method name.", "index.php?page=payment_methods");
|
||||
}
|
||||
|
||||
if ($name_en === '') {
|
||||
$name_en = $name_ar;
|
||||
}
|
||||
if ($name_ar === '') {
|
||||
$name_ar = $name_en;
|
||||
}
|
||||
|
||||
if (db_column_exists('payment_methods', 'name_en') || db_column_exists('payment_methods', 'name_ar')) {
|
||||
$sets = [];
|
||||
$values = [];
|
||||
|
||||
if (db_column_exists('payment_methods', 'name_en')) {
|
||||
$sets[] = 'name_en = ?';
|
||||
$values[] = $name_en;
|
||||
}
|
||||
if (db_column_exists('payment_methods', 'name_ar')) {
|
||||
$sets[] = 'name_ar = ?';
|
||||
$values[] = $name_ar;
|
||||
}
|
||||
|
||||
$values[] = $id;
|
||||
db()->prepare("UPDATE payment_methods SET " . implode(', ', $sets) . " WHERE id = ?")->execute($values);
|
||||
} elseif (db_column_exists('payment_methods', 'name')) {
|
||||
db()->prepare("UPDATE payment_methods SET `name` = ? WHERE id = ?")->execute([$name_en, $id]);
|
||||
} else {
|
||||
throw new RuntimeException('payment_methods table is missing a usable name column.');
|
||||
}
|
||||
|
||||
redirectWithMessage("Payment method updated!", "index.php?page=payment_methods");
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_payment_method'])) {
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
db()->prepare("DELETE FROM payment_methods WHERE id = ?")->execute([$id]);
|
||||
}
|
||||
redirectWithMessage("Payment method deleted!", "index.php?page=payment_methods");
|
||||
}
|
||||
|
||||
if (isset($_POST['delete_invoice'])) {
|
||||
$id = (int)$_POST['id'];
|
||||
$type = ($page === 'purchases') ? 'purchase' : 'sale';
|
||||
@ -7647,6 +7733,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ($page === 'payment_methods'): ?>
|
||||
<div class="card p-4 d-print-none">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="m-0" data-en="Payment Methods" data-ar="طرق الدفع">Payment Methods</h5>
|
||||
@ -7665,22 +7752,61 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($data['payment_methods'] as $pm): ?>
|
||||
<?php if (empty($data['payment_methods'])): ?>
|
||||
<tr>
|
||||
<td><?= $pm['id'] ?></td>
|
||||
<td><?= htmlspecialchars($pm['name_en'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($pm['name_ar'] ?? '') ?></td>
|
||||
<td colspan="4" class="text-center text-muted py-4" data-en="No payment methods found yet. Add one to get started." data-ar="لا توجد طرق دفع حتى الآن. أضف طريقة دفع للبدء.">No payment methods found yet. Add one to get started.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($data['payment_methods'] as $pm): ?>
|
||||
<?php
|
||||
$paymentMethodId = (int)($pm['id'] ?? 0);
|
||||
$paymentMethodNameEn = (string)($pm['name_en'] ?? $pm['name'] ?? '');
|
||||
$paymentMethodNameAr = (string)($pm['name_ar'] ?? $pm['name_en'] ?? $pm['name'] ?? '');
|
||||
?>
|
||||
<tr>
|
||||
<td><?= $paymentMethodId ?></td>
|
||||
<td><?= htmlspecialchars($paymentMethodNameEn) ?></td>
|
||||
<td><?= htmlspecialchars($paymentMethodNameAr) ?></td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-primary" title="Edit" data-bs-toggle="modal" data-bs-target="#editPaymentMethodModal<?= $pm['id'] ?>"><i class="bi bi-pencil"></i></button>
|
||||
<button class="btn btn-outline-primary" title="Edit" data-bs-toggle="modal" data-bs-target="#editPaymentMethodModal<?= $paymentMethodId ?>"><i class="bi bi-pencil"></i></button>
|
||||
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure?')">
|
||||
<input type="hidden" name="id" value="<?= $pm['id'] ?>">
|
||||
<input type="hidden" name="id" value="<?= $paymentMethodId ?>">
|
||||
<button type="submit" name="delete_payment_method" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="editPaymentMethodModal<?= $paymentMethodId ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow text-start">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" data-en="Edit Payment Method" data-ar="تعديل طريقة الدفع">Edit Payment Method</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="id" value="<?= $paymentMethodId ?>">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label" data-en="Name (EN)" data-ar="الاسم (EN)">Name (EN)</label>
|
||||
<input type="text" name="name_en" class="form-control" value="<?= htmlspecialchars($paymentMethodNameEn) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label" data-en="Name (AR)" data-ar="الاسم (AR)">Name (AR)</label>
|
||||
<input type="text" name="name_ar" class="form-control" value="<?= htmlspecialchars($paymentMethodNameAr) ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Cancel" data-ar="إلغاء">Cancel</button>
|
||||
<button type="submit" name="edit_payment_method" class="btn btn-primary" data-en="Update" data-ar="تحديث">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -3,3 +3,5 @@
|
||||
2026-02-26 17:58:46 - Failed login for 'admin'. Reason: Password mismatch
|
||||
2026-02-26 17:59:03 - Failed login for 'admin'. Reason: Password mismatch
|
||||
2026-03-17 10:12:37 - Failed login for 'moosa'. Reason: Password mismatch
|
||||
2026-05-02 04:46:38 - Failed login for 'moosa'. Reason: Password mismatch
|
||||
2026-05-02 04:46:47 - Failed login for 'admin'. Reason: Password mismatch
|
||||
|
||||
@ -151,3 +151,26 @@
|
||||
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"}
|
||||
2026-05-02 04:46:38 - POST: {"username":"moosa","password":"moosa123","login":""}
|
||||
2026-05-02 04:46:47 - POST: {"username":"admin","password":"admin123","login":""}
|
||||
2026-05-02 04:46:55 - POST: {"username":"admin","password":"admin","login":""}
|
||||
2026-05-02 04:50:45 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:50:50 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:50:50 - POST: {"name_en":"Wallet","name_ar":"\u0645\u062d\u0641\u0638\u0629","add_payment_method":"1"}
|
||||
2026-05-02 04:54:23 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:54:23 - POST: {"name_en":"QA Wallet","name_ar":"\u0645\u062d\u0641\u0638\u0629 \u062a\u062c\u0631\u064a\u0628\u064a\u0629","add_payment_method":"1"}
|
||||
2026-05-02 04:54:32 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:54:39 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:54:45 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:54:49 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:54:54 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:55:00 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:55:44 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:55:52 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:55:52 - POST: {"name_en":"QA Wallet","name_ar":"\u0645\u062d\u0641\u0638\u0629 \u062a\u062c\u0631\u064a\u0628\u064a\u0629","add_payment_method":"1"}
|
||||
2026-05-02 04:55:52 - POST: {"id":"5","name_en":"QA Wallet Updated","name_ar":"\u0645\u062d\u0641\u0638\u0629 \u0645\u062d\u062f\u062b\u0629","edit_payment_method":"1"}
|
||||
2026-05-02 04:55:52 - POST: {"id":"5","delete_payment_method":"1"}
|
||||
2026-05-02 04:56:04 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:56:12 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:56:19 - POST: {"username":"admin","password":"admin","login":"1"}
|
||||
2026-05-02 04:56:42 - POST: {"id":"3","name_en":"Bank Transfer","name_ar":"\u062a\u062d\u0648\u064a\u0644 \u0628\u0646\u0643\u064a","edit_payment_method":""}
|
||||
|
||||
@ -83,6 +83,66 @@ function normalizeCreateTableSql(string $sql): string
|
||||
return rtrim($sql, ';') . ';';
|
||||
}
|
||||
|
||||
function schemaSnapshotMigrationSortKey(string $filePath): string
|
||||
{
|
||||
$basename = basename($filePath);
|
||||
|
||||
return match ($basename) {
|
||||
'20260318_add_outlet_id_to_purchases.sql' => '20260318_10_add_outlet_id_to_purchases.sql',
|
||||
'20260318_create_outlets_table.sql' => '20260318_20_create_outlets_table.sql',
|
||||
'20260318_multi_outlet_schema.sql' => '20260318_30_multi_outlet_schema.sql',
|
||||
'20260318_local_definitions.sql' => '20260318_40_local_definitions.sql',
|
||||
'20260318_user_outlets_table.sql' => '20260318_50_user_outlets_table.sql',
|
||||
default => $basename,
|
||||
};
|
||||
}
|
||||
|
||||
function fetchInstallBaselineMigrationNames(): array
|
||||
{
|
||||
$files = array_merge(
|
||||
glob(__DIR__ . '/db/migrations/*.sql') ?: [],
|
||||
glob(__DIR__ . '/db/migrations/*.php') ?: []
|
||||
);
|
||||
|
||||
usort($files, static function (string $left, string $right): int {
|
||||
return strnatcasecmp(schemaSnapshotMigrationSortKey($left), schemaSnapshotMigrationSortKey($right));
|
||||
});
|
||||
|
||||
$names = [];
|
||||
foreach ($files as $filePath) {
|
||||
$migrationName = basename($filePath);
|
||||
if ($migrationName === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$names[$migrationName] = true;
|
||||
}
|
||||
|
||||
return array_keys($names);
|
||||
}
|
||||
|
||||
function buildInstallBaselineContent(array $migrationNames): string
|
||||
{
|
||||
$lines = [
|
||||
'<?php',
|
||||
'declare(strict_types=1);',
|
||||
'',
|
||||
'// Auto-generated install baseline for fresh installs.',
|
||||
'// Refreshed together with complete_schema.sql by refresh_complete_schema.php.',
|
||||
'',
|
||||
'return [',
|
||||
];
|
||||
|
||||
foreach ($migrationNames as $migrationName) {
|
||||
$lines[] = ' ' . var_export((string) $migrationName, true) . ',';
|
||||
}
|
||||
|
||||
$lines[] = '];';
|
||||
$lines[] = '';
|
||||
|
||||
return implode(PHP_EOL, $lines);
|
||||
}
|
||||
|
||||
function fetchBaseTableNames(PDO $pdo): array
|
||||
{
|
||||
$stmt = $pdo->query("SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'");
|
||||
@ -245,7 +305,7 @@ function buildSnapshotContent(PDO $pdo, array &$tableOrder = []): string
|
||||
'/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;',
|
||||
'',
|
||||
'-- Auto-generated full schema snapshot for fresh installs.',
|
||||
'-- Re-run refresh_complete_schema.php after schema changes so new installations stay current.',
|
||||
'-- Re-run refresh_complete_schema.php after schema or migration changes so new installations stay current.',
|
||||
'',
|
||||
];
|
||||
|
||||
@ -291,9 +351,20 @@ function runSchemaSnapshotRefresh(): int
|
||||
throw new RuntimeException('Unable to write complete_schema.sql');
|
||||
}
|
||||
|
||||
$baselineNames = fetchInstallBaselineMigrationNames();
|
||||
$baselineTargetFile = __DIR__ . '/db/install_baseline_migrations.php';
|
||||
$baselineBytesWritten = file_put_contents($baselineTargetFile, buildInstallBaselineContent($baselineNames));
|
||||
|
||||
if ($baselineBytesWritten === false) {
|
||||
throw new RuntimeException('Unable to write db/install_baseline_migrations.php');
|
||||
}
|
||||
|
||||
snapshotOutput('OK: refreshed complete_schema.sql');
|
||||
snapshotOutput('OK: refreshed db/install_baseline_migrations.php');
|
||||
snapshotOutput('Tables: ' . count($tableOrder));
|
||||
snapshotOutput('Baseline migrations: ' . count($baselineNames));
|
||||
snapshotOutput('Path: complete_schema.sql');
|
||||
snapshotOutput('Path: db/install_baseline_migrations.php');
|
||||
return 0;
|
||||
} catch (Throwable $throwable) {
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
|
||||
1
runtime_debug.log
Normal file
1
runtime_debug.log
Normal file
@ -0,0 +1 @@
|
||||
2026-05-02 04:50:50 || [PDOException] || SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'INSERT INTO' || file=/home/ubuntu/executor/workspace/index.php:1977 || page=payment_methods || uri=/index.php?page=payment_methods || user_id=1 || hint=Missing column: name || error_info=["42S22",1054,"Unknown column 'name' in 'INSERT INTO'"] || trace=#0 /home/ubuntu/executor/workspace/index.php(1977): PDOStatement->execute() | #1 {main}
|
||||
Loading…
x
Reference in New Issue
Block a user