fix: mejorar manejo de excepciones y validaciones en controladores de dispositivos, modulos y paquetes

This commit is contained in:
Juan Felipe Zapata Moreno 2026-02-17 14:35:34 -06:00
parent 43269ca04a
commit de9d801d50
5 changed files with 72 additions and 65 deletions

View File

@ -106,6 +106,10 @@ public function show($id)
return ApiResponse::OK->response([
'device' => $device,
]);
} catch (ModelNotFoundException $e) {
return ApiResponse::NOT_FOUND->response([
'message' => 'Dispositivo no encontrado.',
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al obtener el dispositivo.',
@ -125,16 +129,18 @@ public function update(DeviceUpdateRequest $request, $id)
// Validar unicidad solo si los valores cambiaron
if ($request->filled('serie') && $request->serie !== $device->serie) {
if (Device::where('serie', $request->serie)->exists()) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'El número de serie ya está en uso.',
DB::rollBack();
return ApiResponse::UNPROCESSABLE_CONTENT->response([
'serie' => ['El número de serie ya está en uso.'],
]);
}
}
if ($request->filled('mac_address') && $request->mac_address !== $device->mac_address) {
if (Device::where('mac_address', $request->mac_address)->exists()) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'La dirección MAC ya está registrada.',
DB::rollBack();
return ApiResponse::UNPROCESSABLE_CONTENT->response([
'mac_address' => ['La dirección MAC ya está registrada.'],
]);
}
}
@ -187,7 +193,12 @@ public function update(DeviceUpdateRequest $request, $id)
->values(),
],
]);
} catch (ModelNotFoundException $e) {
return ApiResponse::NOT_FOUND->response([
'message' => 'Dispositivo no encontrado.',
]);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al actualizar el dispositivo.',
'error' => $e->getMessage(),
@ -208,6 +219,11 @@ public function destroy($id)
return ApiResponse::OK->response([
'message' => 'Dispositivo eliminado exitosamente.',
]);
} catch (ModelNotFoundException $e) {
DB::rollBack();
return ApiResponse::NOT_FOUND->response([
'message' => 'Dispositivo no encontrado.',
]);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([

View File

@ -106,15 +106,19 @@ public function store(ModuleStoreRequest $request)
public function show($id)
{
try {
$modules = Module::with([
'responsible:id,name,username',
'municipality:id,code,name',
'users:id,name,paternal,maternal,username,module_id',
'users.roles:id,name,description'
])->find($id);
$module = Module::with([
'responsible:id,name,username',
'municipality:id,code,name',
'users:id,name,paternal,maternal,username,module_id',
'users.roles:id,name,description'
])->findOrFail($id);
return ApiResponse::OK->response([
'module' => $modules,
'module' => $module,
]);
} catch (ModelNotFoundException $e) {
return ApiResponse::NOT_FOUND->response([
'message' => 'Módulo no encontrado.',
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([

View File

@ -10,6 +10,7 @@
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use App\Models\Package;
use App\Models\Tag;
@ -129,74 +130,50 @@ public function store(PackageStoreRequest $request)
try {
DB::beginTransaction();
$package = Package::create([
'lot' => $request->lot,
'box_number' => $request->box_number,
'starting_page' => $request->starting_page,
'ending_page' => $request->ending_page,
'user_id' => Auth::id(),
]);
// Verificar folios en el mismo lote antes de crear
$packageIds = Package::where('lot', $request->lot)->pluck('id');
// Obtener el status "available" para los tags
$statusAvailable = CatalogTagStatus::where('code', 'available')->first();
$conflicting = Tag::whereIn('package_id', $packageIds)
->whereRaw('CAST(folio AS UNSIGNED) BETWEEN ? AND ?', [$request->starting_page, $request->ending_page])
->pluck('folio');
if (!$statusAvailable) {
throw new \Exception('No se encontró el status "Disponible" para los tags');
}
$existingTags = Tag::whereHas('package', function ($query) use ($request) {
$query->where('box_number', $request->box_number);
})
->whereBetween('folio', [$request->starting_page, $request->ending_page])
->get(['folio', 'package_id']);
if ($existingTags->isNotEmpty()) {
if ($conflicting->isNotEmpty()) {
DB::rollBack();
return ApiResponse::BAD_REQUEST->response([
'message' => 'Ya existen tags en esta caja con folios en el rango especificado.',
'box_number' => $request->box_number,
'starting_page' => $request->starting_page,
'ending_page' => $request->ending_page,
'folios_conflictivos' => $existingTags->pluck('folio')->toArray(),
'total_folios_conflictivos' => $existingTags->count(),
return ApiResponse::UNPROCESSABLE_CONTENT->response([
'starting_page' => [
'Los folios ' . $conflicting->join(', ') . ' ya están registrados en el lote "' . $request->lot . '".',
],
]);
}
// Crear los tags según el rango de páginas
$form = $request->validated();
$form['user_id'] = Auth::id();
$package = Package::create($form);
$statusAvailable = CatalogTagStatus::where('code', 'available')->firstOrFail();
for ($page = $request->starting_page; $page <= $request->ending_page; $page++) {
Tag::create([
'folio' => $page,
'folio' => $page,
'tag_number' => null,
'package_id' => $package->id,
'status_id' => $statusAvailable->id,
'status_id' => $statusAvailable->id,
]);
}
DB::commit();
return ApiResponse::CREATED->response([
'message' => 'Paquete registrado exitosamente con sus tags',
'package' => $package->load('tags'),
'message' => 'Paquete registrado exitosamente con sus tags',
'package' => $package->load('tags'),
'tags_created' => $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();
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al crear el paquete',
'error' => $e->getMessage(),
'error' => $e->getMessage(),
]);
}
}
@ -209,6 +186,10 @@ public function show($id)
return ApiResponse::OK->response([
'package' => $package,
]);
} catch (ModelNotFoundException $e) {
return ApiResponse::NOT_FOUND->response([
'message' => 'Paquete no encontrado.',
]);
} catch (\Exception $e) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al obtener el paquete',
@ -322,13 +303,13 @@ public function update(PackageUpdateRequest $request, $id)
]);
}
return ApiResponse::INTERNAL_ERROR->response([
return ApiResponse::NOT_FOUND->response([
'message' => 'Error de base de datos al actualizar el paquete',
'error' => $e->getMessage(),
]);
} catch (\Exception $e) {
DB::rollBack();
return ApiResponse::INTERNAL_ERROR->response([
return ApiResponse::NOT_FOUND->response([
'message' => 'Error al actualizar el paquete',
'error' => $e->getMessage(),
]);
@ -407,7 +388,7 @@ public function getBoxTags(Request $request)
// Obtener tags con paginación
$tags = Tag::with(['status:id,code,name', 'vehicle:id,placa,niv', 'module:id,name', 'cancellationLogs.cancellationReason'])
->where('package_id', $package->id)
->orderBy('folio', 'ASC');
->orderBy('id', 'ASC');
// Filtro adicional por status si se proporciona
if ($request->filled('status')) {

View File

@ -1,6 +1,7 @@
<?php namespace App\Http\Requests\Repuve;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PackageStoreRequest extends FormRequest
{
@ -12,7 +13,7 @@ public function authorize(): bool
public function rules(): array
{
return [
'lot' => ['required', 'string'],
'lot' => ['required', 'string', Rule::unique('packages', 'lot')->where(fn($q) => $q->where('box_number', $this->input('box_number')))],
'box_number' => ['required', 'integer'],
'starting_page' => ['required', 'integer', 'min:1'],
'ending_page' => ['required', 'integer', 'min:1', 'gte:starting_page'],
@ -23,6 +24,7 @@ public function messages(): array
{
return [
'lot.required' => 'El lote es requerido',
'lot.unique' => 'Ya existe un paquete con ese lote y número de caja.',
'box_number.required' => 'El número de caja es requerido',

View File

@ -151,14 +151,18 @@
'attributes' => [
'barcode' => 'código de barras',
'section.number' => 'sección',
'name' => 'nombre',
'paternal' => 'apellido paterno',
'name' => 'nombre',
'paternal' => 'apellido paterno',
'maternal' => 'apellido materno',
'email' => 'correo',
'phone' => 'teléfono',
'box_number' => 'número de caja',
'lot' => 'lote',
'starting_page' => 'página inicial',
'ending_page' => 'página final',
'type_ck' => 'tipo',
'*.name' => 'nombre',
'*.paternal' => 'apellido paterno',
'*.name' => 'nombre',
'*.paternal' => 'apellido paterno',
'*.maternal' => 'apellido materno',
'*.email' => 'correo',
'*.phone' => 'teléfono',