Skip to main content

Overview

OptiFlow’s quotation system allows you to create detailed price quotes for customers. Quotations can be tracked through various stages and easily converted to invoices once accepted, streamlining your sales workflow.
Quotations work similarly to invoices but don’t affect inventory until they’re converted. This allows you to quote products without committing stock.

Key Features

Professional Quotes

Generate detailed quotations with line items, pricing, taxes, and custom terms.

Status Tracking

Track quotation lifecycle from draft through accepted, rejected, or expired.

One-Click Conversion

Convert accepted quotations to invoices instantly, preserving all details.

PDF Export

Generate professional PDF quotations with your branding for customer delivery.

Quotation Structure

Core Properties

// Quotation model (app/Models/Quotation.php)
$quotation = Quotation::create([
    'workspace_id' => $workspace->id,
    'contact_id' => $customer->id,
    'document_subtype_id' => $documentSubtype->id,
    'document_number' => 'QUO-2024-0001',
    'status' => QuotationStatus::Draft,
    'issue_date' => now(),
    'due_date' => now()->addDays(15), // Quote validity period
    'subtotal_amount' => 100.00,
    'tax_amount' => 18.00,
    'discount_amount' => 0.00,
    'total_amount' => 118.00,
    'notes' => 'Quote valid for 15 days',
    'created_by' => auth()->id(),
    'currency_id' => 1,
]);

Quotation Fields

  • Document Number: Unique quotation number (auto-generated)
  • Status: Current quotation status
  • Issue Date: When quotation was created
  • Due Date: Quote expiration date
  • Contact: Customer receiving the quote
  • Document Subtype: Quotation type/template
  • Amounts: Subtotal, tax, discount, and total
  • Notes: Terms, validity period, or custom message
  • Currency: Quotation currency
  • Created By: User who created the quotation

Quotation Statuses

Quotation is being prepared:
  • Can be edited freely
  • Not sent to customer
  • Not counted in sales pipeline

Creating Quotations

Quotation Workflow

// Using QuotationController
use App\Actions\CreateQuotationAction;

$result = CreateQuotationAction::handle($workspace, [
    'contact_id' => $customer->id,
    'document_subtype_id' => $documentSubtype->id,
    'issue_date' => now(),
    'due_date' => now()->addDays(15),
    'items' => [
        [
            'product_id' => $product->id,
            'description' => 'Premium Widget',
            'quantity' => 5,
            'unit_price' => 99.99,
            'discount' => 10, // 10% discount
            'tax_ids' => [1], // Apply taxes
        ],
    ],
    'notes' => 'This quotation is valid for 15 days from issue date.',
]);

Quotation Items

Each quotation contains line items:
// QuotationItem model
$item = QuotationItem::create([
    'quotation_id' => $quotation->id,
    'product_id' => $product->id,
    'description' => 'Premium Widget',
    'quantity' => 5,
    'unit_price' => 99.99,
    'discount' => 49.99, // Amount or percentage
    'tax_amount' => 80.99,
    'total' => 529.94,
]);

// Attach taxes to item
$item->taxes()->attach([1]);
Quotation items have the same structure as invoice items, making conversion seamless.

Converting to Invoices

Convert accepted quotations to invoices:
// Using ConvertQuotationToInvoiceController
use App\Http\Controllers\ConvertQuotationToInvoiceController;

// POST /quotations/{quotation}/convert
// Creates an invoice with all quotation details

$invoice = ConvertQuotationToInvoiceAction::handle($quotation, [
    'issue_date' => now(),
    'due_date' => now()->addDays(30),
    'payment_term' => 'Net 30',
]);

Conversion Process

When converting a quotation:
  1. New invoice is created
  2. All quotation items are copied
  3. Taxes and discounts are preserved
  4. Stock is decreased (if products track inventory)
  5. Quotation status can be updated to “Converted”
Conversion creates a new invoice but doesn’t automatically delete or archive the quotation. Update the quotation status separately if needed.

PDF Generation

Generate professional PDF quotations:
// Download quotation PDF
use App\Http\Controllers\DownloadQuotationPdfController;

// Route: GET /quotations/{quotation}/pdf
// Generates branded PDF with company details

Bulk PDF Download

// Download multiple quotations as ZIP
use App\Http\Controllers\BulkDownloadQuotationPdfController;

// Generate PDFs for selected quotations

Quotation Management

Editing Quotations

Quotations can be edited before conversion:
// Update quotation
use App\Actions\UpdateQuotationAction;

$result = UpdateQuotationAction::handle($quotation, [
    'status' => QuotationStatus::Accepted,
    'due_date' => now()->addDays(30),
    'items' => [
        // Updated items
    ],
]);

Recalculating Totals

// Recalculate total from items
$quotation->recalculateTotal();

// Total is sum of all item totals
$total = $quotation->items()->sum('total');

Quotation Relationships

Customer Information

// Get customer for quotation
$customer = $quotation->contact;

// Access customer details
$customerName = $quotation->contact->name;
$customerEmail = $quotation->contact->email;

Document Subtype

// Get document subtype
$subtype = $quotation->documentSubtype;

// Document subtypes control:
// - Number sequences
// - Templates
// - PDF formatting

Workspace

// Get workspace for quotation
$workspace = $quotation->workspace;

// Quotations are workspace-scoped
$workspaceQuotations = Quotation::forWorkspace($workspace)->get();

Querying Quotations

Filter by Status

// Get quotations by status
$pending = Quotation::where('status', QuotationStatus::Sent)->get();
$accepted = Quotation::where('status', QuotationStatus::Accepted)->get();

// Get quotations for contact (customer + related contacts)
$contactIds = $contact->relatedContactIdsWithSelf();
$quotations = Quotation::whereIn('contact_id', $contactIds)
    ->orderByDesc('issue_date')
    ->get();

Date Ranges

// Quotations in date range
$quotations = Quotation::whereBetween('issue_date', [$startDate, $endDate])
    ->get();

// Expired quotations
$expired = Quotation::where('due_date', '<', now())
    ->where('status', QuotationStatus::Sent)
    ->get();

Use Cases

Send quotations to business customers for review and approval before creating formal invoices and shipping products.
Create detailed quotations for custom product configurations or bulk orders with special pricing.
Provide service cost estimates to customers before committing to the work.
Track multiple quote versions during price negotiations with customers.

Best Practices

Clear Validity Period

Always specify a clear expiration date for quotations to manage customer expectations and protect pricing.

Detailed Line Items

Include detailed product descriptions and itemization to avoid confusion when converting to invoices.

Status Updates

Keep quotation statuses current to maintain accurate sales pipeline reporting.

Version Control

If revising quotations during negotiations, consider creating new quotation versions rather than editing.

Quotation vs Invoice

FeatureQuotationInvoice
Affects InventoryNoYes
Requires PaymentNoYes
PDF GenerationYesYes
Status TrackingYesYes
Expiration DateYesDue Date
ConversionTo InvoiceFrom Quotation
Legal DocumentEstimateLegally Binding

API Reference

Key quotation model methods:
  • recalculateTotal() - Recalculate total from items
  • contact() - Relationship to Contact
  • documentSubtype() - Relationship to DocumentSubtype
  • items() - Relationship to QuotationItem
  • workspace() - Relationship to Workspace
Model location: app/Models/Quotation.php:1 Conversion controller: app/Http/Controllers/ConvertQuotationToInvoiceController.php:1

Build docs developers (and LLMs) love