$v) { putenv($k.'='.$v); $_ENV[$k] = $_SERVER[$k] = $v; } require $s['HOST_ROOT'].'/vendor/autoload.php'; $app = require $s['HOST_ROOT'].'/bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap(); use Illuminate\Support\Facades\DB; use Tempo\Finance\Payables\Payables; $db = DB::connection(); if (! preg_match('/^(compras_receipts_test_|payable_sgf_)[a-f0-9]{12}$/D', $db->getDatabaseName()) || $db->getConfig('host') !== '127.0.0.1' || $db->selectOne('select current_database() as d')->d !== $s['DB_DATABASE']) throw new LogicException('Synthetic database guard.'); $newActions = ['payments_view', 'payment_prepare', 'payment_confirm', 'allocation_prepare', 'allocation_confirm']; $tables = ['pa_sources', 'pa_proposals', 'pa_obligations', 'pa_events', 'pa_commands', 'pa_outbox', 'pa_history_imports', 'pa_imported_payments', 'pa_payment_decisions', 'pa_imported_sources', 'permission_grants']; if ($mode === 'compras') $tables = [...$tables, 'invoices', 'invoice_items', 'purchase_orders', 'goods_receipts', 'goods_receipt_items', 'stock_movements', 'budget_entries', 'budget_consumptions', 'external_payments']; $hashes = []; $counts = []; DB::transaction(function () use ($db, $tables, $newActions, &$hashes, &$counts): void { $db->statement('SET TRANSACTION READ ONLY'); foreach ($tables as $table) { if (! $db->getSchemaBuilder()->hasTable($table)) { $hashes[$table] = null; continue; } $rows = $db->table($table)->orderBy($table === 'pa_outbox' ? 'event_id' : 'id')->get()->map(fn ($r) => (array) $r)->all(); $hashes[$table] = hash('sha256', Payables::canonical($rows)); $counts[$table] = count($rows); } $users = $db->table('users')->orderBy('id')->get(['id', 'name', 'email'])->map(fn ($r) => (array) $r)->all(); $hashes['accounts_without_credentials'] = hash('sha256', Payables::canonical($users)); $counts['users'] = count($users); $access = $db->table('pa_access')->whereNotIn('action', $newActions)->orderBy('id')->get()->map(fn ($r) => (array) $r)->all(); $hashes['existing_access'] = hash('sha256', Payables::canonical($access)); }); $path = __DIR__.'/../docs/proofs/supplier-settlements-demo-'.$mode.'.json'; if ($phase === 'before') { file_put_contents($path, json_encode(['host' => $mode, 'hashes_before' => $hashes, 'counts_before' => $counts, 'checked_at' => gmdate('c')], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR).PHP_EOL); echo $mode." synthetic baseline recorded.\n"; } else { $before = json_decode(file_get_contents($path), true, 32, JSON_THROW_ON_ERROR); if ($before['hashes_before'] !== $hashes || $before['counts_before'] !== $counts) throw new LogicException('Existing synthetic data changed; investigate without reset.'); $ps = []; foreach (\Tempo\Finance\Payables\SettlementSchema::TABLES as $t) $ps[$t] = $db->table($t)->count(); $result = $before + ['preserved' => true, 'schema_counts' => $ps, 'checked_after' => gmdate('c')]; file_put_contents($path, json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR).PHP_EOL); echo $mode." original documents, history, accounts and access preserved; synthetic settlements installed.\n"; }