From baf39610367bf6c3a246ad2b404191da4c8722ed Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Fri, 5 Dec 2025 23:04:20 -0600 Subject: [PATCH] REFACTOR: Eliminar el modelo y controlador de cajas, actualizar relaciones en paquetes y etiquetas --- app/Http/Controllers/BoxController.php | 160 ------------------ .../Repuve/InscriptionController.php | 22 +-- .../Controllers/Repuve/PackageController.php | 57 ++++--- .../Controllers/Repuve/TagsController.php | 21 ++- app/Http/Requests/Repuve/BoxStoreRequest.php | 37 ---- app/Http/Requests/Repuve/BoxUpdateRequest.php | 32 ---- .../Requests/Repuve/PackageStoreRequest.php | 22 ++- .../Requests/Repuve/PackageUpdateRequest.php | 23 ++- app/Models/Box.php | 35 ---- app/Models/Package.php | 22 +-- app/Models/Tag.php | 6 +- ...5_222705_remove_boxes_restore_packages.php | 70 ++++++++ ...24733_add_box_fields_to_packages_table.php | 32 ++++ routes/api.php | 4 - 14 files changed, 196 insertions(+), 347 deletions(-) delete mode 100644 app/Http/Controllers/BoxController.php delete mode 100644 app/Http/Requests/Repuve/BoxStoreRequest.php delete mode 100644 app/Http/Requests/Repuve/BoxUpdateRequest.php delete mode 100644 app/Models/Box.php create mode 100644 database/migrations/2025_12_05_222705_remove_boxes_restore_packages.php create mode 100644 database/migrations/2025_12_05_224733_add_box_fields_to_packages_table.php diff --git a/app/Http/Controllers/BoxController.php b/app/Http/Controllers/BoxController.php deleted file mode 100644 index aaed47a..0000000 --- a/app/Http/Controllers/BoxController.php +++ /dev/null @@ -1,160 +0,0 @@ -withCount('tags')->orderBy('id', 'ASC'); - - if ($request->filled('package')) { - $boxes->where('package_id', $request->input('package')); - } - - if ($request->filled('box_number')) { - $boxes->where('box_number', 'LIKE', '%' . $request->box_number . '%'); - } - - if ($request->filled('lote')) { - $boxes->whereHas('package', function ($query) use ($request) { - $query->where('lot', 'LIKE', '%' . $request->lot . '%'); - }); - } - - return ApiResponse::OK->response([ - 'boxes' => $boxes->paginate(config('app.pagination')) - ]); - } catch (\Exception $e) { - return ApiResponse::INTERNAL_ERROR->response([ - 'message' => 'Error al obtener las cajas', - 'error' => $e->getMessage(), - ]); - } - } - - public function store(BoxStoreRequest $request) - { - try { - DB::beginTransaction(); - - $box = Box::create([ - 'box_number' => $request->input('box_number'), - 'package_id' => $request->input('package_id'), - 'starting_page' => $request->input('starting_page'), - 'ending_page' => $request->input('ending_page'), - ]); - - $package = Package::find($request->input('package_id')); - if ($package) { - $package->increment('total_boxes'); - } - - DB::commit(); - - return ApiResponse::CREATED->response([ - 'message' => 'Caja creada exitosamente', - 'box' => $box, - ]); - } catch (\Exception $e) { - return ApiResponse::INTERNAL_ERROR->response([ - 'message' => 'Error al crear la caja', - 'error' => $e->getMessage(), - ]); - } - } - - public function show($id) - { - try { - $box = Box::with(['package:id,lot,description,total_boxes', 'tags' => function ($query) { - $query->with(['status:id,name', 'module:id,name']) - ->orderBy('folio', 'ASC'); - }])->withCount('tags')->findOrFail($id); - - return ApiResponse::OK->response([ - 'box' => $box - ]); - } catch (\Exception $e) { - return ApiResponse::INTERNAL_ERROR->response([ - 'message' => 'Error al obtener la caja', - 'error' => $e->getMessage(), - ]); - } - } - - public function update(BoxUpdateRequest $request, $id) - { - try { - $box = Box::findOrFail($id); - - DB::beginTransaction(); - - $oldPackageId = $box->package_id; - $newPackageId = $request->input('package_id'); - - $box->update($request->validated()); - - if ($oldPackageId != $newPackageId) { - Package::find($oldPackageId)?->decrement('total_boxes'); - Package::find($newPackageId)?->increment('total_boxes'); - } - - DB::commit(); - - return ApiResponse::OK->response([ - 'message' => 'Caja actualizada exitosamente', - 'box' => $box, - ]); - } catch (\Exception $e) { - return ApiResponse::INTERNAL_ERROR->response([ - 'message' => 'Error al actualizar la caja', - 'error' => $e->getMessage(), - ]); - } - } - - public function destroy($id) - { - try { - DB::beginTransaction(); - - $box = Box::findOrFail($id); - - // Verificar si tiene tags antes de eliminar - if ($box->tags()->count() > 0) { - return ApiResponse::BAD_REQUEST->response([ - 'message' => 'No se puede eliminar la caja porque tiene tags asociados.', - 'tags_count' => $box->tags()->count(), - ]); - } - - $packageId = $box->package_id; - $box->delete(); - - // Decrementar el contador de cajas del paquete - Package::find($packageId)?->decrement('total_boxes'); - - DB::commit(); - - return ApiResponse::OK->response([ - 'message' => 'Caja eliminada exitosamente.', - ]); - } catch (\Exception $e) { - DB::rollBack(); - return ApiResponse::INTERNAL_ERROR->response([ - 'message' => 'Error al eliminar la caja.', - 'error' => $e->getMessage(), - ]); - } - } -} diff --git a/app/Http/Controllers/Repuve/InscriptionController.php b/app/Http/Controllers/Repuve/InscriptionController.php index 5d24243..c7c62c6 100644 --- a/app/Http/Controllers/Repuve/InscriptionController.php +++ b/app/Http/Controllers/Repuve/InscriptionController.php @@ -303,11 +303,10 @@ public function searchRecord(Request $request) 'vehicle', 'vehicle.owner', - // Tag con Box y Package - 'vehicle.tag:id,vehicle_id,folio,tag_number,status_id,box_id', + // Tag con Package + 'vehicle.tag:id,vehicle_id,folio,tag_number,status_id,package_id', 'vehicle.tag.status:id,code,name', - 'vehicle.tag.box:id,box_number,package_id', - 'vehicle.tag.box.package:id,lot', + 'vehicle.tag.package:id,lot,box_number', // Archivos 'files:id,record_id,name_id,path,md5', @@ -400,21 +399,18 @@ public function searchRecord(Request $request) // Propietario 'owner' => $record->vehicle->owner, - // Tag y Caja + // Tag y Paquete 'tag' => $record->vehicle->tag ? [ 'id' => $record->vehicle->tag->id, 'folio' => $record->vehicle->tag->folio, 'tag_number' => $record->vehicle->tag->tag_number, 'status' => $record->vehicle->tag->status?->name, - // Caja - 'box' => $record->vehicle->tag->box ? [ - 'id' => $record->vehicle->tag->box->id, - 'box_number' => $record->vehicle->tag->box->box_number, - 'package' => $record->vehicle->tag->box->package ? [ - 'id' => $record->vehicle->tag->box->package->id, - 'lot' => $record->vehicle->tag->box->package->lot, - ] : null, + // Paquete + 'package' => $record->vehicle->tag->package ? [ + 'id' => $record->vehicle->tag->package->id, + 'lot' => $record->vehicle->tag->package->lot, + 'box_number' => $record->vehicle->tag->package->box_number, ] : null, ] : null, diff --git a/app/Http/Controllers/Repuve/PackageController.php b/app/Http/Controllers/Repuve/PackageController.php index a49d121..086ba14 100644 --- a/app/Http/Controllers/Repuve/PackageController.php +++ b/app/Http/Controllers/Repuve/PackageController.php @@ -8,6 +8,7 @@ use App\Http\Requests\Repuve\PackageUpdateRequest; use Illuminate\Support\Facades\DB; use Illuminate\Http\Request; +use Illuminate\Database\QueryException; use App\Models\Package; class PackageController extends Controller @@ -16,17 +17,16 @@ class PackageController extends Controller public function index(Request $request) { try { - $packages = Package::with(['boxes'])->withCount('boxes')->orderBy('id', 'ASC'); + $packages = Package::with(['tags'])->withCount('tags')->orderBy('id', 'ASC'); if ($request->filled('lote')) { $packages->where('lot', 'LIKE', '%' . $request->lote . '%'); } if ($request->filled('caja')) { - $packages->whereHas('boxes', function ($q) use ($request) { - $q->where('box_number', 'LIKE', '%' . $request->caja . '%'); - }); + $packages->where('box_number', 'LIKE', '%' . $request->caja . '%'); } + return ApiResponse::OK->response([ 'packages' => $packages->paginate(config('app.pagination')) ]); @@ -44,23 +44,32 @@ public function store(PackageStoreRequest $request) DB::beginTransaction(); $package = Package::create([ - 'lot' => $request->input('lot'), - 'total_boxes' => $request->input('total_boxes'), - 'description' => $request->input('description'), - + 'lot' => $request->lot, + 'box_number' => $request->box_number, + 'starting_page' => $request->starting_page, + 'ending_page' => $request->ending_page, ]); DB::commit(); return ApiResponse::CREATED->response([ - 'message' => 'Paquete creado exitosamente', - 'package' => [ - 'id' => $package->id, - 'lot' => $package->lot, - 'total_boxes' => $package->total_boxes, - 'description' => $package->description, - 'created_at' => $package->created_at->format('Y-m-d H:i:s'), - ], + 'message' => 'Paquete registrado exitosamente', + 'package' => $package, + 'expected_tags' => $package->ending_page - $package->starting_page + 1, + 'actual_tags' => $package->tags()->count(), + ]); + } catch (QueryException $e) { + DB::rollBack(); + + if ($e->getCode() == 23000 && str_contains($e->getMessage(), 'packages_lot_box_unique')) { + return ApiResponse::BAD_REQUEST->response([ + 'message' => "Ya existe un paquete con el lote '{$request->lot}' y caja número '{$request->box_number}'.", + ]); + } + + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => 'Error al crear el paquete', + 'error' => $e->getMessage(), ]); } catch (\Exception $e) { DB::rollBack(); @@ -74,9 +83,7 @@ public function store(PackageStoreRequest $request) public function show($id) { try { - $package = Package::with([ - 'boxes.tags' - ])->findOrFail($id); + $package = Package::with(['tags'])->findOrFail($id); return ApiResponse::OK->response([ 'package' => $package, @@ -105,8 +112,9 @@ public function update(PackageUpdateRequest $request, $id) 'package' => [ 'id' => $package->id, 'lot' => $package->lot, - 'total_boxes' => $package->total_boxes, - 'description' => $package->description, + 'box_number' => $package->box_number, + 'starting_page' => $package->starting_page, + 'ending_page' => $package->ending_page, 'updated_at' => $package->updated_at->format('Y-m-d H:i:s'), ], ]); @@ -125,10 +133,11 @@ public function destroy($id) DB::beginTransaction(); $package = Package::findOrFail($id); - if ($package->boxes()->count() > 0) { + + if ($package->tags()->count() > 0) { return ApiResponse::BAD_REQUEST->response([ - 'message' => 'No se puede eliminar el paquete porque tiene cajas asociadas.', - 'boxes_count' => $package->boxes()->count(), + 'message' => 'No se puede eliminar el paquete porque tiene tags asociados.', + 'tags_count' => $package->tags()->count(), ]); } diff --git a/app/Http/Controllers/Repuve/TagsController.php b/app/Http/Controllers/Repuve/TagsController.php index a7a376d..b1edcf0 100644 --- a/app/Http/Controllers/Repuve/TagsController.php +++ b/app/Http/Controllers/Repuve/TagsController.php @@ -20,8 +20,7 @@ public function index(Request $request) try { $tags = Tag::with([ 'vehicle:id,placa,niv', - 'box:id,box_number,package_id', - 'box.package:id,lot', + 'package:id,lot,box_number', 'status:id,code,name', 'module:id,name' ])->orderBy('id', 'ASC'); @@ -33,13 +32,13 @@ public function index(Request $request) } if ($request->has('lot')) { - $tags->whereHas('box.package', function ($q) use ($request) { + $tags->whereHas('package', function ($q) use ($request) { $q->where('lot', $request->lot); }); } - if ($request->has('box_id')) { - $tags->where('box_id', $request->box_id); + if ($request->has('package_id')) { + $tags->where('package_id', $request->package_id); } if ($request->has('module_id')) { @@ -61,7 +60,7 @@ public function store(Request $request) { $validated = $request->validate([ 'folio' => 'required|string|max:255', - 'box_id' => 'required|integer|exists:boxes,id', + 'package_id' => 'required|integer|exists:packages,id', 'tag_number' => 'nullable|string|max:255', ]); @@ -74,7 +73,7 @@ public function store(Request $request) public function show(Tag $tag) { - $tag->load(['box.package', 'module', 'vehicle', 'status']); + $tag->load(['package', 'module', 'vehicle', 'status']); return ApiResponse::OK->response([ 'tag' => $tag, @@ -102,7 +101,7 @@ public function tagStore(Request $request) { try { $request->validate([ - 'box_id' => 'required|integer|exists:boxes,id', + 'package_id' => 'required|integer|exists:packages,id', 'tags' => 'required|array', 'tags.*.folio' => 'required|string|max:8', 'tags.*.tag_number' => 'nullable|string|max:32', @@ -126,7 +125,7 @@ public function tagStore(Request $request) $tag = Tag::create([ 'folio' => $tagData['folio'], 'tag_number' => $tagData['tag_number'] ?? null, - 'box_id' => $request->box_id, + 'package_id' => $request->package_id, 'status_id' => $statusAvailable->id, 'vehicle_id' => null, 'module_id' => null, @@ -163,7 +162,7 @@ public function tagStore(Request $request) 'message' => 'Tags importados correctamente.', 'tags' => $createdTags, 'total' => count($createdTags), - 'box' => $request->box_id, + 'package' => $request->package_id, ]); } catch (Exception $e) { DB::rollback(); @@ -180,7 +179,7 @@ public function assignToModule(Request $request) $request->validate([ 'module_id' => 'required|exists:modules,id', 'cantidad' => 'required|integer|min:1', - 'box_id' => 'nullable|integer|exists:boxes,id', + 'package_id' => 'nullable|integer|exists:packages,id', ]); DB::beginTransaction(); diff --git a/app/Http/Requests/Repuve/BoxStoreRequest.php b/app/Http/Requests/Repuve/BoxStoreRequest.php deleted file mode 100644 index d2ca19f..0000000 --- a/app/Http/Requests/Repuve/BoxStoreRequest.php +++ /dev/null @@ -1,37 +0,0 @@ - ['required', 'string', 'max:100', 'unique:boxes,box_number'], - 'package_id' => ['required', 'integer', 'exists:packages,id'], - 'starting_page' => ['required', 'integer', 'min:1'], - 'ending_page' => ['required', 'integer', 'gt:starting_page'], - ]; - } - - public function messages(): array - { - return [ - 'box_number.required' => 'El número de caja es requerido', - 'box_number.unique' => 'El número de caja ya existe', - 'package_id.required' => 'El paquete es requerido', - 'package_id.exists' => 'El paquete no existe', - 'starting_page.required' => 'La página inicial es requerida', - 'starting_page.min' => 'La página inicial debe ser al menos 1', - 'ending_page.required' => 'La página final es requerida', - 'ending_page.gt' => 'La página final debe ser mayor a la página inicial', - ]; - } -} diff --git a/app/Http/Requests/Repuve/BoxUpdateRequest.php b/app/Http/Requests/Repuve/BoxUpdateRequest.php deleted file mode 100644 index 0692e19..0000000 --- a/app/Http/Requests/Repuve/BoxUpdateRequest.php +++ /dev/null @@ -1,32 +0,0 @@ - ['nullable', 'string', 'max:100'], - 'package_id' => ['nullable', 'integer', 'exists:packages,id'], - 'starting_page' => ['nullable', 'integer', 'min:1'], - 'ending_page' => ['nullable', 'integer', 'gt:starting_page'], - ]; - } - - public function messages(): array - { - return [ - 'package_id.exists' => 'El paquete no existe', - 'starting_page.min' => 'La página inicial debe ser al menos 1', - 'ending_page.gt' => 'La página final debe ser mayor a la página inicial', - ]; - } -} diff --git a/app/Http/Requests/Repuve/PackageStoreRequest.php b/app/Http/Requests/Repuve/PackageStoreRequest.php index f1198b3..2360972 100644 --- a/app/Http/Requests/Repuve/PackageStoreRequest.php +++ b/app/Http/Requests/Repuve/PackageStoreRequest.php @@ -12,9 +12,10 @@ public function authorize(): bool public function rules(): array { return [ - 'lot' => ['required', 'string', 'max:50', 'unique:packages,lot'], - 'total_boxes' => ['nullable', 'integer', 'min:0'], - 'description' => ['nullable', 'string', 'max:255'], + 'lot' => ['required', 'string'], + 'box_number' => ['required', 'integer'], + 'starting_page' => ['required', 'integer', 'min:1'], + 'ending_page' => ['required', 'integer', 'min:1', 'gte:starting_page'], ]; } @@ -22,10 +23,17 @@ public function messages(): array { return [ 'lot.required' => 'El lote es requerido', - 'lot.unique' => 'El lote ya existe', - 'total_boxes.integer' => 'El total de cajas debe ser un número', - 'total_boxes.min' => 'El total de cajas no puede ser negativo', - 'description.max' => 'La descripción no puede exceder 255 caracteres', + + 'box_number.required' => 'El número de caja es requerido', + + 'starting_page.required' => 'La página inicial es requerida', + 'starting_page.integer' => 'La página inicial debe ser un número', + 'starting_page.min' => 'La página inicial debe ser al menos 1', + + 'ending_page.required' => 'La página final es requerida', + 'ending_page.integer' => 'La página final debe ser un número', + 'ending_page.min' => 'La página final debe ser al menos 1', + 'ending_page.gte' => 'La página final debe ser mayor o igual a la página inicial', ]; } } diff --git a/app/Http/Requests/Repuve/PackageUpdateRequest.php b/app/Http/Requests/Repuve/PackageUpdateRequest.php index 614b8a2..bf539b2 100644 --- a/app/Http/Requests/Repuve/PackageUpdateRequest.php +++ b/app/Http/Requests/Repuve/PackageUpdateRequest.php @@ -11,12 +11,13 @@ public function authorize(): bool return true; } - public function rules(): array + public function rules(): array { return [ - 'lot' => ['required', 'string', 'max:50'], - 'total_boxes' => ['nullable', 'integer', 'min:0'], - 'description' => ['nullable', 'string', 'max:255'], + 'lot' => ['required', 'string'], + 'box_number' => ['required', 'string'], + 'starting_page' => ['required', 'integer', 'min:1'], + 'ending_page' => ['required', 'integer', 'min:1', 'gte:starting_page'], ]; } @@ -24,9 +25,17 @@ public function messages(): array { return [ 'lot.required' => 'El lote es requerido', - 'total_boxes.integer' => 'El total de cajas debe ser un número', - 'total_boxes.min' => 'El total de cajas no puede ser negativo', - 'description.max' => 'La descripción no puede exceder 255 caracteres', + + 'box_number.required' => 'El número de caja es requerido', + + 'starting_page.required' => 'La página inicial es requerida', + 'starting_page.integer' => 'La página inicial debe ser un número', + 'starting_page.min' => 'La página inicial debe ser al menos 1', + + 'ending_page.required' => 'La página final es requerida', + 'ending_page.integer' => 'La página final debe ser un número', + 'ending_page.min' => 'La página final debe ser al menos 1', + 'ending_page.gte' => 'La página final debe ser mayor o igual a la página inicial', ]; } } diff --git a/app/Models/Box.php b/app/Models/Box.php deleted file mode 100644 index 7cbb417..0000000 --- a/app/Models/Box.php +++ /dev/null @@ -1,35 +0,0 @@ - 'string', - 'package_id' => 'integer', - 'starting_page' => 'integer', - 'ending_page' => 'integer', - ]; - } - - public function package() - { - return $this->belongsTo(Package::class); - } - - public function tags() - { - return $this->hasMany(Tag::class); - } - -} diff --git a/app/Models/Package.php b/app/Models/Package.php index f592bb1..2253105 100644 --- a/app/Models/Package.php +++ b/app/Models/Package.php @@ -11,24 +11,18 @@ class Package extends Model protected $fillable = [ 'lot', - 'total_boxes', - 'description', + 'box_number', + 'starting_page', + 'ending_page', ]; - protected $attributes = [ - 'total_boxes' => 0, + protected $casts = [ + 'starting_page' => 'integer', + 'ending_page' => 'integer', ]; - protected function casts(): array + public function tags() { - return [ - 'total_boxes' => 'integer', - 'description' => 'string', - ]; - } - - public function boxes() - { - return $this->hasMany(Box::class); + return $this->hasMany(Tag::class); } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 63d1f2a..bc19e25 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -19,7 +19,7 @@ class Tag extends Model 'folio', 'tag_number', 'vehicle_id', - 'box_id', + 'package_id', 'module_id', 'status_id', ]; @@ -29,9 +29,9 @@ public function vehicle() return $this->belongsTo(Vehicle::class); } - public function box() + public function package() { - return $this->belongsTo(Box::class); + return $this->belongsTo(Package::class); } public function status() diff --git a/database/migrations/2025_12_05_222705_remove_boxes_restore_packages.php b/database/migrations/2025_12_05_222705_remove_boxes_restore_packages.php new file mode 100644 index 0000000..6877220 --- /dev/null +++ b/database/migrations/2025_12_05_222705_remove_boxes_restore_packages.php @@ -0,0 +1,70 @@ +orderBy('id')->chunk(100, function($boxes) { + foreach ($boxes as $box) { + $parentPackage = DB::table('packages')->find($box->package_id); + + $newPackageId = DB::table('packages')->insertGetId([ + 'lot' => $parentPackage->lot, + 'box_number' => $box->box_number, + 'starting_page' => $box->starting_page, + 'ending_page' => $box->ending_page, + 'created_at' => $box->created_at, + 'updated_at' => $box->updated_at, + ]); + + // Actualizar tags: box_id → package_id + DB::table('tags') + ->where('box_id', $box->id) + ->update(['package_id' => $newPackageId]); + } + }); + + + Schema::table('tags', function (Blueprint $table) { + $table->dropForeign(['box_id']); + }); + + Schema::table('tags', function (Blueprint $table) { + $table->renameColumn('box_id', 'package_id'); + }); + + Schema::table('tags', function (Blueprint $table) { + $table->foreign('package_id')->references('id')->on('packages')->onDelete('cascade'); + }); + + // eliminar tabla boxes + Schema::dropIfExists('boxes'); + + // 6. Eliminar columnas de packages que ya no se usan + Schema::table('packages', function (Blueprint $table) { + if (Schema::hasColumn('packages', 'total_boxes')) { + $table->dropColumn('total_boxes'); + } + if (Schema::hasColumn('packages', 'description')) { + $table->dropColumn('description'); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/migrations/2025_12_05_224733_add_box_fields_to_packages_table.php b/database/migrations/2025_12_05_224733_add_box_fields_to_packages_table.php new file mode 100644 index 0000000..4523997 --- /dev/null +++ b/database/migrations/2025_12_05_224733_add_box_fields_to_packages_table.php @@ -0,0 +1,32 @@ +integer('box_number')->after('lot'); + $table->integer('starting_page')->after('box_number'); + $table->integer('ending_page')->after('starting_page'); + $table->unique(['lot', 'box_number'], 'packages_lot_box_unique'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('packages', function (Blueprint $table) { + $table->dropUnique('packages_lot_box_unique'); + $table->dropColumn(['box_number', 'starting_page', 'ending_page']); + }); + } +}; diff --git a/routes/api.php b/routes/api.php index 6b1f75f..dd5c877 100644 --- a/routes/api.php +++ b/routes/api.php @@ -81,10 +81,6 @@ //Rutas de nombres de archivos en catálogo Route::resource('catalog-name-imgs', CatalogNameImgController::class); - - //Ruta cajas - Route::resource('boxes', BoxController::class); - }); /** Rutas públicas */