316 lines
13 KiB
PHP
316 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Repuve;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Repuve\CancelConstanciaRequest;
|
|
use App\Models\CatalogCancellationReason;
|
|
use App\Models\Record;
|
|
use App\Models\Tag;
|
|
use App\Models\TagCancellationLog;
|
|
use App\Models\VehicleTagLog;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
|
use Barryvdh\DomPDF\Facade\Pdf;
|
|
|
|
class CancellationController extends Controller
|
|
{
|
|
|
|
/**
|
|
* Cancelar la constancia/tag
|
|
*/
|
|
public function cancelarConstancia(CancelConstanciaRequest $request)
|
|
{
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
// Buscar el expediente con sus relaciones
|
|
$record = Record::with('vehicle.tag.status')->findOrFail($request->record_id);
|
|
$vehicle = $record->vehicle;
|
|
$tag = $vehicle->tag;
|
|
|
|
// Validar que el vehículo tiene un tag asignado
|
|
if (!$tag) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El vehículo no tiene un tag/constancia asignada.'
|
|
]);
|
|
}
|
|
|
|
// Validar que el tag está en estado activo
|
|
if (!$tag->isAssigned()) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El tag ya no está activo. Status actual: ' . $tag->status->name
|
|
]);
|
|
}
|
|
|
|
// Guardar información del tag anterior ANTES de cancelarlo
|
|
$oldTagNumber = $tag->tag_number;
|
|
$oldFolio = $tag->folio;
|
|
|
|
// Crear registro en el log de vehiculos
|
|
$cancellationLog = VehicleTagLog::create([
|
|
'vehicle_id' => $vehicle->id,
|
|
'tag_id' => $tag->id,
|
|
'action_type' => 'cancelacion',
|
|
'cancellation_reason_id' => $request->cancellation_reason_id,
|
|
'cancellation_observations' => $request->cancellation_observations,
|
|
'cancellation_at' => now(),
|
|
'cancelled_by' => Auth::id(),
|
|
'performed_by' => Auth::id(),
|
|
]);
|
|
|
|
// Actualizar estado del tag a 'cancelled' y desasignar vehículo
|
|
$tag->markAsCancelled();
|
|
|
|
$newTag = null;
|
|
$substitutionLog = null;
|
|
$isSubstitution = $request->filled('new_folio') && $request->filled('new_tag_number');
|
|
|
|
if ($isSubstitution) {
|
|
$newTag = Tag::where('folio', $request->new_folio)->first();
|
|
|
|
if (!$newTag) {
|
|
DB::rollBack();
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'El nuevo tag proporcionado no existe.',
|
|
'new_tag_number' => $request->new_tag_number,
|
|
]);
|
|
}
|
|
|
|
if (!$newTag->isAvailable()) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El nuevo tag no está disponible para asignación',
|
|
'new_tag_number' => $request->new_tag_number,
|
|
'current_status' => $newTag->status->name,
|
|
]);
|
|
}
|
|
|
|
if (!$newTag->tag_number) {
|
|
$existingTag = Tag::where('tag_number', $request->new_tag_number)->first();
|
|
if ($existingTag && $existingTag->id !== $newTag->id) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El nuevo tag_number ya está asignado a otro folio.',
|
|
'new_tag_number' => $request->new_tag_number,
|
|
'folio_existente' => $existingTag->folio,
|
|
]);
|
|
}
|
|
|
|
$newTag->tag_number = $request->new_tag_number;
|
|
$newTag->save();
|
|
} elseif ($newTag->tag_number !== $request->new_tag_number) {
|
|
DB::rollBack();
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El nuevo tag ya tiene un tag_number diferente asignado.',
|
|
'assigned_tag_number' => $newTag->tag_number,
|
|
'provided_tag_number' => $request->new_tag_number,
|
|
]);
|
|
}
|
|
|
|
// Usar el folio del request
|
|
$newTag->markAsAssigned($vehicle->id, $request->folio);
|
|
|
|
$substitutionLog = VehicleTagLog::create([
|
|
'vehicle_id' => $vehicle->id,
|
|
'tag_id' => $newTag->id,
|
|
'action_type' => 'sustitucion',
|
|
'cancellation_reason_id' => $request->cancellation_reason_id,
|
|
'cancellation_observations' => 'Tag sustituido. Tag anterior: ' . $oldTagNumber . ' (Folio: ' . $oldFolio . '). Motivo: ' . ($request->cancellation_observations ?? ''),
|
|
'performed_by' => Auth::id(),
|
|
]);
|
|
|
|
$record->update(['folio' => $request->folio]);
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
$message = $isSubstitution
|
|
? 'Tag cancelado y sustituido exitosamente'
|
|
: 'Constancia cancelada exitosamente';
|
|
|
|
return ApiResponse::OK->response([
|
|
'message' => $message,
|
|
'is_substitution' => $isSubstitution,
|
|
'cancellation' => [
|
|
'id' => $cancellationLog->id,
|
|
'vehicle' => [
|
|
'id' => $vehicle->id,
|
|
'placa' => $vehicle->placa,
|
|
'niv' => $vehicle->niv,
|
|
],
|
|
'old_tag' => [
|
|
'id' => $tag->id,
|
|
'folio' => $tag->folio,
|
|
'tag_number' => $tag->tag_number,
|
|
'new_status' => 'Cancelado',
|
|
],
|
|
'new_tag' => $newTag ? [
|
|
'id' => $newTag->id,
|
|
'folio' => $newTag->folio,
|
|
'tag_number' => $newTag->tag_number,
|
|
'status' => $newTag->status->name,
|
|
] : null,
|
|
'cancellation_reason_id' => $cancellationLog->cancellationReason->name,
|
|
'cancellation_observations' => $request->cancellation_observations,
|
|
'cancelled_at' => $cancellationLog->cancellation_at->toDateTimeString(),
|
|
'cancelled_by' => Auth::user()->name,
|
|
]
|
|
]);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
|
|
Log::error('Error en cancelarConstancia: ' . $e->getMessage(), [
|
|
'record_id' => $request->record_id ?? null,
|
|
'cancellation_reason' => $request->cancellation_reason ?? null,
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Error al cancelar la constancia',
|
|
'error' => $e->getMessage()
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function cancelarTagNoAsignado(Request $request)
|
|
{
|
|
try {
|
|
$request->validate([
|
|
'folio' => 'required|string|exists:tags,folio',
|
|
'cancellation_reason_id' => 'required|exists:catalog_cancellation_reasons,id',
|
|
'cancellation_observations' => 'nullable|string',
|
|
'id_chip' => 'nullable|string|max:255',
|
|
'module_id' => 'nullable|exists:modules,id',
|
|
]);
|
|
|
|
DB::beginTransaction();
|
|
|
|
$tag = Tag::where('folio', $request->folio)->first();
|
|
|
|
if (!$tag) {
|
|
DB::rollBack();
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'No se encontró el tag con el folio proporcionado.',
|
|
'folio' => $request->folio,
|
|
]);
|
|
}
|
|
|
|
// Validar que el tag NO esté asignado
|
|
if ($tag->isAssigned()) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Este tag está asignado a un vehículo. Usa el endpoint de cancelación de constancia.',
|
|
'tag_status' => $tag->status->name,
|
|
]);
|
|
}
|
|
|
|
// Validar que no esté ya cancelado
|
|
if ($tag->isCancelled()) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'El tag ya está cancelado.',
|
|
]);
|
|
}
|
|
|
|
|
|
$additionalInfo = [];
|
|
if ($request->filled('id_chip')) {
|
|
$additionalInfo[] = "ID CHIP: {$request->id_chip}";
|
|
}
|
|
|
|
$observations = $request->cancellation_observations;
|
|
if (!empty($additionalInfo)) {
|
|
$observations = ($observations ? $observations . ' | ' : '') . implode(' | ', $additionalInfo);
|
|
}
|
|
|
|
$cancellationLog = TagCancellationLog::create([
|
|
'tag_id' => $tag->id,
|
|
'cancellation_reason_id' => $request->cancellation_reason_id,
|
|
'cancellation_observations' => $observations,
|
|
'cancellation_at' => now(),
|
|
'cancelled_by' => Auth::id(),
|
|
]);
|
|
|
|
// Actualizar el módulo del tag si se proporciona
|
|
if ($request->filled('module_id')) {
|
|
$tag->module_id = $request->module_id;
|
|
$tag->save();
|
|
}
|
|
|
|
// Cancelar el tag
|
|
$tag->markAsDamaged();
|
|
|
|
DB::commit();
|
|
|
|
try {
|
|
// Recargar el tag con sus relaciones actualizadas
|
|
$tag->load(['status', 'cancellationLogs.cancellationReason', 'cancellationLogs.cancelledBy', 'module']);
|
|
|
|
// Obtener datos de cancelación del último log
|
|
$lastCancellation = $tag->cancellationLogs()
|
|
->with(['cancellationReason', 'cancelledBy'])
|
|
->latest()
|
|
->first();
|
|
|
|
// Preparar datos para el PDF
|
|
$cancellationData = [
|
|
'fecha' => $lastCancellation ? $lastCancellation->cancellation_at->format('d/m/Y') : now()->format('d/m/Y'),
|
|
'folio' => $tag->folio ?? '',
|
|
'id_chip' => $request->id_chip ?? '',
|
|
'motivo' => $lastCancellation && $lastCancellation->cancellationReason
|
|
? $lastCancellation->cancellationReason->name
|
|
: 'No especificado',
|
|
'operador' => $lastCancellation && $lastCancellation->cancelledBy
|
|
? $lastCancellation->cancelledBy->name
|
|
: 'Sistema',
|
|
'modulo' => $tag->module ? $tag->module->name : 'No especificado',
|
|
'ubicacion' => $tag->module ? $tag->module->address : 'No especificado',
|
|
];
|
|
|
|
$pdf = Pdf::loadView('pdfs.tag', [
|
|
'cancellation' => $cancellationData,
|
|
])
|
|
->setPaper('a4', 'portrait')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('constancia_cancelada_' . ($tag->tag_number ?? $tag->folio) . '.pdf');
|
|
} catch (\Exception $e) {
|
|
// Si falla la generación del PDF, devolver respuesta JSON
|
|
Log::error('Error al generar PDF de tag cancelado: ' . $e->getMessage());
|
|
|
|
return ApiResponse::OK->response([
|
|
'message' => 'Tag cancelado exitosamente (Error al generar PDF)',
|
|
'tag' => [
|
|
'id' => $tag->id,
|
|
'tag_number' => $tag->tag_number,
|
|
'folio' => $tag->folio,
|
|
'previous_status' => 'Disponible',
|
|
'new_status' => 'Dañado',
|
|
],
|
|
'cancellation' => [
|
|
'id' => $cancellationLog->id,
|
|
'reason' => $cancellationLog->cancellationReason->name,
|
|
'observations' => $cancellationLog->cancellation_observations,
|
|
'cancelled_at' => $cancellationLog->cancellation_at->toDateTimeString(),
|
|
'cancelled_by' => Auth::user()->name,
|
|
],
|
|
'pdf_error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Error al cancelar el tag',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|