changes
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FixImportMappingEntities extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'import:fix-mapping-entities {--dry-run : Show changes without applying them}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Fix entity names in import_mappings table to use canonical roots';
|
||||
|
||||
/**
|
||||
* Entity name mappings from incorrect to correct canonical roots
|
||||
*/
|
||||
protected array $entityMapping = [
|
||||
'contracts' => 'contract',
|
||||
'contract' => 'contract',
|
||||
'client_cases' => 'client_case',
|
||||
'client_case' => 'client_case',
|
||||
'person_addresses' => 'address',
|
||||
'addresses' => 'address',
|
||||
'address' => 'address',
|
||||
'person_phones' => 'phone',
|
||||
'phones' => 'phone',
|
||||
'phone' => 'phone',
|
||||
'emails' => 'email',
|
||||
'email' => 'email',
|
||||
'activities' => 'activity',
|
||||
'activity' => 'activity',
|
||||
'persons' => 'person',
|
||||
'person' => 'person',
|
||||
'accounts' => 'account',
|
||||
'account' => 'account',
|
||||
'payments' => 'payment',
|
||||
'payment' => 'payment',
|
||||
'bookings' => 'booking',
|
||||
'booking' => 'booking',
|
||||
];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
if ($dryRun) {
|
||||
$this->info('Running in DRY-RUN mode - no changes will be made');
|
||||
}
|
||||
|
||||
$mappings = DB::table('import_mappings')
|
||||
->whereNotNull('entity')
|
||||
->where('entity', '!=', '')
|
||||
->get();
|
||||
|
||||
if ($mappings->isEmpty()) {
|
||||
$this->info('No mappings found to fix.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info("Found {$mappings->count()} mappings to check");
|
||||
$this->newLine();
|
||||
|
||||
$updates = [];
|
||||
$unchanged = 0;
|
||||
|
||||
foreach ($mappings as $mapping) {
|
||||
$currentEntity = trim($mapping->entity);
|
||||
|
||||
if (isset($this->entityMapping[$currentEntity])) {
|
||||
$correctEntity = $this->entityMapping[$currentEntity];
|
||||
|
||||
if ($currentEntity !== $correctEntity) {
|
||||
$updates[] = [
|
||||
'id' => $mapping->id,
|
||||
'current' => $currentEntity,
|
||||
'correct' => $correctEntity,
|
||||
'source' => $mapping->source_column,
|
||||
'target' => $mapping->target_field,
|
||||
];
|
||||
} else {
|
||||
$unchanged++;
|
||||
}
|
||||
} else {
|
||||
$this->warn("Unknown entity type: {$currentEntity} (ID: {$mapping->id})");
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($updates)) {
|
||||
$this->info("✓ All {$unchanged} mappings already have correct entity names!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Display changes
|
||||
$this->info("Changes to be made:");
|
||||
$this->newLine();
|
||||
|
||||
$table = [];
|
||||
foreach ($updates as $update) {
|
||||
$table[] = [
|
||||
$update['id'],
|
||||
$update['source'],
|
||||
$update['target'],
|
||||
$update['current'],
|
||||
$update['correct'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['ID', 'Source Column', 'Target Field', 'Current Entity', 'Correct Entity'],
|
||||
$table
|
||||
);
|
||||
|
||||
$this->newLine();
|
||||
$this->info("Total changes: " . count($updates));
|
||||
$this->info("Unchanged: {$unchanged}");
|
||||
|
||||
if ($dryRun) {
|
||||
$this->newLine();
|
||||
$this->warn('DRY-RUN mode: No changes were made. Run without --dry-run to apply changes.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Confirm before proceeding
|
||||
if (!$this->confirm('Do you want to apply these changes?', true)) {
|
||||
$this->info('Operation cancelled.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply updates
|
||||
$updated = 0;
|
||||
foreach ($updates as $update) {
|
||||
DB::table('import_mappings')
|
||||
->where('id', $update['id'])
|
||||
->update(['entity' => $update['correct']]);
|
||||
$updated++;
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info("✓ Successfully updated {$updated} mappings!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PopulateImportMappingEntities extends Command
|
||||
{
|
||||
protected $signature = 'import:populate-mapping-entities {--dry-run : Show changes without applying them}';
|
||||
|
||||
protected $description = 'Populate entity column from target_field for mappings where entity is null';
|
||||
|
||||
protected array $entityMap = [
|
||||
'contracts' => 'contract',
|
||||
'client_cases' => 'client_case',
|
||||
'person_addresses' => 'address',
|
||||
'person_phones' => 'phone',
|
||||
'emails' => 'email',
|
||||
'activities' => 'activity',
|
||||
'payments' => 'payment',
|
||||
'accounts' => 'account',
|
||||
'persons' => 'person',
|
||||
'person' => 'person',
|
||||
'contract' => 'contract',
|
||||
'client_case' => 'client_case',
|
||||
'address' => 'address',
|
||||
'phone' => 'phone',
|
||||
'email' => 'email',
|
||||
'activity' => 'activity',
|
||||
'payment' => 'payment',
|
||||
'account' => 'account',
|
||||
];
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$dryRun = $this->option('dry-run');
|
||||
|
||||
$this->info('Populating entity column from target_field...');
|
||||
if ($dryRun) {
|
||||
$this->warn('DRY RUN MODE - No changes will be made');
|
||||
}
|
||||
|
||||
// Get all mappings where entity is null
|
||||
$mappings = DB::table('import_mappings')
|
||||
->whereNull('entity')
|
||||
->get();
|
||||
|
||||
if ($mappings->isEmpty()) {
|
||||
$this->info('No mappings found with null entity.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info("Found {$mappings->count()} mappings to process.");
|
||||
$this->newLine();
|
||||
|
||||
$updated = 0;
|
||||
$skipped = 0;
|
||||
|
||||
foreach ($mappings as $mapping) {
|
||||
$targetField = $mapping->target_field;
|
||||
|
||||
// Parse the target_field to extract entity and field
|
||||
if (str_contains($targetField, '.')) {
|
||||
[$rawEntity, $field] = explode('.', $targetField, 2);
|
||||
} elseif (str_contains($targetField, '->')) {
|
||||
[$rawEntity, $field] = explode('->', $targetField, 2);
|
||||
} else {
|
||||
$this->warn("Skipping mapping ID {$mapping->id}: Cannot parse target_field '{$targetField}'");
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$rawEntity = trim($rawEntity);
|
||||
$field = trim($field);
|
||||
|
||||
// Map to canonical entity name
|
||||
$canonicalEntity = $this->entityMap[$rawEntity] ?? $rawEntity;
|
||||
|
||||
$this->line(sprintf(
|
||||
"ID %d: '%s' -> '%s' => entity='%s', field='%s'",
|
||||
$mapping->id,
|
||||
$mapping->source_column,
|
||||
$targetField,
|
||||
$canonicalEntity,
|
||||
$field
|
||||
));
|
||||
|
||||
if (!$dryRun) {
|
||||
DB::table('import_mappings')
|
||||
->where('id', $mapping->id)
|
||||
->update([
|
||||
'entity' => $canonicalEntity,
|
||||
'target_field' => $field,
|
||||
]);
|
||||
$updated++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
if ($dryRun) {
|
||||
$this->info("Dry run complete. Would have updated {$mappings->count()} mappings.");
|
||||
} else {
|
||||
$this->info("Successfully updated {$updated} mappings.");
|
||||
}
|
||||
|
||||
if ($skipped > 0) {
|
||||
$this->warn("Skipped {$skipped} mappings that couldn't be parsed.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Import;
|
||||
use App\Services\Import\ImportSimulationServiceV2;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SimulateImportV2Command extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'import:simulate-v2 {import_id} {--limit=100 : Number of rows to simulate} {--verbose : Include detailed information}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Simulate ImportServiceV2 without persisting data';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(ImportSimulationServiceV2 $service): int
|
||||
{
|
||||
$importId = $this->argument('import_id');
|
||||
$limit = (int) $this->option('limit');
|
||||
$verbose = (bool) $this->option('verbose');
|
||||
|
||||
$import = Import::find($importId);
|
||||
|
||||
if (! $import) {
|
||||
$this->error("Import #{$importId} not found.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Simulating import #{$importId} - {$import->file_name}");
|
||||
$this->info("Client: ".($import->client->name ?? 'N/A'));
|
||||
$this->info("Limit: {$limit} rows");
|
||||
$this->line('');
|
||||
|
||||
$result = $service->simulate($import, $limit, $verbose);
|
||||
|
||||
if (! $result['success']) {
|
||||
$this->error('Simulation failed: '.$result['error']);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("✓ Simulated {$result['total_simulated']} rows");
|
||||
$this->line('');
|
||||
|
||||
// Display summaries
|
||||
if (! empty($result['summaries'])) {
|
||||
$this->info('=== Entity Summaries ===');
|
||||
$summaryRows = [];
|
||||
|
||||
foreach ($result['summaries'] as $entity => $stats) {
|
||||
$summaryRows[] = [
|
||||
'entity' => $entity,
|
||||
'create' => $stats['create'],
|
||||
'update' => $stats['update'],
|
||||
'skip' => $stats['skip'],
|
||||
'invalid' => $stats['invalid'],
|
||||
'total' => array_sum($stats),
|
||||
];
|
||||
}
|
||||
|
||||
$this->table(
|
||||
['Entity', 'Create', 'Update', 'Skip', 'Invalid', 'Total'],
|
||||
$summaryRows
|
||||
);
|
||||
$this->line('');
|
||||
}
|
||||
|
||||
// Display row previews (first 5)
|
||||
if (! empty($result['rows'])) {
|
||||
$this->info('=== Row Previews (first 5) ===');
|
||||
|
||||
foreach (array_slice($result['rows'], 0, 5) as $row) {
|
||||
$this->line("Row #{$row['row_number']}:");
|
||||
|
||||
if (! empty($row['entities'])) {
|
||||
foreach ($row['entities'] as $entity => $data) {
|
||||
$action = $data['action'];
|
||||
$color = match ($action) {
|
||||
'create' => 'green',
|
||||
'update' => 'yellow',
|
||||
'skip' => 'gray',
|
||||
'invalid', 'error' => 'red',
|
||||
default => 'white',
|
||||
};
|
||||
|
||||
$line = " {$entity}: <fg={$color}>{$action}</>";
|
||||
|
||||
if (isset($data['reference'])) {
|
||||
$line .= " ({$data['reference']})";
|
||||
}
|
||||
|
||||
if (isset($data['existing_id'])) {
|
||||
$line .= " [ID: {$data['existing_id']}]";
|
||||
}
|
||||
|
||||
$this->line($line);
|
||||
|
||||
if ($verbose && ! empty($data['changes'])) {
|
||||
foreach ($data['changes'] as $field => $change) {
|
||||
$this->line(" {$field}: {$change['old']} → {$change['new']}");
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($data['errors'])) {
|
||||
foreach ($data['errors'] as $error) {
|
||||
$this->error(" ✗ {$error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($row['warnings'])) {
|
||||
foreach ($row['warnings'] as $warning) {
|
||||
$this->warn(" ⚠ {$warning}");
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($row['errors'])) {
|
||||
foreach ($row['errors'] as $error) {
|
||||
$this->error(" ✗ {$error}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->line('');
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('Simulation completed successfully.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\ProcessLargeImportJob;
|
||||
use App\Models\Import;
|
||||
use App\Services\Import\ImportServiceV2;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TestImportV2Command extends Command
|
||||
{
|
||||
protected $signature = 'import:test-v2 {import_id : The import ID to process} {--queue : Process via queue}';
|
||||
|
||||
protected $description = 'Test ImportServiceV2 with an existing import';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$importId = $this->argument('import_id');
|
||||
$useQueue = $this->option('queue');
|
||||
|
||||
$import = Import::find($importId);
|
||||
|
||||
if (! $import) {
|
||||
$this->error("Import {$importId} not found.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Processing import: {$import->id} ({$import->file_name})");
|
||||
$this->info("Source: {$import->source_type}");
|
||||
$this->info("Status: {$import->status}");
|
||||
$this->newLine();
|
||||
|
||||
if ($useQueue) {
|
||||
$this->info('Dispatching to queue...');
|
||||
ProcessLargeImportJob::dispatch($import, auth()->id());
|
||||
$this->info('Job dispatched successfully. Monitor queue for progress.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->info('Processing synchronously...');
|
||||
$service = app(ImportServiceV2::class);
|
||||
|
||||
try {
|
||||
$results = $service->process($import, auth()->user());
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Processing completed!');
|
||||
$this->table(
|
||||
['Metric', 'Count'],
|
||||
[
|
||||
['Total rows', $results['total']],
|
||||
['Imported', $results['imported']],
|
||||
['Skipped', $results['skipped']],
|
||||
['Invalid', $results['invalid']],
|
||||
]
|
||||
);
|
||||
|
||||
return 0;
|
||||
} catch (\Throwable $e) {
|
||||
$this->error('Processing failed: '.$e->getMessage());
|
||||
$this->error($e->getTraceAsString());
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user