79 lines
3.9 KiB
PHP
79 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use RuntimeException;
|
|
|
|
class MoveShorakaFinancings extends Command
|
|
{
|
|
protected $signature = 'shoraka:move-financings {--source=mysql} {--apply : Copy records; default only checks}';
|
|
protected $description = 'Copy and verify financing records to shoraka_master without deleting the source';
|
|
|
|
public function handle()
|
|
{
|
|
$sourceName = $this->option('source');
|
|
$targetName = 'shoraka_master';
|
|
$source = DB::connection($sourceName);
|
|
$target = DB::connection($targetName);
|
|
if ($sourceName === $targetName || ($source->getConfig('host') === $target->getConfig('host')
|
|
&& $source->getDatabaseName() === $target->getDatabaseName()
|
|
&& $source->getConfig('port') === $target->getConfig('port'))) {
|
|
$this->error('Source and target must be different databases.');
|
|
return 1;
|
|
}
|
|
if (!Schema::connection($sourceName)->hasTable('shoraka_financings')
|
|
|| !Schema::connection($targetName)->hasTable('shoraka_financings')) {
|
|
$this->error('Create the target table using the API master migration first. Source and target tables are required.');
|
|
return 1;
|
|
}
|
|
if ($this->option('apply') && !app()->isDownForMaintenance()) {
|
|
$this->error('Stop traffic on every counter instance and run artisan down before applying the transfer.');
|
|
return 1;
|
|
}
|
|
if ($source->table('shoraka_financings')->where('state', 'PROCESSING')->exists()) {
|
|
$this->error('PROCESSING records require reconciliation before transfer.');
|
|
return 1;
|
|
}
|
|
$count = $source->table('shoraka_financings')->count();
|
|
if (!$this->option('apply')) {
|
|
$this->info($count . ' source records. Check only; no data was copied.');
|
|
return 0;
|
|
}
|
|
try {
|
|
$target->transaction(function () use ($source, $target) {
|
|
$source->table('shoraka_financings')->orderBy('id')->chunkById(200, function ($rows) use ($target) {
|
|
foreach ($rows as $row) {
|
|
$data = (array) $row;
|
|
unset($data['id']); // IDs may overlap across counter databases.
|
|
$key = ['kodcaw' => $row->kodcaw, 'norujukan' => $row->norujukan];
|
|
$existing = $target->table('shoraka_financings')->where($key)->first();
|
|
if (!$existing) {
|
|
$target->table('shoraka_financings')->insert($data);
|
|
$existing = $target->table('shoraka_financings')->where($key)->first();
|
|
}
|
|
foreach ($data as $field => $value) {
|
|
$actual = $existing->$field;
|
|
if ($field === 'context') {
|
|
if (json_decode($actual, true) != json_decode($value, true)) {
|
|
throw new RuntimeException('Conflicting context: ' . $row->kodcaw . '/' . $row->norujukan);
|
|
}
|
|
} elseif ((string) $actual !== (string) $value) {
|
|
throw new RuntimeException('Conflicting field ' . $field . ': ' . $row->kodcaw . '/' . $row->norujukan);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
} catch (\Throwable $exception) {
|
|
$this->error('Transfer rolled back. ' . $exception->getMessage());
|
|
return 1;
|
|
}
|
|
$this->info($count . ' source records copied and verified. Source retained.');
|
|
$this->info('Ensure shoraka.financing_connection is shoraka_master and reload configuration before reopening traffic.');
|
|
return 0;
|
|
}
|
|
}
|