Skip to main content
Every submitted quote produces a branded A4 PDF. PDFs are generated by QuotePdfService, stored on the public disk, and made accessible through a signed storage URL. Admins can also trigger a fresh PDF download at any time.

QuotePdfService

App\Services\QuotePdfService is a thin wrapper around the barryvdh/laravel-dompdf package. It exposes a single generate(array $quoteData): string method that returns the raw PDF binary.
// app/Services/QuotePdfService.php

public function generate(array $quoteData): string
{
    $data = [
        'quote'   => $quoteData,
        'date'    => now()->format('d/m/Y'),
        'taxRate' => 0.16,
    ];

    $pdf = Pdf::loadView('quotes.pdf.quote', $data);
    $pdf->setPaper('A4', 'portrait');
    $pdf->setOptions([
        'isHtml5ParserEnabled' => true,
        'isRemoteEnabled'      => true,
        'defaultFont'          => 'dejavu sans',
    ]);

    return $pdf->output();
}

Configuration

OptionValue
Paper sizeA4
OrientationPortrait
HTML5 parserEnabled
Remote resources (CSS, images)Enabled
Default fontDejaVu Sans
Tax rate injected0.16 (16 %)

Blade template

The service renders the Blade view at resources/views/quotes/pdf/quote.blade.php. The view receives three variables:
VariableDescription
$quoteThe raw quote data array passed to generate()
$dateToday’s date formatted as dd/mm/yyyy
$taxRateFixed value 0.16
A second Blade view exists at resources/views/quotes/pdf/cotizacion.blade.php. This view is used directly by the admin reply flow via Pdf::loadView('quotes.pdf.cotizacion', ...) inside QuoteController::generateQuotePdf(), rather than through QuotePdfService.

When PDFs are generated

On quote submission (POST /api/quotes/submit)

When a client submits a quote, QuoteController::submit() calls QuotePdfService::generate() with the raw request payload and stores the result:
$pdfService = new QuotePdfService();
$pdfContent = $pdfService->generate($request->all());

$pdfPath = 'quotes/' . $quote->reference . '.pdf';
Storage::disk('public')->put($pdfPath, $pdfContent);

$quote->update(['pdf_path' => $pdfPath]);
The PDF is stored at:
storage/app/public/quotes/{reference}.pdf
The pdf_path column on the Quote record is set to quotes/{reference}.pdf (relative to the public storage disk).
When an admin schedules a meeting reply, QuoteController::reply() regenerates the PDF from the live Quote model data (not the original request payload) and overwrites the stored file:
$pdfContent = $this->generateQuotePdf($quote);
$pdfPath    = 'quotes/' . $quote->reference . '.pdf';
Storage::disk('public')->put($pdfPath, $pdfContent);
The refreshed PDF is then attached to the QuoteReplyMail email sent to the client.
The admin can download a PDF at any time via GET /admin/cotizaciones/{quote}/pdf (named route admin.quotes.pdf). This route calls QuoteController::generatePdf(Request $request, Quote $quote) which:
  1. Rebuilds the data array from the Quote model and its items relationship.
  2. Instantiates QuotePdfService and calls generate().
  3. Streams the PDF directly as an application/pdf response with the filename cotizacion-{reference}.pdf.
return response($pdf, 200, [
    'Content-Type'        => 'application/pdf',
    'Content-Disposition' => 'attachment; filename="cotizacion-' . $quote->reference . '.pdf"',
]);
This download does not overwrite the stored file.

Accessing stored PDFs

The Quote model exposes a pdf_url accessor:
// app/Models/Quote.php

public function getPdfUrlAttribute()
{
    return $this->pdf_path ? asset('storage/' . $this->pdf_path) : null;
}
This generates a public URL such as:
https://your-domain.com/storage/quotes/COT-63F9A1B2C3D4E.pdf
The storage/public disk must be linked via php artisan storage:link for URLs generated by asset('storage/...') to resolve correctly in production.

Storage path summary

ContextDiskPathPublicly accessible
Quote submissionpublicquotes/{reference}.pdfYes, via /storage/quotes/{reference}.pdf
Admin reply regenerationpublicquotes/{reference}.pdfYes (overwrites previous file)
Admin downloadStreamed, not storedn/a

Build docs developers (and LLMs) love