feat: agrega soporte para generación de documentos Word y PDF, actualiza dependencias y mejora la configuración de Docker

This commit is contained in:
Juan Felipe Zapata Moreno 2026-03-04 13:00:13 -06:00
parent 3e7d381f15
commit 4784bbdb9e
12 changed files with 220 additions and 41 deletions

View File

@ -15,6 +15,8 @@ RUN apk add --no-cache \
openssl \
bash \
mysql-client \
libreoffice \
ttf-dejavu \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

View File

@ -12,6 +12,8 @@ RUN apk add --no-cache \
libzip-dev \
openssl \
bash \
libreoffice \
ttf-dejavu \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

View File

@ -12,6 +12,8 @@
use Codedge\Fpdf\Fpdf\Fpdf;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\HasMiddleware;
use PhpOffice\PhpWord\TemplateProcessor;
use Symfony\Component\Process\Process;
class RecordController extends Controller implements HasMiddleware
{
@ -62,29 +64,51 @@ public function generatePdfConstancia($id)
{
$record = Record::with('vehicle.owner.municipality', 'user')->findOrFail($id);
// Preparar datos con conversión UTF-8 a mayúsculas
$data = [
'niv' => $record->vehicle->niv,
'placa' => mb_strtoupper($record->vehicle->placa, 'UTF-8'),
'marca' => mb_strtoupper($record->vehicle->marca, 'UTF-8'),
'linea' => mb_strtoupper($record->vehicle->linea, 'UTF-8'),
'modelo' => $record->vehicle->modelo,
'full_name' => mb_strtoupper($record->vehicle->owner->full_name, 'UTF-8'),
'callep' => mb_strtoupper($record->vehicle->owner->callep ?? '', 'UTF-8'),
'num_ext' => $record->vehicle->owner->num_ext,
'municipality' => mb_strtoupper($record->vehicle->owner->municipality->name ?? '', 'UTF-8'),
$template = new TemplateProcessor(storage_path('app/templates/constancia.docx'));
$template->setValues([
'niv' => $record->vehicle->niv,
'placa' => mb_strtoupper($record->vehicle->placa, 'UTF-8'),
'marca' => mb_strtoupper($record->vehicle->marca, 'UTF-8'),
'linea' => mb_strtoupper($record->vehicle->linea, 'UTF-8'),
'modelo' => $record->vehicle->modelo,
'full_name' => mb_strtoupper($record->vehicle->owner->full_name, 'UTF-8'),
'callep' => mb_strtoupper($record->vehicle->owner->callep ?? '', 'UTF-8'),
'num_ext' => $record->vehicle->owner->num_ext ?? '',
'municipality' => mb_strtoupper($record->vehicle->owner->municipality->name ?? '', 'UTF-8'),
'tipo_servicio' => mb_strtoupper($record->vehicle->tipo_servicio, 'UTF-8'),
];
]);
$pdf = Pdf::loadView('pdfs.constancia', $data)
->setPaper('a4', 'landscape')
->setOptions([
'defaultFont' => 'DejaVu Sans',
'isHtml5ParserEnabled' => true,
'isRemoteEnabled' => true,
$tempDocx = storage_path('app/temp/constancia_' . $id . '_' . uniqid() . '.docx');
$template->saveAs($tempDocx);
$profilePath = storage_path('app/temp/lo_profile_' . uniqid());
$process = new Process([
'libreoffice',
'--headless',
'--convert-to', 'pdf',
'--outdir', storage_path('app/temp'),
$tempDocx,
"-env:UserInstallation=file://{$profilePath}",
]);
$process->setTimeout(60);
$process->run();
@unlink($tempDocx);
if (!$process->isSuccessful()) {
return ApiResponse::INTERNAL_ERROR->response([
'message' => 'Error al convertir el documento a PDF.',
'error' => $process->getErrorOutput(),
]);
}
return $pdf->stream('constancia-inscripcion' . $id . '.pdf');
$pdfPath = storage_path('app/temp/' . pathinfo($tempDocx, PATHINFO_FILENAME) . '.pdf');
return response()->file($pdfPath, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="constancia-inscripcion-' . $id . '.pdf"',
])->deleteFileAfterSend(true);
}
/**

View File

@ -33,8 +33,7 @@ class UpdateController extends Controller implements HasMiddleware
public static function middleware(): array
{
return [
self::can('updates.vehicle-data', ['vehicleUpdate']),
self::can('updates.vehicle-update', ['updateData']),
self::can('updates.vehicle-data', ['updateData']),
self::can('updates.resend-to-repuve', ['resendToRepuve']),
];
}

View File

@ -7,7 +7,7 @@ class VehicleStoreRequest extends FormRequest
public function authorize(): bool
{
return auth()->user()->can('inscription.vehicle');
return true;
}
public function rules(): array

View File

@ -9,7 +9,7 @@ class VehicleUpdateRequest extends FormRequest
public function authorize(): bool
{
return auth()->user()->can('updates.vehicle-update');
return true;
}
public function rules(): array

View File

@ -17,6 +17,7 @@
"milon/barcode": "^12.0",
"notsoweb/laravel-core": "dev-main",
"phpoffice/phpspreadsheet": "*",
"phpoffice/phpword": "^1.4",
"setasign/fpdf": "^1.8",
"spatie/laravel-permission": "^6.16",
"spatie/simple-excel": "^3.8",

162
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7a79a1a1e064bf49d5a8cfcf7ee4c69c",
"content-hash": "6a0ffe38dfceb5fa19bb55a5017b4461",
"packages": [
{
"name": "barryvdh/laravel-dompdf",
@ -4653,6 +4653,58 @@
},
"time": "2025-12-30T16:12:18+00:00"
},
{
"name": "phpoffice/math",
"version": "0.3.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/Math.git",
"reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a",
"reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xml": "*",
"php": "^7.1|^8.0"
},
"require-dev": {
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
"phpunit/phpunit": "^7.0 || ^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"PhpOffice\\Math\\": "src/Math/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Progi1984",
"homepage": "https://lefevre.dev"
}
],
"description": "Math - Manipulate Math Formula",
"homepage": "https://phpoffice.github.io/Math/",
"keywords": [
"MathML",
"officemathml",
"php"
],
"support": {
"issues": "https://github.com/PHPOffice/Math/issues",
"source": "https://github.com/PHPOffice/Math/tree/0.3.0"
},
"time": "2025-05-29T08:31:49+00:00"
},
{
"name": "phpoffice/phpspreadsheet",
"version": "5.4.0",
@ -4762,6 +4814,114 @@
},
"time": "2026-01-11T04:52:00+00:00"
},
{
"name": "phpoffice/phpword",
"version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PHPWord.git",
"reference": "6d75328229bc93790b37e93741adf70646cea958"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/6d75328229bc93790b37e93741adf70646cea958",
"reference": "6d75328229bc93790b37e93741adf70646cea958",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-xml": "*",
"ext-zip": "*",
"php": "^7.1|^8.0",
"phpoffice/math": "^0.3"
},
"require-dev": {
"dompdf/dompdf": "^2.0 || ^3.0",
"ext-libxml": "*",
"friendsofphp/php-cs-fixer": "^3.3",
"mpdf/mpdf": "^7.0 || ^8.0",
"phpmd/phpmd": "^2.13",
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
"phpunit/phpunit": ">=7.0",
"symfony/process": "^4.4 || ^5.0",
"tecnickcom/tcpdf": "^6.5"
},
"suggest": {
"dompdf/dompdf": "Allows writing PDF",
"ext-xmlwriter": "Allows writing OOXML and ODF",
"ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template"
},
"type": "library",
"autoload": {
"psr-4": {
"PhpOffice\\PhpWord\\": "src/PhpWord"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-only"
],
"authors": [
{
"name": "Mark Baker"
},
{
"name": "Gabriel Bull",
"email": "me@gabrielbull.com",
"homepage": "http://gabrielbull.com/"
},
{
"name": "Franck Lefevre",
"homepage": "https://rootslabs.net/blog/"
},
{
"name": "Ivan Lanin",
"homepage": "http://ivan.lanin.org"
},
{
"name": "Roman Syroeshko",
"homepage": "http://ru.linkedin.com/pub/roman-syroeshko/34/a53/994/"
},
{
"name": "Antoine de Troostembergh"
}
],
"description": "PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)",
"homepage": "https://phpoffice.github.io/PHPWord/",
"keywords": [
"ISO IEC 29500",
"OOXML",
"Office Open XML",
"OpenDocument",
"OpenXML",
"PhpOffice",
"PhpWord",
"Rich Text Format",
"WordprocessingML",
"doc",
"docx",
"html",
"odf",
"odt",
"office",
"pdf",
"php",
"reader",
"rtf",
"template",
"template processor",
"word",
"writer"
],
"support": {
"issues": "https://github.com/PHPOffice/PHPWord/issues",
"source": "https://github.com/PHPOffice/PHPWord/tree/1.4.0"
},
"time": "2025-06-05T10:32:36+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.5",

View File

@ -18,8 +18,6 @@ public function run(): void
'name' => 'MODULO PARQUE LA CHOCA',
'municipality_id' => 4,
'address' => 'ESTACIONAMIENTO DEL PARQUE LA CHOCA, CENTRO, TABASCO',
'colony' => 'CENTRO',
'cp' => null,
'longitude' => -92.954605,
'latitude' => 18.004537,
'status' => true,
@ -28,8 +26,6 @@ public function run(): void
'name' => 'MODULO FINANZAS BASE 4',
'municipality_id' => 4,
'address' => 'ESTACIONAMIENTO DE FINANZAS BASE 4, CENTRO, TABASCO',
'colony' => 'CASA BLANCA',
'cp' => null,
'longitude' => -92.923486,
'latitude' => 18.001417,
'status' => true,
@ -38,8 +34,6 @@ public function run(): void
'name' => 'MODULO CARDENAS',
'municipality_id' => 2,
'address' => 'ESTACIONAMIENTO DE LA POLICIA ESTATAL DE CAMINOS, CARDENAS, TABASCO',
'colony' => 'SANTA MARIA DE GUADALUPE',
'cp' => null,
'longitude' => -93.362824,
'latitude' => 17.996747,
'status' => true,
@ -48,8 +42,6 @@ public function run(): void
'name' => 'MODULO PASEO DE LA SIERRA',
'municipality_id' => 4,
'address' => 'ESTACIONAMIENTO PASEO DE LA SECRETARIA DE FINANZAS, CENTRO, TABASCO',
'colony' => 'REFORMA',
'cp' => null,
'longitude' => -92.929378,
'latitude' => 17.981033,
'status' => true,
@ -58,8 +50,6 @@ public function run(): void
'name' => 'CENTRO DE ACTIVACION COMALCALCO',
'municipality_id' => 5,
'address' => '',
'colony' => 'ESTACIONAMIENTO DE LA POLICIA ESTATAL DE CAMINOS, COMALCALCO, TABASCO',
'cp' => null,
'longitude' => -93.218679,
'latitude' => 18.264577,
'status' => true,

View File

@ -78,7 +78,6 @@ public function run(): void
// === INSCRIPCIONES ===
$inscriptions = PermissionType::updateOrCreate(['name' => 'Proceso de Sustitución por primera vez']);
$inscriptionVehicle = $this->onPermission('inscription.vehicle', 'Inscribir vehículo', $inscriptions, 'api');
$inscriptionSearchNational = $this->onPermission('inscription.search.national', 'Buscar en consulta nacional', $inscriptions, 'api');
$inscriptionSearch = $this->onPermission('inscription.search', 'Buscar en consulta', $inscriptions, 'api');
@ -93,10 +92,9 @@ public function run(): void
$systemSettings = $this->onPermission('system.settings', 'Configurar credenciales REPUVE', $system, 'api');
// === ACTUALIZAR REGISTRO ===
$updates = PermissionType::updateOrCreate(['name' => 'Actualizar Registro']);
$updates = PermissionType::updateOrCreate(['name' => 'Enviar registros a REPUVE Nacional']);
$updateVehicleData = $this->onPermission('updates.vehicle-data', 'Actualizar datos de vehículo por formulario', $updates, 'api');
$updateVehicleUpdate = $this->onPermission('updates.vehicle-update', 'Actualizar datos de vehículo', $updates, 'api');
$updateResendToRepuve = $this->onPermission('updates.resend-to-repuve', 'Reenviar a REPUVE', $updates, 'api');
// === GENERAR FORMATOS ===
@ -171,11 +169,11 @@ public function run(): void
// Sistema
$systemSettings,
// Inscripciones
$inscriptionVehicle, $inscriptionSearch, $inscriptionSearchNational,
$inscriptionSearch, $inscriptionSearchNational,
// Cancelaciones
$cancellationTagNoAsignado,
// Actualizaciones
$updateVehicleData, $updateVehicleUpdate, $updateResendToRepuve,
$updateVehicleData, $updateResendToRepuve,
// Generar formatos
$recordGeneratePdf, $recordGeneratePdfVerification, $recordGeneratePdfConstancia, $recordGeneratePdfForm,
// Reportes
@ -195,11 +193,11 @@ public function run(): void
// Dispositivos
$deviceIndex, $deviceCreate, $deviceEdit, $deviceDestroy, $deviceToggleStatus,
// Inscripciones
$inscriptionVehicle, $inscriptionSearch, $inscriptionSearchNational,
$inscriptionSearch, $inscriptionSearchNational,
// Cancelaciones
$cancellationTagNoAsignado,
// Actualizaciones
$updateVehicleData, $updateVehicleUpdate,
$updateVehicleData,
// Generar formatos
$recordGeneratePdf, $recordGeneratePdfVerification, $recordGeneratePdfConstancia, $recordGeneratePdfForm,
// Reportes

View File

@ -4,4 +4,5 @@
!private/
!profile/
!public/
!templates/
!.gitignore

2
storage/app/templates/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
*
!.gitignore