refactor(migrations): consolidar add_* en sus create_* (49 → 28)

Pliega las migraciones de "añadir/modificar columna" dentro de la migración create
de cada tabla, con esquema final idéntico (verificado por diff normalizado de columnas
+ índices y suite completa):
- users (locale, notes), projects (reference/external_reference_1/country),
  companies (apodo/estado/logo_path), layers (color; sin geojson_data),
  phases (fechas), features (status/responsible_user_id + sync + softDeletes),
  inspection_templates (phase_id), inspections (workflow + sync + softDeletes),
  issues (type + sync), progress_updates/media (sync), issue_tasks (overdue),
  permissions (group/description) y roles (description); soft-deletes en sus creates.
- Se mantienen las que dependen de orden/FK: add_profile_fields (users.company_id → companies)
  y update_inspections_feature_id_foreign (feature_id → features).
- Eliminado database/schema/mysql-schema.sql (obsoleto): las instalaciones nuevas
  ejecutan las migraciones; las BD existentes no se tocan. Regenerable con schema:dump.

Suite 75 passing. Esquema SQLite idéntico antes/después.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 18:40:54 +02:00
parent 2dccbe3a14
commit 9bd6fd898d
35 changed files with 43 additions and 1042 deletions
@@ -18,6 +18,8 @@ return new class extends Migration
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->string('locale', 5)->default('es');
$table->text('notes')->nullable();
$table->timestamps();
});
@@ -10,8 +10,11 @@ return new class extends Migration
{
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->string('reference')->nullable();
$table->string('external_reference_1')->nullable();
$table->string('name');
$table->text('address');
$table->char('country', 2)->nullable();
$table->decimal('lat', 10, 8);
$table->decimal('lng', 11, 8);
$table->date('start_date');
@@ -19,6 +22,7 @@ return new class extends Migration
$table->enum('status', ['planning', 'in_progress', 'paused', 'completed'])->default('planning');
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
}
@@ -16,7 +16,12 @@ return new class extends Migration
$table->integer('order')->default(0);
$table->string('color', 7)->default('#3b82f6'); // hex color
$table->integer('progress_percent')->default(0);
$table->date('planned_start')->nullable();
$table->date('planned_end')->nullable();
$table->date('actual_start')->nullable();
$table->date('actual_end')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
@@ -13,10 +13,11 @@ return new class extends Migration
$table->foreignId('project_id')->constrained()->onDelete('cascade');
$table->foreignId('phase_id')->nullable()->constrained()->onDelete('set null');
$table->string('name');
$table->json('geojson_data'); // GeoJSON geometry collection
$table->string('color', 7)->default('#3b82f6');
$table->string('original_file')->nullable(); // path to original uploaded file
$table->foreignId('uploaded_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
}
@@ -10,11 +10,13 @@ return new class extends Migration
{
Schema::create('progress_updates', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->nullable()->unique();
$table->foreignId('phase_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained();
$table->integer('progress_percent');
$table->text('comment')->nullable();
$table->json('location')->nullable(); // GPS point from mobile
$table->timestamp('client_updated_at')->nullable();
$table->timestamps();
});
}
@@ -24,6 +24,8 @@ return new class extends Migration
// $table->engine('InnoDB');
$table->bigIncrements('id'); // permission id
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
$table->string('group')->nullable();
$table->string('description')->nullable();
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
$table->timestamps();
@@ -38,6 +40,7 @@ return new class extends Migration
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
$table->string('description')->nullable();
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
$table->timestamps();
if ($teams || config('permission.testing')) {
@@ -16,7 +16,9 @@ return new class extends Migration
$table->string('name');
$table->text('description')->nullable();
$table->foreignId('project_id')->nullable()->constrained()->onDelete('cascade');
$table->foreignId('phase_id')->nullable()->constrained('phases')->onDelete('set null');
$table->json('fields'); // [{name, label, type, options, required}]
$table->index('phase_id');
$table->timestamps();
});
}
@@ -13,13 +13,21 @@ return new class extends Migration
{
Schema::create('inspections', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->nullable()->unique();
$table->foreignId('project_id')->constrained()->onDelete('cascade');
$table->foreignId('layer_id')->constrained()->onDelete('cascade');
$table->string('feature_id'); // ID del elemento GeoJSON
$table->foreignId('template_id')->nullable()->constrained('inspection_templates')->onDelete('set null');
$table->foreignId('user_id')->constrained();
$table->json('data'); // Valores de los campos del template
$table->enum('status', ['pending', 'in_progress', 'completed', 'approved', 'rejected'])->default('pending');
$table->foreignId('inspector_user_id')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('completed_at')->nullable();
$table->enum('result', ['pass', 'fail', 'conditional'])->nullable();
$table->text('notes')->nullable();
$table->timestamp('client_updated_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
@@ -13,14 +13,19 @@ return new class extends Migration
{
Schema::create('features', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->nullable()->unique();
$table->foreignId('layer_id')->constrained()->onDelete('cascade');
$table->string('name')->nullable();
$table->json('geometry'); // GeoJSON geometry object (Point, LineString, Polygon)
$table->json('properties')->nullable(); // propiedades extra (progreso, responsable, etc.)
$table->foreignId('template_id')->nullable()->constrained('inspection_templates');
$table->integer('progress')->default(0);
$table->enum('status', ['planned', 'started', 'in_progress', 'completed', 'verified'])->default('planned');
$table->string('responsible')->nullable();
$table->foreignId('responsible_user_id')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('client_updated_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('layers', function (Blueprint $table) {
$table->dropColumn('geojson_data');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('layers', function (Blueprint $table) {
$table->json('geojson_data'); // GeoJSON geometry collection
});
}
};
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('layers', function (Blueprint $table) {
$table->string('color', 7)->default('#3b82f6')->after('name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('layers', function (Blueprint $table) {
$table->dropColumn('color');
});
}
};
@@ -10,6 +10,7 @@ return new class extends Migration
{
Schema::create('media', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->nullable()->unique();
$table->morphs('mediable'); // project, phase, layer, feature
$table->string('name');
$table->string('file_path');
@@ -20,6 +21,7 @@ return new class extends Migration
$table->text('description')->nullable();
$table->json('metadata')->nullable(); // EXIF, GPS coords, etc.
$table->foreignId('uploaded_by')->constrained('users');
$table->timestamp('client_updated_at')->nullable();
$table->timestamps();
});
}
@@ -1,22 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('locale', 5)->default('en')->after('remember_token');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('locale');
});
}
};
@@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('inspection_templates', function (Blueprint $table) {
$table->foreignId('phase_id')->nullable()->constrained('phases')->onDelete('set null');
$table->index('phase_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('inspection_templates', function (Blueprint $table) {
$table->dropForeign(['phase_id']);
$table->dropColumn('phase_id');
});
}
};
@@ -14,6 +14,8 @@ return new class extends Migration
Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('apodo')->nullable();
$table->enum('estado', ['activo', 'inactivo', 'suspendido'])->default('activo');
$table->string('tax_id')->nullable()->unique();
$table->string('address')->nullable();
$table->string('phone')->nullable();
@@ -21,6 +23,7 @@ return new class extends Migration
$table->string('website')->nullable();
$table->enum('type', ['owner', 'constructor', 'subcontractor', 'consultant', 'supplier', 'other'])->default('other');
$table->text('notes')->nullable();
$table->string('logo_path')->nullable();
$table->timestamps();
$table->softDeletes();
});
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('companies', function (Blueprint $table) {
$table->string('logo_path')->nullable()->after('notes');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('companies', function (Blueprint $table) {
$table->dropColumn('logo_path');
});
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('companies', function (Blueprint $table) {
$table->string('apodo')->nullable()->after('name');
$table->enum('estado', ['activo', 'inactivo', 'suspendido'])->default('activo')->after('apodo');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('companies', function (Blueprint $table) {
$table->dropColumn(['apodo', 'estado']);
});
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('features', function (Blueprint $table) {
$table->enum('status', ['planned', 'started', 'in_progress', 'completed', 'verified'])
->default('planned')
->after('progress');
$table->foreignId('responsible_user_id')
->nullable()
->constrained('users')
->nullOnDelete()
->after('responsible');
});
}
public function down(): void
{
Schema::table('features', function (Blueprint $table) {
$table->dropForeign(['responsible_user_id']);
$table->dropColumn(['status', 'responsible_user_id']);
});
}
};
@@ -1,43 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('inspections', function (Blueprint $table) {
$table->enum('status', ['pending', 'in_progress', 'completed', 'approved', 'rejected'])
->default('pending')
->after('data');
$table->foreignId('inspector_user_id')
->nullable()
->constrained('users')
->nullOnDelete()
->after('status');
$table->timestamp('completed_at')
->nullable()
->after('inspector_user_id');
$table->enum('result', ['pass', 'fail', 'conditional'])
->nullable()
->after('completed_at');
$table->text('notes')
->nullable()
->after('result');
});
}
public function down(): void
{
Schema::table('inspections', function (Blueprint $table) {
$table->dropForeign(['inspector_user_id']);
$table->dropColumn(['status', 'inspector_user_id', 'completed_at', 'result', 'notes']);
});
}
};
@@ -1,25 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('phases', function (Blueprint $table) {
$table->date('planned_start')->nullable()->after('progress_percent');
$table->date('planned_end')->nullable()->after('planned_start');
$table->date('actual_start')->nullable()->after('planned_end');
$table->date('actual_end')->nullable()->after('actual_start');
});
}
public function down(): void
{
Schema::table('phases', function (Blueprint $table) {
$table->dropColumn(['planned_start', 'planned_end', 'actual_start', 'actual_end']);
});
}
};
@@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$tables = ['projects', 'phases', 'layers', 'features', 'inspections'];
foreach ($tables as $table) {
if (!Schema::hasColumn($table, 'deleted_at')) {
Schema::table($table, function (Blueprint $t) {
$t->softDeletes();
});
}
}
}
public function down(): void
{
$tables = ['projects', 'phases', 'layers', 'features', 'inspections'];
foreach ($tables as $table) {
if (Schema::hasColumn($table, 'deleted_at')) {
Schema::table($table, function (Blueprint $t) {
$t->dropSoftDeletes();
});
}
}
}
};
@@ -10,6 +10,7 @@ return new class extends Migration
{
Schema::create('issues', function (Blueprint $table) {
$table->id();
$table->uuid('uuid')->nullable()->unique();
$table->foreignId('project_id')
->constrained('projects')
@@ -34,6 +35,8 @@ return new class extends Migration
$table->enum('priority', ['low', 'medium', 'high', 'critical'])
->default('medium');
$table->string('type', 30)->default('other');
$table->foreignId('reported_by')
->constrained('users');
@@ -44,6 +47,7 @@ return new class extends Migration
$table->timestamp('resolved_at')->nullable();
$table->text('resolution_notes')->nullable();
$table->timestamp('client_updated_at')->nullable();
$table->timestamps();
$table->softDeletes();
@@ -1,25 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->text('notes')->nullable()->after('address');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('notes');
});
}
};
@@ -1,25 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('projects', function (Blueprint $table) {
$table->char('country', 2)->nullable()->after('address');
});
}
public function down(): void
{
Schema::table('projects', function (Blueprint $table) {
$table->dropColumn('country');
});
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('locale', 5)->default('es')->change();
});
// Reset all users still on the old default so they load in Spanish.
// Users that explicitly chose 'en' keep their preference.
DB::table('users')->where('locale', 'en')->update(['locale' => 'es']);
}
public function down(): void
{
DB::table('users')->where('locale', 'es')->update(['locale' => 'en']);
Schema::table('users', function (Blueprint $table) {
$table->string('locale', 5)->default('en')->change();
});
}
};
@@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$table = config('permission.table_names.roles', 'roles');
Schema::table($table, function (Blueprint $table) {
if (! Schema::hasColumn($table->getTable(), 'description')) {
$table->string('description')->nullable()->after('name');
}
});
}
public function down(): void
{
$table = config('permission.table_names.roles', 'roles');
Schema::table($table, function (Blueprint $table) {
if (Schema::hasColumn($table->getTable(), 'description')) {
$table->dropColumn('description');
}
});
}
};
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$table = config('permission.table_names.permissions', 'permissions');
Schema::table($table, function (Blueprint $table) {
if (! Schema::hasColumn($table->getTable(), 'group')) {
$table->string('group')->nullable()->after('name');
}
if (! Schema::hasColumn($table->getTable(), 'description')) {
$table->string('description')->nullable()->after('group');
}
});
}
public function down(): void
{
$table = config('permission.table_names.permissions', 'permissions');
Schema::table($table, function (Blueprint $table) {
foreach (['group', 'description'] as $col) {
if (Schema::hasColumn($table->getTable(), $col)) {
$table->dropColumn($col);
}
}
});
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('progress_updates', function (Blueprint $table) {
if (! Schema::hasColumn('progress_updates', 'uuid')) {
$table->uuid('uuid')->nullable()->unique()->after('id');
}
if (! Schema::hasColumn('progress_updates', 'client_updated_at')) {
$table->timestamp('client_updated_at')->nullable()->after('location');
}
});
}
public function down(): void
{
Schema::table('progress_updates', function (Blueprint $table) {
foreach (['uuid', 'client_updated_at'] as $col) {
if (Schema::hasColumn('progress_updates', $col)) {
$table->dropColumn($col);
}
}
});
}
};
@@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* `reference` (and `external_reference_1`) exist in the live DB but were never
* created by a migration (the "add_reference_and_country" migration only added
* `country`). This guarded migration reconciles the schema: on the live DB the
* columns already exist and are skipped; on a fresh install they get created.
*/
public function up(): void
{
Schema::table('projects', function (Blueprint $table) {
if (! Schema::hasColumn('projects', 'reference')) {
$table->string('reference')->nullable()->after('id');
}
if (! Schema::hasColumn('projects', 'external_reference_1')) {
$table->string('external_reference_1')->nullable()->after('reference');
}
});
}
public function down(): void
{
Schema::table('projects', function (Blueprint $table) {
foreach (['reference', 'external_reference_1'] as $col) {
if (Schema::hasColumn('projects', $col)) {
$table->dropColumn($col);
}
}
});
}
};
@@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
private array $tables = ['features', 'inspections', 'issues'];
public function up(): void
{
foreach ($this->tables as $name) {
Schema::table($name, function (Blueprint $table) use ($name) {
if (! Schema::hasColumn($name, 'uuid')) {
$table->uuid('uuid')->nullable()->unique()->after('id');
}
if (! Schema::hasColumn($name, 'client_updated_at')) {
$table->timestamp('client_updated_at')->nullable();
}
});
}
}
public function down(): void
{
foreach ($this->tables as $name) {
Schema::table($name, function (Blueprint $table) use ($name) {
foreach (['uuid', 'client_updated_at'] as $col) {
if (Schema::hasColumn($name, $col)) {
$table->dropColumn($col);
}
}
});
}
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('media', function (Blueprint $table) {
if (! Schema::hasColumn('media', 'uuid')) {
$table->uuid('uuid')->nullable()->unique()->after('id');
}
if (! Schema::hasColumn('media', 'client_updated_at')) {
$table->timestamp('client_updated_at')->nullable();
}
});
}
public function down(): void
{
Schema::table('media', function (Blueprint $table) {
foreach (['uuid', 'client_updated_at'] as $col) {
if (Schema::hasColumn('media', $col)) {
$table->dropColumn($col);
}
}
});
}
};
@@ -17,6 +17,7 @@ return new class extends Migration
$table->foreignId('done_by')->nullable()->constrained('users')->nullOnDelete();
$table->foreignId('assigned_to')->nullable()->constrained('users')->nullOnDelete();
$table->date('due_date')->nullable();
$table->timestamp('overdue_notified_at')->nullable();
$table->integer('order')->default(0);
// Offline sync
@@ -1,22 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('issue_tasks', function (Blueprint $table) {
$table->timestamp('overdue_notified_at')->nullable()->after('due_date');
});
}
public function down(): void
{
Schema::table('issue_tasks', function (Blueprint $table) {
$table->dropColumn('overdue_notified_at');
});
}
};
@@ -1,22 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('issues', function (Blueprint $table) {
$table->string('type', 30)->default('other')->after('priority');
});
}
public function down(): void
{
Schema::table('issues', function (Blueprint $table) {
$table->dropColumn('type');
});
}
};