ADD: Cancelación, pdfs agregados
This commit is contained in:
parent
bf0346dabf
commit
d281e505e0
@ -3,10 +3,8 @@
|
|||||||
namespace App\Http\Controllers\Repuve;
|
namespace App\Http\Controllers\Repuve;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\Repuve\ConsultaVehiculoRequest;
|
|
||||||
use App\Http\Requests\Repuve\CancelConstanciaRequest;
|
use App\Http\Requests\Repuve\CancelConstanciaRequest;
|
||||||
use App\Models\Vehicle;
|
use App\Models\Record;
|
||||||
use App\Models\Tag;
|
|
||||||
use App\Models\VehicleTagLog;
|
use App\Models\VehicleTagLog;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -14,306 +12,85 @@
|
|||||||
|
|
||||||
class CancellationController extends Controller
|
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);
|
* Cancelar la constancia/tag
|
||||||
|
|
||||||
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)
|
public function cancelarConstancia(CancelConstanciaRequest $request)
|
||||||
{
|
{
|
||||||
// Iniciar transacción
|
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$vehicleId = $request->input('vehicle_id');
|
DB::beginTransaction();
|
||||||
$tagId = $request->input('tag_id');
|
|
||||||
$reason = $request->input('cancellation_reason');
|
|
||||||
$observations = $request->input('cancellation_observations');
|
|
||||||
|
|
||||||
// Validar que el vehículo existe
|
// Buscar el expediente con sus relaciones
|
||||||
$vehicle = Vehicle::findOrFail($vehicleId);
|
$record = Record::with('vehicle.tag')->findOrFail($request->record_id);
|
||||||
|
$vehicle = $record->vehicle;
|
||||||
|
$tag = $vehicle->tag;
|
||||||
|
|
||||||
// Validar que el tag existe
|
// Validar que el vehículo tiene un tag asignado
|
||||||
$tag = Tag::findOrFail($tagId);
|
if (!$tag) {
|
||||||
|
|
||||||
// Validar que el tag pertenece al vehículo
|
|
||||||
if ($tag->vehicle_id !== $vehicle->id) {
|
|
||||||
DB::rollBack();
|
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
'message' => 'El tag no está asignado al vehículo especificado',
|
'message' => 'El vehículo no tiene un tag/constancia asignada.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validar que el tag esté en estado 'assigned'
|
// Validar que el tag está en estado activo
|
||||||
if ($tag->status !== 'assigned') {
|
if ($tag->status !== 'assigned') {
|
||||||
DB::rollBack();
|
|
||||||
return ApiResponse::BAD_REQUEST->response([
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
'message' => "El tag no puede ser cancelado. Estado actual: {$tag->status}",
|
'message' => 'El tag ya no está activo. Status actual: ' . $tag->status
|
||||||
'current_status' => $tag->status,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar que no exista ya un registro de cancelación para este tag
|
// Crear registro en el log de cancelaciones (HISTORIAL)
|
||||||
$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([
|
$cancellationLog = VehicleTagLog::create([
|
||||||
'vehicle_id' => $vehicleId,
|
'vehicle_id' => $vehicle->id,
|
||||||
'tag_id' => $tagId,
|
'tag_id' => $tag->id,
|
||||||
|
'cancellation_reason' => $request->cancellation_reason,
|
||||||
|
'cancellation_observations' => $request->cancellation_observations,
|
||||||
'cancellation_at' => now(),
|
'cancellation_at' => now(),
|
||||||
'cancellation_reason' => $reason,
|
|
||||||
'cancellation_observations' => $observations,
|
|
||||||
'cancelled_by' => auth()->id(),
|
'cancelled_by' => auth()->id(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Actualizar estado del tag a 'cancelled'
|
// Actualizar estado del tag a 'cancelled'
|
||||||
$tag->update(['status' => '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',
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Constancia cancelada exitosamente',
|
||||||
|
'cancellation' => [
|
||||||
|
'id' => $cancellationLog->id,
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $vehicle->id,
|
||||||
|
'placa' => $vehicle->placa,
|
||||||
|
'numero_serie' => $vehicle->numero_serie,
|
||||||
|
],
|
||||||
|
'tag' => [
|
||||||
|
'id' => $tag->id,
|
||||||
|
'folio' => $tag->folio,
|
||||||
|
'old_status' => 'assigned',
|
||||||
|
'new_status' => 'cancelled',
|
||||||
|
],
|
||||||
|
'cancellation_reason' => $request->cancellation_reason,
|
||||||
|
'cancellation_observations' => $request->cancellation_observations,
|
||||||
|
'cancelled_at' => $cancellationLog->cancellation_at->toDateTimeString(),
|
||||||
|
'cancelled_by' => auth()->user()->name,
|
||||||
|
]
|
||||||
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Revertir transacción en caso de error
|
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
|
|
||||||
Log::error('Error en cancelarConstancia: ' . $e->getMessage(), [
|
Log::error('Error en cancelarConstancia: ' . $e->getMessage(), [
|
||||||
'vehicle_id' => $request->input('vehicle_id'),
|
'record_id' => $request->record_id ?? null,
|
||||||
'tag_id' => $request->input('tag_id'),
|
'cancellation_reason' => $request->cancellation_reason ?? null,
|
||||||
'trace' => $e->getTraceAsString()
|
'trace' => $e->getTraceAsString()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return ApiResponse::INTERNAL_SERVER_ERROR->response([
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
'message' => 'Error al cancelar la constancia de inscripción',
|
'message' => 'Error al cancelar la constancia',
|
||||||
'error' => $e->getMessage(),
|
'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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,7 @@
|
|||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Barryvdh\DomPDF\Facade\Pdf;
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
use App\Http\Requests\Repuve\FileStoreRequest;
|
|
||||||
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use App\Models\Record;
|
use App\Models\Record;
|
||||||
use App\Models\File;
|
|
||||||
|
|
||||||
class RecordController extends Controller
|
class RecordController extends Controller
|
||||||
{
|
{
|
||||||
@ -24,94 +20,39 @@ public function generatePdf($id)
|
|||||||
'isRemoteEnabled' => true,
|
'isRemoteEnabled' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $pdf->stream('record-' . $id . '.pdf');
|
return $pdf->stream('constancia-inscripcion-' . $id . '.pdf');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uploadFile(FileStoreRequest $request){
|
public function generatePdfVerification($id)
|
||||||
try {
|
|
||||||
// Verificar que existe el record
|
|
||||||
$record = Record::findOrFail($request->record_id);
|
|
||||||
|
|
||||||
// Obtener el archivo
|
|
||||||
$uploadedFile = $request->file('file');
|
|
||||||
|
|
||||||
// Generar nombre único
|
|
||||||
$fileName = time() . '_' . $uploadedFile->getClientOriginalName();
|
|
||||||
|
|
||||||
// Guardar en storage
|
|
||||||
$path = $uploadedFile->storeAs('records', $fileName, 'public');
|
|
||||||
|
|
||||||
// Calcular MD5 del archivo
|
|
||||||
$md5 = md5_file($uploadedFile->getRealPath());
|
|
||||||
|
|
||||||
// Crear registro en la tabla files
|
|
||||||
$file = File::create([
|
|
||||||
'name' => $request->name,
|
|
||||||
'path' => $path,
|
|
||||||
'md5' => $md5,
|
|
||||||
'record_id' => $record->id,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'message' => 'Archivo subido exitosamente',
|
|
||||||
'file' => [
|
|
||||||
'id' => $file->id,
|
|
||||||
'name' => $file->name,
|
|
||||||
'path' => $file->path,
|
|
||||||
'md5' => $file->md5,
|
|
||||||
'url' => $file->url,
|
|
||||||
'record_id' => $file->record_id,
|
|
||||||
'created_at' => $file->created_at->toDateTimeString(),
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return ApiResponse::UNPROCESSABLE_CONTENT->response([
|
|
||||||
'message' => 'Error al subir el archivo',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFile($recordId) {
|
|
||||||
try {
|
|
||||||
$record = Record::with('files')->findOrFail($recordId);
|
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'record_id' => $record->id,
|
|
||||||
'files' => $record->files,
|
|
||||||
'folio' => $record->folio,
|
|
||||||
]);
|
|
||||||
}catch(\Exception $e) {
|
|
||||||
return ApiResponse::NOT_FOUND->response([
|
|
||||||
'message' => 'No se encontró el expediente o no tiene archivos asociados.',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteFile($fileId)
|
|
||||||
{
|
{
|
||||||
try {
|
$record = Record::with('vehicle.owner', 'user')->findOrFail($id);
|
||||||
$file = File::findOrFail($fileId);
|
|
||||||
|
|
||||||
// Eliminar archivo físico del storage
|
$pdf = Pdf::loadView('pdfs.verification', compact('record'))
|
||||||
if (Storage::disk('public')->exists($file->path)) {
|
->setPaper('a4', 'landscape')
|
||||||
Storage::disk('public')->delete($file->path);
|
->setOptions([
|
||||||
}
|
'defaultFont' => 'sans-serif',
|
||||||
|
'isHtml5ParserEnabled' => true,
|
||||||
// Eliminar registro de la BD
|
'isRemoteEnabled' => true,
|
||||||
$file->delete();
|
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'message' => 'Archivo eliminado exitosamente',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
return $pdf->stream('hoja-verificacion-' . $id . '.pdf');
|
||||||
return ApiResponse::OK->response([
|
|
||||||
'message' => 'Error al eliminar el archivo',
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function generatePdfConstancia($id)
|
||||||
|
{
|
||||||
|
$record = Record::with('vehicle.owner', 'user')->findOrFail($id);
|
||||||
|
|
||||||
|
$pdf = Pdf::loadView('pdfs.constancia', compact('record'))
|
||||||
|
->setPaper('a4', 'landscape')
|
||||||
|
->setOptions([
|
||||||
|
'defaultFont' => 'sans-serif',
|
||||||
|
'isHtml5ParserEnabled' => true,
|
||||||
|
'isRemoteEnabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $pdf->stream('constancia-inscripcion' . $id . '.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
<?php namespace App\Http\Controllers\Repuve;
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Repuve;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
use App\Http\Requests\Repuve\VehicleStoreRequest;
|
use App\Http\Requests\Repuve\VehicleStoreRequest;
|
||||||
|
use App\Http\Requests\Repuve\VehicleUpdateRequest;
|
||||||
use App\Models\Vehicle;
|
use App\Models\Vehicle;
|
||||||
use App\Models\Record;
|
use App\Models\Record;
|
||||||
use App\Models\Owner;
|
use App\Models\Owner;
|
||||||
@ -97,7 +100,7 @@ public function inscripcionVehiculo(VehicleStoreRequest $request)
|
|||||||
$record = Record::create([
|
$record = Record::create([
|
||||||
'folio' => $folio,
|
'folio' => $folio,
|
||||||
'vehicle_id' => $vehicle->id,
|
'vehicle_id' => $vehicle->id,
|
||||||
'user_id' => auth()->id() ?? null,
|
'user_id' => auth()->id(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Procesar y guardar archivos si existen
|
//Procesar y guardar archivos si existen
|
||||||
@ -108,7 +111,7 @@ public function inscripcionVehiculo(VehicleStoreRequest $request)
|
|||||||
|
|
||||||
foreach ($files as $index => $file) {
|
foreach ($files as $index => $file) {
|
||||||
// Generar nombre único
|
// Generar nombre único
|
||||||
$fileName = time() . '_' . $index . '_' . $file->getClientOriginalName();
|
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
||||||
|
|
||||||
// Guardar archivos
|
// Guardar archivos
|
||||||
$path = $file->storeAs('records', $fileName, 'public');
|
$path = $file->storeAs('records', $fileName, 'public');
|
||||||
@ -164,7 +167,6 @@ public function inscripcionVehiculo(VehicleStoreRequest $request)
|
|||||||
'files' => $uploadedFiles,
|
'files' => $uploadedFiles,
|
||||||
'total_files' => count($uploadedFiles),
|
'total_files' => count($uploadedFiles),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Revertir transacción en caso de error
|
// Revertir transacción en caso de error
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
@ -192,15 +194,34 @@ private function checkIfStolen(string $folio): bool
|
|||||||
public function consultaExpediente(Request $request)
|
public function consultaExpediente(Request $request)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'folio' => 'required|string',
|
'folio' => 'nullable|string',
|
||||||
|
'placa' => 'nullable|string',
|
||||||
|
'niv' => 'nullable|string',
|
||||||
|
], [
|
||||||
|
'required_without_all' => 'Debe proporcionar al menos uno de los siguientes: folio, placa o NIV.'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$folio = $request->input('folio');
|
if (!$request->filled('folio') && !$request->filled('placa') && !$request->filled('niv')) {
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Debe proporcionar al menos uno de los siguientes parámetros: folio, placa o niv.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Cargar record con todas sus relaciones
|
$query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user']);
|
||||||
$record = Record::where('folio', $folio)
|
|
||||||
->with(['vehicle.owner', 'files', 'user'])
|
if ($request->filled('folio')) {
|
||||||
->first();
|
$query->where('folio', $request->input('folio'));
|
||||||
|
} elseif ($request->filled('placa')) {
|
||||||
|
$query->whereHas('vehicle', function ($q) use ($request) {
|
||||||
|
$q->where('placa', $request->input('placa'));
|
||||||
|
});
|
||||||
|
} elseif ($request->filled('niv')) {
|
||||||
|
$query->whereHas('vehicle', function ($q) use ($request) {
|
||||||
|
$q->where('numero_serie', $request->input('niv'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$record = $query->first();
|
||||||
|
|
||||||
if (!$record) {
|
if (!$record) {
|
||||||
return ApiResponse::NOT_FOUND->response([
|
return ApiResponse::NOT_FOUND->response([
|
||||||
@ -214,6 +235,7 @@ public function consultaExpediente(Request $request)
|
|||||||
'folio' => $record->folio,
|
'folio' => $record->folio,
|
||||||
'vehicle' => $record->vehicle,
|
'vehicle' => $record->vehicle,
|
||||||
'owner' => $record->vehicle->owner,
|
'owner' => $record->vehicle->owner,
|
||||||
|
'tag' => $record->vehicle->tag,
|
||||||
'files' => $record->files,
|
'files' => $record->files,
|
||||||
'user' => $record->user,
|
'user' => $record->user,
|
||||||
'created_at' => $record->created_at->toDateTimeString(),
|
'created_at' => $record->created_at->toDateTimeString(),
|
||||||
@ -221,6 +243,139 @@ public function consultaExpediente(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function actualizarVehiculo(VehicleUpdateRequest $request, $recordId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Buscar el registro existente
|
||||||
|
$record = Record::with(['vehicle.owner'])->findOrFail($recordId);
|
||||||
|
|
||||||
|
// Iniciar transacción
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
$vehicleData = $this->getVehicle2();
|
||||||
|
$ownerData = $this->getOwner();
|
||||||
|
|
||||||
|
// Actualizar propietario
|
||||||
|
$owner = Owner::updateOrCreate(
|
||||||
|
['rfc' => $ownerData['rfc']],
|
||||||
|
[
|
||||||
|
'name' => $ownerData['name'],
|
||||||
|
'paternal' => $ownerData['paternal'],
|
||||||
|
'maternal' => $ownerData['maternal'],
|
||||||
|
'curp' => $ownerData['curp'],
|
||||||
|
'address' => $ownerData['address'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Actualizar vehículo
|
||||||
|
$record->vehicle->update([
|
||||||
|
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
||||||
|
'placa' => $vehicleData['PLACA'],
|
||||||
|
'numero_serie' => $vehicleData['NO_SERIE'],
|
||||||
|
'rfc' => $vehicleData['RFC'],
|
||||||
|
'folio' => $vehicleData['FOLIO'],
|
||||||
|
'vigencia' => $vehicleData['VIGENCIA'],
|
||||||
|
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
||||||
|
'qr_hash' => $vehicleData['QR_HASH'],
|
||||||
|
'valido' => $vehicleData['VALIDO'],
|
||||||
|
'foliotemp' => $vehicleData['FOLIOTEMP'],
|
||||||
|
'nombre' => $vehicleData['NOMBRE'],
|
||||||
|
'nombre2' => $vehicleData['NOMBRE2'],
|
||||||
|
'municipio' => $vehicleData['MUNICIPIO'],
|
||||||
|
'localidad' => $vehicleData['LOCALIDAD'],
|
||||||
|
'calle' => $vehicleData['CALLE'],
|
||||||
|
'calle2' => $vehicleData['CALLE2'],
|
||||||
|
'tipo' => $vehicleData['TIPO'],
|
||||||
|
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
||||||
|
'marca' => $vehicleData['MARCA'],
|
||||||
|
'linea' => $vehicleData['LINEA'],
|
||||||
|
'sublinea' => $vehicleData['SUBLINEA'],
|
||||||
|
'modelo' => $vehicleData['MODELO'],
|
||||||
|
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
||||||
|
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
||||||
|
'color' => $vehicleData['COLOR'],
|
||||||
|
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
||||||
|
'serie_folio' => $vehicleData['SERIE_FOLIO'],
|
||||||
|
'sfolio' => $vehicleData['SFOLIO'],
|
||||||
|
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
||||||
|
'owner_id' => $owner->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Procesar nuevos archivos si existen
|
||||||
|
$uploadedFiles = [];
|
||||||
|
if ($request->hasFile('files')) {
|
||||||
|
$files = $request->file('files');
|
||||||
|
$fileNames = $request->input('names', []);
|
||||||
|
|
||||||
|
foreach ($files as $index => $file) {
|
||||||
|
// Generar nombre
|
||||||
|
$fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName();
|
||||||
|
|
||||||
|
// Guardar archivos
|
||||||
|
$path = $file->storeAs('records', $fileName, 'public');
|
||||||
|
|
||||||
|
// Calcular MD5
|
||||||
|
$md5 = md5_file($file->getRealPath());
|
||||||
|
|
||||||
|
// Crear registro en BD
|
||||||
|
$fileRecord = File::create([
|
||||||
|
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
||||||
|
'path' => $path,
|
||||||
|
'md5' => $md5,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$uploadedFiles[] = [
|
||||||
|
'id' => $fileRecord->id,
|
||||||
|
'name' => $fileRecord->name,
|
||||||
|
'path' => $fileRecord->path,
|
||||||
|
'url' => $fileRecord->url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirmar transacción
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// Responder con éxito
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Vehículo actualizado exitosamente',
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
'vehicle_id' => $record->vehicle->id,
|
||||||
|
'updated_at' => $record->updated_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $record->vehicle->id,
|
||||||
|
'placa' => $record->vehicle->placa,
|
||||||
|
'numero_serie' => $record->vehicle->numero_serie,
|
||||||
|
'marca' => $record->vehicle->marca,
|
||||||
|
'modelo' => $record->vehicle->modelo,
|
||||||
|
],
|
||||||
|
'owner' => [
|
||||||
|
'id' => $owner->id,
|
||||||
|
'full_name' => $owner->full_name,
|
||||||
|
'rfc' => $owner->rfc,
|
||||||
|
],
|
||||||
|
'new_files' => $uploadedFiles,
|
||||||
|
'total_new_files' => count($uploadedFiles),
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
Log::error('Error en actualizarVehiculo: ' . $e->getMessage(), [
|
||||||
|
'record_id' => $recordId ?? null,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Error al actualizar el vehículo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getVehicle(): array
|
private function getVehicle(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -228,7 +383,7 @@ private function getVehicle(): array
|
|||||||
"PLACA" => "WNU700B",
|
"PLACA" => "WNU700B",
|
||||||
"NO_SERIE" => "LSGHD52H0ND032457",
|
"NO_SERIE" => "LSGHD52H0ND032457",
|
||||||
"RFC" => "GME111116GJA",
|
"RFC" => "GME111116GJA",
|
||||||
"FOLIO" => "3962243",
|
"FOLIO" => "EXP-2025-201030",
|
||||||
"VIGENCIA" => "2025",
|
"VIGENCIA" => "2025",
|
||||||
"FECHA_IMPRESION" => "10-01-2025",
|
"FECHA_IMPRESION" => "10-01-2025",
|
||||||
"QR_HASH" => "Vu5TF4kYsbbltzjDdGQyenKfZoIk2wro34a5Gkh9JVh0CFxfPlrd92YEWK21JF.nLjQNyzKmqRvWYuPiS.kU7A--",
|
"QR_HASH" => "Vu5TF4kYsbbltzjDdGQyenKfZoIk2wro34a5Gkh9JVh0CFxfPlrd92YEWK21JF.nLjQNyzKmqRvWYuPiS.kU7A--",
|
||||||
@ -256,6 +411,41 @@ private function getVehicle(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getVehicle2(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"ANIO_PLACA" => "2027",
|
||||||
|
"PLACA" => "WNU700Z",
|
||||||
|
"NO_SERIE" => "EXP-2025-201030",
|
||||||
|
"RFC" => "GME111116GJA",
|
||||||
|
"FOLIO" => "EXP-2025-201030",
|
||||||
|
"VIGENCIA" => "2026",
|
||||||
|
"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 119 ",
|
||||||
|
"CALLE2" => "C BU*ILIA*18 ",
|
||||||
|
"TIPO" => "SEDAN",
|
||||||
|
"TIPO_SERVICIO" => "PARTICULAR",
|
||||||
|
"MARCA" => "CHEVROLET G.M.C.",
|
||||||
|
"LINEA" => "AVEO",
|
||||||
|
"SUBLINEA" => "PAQ. \"A\" LS",
|
||||||
|
"MODELO" => 2023,
|
||||||
|
"NUMERO_SERIE" => "EXP-2025-201030",
|
||||||
|
"NUMERO_MOTOR" => "H. EN WUHANLL,SGM",
|
||||||
|
"DESCRIPCION_ORIGEN" => "IMPORTADO",
|
||||||
|
"COLOR" => "AZUL",
|
||||||
|
"CODIGO_POSTAL" => "86181",
|
||||||
|
"SERIE_FOLIO" => "D3962242",
|
||||||
|
"SFOLIO" => "EXP-2025-201030"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
private function getOwner(): array
|
private function getOwner(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -267,6 +457,4 @@ private function getOwner(): array
|
|||||||
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,7 @@ public function authorize(): bool
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'vehicle_id' => 'required|integer|exists:vehicle,id',
|
'record_id' => 'required|integer|exists:records,id',
|
||||||
'tag_id' => 'required|integer|exists:tags,id',
|
|
||||||
'cancellation_reason' => 'required|in:fallo_lectura_handheld,cambio_parabrisas,roto_al_pegarlo,extravio,otro',
|
'cancellation_reason' => 'required|in:fallo_lectura_handheld,cambio_parabrisas,roto_al_pegarlo,extravio,otro',
|
||||||
'cancellation_observations' => 'nullable|string|max:1000',
|
'cancellation_observations' => 'nullable|string|max:1000',
|
||||||
];
|
];
|
||||||
@ -33,13 +32,9 @@ public function rules(): array
|
|||||||
public function messages(): array
|
public function messages(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'vehicle_id.required' => 'El id del vehículo es obligatorio.',
|
'record_id.required' => 'El id del expediente es obligatorio.',
|
||||||
'vehicle_id.integer' => 'El id del vehículo debe ser un número entero.',
|
'record_id.integer' => 'El id del expediente debe ser un número entero.',
|
||||||
'vehicle_id.exists' => 'El vehículo especificado no existe.',
|
'record_id.exists' => 'El expediente 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.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_reason.in' => 'El motivo de cancelación no es válido. Opciones: fallo_lectura_handheld, cambio_parabrisas, roto_al_pegarlo, extravio, otro.',
|
||||||
@ -55,8 +50,7 @@ public function messages(): array
|
|||||||
public function attributes(): array
|
public function attributes(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'vehicle_id' => 'id del vehículo',
|
'record_id' => 'id del expediente',
|
||||||
'tag_id' => 'id del tag',
|
|
||||||
'cancellation_reason' => 'motivo de cancelación',
|
'cancellation_reason' => 'motivo de cancelación',
|
||||||
'cancellation_observations' => 'observaciones',
|
'cancellation_observations' => 'observaciones',
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
<?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',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
50
app/Http/Requests/Repuve/RecordSearchRequest.php
Normal file
50
app/Http/Requests/Repuve/RecordSearchRequest.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php namespace App\Http\Requests\Repuve;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class RecordSearchRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'folio' => ['nullable', 'string', 'max:50'],
|
||||||
|
'niv' => ['nullable', 'string', 'max:50'],
|
||||||
|
'numero_serie' => ['nullable', 'string', 'max:50'],
|
||||||
|
'fecha_desde' => ['nullable', 'date', 'date_format:Y-m-d'],
|
||||||
|
'fecha_hasta' => ['nullable', 'date', 'date_format:Y-m-d', 'after_or_equal:fecha_desde'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'folio.string' => 'El folio debe ser una cadena de texto',
|
||||||
|
'niv.string' => 'El NIV debe ser una cadena de texto',
|
||||||
|
'numero_serie.string' => 'El número de serie debe ser una cadena de texto',
|
||||||
|
'fecha_desde.date' => 'La fecha desde debe ser una fecha válida',
|
||||||
|
'fecha_desde.date_format' => 'La fecha desde debe tener el formato Y-m-d',
|
||||||
|
'fecha_hasta.date' => 'La fecha hasta debe ser una fecha válida',
|
||||||
|
'fecha_hasta.after_or_equal' => 'La fecha hasta debe ser posterior o igual a la fecha desde',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withValidator($validator)
|
||||||
|
{
|
||||||
|
$validator->after(function ($validator) {
|
||||||
|
if (!$this->filled('folio') &&
|
||||||
|
!$this->filled('niv') &&
|
||||||
|
!$this->filled('numero_serie') &&
|
||||||
|
!$this->filled('fecha_desde')) {
|
||||||
|
$validator->errors()->add(
|
||||||
|
'search',
|
||||||
|
'Debe proporcionar al menos un criterio de búsqueda (folio, niv o fecha_desde)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,8 +16,8 @@ public function rules(): array
|
|||||||
'folio' => ['required', 'string', 'max:50'],
|
'folio' => ['required', 'string', 'max:50'],
|
||||||
'files' => ['nullable', 'array', 'min:1'],
|
'files' => ['nullable', 'array', 'min:1'],
|
||||||
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
||||||
'file_names' => ['nullable', 'array'],
|
'names' => ['nullable', 'array'],
|
||||||
'file_names.*' => ['string', 'max:255'],
|
'names.*' => ['string', 'max:255'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
app/Http/Requests/Repuve/VehicleUpdateRequest.php
Normal file
24
app/Http/Requests/Repuve/VehicleUpdateRequest.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php namespace App\Http\Requests\Repuve;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class VehicleUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'folio' => ['sometimes', 'string', 'max:50'],
|
||||||
|
'files' => ['nullable', 'array', 'min:1'],
|
||||||
|
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
||||||
|
'names' => ['nullable', 'array'],
|
||||||
|
'names.*' => ['string', 'max:255'],
|
||||||
|
'replace_files' => ['nullable', 'boolean'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,4 +23,23 @@ protected function casts(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function modules()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Module::class, 'device_module')
|
||||||
|
->withPivot('status')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deviceModules()
|
||||||
|
{
|
||||||
|
return $this->hasMany(DeviceModule::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activeModules()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Module::class, 'device_module')
|
||||||
|
->wherePivot('status', true)
|
||||||
|
->withPivot('status')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,4 +23,14 @@ protected function casts(): array
|
|||||||
'status' => 'boolean',
|
'status' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function device()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Device::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function module()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Module::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,4 +14,8 @@ class Error extends Model
|
|||||||
'description',
|
'description',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function records()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Record::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,4 +28,28 @@ protected function casts(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function devices()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Device::class, 'device_module')
|
||||||
|
->withPivot('status')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deviceModules()
|
||||||
|
{
|
||||||
|
return $this->hasMany(DeviceModule::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activeDevices()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Device::class, 'device_module')
|
||||||
|
->wherePivot('status', true)
|
||||||
|
->withPivot('status')
|
||||||
|
->withTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function packages()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Package::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,4 +29,9 @@ protected function fullName(): Attribute
|
|||||||
get: fn() => trim("{$this->name} {$this->paternal} {$this->maternal}")
|
get: fn() => trim("{$this->name} {$this->paternal} {$this->maternal}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function vehicles()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Vehicle::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,4 +25,13 @@ protected function casts(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function module()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Module::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tags()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Tag::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,4 +30,9 @@ public function files()
|
|||||||
{
|
{
|
||||||
return $this->hasMany(File::class);
|
return $this->hasMany(File::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function error()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Error::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,4 +15,22 @@ class ScanHistory extends Model
|
|||||||
'user_id',
|
'user_id',
|
||||||
'tag_id',
|
'tag_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relación con User
|
||||||
|
* Un escaneo pertenece a un usuario
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relación con Tag
|
||||||
|
* Un escaneo pertenece a una etiqueta
|
||||||
|
*/
|
||||||
|
public function tag()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Tag::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,4 +15,24 @@ class Tag extends Model
|
|||||||
'package_id',
|
'package_id',
|
||||||
'status',
|
'status',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function vehicle()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Vehicle::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function package()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Package::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function vehicleTagLogs()
|
||||||
|
{
|
||||||
|
return $this->hasMany(VehicleTagLog::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scanHistories()
|
||||||
|
{
|
||||||
|
return $this->hasMany(ScanHistory::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,11 @@ class Vehicle extends Model
|
|||||||
'placa',
|
'placa',
|
||||||
'numero_serie',
|
'numero_serie',
|
||||||
'rfc',
|
'rfc',
|
||||||
|
'folio',
|
||||||
'vigencia',
|
'vigencia',
|
||||||
'fecha_impresion',
|
'fecha_impresion',
|
||||||
'qr_hash',
|
'qr_hash',
|
||||||
'valido',
|
'valido',
|
||||||
'foliotemp',
|
|
||||||
'nombre',
|
'nombre',
|
||||||
'nombre2',
|
'nombre2',
|
||||||
'municipio',
|
'municipio',
|
||||||
@ -48,4 +48,18 @@ public function owner()
|
|||||||
return $this->belongsTo(Owner::class);
|
return $this->belongsTo(Owner::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function records()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Record::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tag()
|
||||||
|
{
|
||||||
|
return $this->hasOne(Tag::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function vehicleTagLogs()
|
||||||
|
{
|
||||||
|
return $this->hasMany(VehicleTagLog::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
"laravel/pulse": "^1.4",
|
"laravel/pulse": "^1.4",
|
||||||
"laravel/reverb": "^1.4",
|
"laravel/reverb": "^1.4",
|
||||||
"laravel/tinker": "^2.10",
|
"laravel/tinker": "^2.10",
|
||||||
|
"milon/barcode": "^12.0",
|
||||||
"notsoweb/laravel-core": "dev-main",
|
"notsoweb/laravel-core": "dev-main",
|
||||||
"spatie/laravel-permission": "^6.16",
|
"spatie/laravel-permission": "^6.16",
|
||||||
"tightenco/ziggy": "^2.5"
|
"tightenco/ziggy": "^2.5"
|
||||||
|
|||||||
77
composer.lock
generated
77
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "28203b03d474b52340b6e7941df2e111",
|
"content-hash": "2e95bbdb182abae36557342b05654934",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "barryvdh/laravel-dompdf",
|
"name": "barryvdh/laravel-dompdf",
|
||||||
@ -3270,6 +3270,81 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-07-25T09:04:22+00:00"
|
"time": "2025-07-25T09:04:22+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "milon/barcode",
|
||||||
|
"version": "v12.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/milon/barcode.git",
|
||||||
|
"reference": "252dc9a530c72454bc6cefb8d274c2acaba24f15"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/milon/barcode/zipball/252dc9a530c72454bc6cefb8d274c2acaba24f15",
|
||||||
|
"reference": "252dc9a530c72454bc6cefb8d274c2acaba24f15",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-gd": "*",
|
||||||
|
"illuminate/support": "^7.0|^8.0|^9.0|^10.0 | ^11.0 | ^12.0",
|
||||||
|
"php": "^7.3 | ^8.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"aliases": {
|
||||||
|
"DNS1D": "Milon\\Barcode\\Facades\\DNS1DFacade",
|
||||||
|
"DNS2D": "Milon\\Barcode\\Facades\\DNS2DFacade"
|
||||||
|
},
|
||||||
|
"providers": [
|
||||||
|
"Milon\\Barcode\\BarcodeServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Milon\\Barcode": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-3.0"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nuruzzaman Milon",
|
||||||
|
"email": "contact@milon.im"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Barcode generator like Qr Code, PDF417, C39, C39+, C39E, C39E+, C93, S25, S25+, I25, I25+, C128, C128A, C128B, C128C, 2-Digits UPC-Based Extention, 5-Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI (Variation of Plessey code)",
|
||||||
|
"keywords": [
|
||||||
|
"CODABAR",
|
||||||
|
"CODE 128",
|
||||||
|
"CODE 39",
|
||||||
|
"barcode",
|
||||||
|
"datamatrix",
|
||||||
|
"ean",
|
||||||
|
"laravel",
|
||||||
|
"pdf417",
|
||||||
|
"qr code",
|
||||||
|
"qrcode"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/milon/barcode/issues",
|
||||||
|
"source": "https://github.com/milon/barcode/tree/v12.0.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://paypal.me/nuruzzamanmilon",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/milon",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-02-24T18:09:25+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.9.0",
|
"version": "3.9.0",
|
||||||
|
|||||||
64
database/factories/ErrorFactory.php
Normal file
64
database/factories/ErrorFactory.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Error;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Error>
|
||||||
|
*/
|
||||||
|
class ErrorFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Error::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$errorTypes = [
|
||||||
|
['code' => 'E001', 'description' => 'Vehículo reportado como robado'],
|
||||||
|
['code' => 'E002', 'description' => 'Número de serie no válido o no coincide'],
|
||||||
|
['code' => 'E003', 'description' => 'Documentos incompletos o ilegibles'],
|
||||||
|
['code' => 'E004', 'description' => 'Propietario no coincide con documentos'],
|
||||||
|
['code' => 'E005', 'description' => 'Placas no corresponden al vehículo'],
|
||||||
|
['code' => 'E006', 'description' => 'Vehículo presenta adulteración'],
|
||||||
|
['code' => 'E007', 'description' => 'RFC o CURP inválido'],
|
||||||
|
['code' => 'E008', 'description' => 'Factura apócrifa o alterada'],
|
||||||
|
['code' => 'E009', 'description' => 'Vehículo importado sin documentación legal'],
|
||||||
|
['code' => 'E010', 'description' => 'Error en sistema REPUVE externo'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$error = fake()->randomElement($errorTypes);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'code' => $error['code'] . '-' . fake()->unique()->numberBetween(1000, 9999),
|
||||||
|
'description' => $error['description'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State for stolen vehicle error
|
||||||
|
*/
|
||||||
|
public function stolen(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'code' => 'E001-' . fake()->unique()->numberBetween(1000, 9999),
|
||||||
|
'description' => 'Vehículo reportado como robado',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State for invalid VIN error
|
||||||
|
*/
|
||||||
|
public function invalidVin(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'code' => 'E002-' . fake()->unique()->numberBetween(1000, 9999),
|
||||||
|
'description' => 'Número de serie no válido o no coincide',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
database/factories/FileFactory.php
Normal file
128
database/factories/FileFactory.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\File;
|
||||||
|
use App\Models\Record;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\File>
|
||||||
|
*/
|
||||||
|
class FileFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = File::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$fileTypes = [
|
||||||
|
['name' => 'Foto frontal del vehículo', 'ext' => 'jpg'],
|
||||||
|
['name' => 'Foto lateral derecha del vehículo', 'ext' => 'jpg'],
|
||||||
|
['name' => 'Foto lateral izquierda del vehículo', 'ext' => 'jpg'],
|
||||||
|
['name' => 'Foto trasera del vehículo', 'ext' => 'jpg'],
|
||||||
|
['name' => 'Foto del NIV/VIN', 'ext' => 'jpg'],
|
||||||
|
['name' => 'Tarjeta de circulación', 'ext' => 'pdf'],
|
||||||
|
['name' => 'Factura original', 'ext' => 'pdf'],
|
||||||
|
['name' => 'Comprobante de domicilio', 'ext' => 'pdf'],
|
||||||
|
['name' => 'Identificación oficial del propietario', 'ext' => 'pdf'],
|
||||||
|
['name' => 'Constancia de inscripción', 'ext' => 'pdf'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$fileType = fake()->randomElement($fileTypes);
|
||||||
|
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
|
||||||
|
$fileName = $timestamp . '.' . $fileType['ext'];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $fileType['name'],
|
||||||
|
'path' => 'records/' . $fileName,
|
||||||
|
'md5' => md5(fake()->uuid()),
|
||||||
|
'record_id' => Record::factory(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the file is an image
|
||||||
|
*/
|
||||||
|
public function image(): static
|
||||||
|
{
|
||||||
|
$imageTypes = [
|
||||||
|
'Foto frontal del vehículo',
|
||||||
|
'Foto lateral derecha del vehículo',
|
||||||
|
'Foto lateral izquierda del vehículo',
|
||||||
|
'Foto trasera del vehículo',
|
||||||
|
'Foto del NIV/VIN',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->state(function (array $attributes) use ($imageTypes) {
|
||||||
|
$name = fake()->randomElement($imageTypes);
|
||||||
|
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $name,
|
||||||
|
'path' => 'records/' . $timestamp . '.jpg',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the file is a PDF document
|
||||||
|
*/
|
||||||
|
public function pdf(): static
|
||||||
|
{
|
||||||
|
$pdfTypes = [
|
||||||
|
'Tarjeta de circulación',
|
||||||
|
'Factura original',
|
||||||
|
'Comprobante de domicilio',
|
||||||
|
'Identificación oficial del propietario',
|
||||||
|
'Constancia de inscripción',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->state(function (array $attributes) use ($pdfTypes) {
|
||||||
|
$name = fake()->randomElement($pdfTypes);
|
||||||
|
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $name,
|
||||||
|
'path' => 'records/' . $timestamp . '.pdf',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the file belongs to a specific record
|
||||||
|
*/
|
||||||
|
public function forRecord(int $recordId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'record_id' => $recordId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a vehicle photo file
|
||||||
|
*/
|
||||||
|
public function vehiclePhoto(): static
|
||||||
|
{
|
||||||
|
$photoTypes = [
|
||||||
|
'Foto frontal del vehículo',
|
||||||
|
'Foto lateral derecha del vehículo',
|
||||||
|
'Foto lateral izquierda del vehículo',
|
||||||
|
'Foto trasera del vehículo',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->state(function (array $attributes) use ($photoTypes) {
|
||||||
|
$name = fake()->randomElement($photoTypes);
|
||||||
|
$timestamp = now()->timestamp . '_' . fake()->numberBetween(1000, 9999);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => $name,
|
||||||
|
'path' => 'records/' . $timestamp . '.jpg',
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
76
database/factories/ModuleFactory.php
Normal file
76
database/factories/ModuleFactory.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Module;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Module>
|
||||||
|
*/
|
||||||
|
class ModuleFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Module::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$municipalities = [
|
||||||
|
'Centro',
|
||||||
|
'Cárdenas',
|
||||||
|
'Comalcalco',
|
||||||
|
'Cunduacán',
|
||||||
|
'Huimanguillo',
|
||||||
|
'Macuspana',
|
||||||
|
'Paraíso',
|
||||||
|
'Tacotalpa',
|
||||||
|
'Teapa',
|
||||||
|
'Tenosique',
|
||||||
|
];
|
||||||
|
|
||||||
|
$colonies = [
|
||||||
|
'Centro',
|
||||||
|
'Tierra Colorada',
|
||||||
|
'Atasta de Serra',
|
||||||
|
'José Colomo',
|
||||||
|
'La Manga',
|
||||||
|
'Tamulté de las Barrancas',
|
||||||
|
'Gaviotas Norte',
|
||||||
|
'Carrizal',
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => fake()->company() . ' - ' . fake()->randomElement($municipalities),
|
||||||
|
'municipality' => fake()->randomElement($municipalities),
|
||||||
|
'address' => fake()->streetAddress(),
|
||||||
|
'colony' => fake()->randomElement($colonies),
|
||||||
|
'longitude' => fake()->longitude(-93.5, -92.5), // Tabasco longitude range
|
||||||
|
'latitude' => fake()->latitude(17.5, 18.5), // Tabasco latitude range
|
||||||
|
'status' => fake()->boolean(90), // 90% activos
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the module is inactive.
|
||||||
|
*/
|
||||||
|
public function inactive(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'status' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the module is in Centro municipality.
|
||||||
|
*/
|
||||||
|
public function centro(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'municipality' => 'Centro',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
116
database/factories/OwnerFactory.php
Normal file
116
database/factories/OwnerFactory.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Owner;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Owner>
|
||||||
|
*/
|
||||||
|
class OwnerFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Owner::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$name = fake()->firstName();
|
||||||
|
$paternal = fake()->lastName();
|
||||||
|
$maternal = fake()->lastName();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => strtoupper($name),
|
||||||
|
'paternal' => strtoupper($paternal),
|
||||||
|
'maternal' => strtoupper($maternal),
|
||||||
|
'rfc' => $this->generateRFC($paternal, $maternal, $name),
|
||||||
|
'curp' => $this->generateCURP($paternal, $maternal, $name),
|
||||||
|
'address' => strtoupper(fake()->address()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a realistic RFC (13 characters)
|
||||||
|
*/
|
||||||
|
private function generateRFC(string $paternal, string $maternal, string $name): string
|
||||||
|
{
|
||||||
|
$firstPaternal = substr($paternal, 0, 1);
|
||||||
|
$firstVowelPaternal = $this->getFirstVowel(substr($paternal, 1));
|
||||||
|
$firstMaternal = substr($maternal, 0, 1);
|
||||||
|
$firstName = substr($name, 0, 1);
|
||||||
|
|
||||||
|
$year = fake()->numberBetween(50, 99);
|
||||||
|
$month = str_pad(fake()->numberBetween(1, 12), 2, '0', STR_PAD_LEFT);
|
||||||
|
$day = str_pad(fake()->numberBetween(1, 28), 2, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
$homoclave = strtoupper(fake()->bothify('???'));
|
||||||
|
|
||||||
|
return strtoupper($firstPaternal . $firstVowelPaternal . $firstMaternal . $firstName . $year . $month . $day . $homoclave);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a realistic CURP (18 characters)
|
||||||
|
*/
|
||||||
|
private function generateCURP(string $paternal, string $maternal, string $name): string
|
||||||
|
{
|
||||||
|
$firstPaternal = substr($paternal, 0, 1);
|
||||||
|
$firstVowelPaternal = $this->getFirstVowel(substr($paternal, 1));
|
||||||
|
$firstMaternal = substr($maternal, 0, 1);
|
||||||
|
$firstName = substr($name, 0, 1);
|
||||||
|
|
||||||
|
$year = str_pad(fake()->numberBetween(50, 99), 2, '0', STR_PAD_LEFT);
|
||||||
|
$month = str_pad(fake()->numberBetween(1, 12), 2, '0', STR_PAD_LEFT);
|
||||||
|
$day = str_pad(fake()->numberBetween(1, 28), 2, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
$gender = fake()->randomElement(['H', 'M']);
|
||||||
|
$state = 'TC'; // Tabasco
|
||||||
|
|
||||||
|
$consonants = strtoupper(
|
||||||
|
$this->getFirstConsonant(substr($paternal, 1)) .
|
||||||
|
$this->getFirstConsonant(substr($maternal, 1)) .
|
||||||
|
$this->getFirstConsonant(substr($name, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
$homoclave = strtoupper(fake()->bothify('??'));
|
||||||
|
|
||||||
|
return strtoupper($firstPaternal . $firstVowelPaternal . $firstMaternal . $firstName . $year . $month . $day . $gender . $state . $consonants . $homoclave);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get first vowel from string
|
||||||
|
*/
|
||||||
|
private function getFirstVowel(string $str): string
|
||||||
|
{
|
||||||
|
$vowels = ['A', 'E', 'I', 'O', 'U'];
|
||||||
|
$str = strtoupper($str);
|
||||||
|
|
||||||
|
for ($i = 0; $i < strlen($str); $i++) {
|
||||||
|
if (in_array($str[$i], $vowels)) {
|
||||||
|
return $str[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'X';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get first consonant from string
|
||||||
|
*/
|
||||||
|
private function getFirstConsonant(string $str): string
|
||||||
|
{
|
||||||
|
$vowels = ['A', 'E', 'I', 'O', 'U'];
|
||||||
|
$str = strtoupper($str);
|
||||||
|
|
||||||
|
for ($i = 0; $i < strlen($str); $i++) {
|
||||||
|
if (!in_array($str[$i], $vowels) && ctype_alpha($str[$i])) {
|
||||||
|
return $str[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'X';
|
||||||
|
}
|
||||||
|
}
|
||||||
76
database/factories/PackageFactory.php
Normal file
76
database/factories/PackageFactory.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Package;
|
||||||
|
use App\Models\Module;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Package>
|
||||||
|
*/
|
||||||
|
class PackageFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Package::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$year = fake()->numberBetween(2020, 2025);
|
||||||
|
$lot = 'LOTE-' . $year . '-' . fake()->numberBetween(1, 99);
|
||||||
|
$boxNumber = 'CAJA-' . strtoupper(fake()->bothify('??##'));
|
||||||
|
|
||||||
|
$startingPage = fake()->numberBetween(1, 100) * 100;
|
||||||
|
$endingPage = $startingPage + fake()->numberBetween(50, 200);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'lot' => $lot,
|
||||||
|
'box_number' => $boxNumber,
|
||||||
|
'starting_page' => $startingPage,
|
||||||
|
'ending_page' => $endingPage,
|
||||||
|
'module_id' => Module::factory(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the package belongs to a specific module.
|
||||||
|
*/
|
||||||
|
public function forModule(int $moduleId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'module_id' => $moduleId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a small package (50-100 pages)
|
||||||
|
*/
|
||||||
|
public function small(): static
|
||||||
|
{
|
||||||
|
return $this->state(function (array $attributes) {
|
||||||
|
$startingPage = fake()->numberBetween(1, 50) * 100;
|
||||||
|
return [
|
||||||
|
'starting_page' => $startingPage,
|
||||||
|
'ending_page' => $startingPage + fake()->numberBetween(50, 100),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a large package (200-500 pages)
|
||||||
|
*/
|
||||||
|
public function large(): static
|
||||||
|
{
|
||||||
|
return $this->state(function (array $attributes) {
|
||||||
|
$startingPage = fake()->numberBetween(1, 100) * 100;
|
||||||
|
return [
|
||||||
|
'starting_page' => $startingPage,
|
||||||
|
'ending_page' => $startingPage + fake()->numberBetween(200, 500),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
93
database/factories/RecordFactory.php
Normal file
93
database/factories/RecordFactory.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Record;
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Error;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Record>
|
||||||
|
*/
|
||||||
|
class RecordFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Record::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'folio' => $this->generateFolio(),
|
||||||
|
'vehicle_id' => Vehicle::factory(),
|
||||||
|
'user_id' => User::inRandomOrder()->first()?->id ?? User::factory(),
|
||||||
|
'error_id' => fake()->boolean(10) ? Error::factory() : null, // 10% con error
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique record folio
|
||||||
|
*/
|
||||||
|
private function generateFolio(): string
|
||||||
|
{
|
||||||
|
$year = now()->year;
|
||||||
|
$number = fake()->unique()->numerify('######');
|
||||||
|
|
||||||
|
return 'EXP-' . $year . '-' . $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the record has an error
|
||||||
|
*/
|
||||||
|
public function withError(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'error_id' => Error::factory(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the record has no error
|
||||||
|
*/
|
||||||
|
public function withoutError(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'error_id' => null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the record belongs to a specific vehicle
|
||||||
|
*/
|
||||||
|
public function forVehicle(int $vehicleId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => $vehicleId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the record belongs to a specific user
|
||||||
|
*/
|
||||||
|
public function forUser(int $userId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'user_id' => $userId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the record has a specific error
|
||||||
|
*/
|
||||||
|
public function withSpecificError(int $errorId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'error_id' => $errorId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
database/factories/TagFactory.php
Normal file
110
database/factories/TagFactory.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Tag;
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use App\Models\Package;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Tag>
|
||||||
|
*/
|
||||||
|
class TagFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Tag::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$statuses = ['available', 'assigned', 'cancelled', 'lost'];
|
||||||
|
$weights = [40, 50, 7, 3]; // Probabilidades: 40% available, 50% assigned, 7% cancelled, 3% lost
|
||||||
|
|
||||||
|
return [
|
||||||
|
'folio' => $this->generateFolio(),
|
||||||
|
'vehicle_id' => fake()->boolean(60) ? Vehicle::factory() : null, // 60% asignados
|
||||||
|
'package_id' => Package::factory(),
|
||||||
|
'status' => fake()->randomElement(array_combine($statuses, $weights)) ?? 'available',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique tag folio
|
||||||
|
*/
|
||||||
|
private function generateFolio(): string
|
||||||
|
{
|
||||||
|
$year = fake()->numberBetween(2020, 2025);
|
||||||
|
$series = strtoupper(fake()->bothify('??'));
|
||||||
|
$number = fake()->unique()->numerify('########');
|
||||||
|
|
||||||
|
return 'TAG-' . $year . '-' . $series . '-' . $number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is available (not assigned)
|
||||||
|
*/
|
||||||
|
public function available(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => null,
|
||||||
|
'status' => 'available',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is assigned to a vehicle
|
||||||
|
*/
|
||||||
|
public function assigned(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => Vehicle::factory(),
|
||||||
|
'status' => 'assigned',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is cancelled
|
||||||
|
*/
|
||||||
|
public function cancelled(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'status' => 'cancelled',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is lost
|
||||||
|
*/
|
||||||
|
public function lost(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => null,
|
||||||
|
'status' => 'lost',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag belongs to a specific package
|
||||||
|
*/
|
||||||
|
public function forPackage(int $packageId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'package_id' => $packageId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the tag is assigned to a specific vehicle
|
||||||
|
*/
|
||||||
|
public function forVehicle(int $vehicleId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'vehicle_id' => $vehicleId,
|
||||||
|
'status' => 'assigned',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
database/factories/VehicleFactory.php
Normal file
128
database/factories/VehicleFactory.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use App\Models\Owner;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Vehicle>
|
||||||
|
*/
|
||||||
|
class VehicleFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Vehicle::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
$brands = [
|
||||||
|
'CHEVROLET G.M.C.',
|
||||||
|
'NISSAN MEXICANA',
|
||||||
|
'VOLKSWAGEN',
|
||||||
|
'FORD MOTOR COMPANY',
|
||||||
|
'TOYOTA',
|
||||||
|
'HONDA',
|
||||||
|
'MAZDA',
|
||||||
|
'KIA MOTORS',
|
||||||
|
'HYUNDAI',
|
||||||
|
];
|
||||||
|
|
||||||
|
$types = ['SEDAN', 'SUV', 'PICKUP', 'HATCHBACK', 'VAN'];
|
||||||
|
$colors = ['BLANCO', 'NEGRO', 'GRIS', 'ROJO', 'AZUL', 'PLATA'];
|
||||||
|
$municipalities = ['CENTRO', 'CÁRDENAS', 'COMALCALCO', 'CUNDUACÁN', 'HUIMANGUILLO'];
|
||||||
|
|
||||||
|
$year = fake()->numberBetween(2015, 2025);
|
||||||
|
$vin = $this->generateVIN();
|
||||||
|
$placa = $this->generatePlaca();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'anio_placa' => (string) $year,
|
||||||
|
'placa' => $placa,
|
||||||
|
'numero_serie' => $vin,
|
||||||
|
'rfc' => 'GME' . fake()->numerify('######') . 'GJA',
|
||||||
|
'folio' => fake()->unique()->numerify('#######'),
|
||||||
|
'vigencia' => (string) ($year + 1),
|
||||||
|
'fecha_impresion' => fake()->date('d-m-Y'),
|
||||||
|
'qr_hash' => fake()->sha256(),
|
||||||
|
'valido' => fake()->boolean(95), // 95% válidos
|
||||||
|
'nombre' => strtoupper(fake()->company()),
|
||||||
|
'nombre2' => strtoupper(fake()->lexify('????*????')),
|
||||||
|
'municipio' => fake()->randomElement($municipalities),
|
||||||
|
'localidad' => 'VILLAHERMOSA',
|
||||||
|
'calle' => strtoupper(fake()->streetAddress()),
|
||||||
|
'calle2' => strtoupper(fake()->lexify('? ??*????')),
|
||||||
|
'tipo' => fake()->randomElement($types),
|
||||||
|
'tipo_servicio' => fake()->randomElement(['PARTICULAR', 'PUBLICO', 'OFICIAL']),
|
||||||
|
'marca' => fake()->randomElement($brands),
|
||||||
|
'linea' => strtoupper(fake()->word()),
|
||||||
|
'sublinea' => 'PAQ. "' . strtoupper(fake()->randomLetter()) . '" ' . strtoupper(fake()->word()),
|
||||||
|
'modelo' => $year,
|
||||||
|
'numero_motor' => strtoupper(fake()->bothify('??####??##')),
|
||||||
|
'descripcion_origen' => fake()->randomElement(['NACIONAL', 'IMPORTADO']),
|
||||||
|
'color' => fake()->randomElement($colors),
|
||||||
|
'codigo_postal' => fake()->numerify('86###'),
|
||||||
|
'serie_folio' => 'D' . fake()->unique()->numerify('#######'),
|
||||||
|
'sfolio' => fake()->unique()->numerify('#######'),
|
||||||
|
'nrpv' => $vin,
|
||||||
|
'owner_id' => Owner::factory(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a realistic VIN (17 characters)
|
||||||
|
*/
|
||||||
|
private function generateVIN(): string
|
||||||
|
{
|
||||||
|
$wmi = strtoupper(fake()->bothify('???')); // World Manufacturer Identifier
|
||||||
|
$vds = strtoupper(fake()->bothify('??????')); // Vehicle Descriptor Section
|
||||||
|
$check = fake()->randomDigit();
|
||||||
|
$year = strtoupper(fake()->randomLetter());
|
||||||
|
$plant = fake()->randomDigit();
|
||||||
|
$serial = fake()->numerify('######');
|
||||||
|
|
||||||
|
return $wmi . $vds . $check . $year . $plant . $serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a Tabasco plate
|
||||||
|
*/
|
||||||
|
private function generatePlaca(): string
|
||||||
|
{
|
||||||
|
return strtoupper(fake()->bothify('???###?'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the vehicle belongs to a specific owner.
|
||||||
|
*/
|
||||||
|
public function forOwner(int $ownerId): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'owner_id' => $ownerId,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an imported vehicle
|
||||||
|
*/
|
||||||
|
public function imported(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'descripcion_origen' => 'IMPORTADO',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a national vehicle
|
||||||
|
*/
|
||||||
|
public function national(): static
|
||||||
|
{
|
||||||
|
return $this->state(fn (array $attributes) => [
|
||||||
|
'descripcion_origen' => 'NACIONAL',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ public function up(): void
|
|||||||
$table->string('placa')->unique()->nullable();
|
$table->string('placa')->unique()->nullable();
|
||||||
$table->string('numero_serie')->unique()->nullable();
|
$table->string('numero_serie')->unique()->nullable();
|
||||||
$table->string('rfc')->nullable();
|
$table->string('rfc')->nullable();
|
||||||
|
$table->string('folio')->nullable();
|
||||||
$table->string('vigencia')->nullable();
|
$table->string('vigencia')->nullable();
|
||||||
$table->string('fecha_impresion')->nullable();
|
$table->string('fecha_impresion')->nullable();
|
||||||
$table->string('qr_hash')->nullable();
|
$table->string('qr_hash')->nullable();
|
||||||
|
|||||||
@ -14,7 +14,7 @@ public function up(): void
|
|||||||
Schema::create('tags', function (Blueprint $table) {
|
Schema::create('tags', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('folio')->unique();
|
$table->string('folio')->unique();
|
||||||
$table->foreignId('vehicle_id')->nullable()->constrained('vehicle')->nullOnDelete();
|
$table->foreignId('vehicle_id')->nullable()->unique()->constrained('vehicle')->nullOnDelete();
|
||||||
$table->foreignId('package_id')->nullable()->constrained('packages')->nullOnDelete();
|
$table->foreignId('package_id')->nullable()->constrained('packages')->nullOnDelete();
|
||||||
$table->enum('status', ['available', 'assigned', 'cancelled', 'lost'])->default('available');
|
$table->enum('status', ['available', 'assigned', 'cancelled', 'lost'])->default('available');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|||||||
@ -13,7 +13,7 @@ public function up(): void
|
|||||||
{
|
{
|
||||||
Schema::create('records', function (Blueprint $table) {
|
Schema::create('records', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('folio');
|
$table->string('folio')->unique();
|
||||||
$table->foreignId('vehicle_id')->constrained('vehicle')->cascadeOnDelete();
|
$table->foreignId('vehicle_id')->constrained('vehicle')->cascadeOnDelete();
|
||||||
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
|
||||||
$table->foreignId('error_id')->nullable()->constrained('errors')->nullOnDelete();
|
$table->foreignId('error_id')->nullable()->constrained('errors')->nullOnDelete();
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Seeder Producción
|
* Seeder Producción
|
||||||
*
|
*
|
||||||
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
*
|
*
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
class DatabaseSeeder extends Seeder
|
class DatabaseSeeder extends Seeder
|
||||||
@ -19,6 +19,7 @@ class DatabaseSeeder extends Seeder
|
|||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
// Seeders de producción (siempre se ejecutan)
|
||||||
$this->call(RoleSeeder::class);
|
$this->call(RoleSeeder::class);
|
||||||
$this->call(UserSeeder::class);
|
$this->call(UserSeeder::class);
|
||||||
$this->call(SettingSeeder::class);
|
$this->call(SettingSeeder::class);
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Seeder de desarrollo
|
* Seeder de desarrollo
|
||||||
*
|
*
|
||||||
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
*
|
*
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
class DevSeeder extends Seeder
|
class DevSeeder extends Seeder
|
||||||
@ -22,5 +22,21 @@ public function run(): void
|
|||||||
$this->call(RoleSeeder::class);
|
$this->call(RoleSeeder::class);
|
||||||
$this->call(UserSeeder::class);
|
$this->call(UserSeeder::class);
|
||||||
$this->call(SettingSeeder::class);
|
$this->call(SettingSeeder::class);
|
||||||
|
|
||||||
|
// Nivel 1 - Sin dependencias
|
||||||
|
$this->call(ModuleSeeder::class);
|
||||||
|
$this->call(OwnerSeeder::class);
|
||||||
|
$this->call(ErrorSeeder::class);
|
||||||
|
|
||||||
|
// Nivel 2 - Dependen de Nivel 1
|
||||||
|
$this->call(PackageSeeder::class);
|
||||||
|
$this->call(VehicleSeeder::class);
|
||||||
|
|
||||||
|
// Nivel 3 - Dependen de Nivel 2
|
||||||
|
$this->call(TagSeeder::class);
|
||||||
|
$this->call(RecordSeeder::class);
|
||||||
|
|
||||||
|
// Nivel 4 - Dependen de Nivel 3
|
||||||
|
$this->call(FileSeeder::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
database/seeders/ErrorSeeder.php
Normal file
38
database/seeders/ErrorSeeder.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Error;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ErrorSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Catálogo de errores predefinidos
|
||||||
|
$errors = [
|
||||||
|
['code' => 'E001', 'description' => 'Vehículo reportado como robado'],
|
||||||
|
['code' => 'E002', 'description' => 'Número de serie (NIV/VIN) no válido o no coincide'],
|
||||||
|
['code' => 'E003', 'description' => 'Documentos incompletos o ilegibles'],
|
||||||
|
['code' => 'E004', 'description' => 'Datos del propietario no coinciden con documentos oficiales'],
|
||||||
|
['code' => 'E005', 'description' => 'Placas de circulación no corresponden al vehículo'],
|
||||||
|
['code' => 'E006', 'description' => 'Vehículo presenta evidencia de adulteración en NIV'],
|
||||||
|
['code' => 'E007', 'description' => 'RFC o CURP inválido o no coincide'],
|
||||||
|
['code' => 'E008', 'description' => 'Factura apócrifa o presenta alteraciones'],
|
||||||
|
['code' => 'E009', 'description' => 'Vehículo importado sin documentación legal'],
|
||||||
|
['code' => 'E010', 'description' => 'Error en consulta al sistema REPUVE externo'],
|
||||||
|
['code' => 'E011', 'description' => 'Vehículo tiene adeudos de tenencia o infracciones'],
|
||||||
|
['code' => 'E012', 'description' => 'Tarjeta de circulación no válida o vencida'],
|
||||||
|
['code' => 'E013', 'description' => 'Comprobante de domicilio no válido'],
|
||||||
|
['code' => 'E014', 'description' => 'Verificación física del vehículo no aprobada'],
|
||||||
|
['code' => 'E015', 'description' => 'Modelo y año del vehículo no coinciden'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($errors as $error) {
|
||||||
|
Error::create($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
database/seeders/FileSeeder.php
Normal file
50
database/seeders/FileSeeder.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\File;
|
||||||
|
use App\Models\Record;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class FileSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$records = Record::all();
|
||||||
|
|
||||||
|
$totalFiles = 0;
|
||||||
|
$imageCount = 0;
|
||||||
|
$pdfCount = 0;
|
||||||
|
|
||||||
|
// Crear archivos para cada expediente
|
||||||
|
foreach ($records as $record) {
|
||||||
|
// Cada expediente tiene entre 3-6 archivos
|
||||||
|
$filesPerRecord = rand(3, 6);
|
||||||
|
|
||||||
|
// Crear al menos 2 fotos del vehículo
|
||||||
|
$vehiclePhotos = rand(2, 4);
|
||||||
|
for ($i = 0; $i < $vehiclePhotos; $i++) {
|
||||||
|
File::factory()
|
||||||
|
->forRecord($record->id)
|
||||||
|
->vehiclePhoto()
|
||||||
|
->create();
|
||||||
|
$imageCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// El resto son PDFs (documentos)
|
||||||
|
$pdfFiles = $filesPerRecord - $vehiclePhotos;
|
||||||
|
for ($i = 0; $i < $pdfFiles; $i++) {
|
||||||
|
File::factory()
|
||||||
|
->forRecord($record->id)
|
||||||
|
->pdf()
|
||||||
|
->create();
|
||||||
|
$pdfCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalFiles += $filesPerRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
database/seeders/ModuleSeeder.php
Normal file
72
database/seeders/ModuleSeeder.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Module;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ModuleSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Crear módulos reales de Tabasco
|
||||||
|
$modules = [
|
||||||
|
[
|
||||||
|
'name' => 'Módulo Centro Villahermosa',
|
||||||
|
'municipality' => 'Centro',
|
||||||
|
'address' => 'Av. Paseo Tabasco 1203',
|
||||||
|
'colony' => 'Tabasco 2000',
|
||||||
|
'longitude' => -92.9376,
|
||||||
|
'latitude' => 17.9892,
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Módulo Cárdenas',
|
||||||
|
'municipality' => 'Cárdenas',
|
||||||
|
'address' => 'Calle Benito Juárez No. 305',
|
||||||
|
'colony' => 'Centro',
|
||||||
|
'longitude' => -93.3808,
|
||||||
|
'latitude' => 18.0011,
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Módulo Comalcalco',
|
||||||
|
'municipality' => 'Comalcalco',
|
||||||
|
'address' => 'Av. Gregorio Méndez Magaña',
|
||||||
|
'colony' => 'Centro',
|
||||||
|
'longitude' => -93.2042,
|
||||||
|
'latitude' => 18.2667,
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Módulo Cunduacán',
|
||||||
|
'municipality' => 'Cunduacán',
|
||||||
|
'address' => 'Calle Carlos Pellicer Cámara',
|
||||||
|
'colony' => 'Centro',
|
||||||
|
'longitude' => -93.1608,
|
||||||
|
'latitude' => 18.0667,
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Módulo Huimanguillo',
|
||||||
|
'municipality' => 'Huimanguillo',
|
||||||
|
'address' => 'Av. Constitución s/n',
|
||||||
|
'colony' => 'Centro',
|
||||||
|
'longitude' => -93.3908,
|
||||||
|
'latitude' => 17.8422,
|
||||||
|
'status' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
Module::create($module);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear módulos adicionales con factory para pruebas
|
||||||
|
Module::factory(5)->create();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
18
database/seeders/OwnerSeeder.php
Normal file
18
database/seeders/OwnerSeeder.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Owner;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class OwnerSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Crear 100 propietarios
|
||||||
|
Owner::factory(100)->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
database/seeders/PackageSeeder.php
Normal file
31
database/seeders/PackageSeeder.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Package;
|
||||||
|
use App\Models\Module;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class PackageSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$modules = Module::all();
|
||||||
|
|
||||||
|
$totalPackages = 0;
|
||||||
|
|
||||||
|
// Crear entre 5-10 paquetes por módulo
|
||||||
|
foreach ($modules as $module) {
|
||||||
|
$packagesCount = rand(5, 10);
|
||||||
|
|
||||||
|
Package::factory($packagesCount)
|
||||||
|
->forModule($module->id)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$totalPackages += $packagesCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
database/seeders/RecordSeeder.php
Normal file
55
database/seeders/RecordSeeder.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Record;
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Error;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class RecordSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$vehicles = Vehicle::all();
|
||||||
|
$users = User::all();
|
||||||
|
$errors = Error::all();
|
||||||
|
|
||||||
|
$recordsWithError = 0;
|
||||||
|
$recordsWithoutError = 0;
|
||||||
|
|
||||||
|
// Crear un expediente por cada vehículo (algunos vehículos pueden tener más de uno)
|
||||||
|
foreach ($vehicles as $vehicle) {
|
||||||
|
$recordsForVehicle = rand(1, 2); // 1 o 2 expedientes por vehículo
|
||||||
|
|
||||||
|
for ($i = 0; $i < $recordsForVehicle; $i++) {
|
||||||
|
$randomUser = $users->random();
|
||||||
|
|
||||||
|
// 10% de probabilidad de tener error
|
||||||
|
$hasError = rand(1, 100) <= 10;
|
||||||
|
|
||||||
|
if ($hasError && $errors->isNotEmpty()) {
|
||||||
|
$randomError = $errors->random();
|
||||||
|
Record::factory()
|
||||||
|
->forVehicle($vehicle->id)
|
||||||
|
->forUser($randomUser->id)
|
||||||
|
->withSpecificError($randomError->id)
|
||||||
|
->create();
|
||||||
|
$recordsWithError++;
|
||||||
|
} else {
|
||||||
|
Record::factory()
|
||||||
|
->forVehicle($vehicle->id)
|
||||||
|
->forUser($randomUser->id)
|
||||||
|
->withoutError()
|
||||||
|
->create();
|
||||||
|
$recordsWithoutError++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
93
database/seeders/TagSeeder.php
Normal file
93
database/seeders/TagSeeder.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Tag;
|
||||||
|
use App\Models\Package;
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class TagSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
|
||||||
|
$packages = Package::all();
|
||||||
|
$vehicles = Vehicle::all();
|
||||||
|
|
||||||
|
$totalTags = 0;
|
||||||
|
$statusCounts = [
|
||||||
|
'available' => 0,
|
||||||
|
'assigned' => 0,
|
||||||
|
'cancelled' => 0,
|
||||||
|
'lost' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Vehículos que ya tienen un tag asignado
|
||||||
|
$vehiclesWithTag = [];
|
||||||
|
|
||||||
|
// Crear tags para cada paquete
|
||||||
|
foreach ($packages as $package) {
|
||||||
|
// Cada paquete tiene entre 20-50 tags
|
||||||
|
$tagsPerPackage = rand(20, 50);
|
||||||
|
|
||||||
|
// 40% disponibles
|
||||||
|
$availableCount = (int)($tagsPerPackage * 0.4);
|
||||||
|
Tag::factory($availableCount)
|
||||||
|
->forPackage($package->id)
|
||||||
|
->available()
|
||||||
|
->create();
|
||||||
|
$statusCounts['available'] += $availableCount;
|
||||||
|
|
||||||
|
// 50% asignados (si hay vehículos disponibles sin tag)
|
||||||
|
$assignedCount = (int)($tagsPerPackage * 0.5);
|
||||||
|
if ($vehicles->isNotEmpty()) {
|
||||||
|
// Filtrar vehículos que NO tienen tag asignado
|
||||||
|
$availableVehicles = $vehicles->filter(function($vehicle) use ($vehiclesWithTag) {
|
||||||
|
return !in_array($vehicle->id, $vehiclesWithTag);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Limitar la cantidad de tags asignados a los vehículos disponibles
|
||||||
|
$actualAssignedCount = min($assignedCount, $availableVehicles->count());
|
||||||
|
|
||||||
|
for ($i = 0; $i < $actualAssignedCount; $i++) {
|
||||||
|
$randomVehicle = $availableVehicles->random();
|
||||||
|
Tag::factory()
|
||||||
|
->forPackage($package->id)
|
||||||
|
->forVehicle($randomVehicle->id)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
// Marcar el vehículo como que ya tiene tag
|
||||||
|
$vehiclesWithTag[] = $randomVehicle->id;
|
||||||
|
|
||||||
|
// Remover del pool de vehículos disponibles
|
||||||
|
$availableVehicles = $availableVehicles->reject(function($vehicle) use ($randomVehicle) {
|
||||||
|
return $vehicle->id === $randomVehicle->id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$statusCounts['assigned'] += $actualAssignedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7% cancelados
|
||||||
|
$cancelledCount = (int)($tagsPerPackage * 0.07);
|
||||||
|
Tag::factory($cancelledCount)
|
||||||
|
->forPackage($package->id)
|
||||||
|
->cancelled()
|
||||||
|
->create();
|
||||||
|
$statusCounts['cancelled'] += $cancelledCount;
|
||||||
|
|
||||||
|
// 3% perdidos
|
||||||
|
$lostCount = max(1, (int)($tagsPerPackage * 0.03));
|
||||||
|
Tag::factory($lostCount)
|
||||||
|
->forPackage($package->id)
|
||||||
|
->lost()
|
||||||
|
->create();
|
||||||
|
$statusCounts['lost'] += $lostCount;
|
||||||
|
|
||||||
|
$totalTags += $tagsPerPackage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
database/seeders/VehicleSeeder.php
Normal file
39
database/seeders/VehicleSeeder.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Vehicle;
|
||||||
|
use App\Models\Owner;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class VehicleSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$owners = Owner::all();
|
||||||
|
|
||||||
|
// Crear 200 vehículos distribuidos entre los owners
|
||||||
|
$vehiclesCount = 100;
|
||||||
|
$vehiclesCreated = 0;
|
||||||
|
|
||||||
|
foreach ($owners as $owner) {
|
||||||
|
// Cada owner puede tener entre 1-3 vehículos
|
||||||
|
$vehiclesForOwner = rand(1,2);
|
||||||
|
|
||||||
|
if ($vehiclesCreated >= $vehiclesCount) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$toCreate = min($vehiclesForOwner, $vehiclesCount - $vehiclesCreated);
|
||||||
|
|
||||||
|
Vehicle::factory($toCreate)
|
||||||
|
->forOwner($owner->id)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$vehiclesCreated += $toCreate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
181
resources/views/pdfs/constancia.blade.php
Normal file
181
resources/views/pdfs/constancia.blade.php
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Etiquetas Vehiculares</title>
|
||||||
|
<style>
|
||||||
|
@page {
|
||||||
|
margin: 0.5cm;
|
||||||
|
size: landscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 9pt;
|
||||||
|
color: #000;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 78%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-row {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-content {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.barcode-container {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
display: table-cell;
|
||||||
|
font-weight: bold;
|
||||||
|
width: 40%;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
display: table-cell;
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.owner-name {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 8pt;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
margin-top: 5px;
|
||||||
|
font-size: 7pt;
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.barcode-text {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- Etiqueta 1 - Código de Barras -->
|
||||||
|
<div class="label">
|
||||||
|
<div class="label-content">
|
||||||
|
<!-- Código de barras del NIV -->
|
||||||
|
<div class="barcode-container">
|
||||||
|
{!! DNS1D::getBarcodeHTML($record->vehicle->numero_serie, 'C128', 2, 60) !!}
|
||||||
|
<div class="barcode-text">
|
||||||
|
{{ $record->vehicle->numero_serie }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Información del vehículo -->
|
||||||
|
<div class="vehicle-info">
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label"></div>
|
||||||
|
<div class="info-value">{{ $record->vehicle->placa }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->marca) }}</div>
|
||||||
|
<div class="info-value"></div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->linea) }}</div>
|
||||||
|
<div class="info-value">{{ $record->vehicle->modelo }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->tipo) }}</div>
|
||||||
|
<div class="info-value"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propietario -->
|
||||||
|
<div class="owner-name">
|
||||||
|
{{ strtoupper($record->vehicle->owner->full_name) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dirección -->
|
||||||
|
<div class="address">
|
||||||
|
{{ strtoupper($record->vehicle->owner->address) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Etiqueta 2 - Código de Barras (duplicado) -->
|
||||||
|
<div class="label">
|
||||||
|
<div class="label-content">
|
||||||
|
<!-- Código de barras del NIV -->
|
||||||
|
<div class="barcode-container">
|
||||||
|
{!! DNS1D::getBarcodeHTML($record->vehicle->numero_serie, 'C128', 2, 60) !!}
|
||||||
|
<div class="barcode-text">
|
||||||
|
{{ $record->vehicle->numero_serie }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Información del vehículo -->
|
||||||
|
<div class="vehicle-info">
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label"></div>
|
||||||
|
<div class="info-value">{{ $record->vehicle->placa }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->marca) }}</div>
|
||||||
|
<div class="info-value"></div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->linea) }}</div>
|
||||||
|
<div class="info-value">{{ $record->vehicle->modelo }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-label">{{ strtoupper($record->vehicle->tipo) }}</div>
|
||||||
|
<div class="info-value"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Propietario -->
|
||||||
|
<div class="owner-name">
|
||||||
|
{{ strtoupper($record->vehicle->owner->full_name) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dirección -->
|
||||||
|
<div class="address">
|
||||||
|
{{ strtoupper($record->vehicle->owner->address) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
360
resources/views/pdfs/verification.blade.php
Normal file
360
resources/views/pdfs/verification.blade.php
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Hoja de Verificación Vehicular</title>
|
||||||
|
<style>
|
||||||
|
@page {
|
||||||
|
margin: 0.8cm;
|
||||||
|
size: landscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 8pt;
|
||||||
|
line-height: 1.0;
|
||||||
|
color: #000;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-info {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-cell {
|
||||||
|
display: table-cell;
|
||||||
|
width: 50%;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-section {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 180px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-line {
|
||||||
|
border-top: 1px solid #000;
|
||||||
|
margin: 60px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-text {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 9pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
background-color: #c00040;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px 5px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
background-color: #f5deb3;
|
||||||
|
padding: 6px 5px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
font-size: 8pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.white-bg {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.gray-bg {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-original td:nth-child(1),
|
||||||
|
.section-original td:nth-child(2) {
|
||||||
|
background-color: #f5deb3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.id-column {
|
||||||
|
width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc-column {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.obs-column {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<h1>HOJA DE VERIFICACION VEHICULAR</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Información del vehículo -->
|
||||||
|
<div class="vehicle-info">
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Placa:</span> {{ $record->vehicle->placa }}
|
||||||
|
</div>
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">NIV:</span> {{ $record->vehicle->numero_serie }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Marca:</span> {{ strtoupper($record->vehicle->marca) }}
|
||||||
|
</div>
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Sub Marca:</span> {{ strtoupper($record->vehicle->linea) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Modelo:</span> {{ $record->vehicle->modelo }}
|
||||||
|
</div>
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Tipo:</span> {{ strtoupper($record->vehicle->tipo_servicio) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-row">
|
||||||
|
<div class="info-cell"></div>
|
||||||
|
<div class="info-cell">
|
||||||
|
<span class="info-label">Tipo Vehi:</span> {{ strtoupper($record->vehicle->tipo) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Firma del operador -->
|
||||||
|
<div class="signature-section">
|
||||||
|
<div class="signature-line"></div>
|
||||||
|
<p class="signature-text">{{ $record->user->full_name }}</p>
|
||||||
|
<p class="signature-text">OPERADOR</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabla de verificación -->
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">ORIGINAL</th>
|
||||||
|
<th>CORRECTO</th>
|
||||||
|
<th>NUEVO</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">COLOR</td>
|
||||||
|
<td colspan="3">{{ strtoupper($record->vehicle->color) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">PLACA</td>
|
||||||
|
<td>{{ $record->vehicle->placa }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">VIN</td>
|
||||||
|
<td>{{ $record->vehicle->numero_serie }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">MARCA</td>
|
||||||
|
<td>{{ strtoupper($record->vehicle->marca) }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">SUBMARCA</td>
|
||||||
|
<td>{{ strtoupper($record->vehicle->linea) }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">MODELO</td>
|
||||||
|
<td>{{ $record->vehicle->modelo }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">TIPO</td>
|
||||||
|
<td>{{ strtoupper($record->vehicle->tipo) }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="section-original">
|
||||||
|
<td class="id-column">MOTOR</td>
|
||||||
|
<td>{{ $record->vehicle->numero_motor ?? 'S/N' }}</td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="gray-bg"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Tabla de verificación de partes -->
|
||||||
|
<table style="margin-top: 15px;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="id-column">ID</th>
|
||||||
|
<th class="desc-column">DESCRIPCIÓN</th>
|
||||||
|
<th class="id-column">ID</th>
|
||||||
|
<th class="desc-column">DESCRIPCIÓN</th>
|
||||||
|
<th class="obs-column">OBSERVACIONES</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">1</td>
|
||||||
|
<td>PLACA VIN TABLERO IZQ</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">2</td>
|
||||||
|
<td>PARED DE FUEGO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">3</td>
|
||||||
|
<td>BASE DE AMORTIGUADOR DERECHO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">4</td>
|
||||||
|
<td>PISO ABAJO DEL ASIENTO DEL COPILOTO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">5</td>
|
||||||
|
<td>PISO DE CAJUELA</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">6</td>
|
||||||
|
<td>ESTRIBO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">7</td>
|
||||||
|
<td>CHASIS DERECHO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">8</td>
|
||||||
|
<td>CHASIS IZQUIERDO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">9</td>
|
||||||
|
<td>BAZTIDOR IZQUIERDO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">10</td>
|
||||||
|
<td>BAZTIDOR DERECHO</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">11</td>
|
||||||
|
<td>MARCO RADIADOR</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">12</td>
|
||||||
|
<td>PISO CARROCERIA</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">13</td>
|
||||||
|
<td>TABLERO DE PARTE MEDIA</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">14</td>
|
||||||
|
<td>MOTOR</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="id-column">15</td>
|
||||||
|
<td>TRANSMISION</td>
|
||||||
|
<td class="id-column white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
<td class="white-bg"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
// Rutas de expedientes y documentos
|
// Rutas de expedientes y documentos
|
||||||
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
||||||
Route::get('expediente/{recordId}/documentos', [RecordController::class, 'getFile']);
|
Route::get('expediente/{id}/pdfVerificacion', [RecordController::class, 'generatePdfVerification']);
|
||||||
Route::post('expediente/documentos', [RecordController::class, 'uploadFile']);
|
Route::get('expediente/{id}/pdfConstancia', [RecordController::class, 'generatePdfConstancia']);
|
||||||
Route::delete('expediente/documentos/{fileId}', [RecordController::class, 'deleteFile']);
|
|
||||||
|
//Rutas de Actualización
|
||||||
|
Route::put('expediente/{recordId}', [RepuveController::class, 'actualizarVehiculo']);
|
||||||
|
|
||||||
// Rutas de cancelación de constancias
|
// Rutas de cancelación de constancias
|
||||||
Route::post('cancelacion/buscar', [CancellationController::class, 'searchToCancel']);
|
|
||||||
Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']);
|
Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']);
|
||||||
Route::get('cancelacion/historial/{vehicleId}', [CancellationController::class, 'historialCancelaciones']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Rutas públicas */
|
/** Rutas públicas */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user