Files
SGF/scripts/product-demo.php
2026-10-29 18:43:29 +02:00

86 lines
5.5 KiB
PHP

<?php
// Consolidates existing synthetic fixtures; never migrates, resets or touches client data.
require __DIR__.'/../vendor/autoload.php';
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
use App\Models\User;
use App\Modules\Identity\Services\Permissions;
use App\Shared\Testing\TestingDatabaseGuard;
use Illuminate\Support\Facades\DB;
use Tests\Support\{DocumentPreviewFixture, WorkshopCorrectionFixture, IndustrialBudgetFixture, ReceivablesFixture};
TestingDatabaseGuard::assertSafe();
\App\Modules\Services\ServiceInvoicing::guard();
$file = 'C:/laragon/data/sgf-tempo/secrets/browser-fixture.json';
$verify = in_array('--verify', $argv, true);
$manifest = is_file($file) ? json_decode(file_get_contents($file), true, 64, JSON_THROW_ON_ERROR) : null;
if (isset($manifest['product_demo'])) {
foreach ($manifest['product_demo']['actors'] as $role => $id) User::findOrFail($id);
echo json_encode(['demo' => $manifest['product_demo'], 'changed' => false], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR).PHP_EOL;
exit;
}
// A missing manifest must never silently adopt unrelated test records.
if (!$manifest && DB::table('users')->exists()) throw new RuntimeException('Existing test data has no manifest. Nothing reset; recover its manifest first.');
if ($manifest && empty($manifest['password'])) throw new RuntimeException('Synthetic credential manifest required; no passwords changed.');
foreach (['ic_lots', 'si_invoices'] as $table) {
if (DB::table($table)->exists()) throw new RuntimeException('Existing isolated scenario requires reconciliation: '.$table.'. Nothing deleted.');
}
DB::beginTransaction();
try {
if ($manifest) {
$base = array_intersect_key($manifest, array_flip(['tenant', 'otherTenant', 'company', 'sibling', 'foreign', 'unit', 'hidden', 'siblingUnit', 'password']));
$base['manager'] = User::findOrFail($manifest['manager_id']);
$base['reader'] = User::findOrFail($manifest['reader_id']);
} else {
$base = DocumentPreviewFixture::create();
$manifest = array_intersect_key($base, array_flip(['tenant', 'otherTenant', 'company', 'sibling', 'foreign', 'unit', 'hidden', 'siblingUnit', 'password']));
$manifest['manager_id'] = $base['manager']->id;
$manifest['reader_id'] = $base['reader']->id;
}
if (isset($manifest['workshop_corrections'])) {
$workshop = $manifest['workshop_corrections'];
$tech = User::where('email', 'tecnico-oficina@example.test')->firstOrFail();
$reviewer = User::where('email', 'verificador-oficina@example.test')->firstOrFail();
if (!DB::table('wo_orders')->where(['id' => $workshop['order'], 'company_id' => $base['company'], 'unit_id' => $base['unit']])->exists()) throw new RuntimeException('Workshop manifest differs.');
} else {
if (DB::table('wo_orders')->exists()) throw new RuntimeException('Existing workshop has no reconciled manifest. Nothing overwritten.');
$w = WorkshopCorrectionFixture::create($base);
$workshop = array_intersect_key($w, array_flip(['order', 'time', 'material', 'service', 'client', 'generation', 'service_draft', 'billing_draft']));
$tech = $w['technician'];
$reviewer = $w['reviewer'];
$manifest['workshop_corrections'] = $workshop;
}
$industry = IndustrialBudgetFixture::create($base);
$receivables = ReceivablesFixture::create($base);
$actors = ['preparador' => $base['manager']->id, 'validador' => $base['reader']->id, 'tecnico' => $tech->id, 'verificador' => $reviewer->id];
if (count(array_unique($actors)) !== 4) throw new RuntimeException('Four distinct synthetic actors required.');
$permissions = app(Permissions::class);
foreach (['preparador' => ['workshop.verify', 'industrial_costs.validate'], 'validador' => ['workshop.record', 'industrial_costs.prepare'], 'tecnico' => ['workshop.verify'], 'verificador' => ['workshop.record']] as $role => $forbidden) {
foreach ($forbidden as $right) if ($permissions->has($actors[$role], $base['tenant'], $base['company'], $right, $base['unit'])) throw new RuntimeException('Role separation differs: '.$role);
}
$demo = ['version' => 'SGF-PRODUCT-DEMO-v1', 'synthetic_only' => true, 'actors' => $actors,
'workshop' => $workshop,
'industry' => array_intersect_key($industry, array_flip(['lot', 'refs', 'result'])),
'receivables' => array_intersect_key($receivables, array_flip(['source', 'draft', 'invoice', 'series', 'rule'])),
'prepared_at' => now()->toIso8601String()];
if ($verify) {
DB::rollBack();
} else {
// Manifest and data are committed together as far as possible; retain old file on write failure.
$encoded = json_encode($manifest + ['product_demo' => $demo], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
if (file_put_contents($file, $encoded, LOCK_EX) === false) throw new RuntimeException('Cannot save protected manifest.');
try { DB::commit(); } catch (\Throwable $e) {
file_put_contents($file, json_encode($manifest, JSON_THROW_ON_ERROR), LOCK_EX);
throw $e;
}
}
echo json_encode(['demo' => $demo, 'rolled_back' => $verify, 'client_database_changed' => false,
'credentials' => 'existing protected local manifest only', 'real_grants_created' => false], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR).PHP_EOL;
} catch (\Throwable $e) {
if (DB::transactionLevel()) DB::rollBack();
throw $e;
}