ADD: Proceso de Cancelación
This commit is contained in:
parent
de0ac7a3aa
commit
bf0346dabf
319
app/Http/Controllers/Repuve/CancellationController.php
Normal file
319
app/Http/Controllers/Repuve/CancellationController.php
Normal file
@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Repuve;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Repuve\ConsultaVehiculoRequest;
|
||||
use App\Http\Requests\Repuve\CancelConstanciaRequest;
|
||||
use App\Models\Vehicle;
|
||||
use App\Models\Tag;
|
||||
use App\Models\VehicleTagLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||
|
||||
class CancellationController extends Controller
|
||||
{
|
||||
/* ===========================================================
|
||||
* PASO 1: Buscar vehículo para cancelar
|
||||
* Muestra la tabla con los datos del vehículo encontrado
|
||||
* ===========================================================
|
||||
*/
|
||||
public function searchToCancel(ConsultaVehiculoRequest $request)
|
||||
{
|
||||
try {
|
||||
$searchType = $request->input('search_type');
|
||||
$searchValue = $request->input('search_value');
|
||||
|
||||
// Simulación: consulta a base de datos REPUVE hardcodeada
|
||||
$vehicleData = $this->getVehicleDataFromRepuve($searchType, $searchValue);
|
||||
|
||||
if (!$vehicleData) {
|
||||
return ApiResponse::NOT_FOUND->response([
|
||||
'message' => 'No se encontró ningún vehículo con los datos proporcionados en REPUVE.',
|
||||
'search_type' => $searchType,
|
||||
'search_value' => $searchValue,
|
||||
]);
|
||||
}
|
||||
|
||||
// Buscar vehículo en nuestra base de datos local
|
||||
$vehicle = Vehicle::where('numero_serie', $vehicleData['NO_SERIE'])
|
||||
->with(['owner'])
|
||||
->first();
|
||||
|
||||
if (!$vehicle) {
|
||||
return ApiResponse::NOT_FOUND->response([
|
||||
'message' => 'El vehículo existe en REPUVE pero no está registrado localmente.',
|
||||
'repuve_data' => $vehicleData,
|
||||
]);
|
||||
}
|
||||
|
||||
// Buscar tag asignado al vehículo (solo assigned)
|
||||
$tag = Tag::where('vehicle_id', $vehicle->id)
|
||||
->where('status', 'assigned')
|
||||
->first();
|
||||
|
||||
// Verificar si ya tiene cancelaciones previas
|
||||
$cancelaciones = VehicleTagLog::where('vehicle_id', $vehicle->id)
|
||||
->whereNotNull('cancellation_at')
|
||||
->count();
|
||||
|
||||
$canCancel = !is_null($tag) && $tag->status === 'assigned';
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'vehicle' => [
|
||||
'id' => $vehicle->id,
|
||||
'estatus' => $tag ? $tag->status : 'sin_tag',
|
||||
'folio' => $vehicle->folio,
|
||||
'tag' => $tag ? $tag->folio : null,
|
||||
'niv' => $vehicle->numero_serie,
|
||||
'tipo' => $vehicle->tipo,
|
||||
'registro' => $vehicle->created_at->format('d/m/Y'),
|
||||
'placa' => $vehicle->placa,
|
||||
'marca' => $vehicle->marca,
|
||||
'modelo' => $vehicle->modelo,
|
||||
'color' => $vehicle->color,
|
||||
],
|
||||
'tag' => $tag ? [
|
||||
'id' => $tag->id,
|
||||
'folio' => $tag->folio,
|
||||
'status' => $tag->status,
|
||||
] : null,
|
||||
'can_cancel' => $canCancel,
|
||||
'total_cancelaciones_previas' => $cancelaciones,
|
||||
'message' => $canCancel
|
||||
? 'Vehículo encontrado. Puede proceder con la cancelación.'
|
||||
: 'Vehículo encontrado pero no tiene tag asignado o ya está cancelado.',
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error en buscarVehiculoParaCancelar: ' . $e->getMessage(), [
|
||||
'search_type' => $searchType ?? null,
|
||||
'search_value' => $searchValue ?? null,
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return ApiResponse::INTERNAL_SERVER_ERROR->response([
|
||||
'message' => 'Error al buscar el vehículo',
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===========================================================
|
||||
* Cancelar constancia
|
||||
* ===========================================================
|
||||
*/
|
||||
public function cancelarConstancia(CancelConstanciaRequest $request)
|
||||
{
|
||||
// Iniciar transacción
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$vehicleId = $request->input('vehicle_id');
|
||||
$tagId = $request->input('tag_id');
|
||||
$reason = $request->input('cancellation_reason');
|
||||
$observations = $request->input('cancellation_observations');
|
||||
|
||||
// Validar que el vehículo existe
|
||||
$vehicle = Vehicle::findOrFail($vehicleId);
|
||||
|
||||
// Validar que el tag existe
|
||||
$tag = Tag::findOrFail($tagId);
|
||||
|
||||
// Validar que el tag pertenece al vehículo
|
||||
if ($tag->vehicle_id !== $vehicle->id) {
|
||||
DB::rollBack();
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'message' => 'El tag no está asignado al vehículo especificado',
|
||||
]);
|
||||
}
|
||||
|
||||
// Validar que el tag esté en estado 'assigned'
|
||||
if ($tag->status !== 'assigned') {
|
||||
DB::rollBack();
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'message' => "El tag no puede ser cancelado. Estado actual: {$tag->status}",
|
||||
'current_status' => $tag->status,
|
||||
]);
|
||||
}
|
||||
|
||||
// Verificar que no exista ya un registro de cancelación para este tag
|
||||
$existingCancellation = VehicleTagLog::where('tag_id', $tagId)
|
||||
->whereNotNull('cancellation_at')
|
||||
->first();
|
||||
|
||||
if ($existingCancellation) {
|
||||
DB::rollBack();
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'message' => 'Este tag ya tiene un registro de cancelación',
|
||||
'cancellation_date' => $existingCancellation->cancellation_at->toDateTimeString(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Crear registro de cancelación en vehicle_tags_logs
|
||||
$cancellationLog = VehicleTagLog::create([
|
||||
'vehicle_id' => $vehicleId,
|
||||
'tag_id' => $tagId,
|
||||
'cancellation_at' => now(),
|
||||
'cancellation_reason' => $reason,
|
||||
'cancellation_observations' => $observations,
|
||||
'cancelled_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
// Actualizar estado del tag a 'cancelled'
|
||||
$tag->update(['status' => 'cancelled']);
|
||||
|
||||
// Confirmar transacción
|
||||
DB::commit();
|
||||
|
||||
// Recargar vehículo con tag actualizado
|
||||
$vehicle->load('owner');
|
||||
$tag->refresh();
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'success' => true,
|
||||
'message' => 'Constancia de inscripción cancelada exitosamente',
|
||||
'vehicle' => [
|
||||
'id' => $vehicle->id,
|
||||
'estatus' => 'cancelada',
|
||||
'folio' => $vehicle->folio,
|
||||
'tag' => $tag->folio,
|
||||
'niv' => $vehicle->numero_serie,
|
||||
'tipo' => $vehicle->tipo,
|
||||
'registro' => $vehicle->created_at->format('d/m/Y'),
|
||||
],
|
||||
'cancellation' => [
|
||||
'id' => $cancellationLog->id,
|
||||
'motivo' => $cancellationLog->cancellation_reason,
|
||||
'observaciones' => $cancellationLog->cancellation_observations,
|
||||
'fecha_cancelacion' => $cancellationLog->cancellation_at->format('d/m/Y H:i:s'),
|
||||
'cancelado_por' => auth()->user()->name ?? 'Sistema',
|
||||
],
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// Revertir transacción en caso de error
|
||||
DB::rollBack();
|
||||
|
||||
Log::error('Error en cancelarConstancia: ' . $e->getMessage(), [
|
||||
'vehicle_id' => $request->input('vehicle_id'),
|
||||
'tag_id' => $request->input('tag_id'),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return ApiResponse::INTERNAL_SERVER_ERROR->response([
|
||||
'message' => 'Error al cancelar la constancia de inscripción',
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene historial de cancelaciones de un vehículo
|
||||
*/
|
||||
public function historialCancelaciones($vehicleId)
|
||||
{
|
||||
try {
|
||||
$vehicle = Vehicle::findOrFail($vehicleId);
|
||||
|
||||
$cancelaciones = VehicleTagLog::where('vehicle_id', $vehicleId)
|
||||
->whereNotNull('cancellation_at')
|
||||
->with(['tag', 'cancelledBy'])
|
||||
->orderBy('cancellation_at', 'desc')
|
||||
->get();
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'vehicle' => [
|
||||
'id' => $vehicle->id,
|
||||
'placa' => $vehicle->placa,
|
||||
'numero_serie' => $vehicle->numero_serie,
|
||||
],
|
||||
'total_cancelaciones' => $cancelaciones->count(),
|
||||
'cancelaciones' => $cancelaciones->map(function ($log) {
|
||||
return [
|
||||
'id' => $log->id,
|
||||
'tag_folio' => $log->tag->folio ?? 'N/A',
|
||||
'cancellation_at' => $log->cancellation_at->toDateTimeString(),
|
||||
'cancellation_reason' => $log->cancellation_reason,
|
||||
'cancellation_observations' => $log->cancellation_observations,
|
||||
'cancelled_by' => $log->cancelledBy ? [
|
||||
'id' => $log->cancelledBy->id,
|
||||
'name' => $log->cancelledBy->name,
|
||||
] : null,
|
||||
];
|
||||
}),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error en historialCancelaciones: ' . $e->getMessage(), [
|
||||
'vehicle_id' => $vehicleId ?? null,
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
return ApiResponse::INTERNAL_SERVER_ERROR->response([
|
||||
'message' => 'Error al obtener el historial de cancelaciones',
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simula consulta a base de datos REPUVE
|
||||
*
|
||||
*/
|
||||
private function getVehicleDataFromRepuve(string $searchType, string $searchValue): ?array
|
||||
{
|
||||
// Datos hardcodeados del vehículo de ejemplo
|
||||
$vehicleData = [
|
||||
"ANIO_PLACA" => "2020",
|
||||
"PLACA" => "WNU700B",
|
||||
"NO_SERIE" => "LSGHD52H0ND032457",
|
||||
"RFC" => "GME111116GJA",
|
||||
"FOLIO" => "3962243",
|
||||
"VIGENCIA" => "2025",
|
||||
"FECHA_IMPRESION" => "10-01-2025",
|
||||
"QR_HASH" => "Vu5TF4kYsbbltzjDdGQyenKfZoIk2wro34a5Gkh9JVh0CFxfPlrd92YEWK21JF.nLjQNyzKmqRvWYuPiS.kU7A--",
|
||||
"VALIDO" => true,
|
||||
"FOLIOTEMP" => false,
|
||||
"NOMBRE" => "GOLSYSTEMS DE MEXICO S DE RL DE CV",
|
||||
"NOMBRE2" => "GOLS*MS DXICOE RL*CV",
|
||||
"MUNICIPIO" => "CENTRO",
|
||||
"LOCALIDAD" => "VILLAHERMOSA",
|
||||
"CALLE" => "C BUGAMBILIAS 118 ",
|
||||
"CALLE2" => "C BU*ILIA*18 ",
|
||||
"TIPO" => "SEDAN",
|
||||
"TIPO_SERVICIO" => "PARTICULAR",
|
||||
"MARCA" => "CHEVROLET G.M.C.",
|
||||
"LINEA" => "AVEO",
|
||||
"SUBLINEA" => "PAQ. \"A\" LS",
|
||||
"MODELO" => 2022,
|
||||
"NUMERO_SERIE" => "LSGHD52H0ND032457",
|
||||
"NUMERO_MOTOR" => "H. EN WUHANLL,SGM",
|
||||
"DESCRIPCION_ORIGEN" => "IMPORTADO",
|
||||
"COLOR" => "BLANCO",
|
||||
"CODIGO_POSTAL" => "86179",
|
||||
"SERIE_FOLIO" => "D3962243",
|
||||
"SFOLIO" => "3962243"
|
||||
];
|
||||
|
||||
// Normalizar el valor de búsqueda (trim y uppercase)
|
||||
$searchValue = trim(strtoupper($searchValue));
|
||||
|
||||
// Simular búsqueda por tipo
|
||||
switch ($searchType) {
|
||||
case 'folio':
|
||||
return (strtoupper($vehicleData['FOLIO']) === $searchValue) ? $vehicleData : null;
|
||||
|
||||
case 'vin':
|
||||
return (strtoupper($vehicleData['NO_SERIE']) === $searchValue) ? $vehicleData : null;
|
||||
|
||||
case 'fecha':
|
||||
// Para fecha, comparar sin case sensitivity
|
||||
return (strtoupper($vehicleData['FECHA_IMPRESION']) === $searchValue) ? $vehicleData : null;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,12 @@
|
||||
|
||||
class RepuveController extends Controller
|
||||
{
|
||||
|
||||
/* ===========================================================
|
||||
* Inscripción de vehículo al REPUVE
|
||||
* ===========================================================
|
||||
*/
|
||||
|
||||
public function inscripcionVehiculo(VehicleStoreRequest $request)
|
||||
{
|
||||
try {
|
||||
@ -98,7 +104,7 @@ public function inscripcionVehiculo(VehicleStoreRequest $request)
|
||||
$uploadedFiles = [];
|
||||
if ($request->hasFile('files')) {
|
||||
$files = $request->file('files');
|
||||
$fileNames = $request->input('file_names', []);
|
||||
$fileNames = $request->input('names', []);
|
||||
|
||||
foreach ($files as $index => $file) {
|
||||
// Generar nombre único
|
||||
|
||||
64
app/Http/Requests/Repuve/CancelConstanciaRequest.php
Normal file
64
app/Http/Requests/Repuve/CancelConstanciaRequest.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Repuve;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CancelConstanciaRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'vehicle_id' => 'required|integer|exists:vehicle,id',
|
||||
'tag_id' => 'required|integer|exists:tags,id',
|
||||
'cancellation_reason' => 'required|in:fallo_lectura_handheld,cambio_parabrisas,roto_al_pegarlo,extravio,otro',
|
||||
'cancellation_observations' => 'nullable|string|max:1000',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom messages for validator errors.
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'vehicle_id.required' => 'El id del vehículo es obligatorio.',
|
||||
'vehicle_id.integer' => 'El id del vehículo debe ser un número entero.',
|
||||
'vehicle_id.exists' => 'El vehículo especificado no existe.',
|
||||
|
||||
'tag_id.required' => 'El id del tag es obligatorio.',
|
||||
'tag_id.integer' => 'El id del tag debe ser un número entero.',
|
||||
'tag_id.exists' => 'El tag especificado no existe.',
|
||||
|
||||
'cancellation_reason.required' => 'El motivo de cancelación es obligatorio.',
|
||||
'cancellation_reason.in' => 'El motivo de cancelación no es válido. Opciones: fallo_lectura_handheld, cambio_parabrisas, roto_al_pegarlo, extravio, otro.',
|
||||
|
||||
'cancellation_observations.string' => 'Las observaciones deben ser texto.',
|
||||
'cancellation_observations.max' => 'Las observaciones no pueden exceder 1000 caracteres.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'vehicle_id' => 'id del vehículo',
|
||||
'tag_id' => 'id del tag',
|
||||
'cancellation_reason' => 'motivo de cancelación',
|
||||
'cancellation_observations' => 'observaciones',
|
||||
];
|
||||
}
|
||||
}
|
||||
52
app/Http/Requests/Repuve/ConsultaVehiculoRequest.php
Normal file
52
app/Http/Requests/Repuve/ConsultaVehiculoRequest.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Repuve;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ConsultaVehiculoRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'search_type' => 'required|in:folio,vin,fecha',
|
||||
'search_value' => 'required|string|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom messages for validator errors.
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'search_type.required' => 'El tipo de búsqueda es obligatorio.',
|
||||
'search_type.in' => 'El tipo de búsqueda debe ser: folio, vin o fecha.',
|
||||
'search_value.required' => 'El valor de búsqueda es obligatorio.',
|
||||
'search_value.string' => 'El valor de búsqueda debe ser una cadena de texto.',
|
||||
'search_value.max' => 'El valor de búsqueda no puede exceder 255 caracteres.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'search_type' => 'tipo de búsqueda',
|
||||
'search_value' => 'valor de búsqueda',
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,10 @@ class VehicleTagLog extends Model
|
||||
protected $fillable = [
|
||||
'vehicle_id',
|
||||
'tag_id',
|
||||
'cancellation_reason',
|
||||
'cancellation_observations',
|
||||
'cancellation_at',
|
||||
'cancelled_by',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
@ -24,4 +27,16 @@ protected function casts(): array
|
||||
];
|
||||
}
|
||||
|
||||
public function vehicle() {
|
||||
return $this->belongsTo(Vehicle::class);
|
||||
}
|
||||
|
||||
public function tag() {
|
||||
return $this->belongsTo(Tag::class);
|
||||
}
|
||||
|
||||
public function cancelledBy() {
|
||||
return $this->belongsTo(User::class, 'cancelled_by');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,6 +16,15 @@ public function up(): void
|
||||
$table->foreignId('vehicle_id')->constrained('vehicle')->cascadeOnDelete();
|
||||
$table->foreignId('tag_id')->constrained('tags')->cascadeOnDelete();
|
||||
$table->timestamp('cancellation_at')->nullable();
|
||||
$table->enum('cancellation_reason', [
|
||||
'fallo_lectura_handheld',
|
||||
'cambio_parabrisas',
|
||||
'roto_al_pegarlo',
|
||||
'extravio',
|
||||
'otro'
|
||||
])->nullable();
|
||||
$table->text('cancellation_observations')->nullable();
|
||||
$table->foreignId('cancelled_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
BIN
resources/images/logo-seguridad.png
Executable file
BIN
resources/images/logo-seguridad.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@ -174,7 +174,7 @@
|
||||
<!-- Header con logo centrado -->
|
||||
<div class="header">
|
||||
<?php
|
||||
$imagePath = storage_path('app/images/logo-seguridad.png');
|
||||
$imagePath = resource_path('images/logo-seguridad.png');
|
||||
$imageData = base64_encode(file_get_contents($imagePath));
|
||||
$imageSrc = 'data:image/png;base64,' . $imageData;
|
||||
?>
|
||||
@ -258,7 +258,8 @@
|
||||
<p class="signature-role">Propietario</p>
|
||||
</td>
|
||||
<td class="signature-cell">
|
||||
<div class="signature-line">{{ $record->user->full_name }}</div>
|
||||
<div class="signature-line"></div>
|
||||
<p class="signature-name">{{ $record->user->full_name }}</p>
|
||||
<p class="signature-role">Operador</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Repuve\RepuveController;
|
||||
use App\Http\Controllers\Repuve\RecordController;
|
||||
use App\Http\Controllers\Repuve\CancellationController;
|
||||
|
||||
/**
|
||||
* Rutas del núcleo de la aplicación.
|
||||
@ -19,14 +20,20 @@
|
||||
|
||||
/** Rutas protegidas (requieren autenticación) */
|
||||
Route::middleware('auth:api')->group(function() {
|
||||
// Tus rutas protegidas
|
||||
// Rutas de inscripción de vehículos
|
||||
Route::post('inscripcion', [RepuveController::class, 'inscripcionVehiculo']);
|
||||
Route::post('consulta', [RepuveController::class, 'consultaExpediente']);
|
||||
|
||||
// Rutas de expedientes y documentos
|
||||
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
||||
Route::get('expediente/{recordId}/documentos', [RecordController::class, 'getFile']);
|
||||
Route::post('expediente/documentos', [RecordController::class, 'uploadFile']);
|
||||
Route::delete('expediente/documentos/{fileId}', [RecordController::class, 'deleteFile']);
|
||||
|
||||
// Rutas de cancelación de constancias
|
||||
Route::post('cancelacion/buscar', [CancellationController::class, 'searchToCancel']);
|
||||
Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']);
|
||||
Route::get('cancelacion/historial/{vehicleId}', [CancellationController::class, 'historialCancelaciones']);
|
||||
});
|
||||
|
||||
/** Rutas públicas */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user