Skip to main content

Overview

OptiFlow’s invoicing system provides complete invoice lifecycle management from creation to payment. The system handles complex tax calculations, generates professional PDFs, tracks payments, and automatically updates inventory when invoices are created.
Invoices are workspace-scoped, ensuring each business location maintains separate invoicing records and sequences.

Key Features

Automated Calculations

Automatic subtotal, tax, discount, and total calculations with support for multiple tax types and rates.

Payment Tracking

Track partial and full payments with multiple payment methods and automatic status updates.

PDF Generation

Generate professional PDF invoices with your branding and business information.

Inventory Integration

Automatically decrease stock levels when invoices are created and restore stock when invoices are deleted.

Invoice Structure

Core Properties

// Invoice model (app/Models/Invoice.php)
$invoice = Invoice::create([
    'workspace_id' => $workspace->id,
    'contact_id' => $customer->id,
    'document_subtype_id' => $documentSubtype->id,
    'document_number' => 'INV-2024-0001',
    'status' => InvoiceStatus::PendingPayment,
    'issue_date' => now(),
    'due_date' => now()->addDays(30),
    'payment_term' => 'Net 30',
    'subtotal_amount' => 100.00,
    'tax_amount' => 18.00,
    'discount_amount' => 0.00,
    'total_amount' => 118.00,
    'notes' => 'Thank you for your business',
    'created_by' => auth()->id(),
    'currency_id' => 1,
]);

Invoice Fields

  • Document Number: Unique invoice number (auto-generated)
  • Status: Current invoice status (draft, pending payment, paid, etc.)
  • Issue Date: When invoice was created
  • Due Date: Payment deadline
  • Payment Term: Payment terms description
  • Contact: Customer receiving the invoice
  • Document Subtype: Invoice type/template
  • Amounts: Subtotal, tax, discount, and total
  • Notes: Terms, conditions, or custom message
  • Currency: Invoice currency
  • Created By: User who created the invoice

Invoice Lifecycle

Invoice Statuses

Invoice is being prepared:
  • Can be edited freely
  • Not counted in financial reports
  • Stock not affected
  • Cannot receive payments

Status Flow

Creating Invoices

Invoice Workflow

// Using InvoiceController (app/Http/Controllers/InvoiceController.php)
use App\Actions\CreateInvoiceAction;

$result = CreateInvoiceAction::handle($workspace, [
    'contact_id' => $customer->id,
    'document_subtype_id' => $documentSubtype->id,
    'issue_date' => now(),
    'due_date' => now()->addDays(30),
    'items' => [
        [
            'product_id' => $product->id,
            'description' => 'Premium Widget',
            'quantity' => 5,
            'unit_price' => 99.99,
            'discount' => 0,
            'tax_ids' => [1, 2], // Apply multiple taxes
        ],
    ],
    'notes' => 'Net 30 payment terms',
]);

Invoice Items

Each invoice contains line items:
// InvoiceItem model
$item = InvoiceItem::create([
    'invoice_id' => $invoice->id,
    'product_id' => $product->id,
    'description' => 'Premium Widget',
    'quantity' => 5,
    'unit_price' => 99.99,
    'discount' => 10.00, // 10% discount
    'tax_amount' => 80.99,
    'total' => 529.94, // Calculated automatically
]);

// Attach taxes to item
$item->taxes()->attach([1, 2]);
Item totals are calculated as: (quantity * unit_price) - discount + tax_amount

Tax Handling

Tax Types

OptiFlow supports multiple tax types:
  • VAT/Sales Tax: Standard sales tax
  • Excise Tax: Special product taxes
  • Withholding Tax: Tax withheld from total

Applying Taxes

// Taxes are grouped by type for the UI
$taxesGroupedByType = Tax::query()
    ->orderBy('is_default', 'desc')
    ->get()
    ->groupBy('type')
    ->mapWithKeys(fn ($taxes, $type) => [
        $type => [
            'label' => TaxType::from($type)->label(),
            'isExclusive' => TaxType::from($type)->isExclusive(),
            'taxes' => $taxes->toArray(),
        ],
    ]);

// Multiple taxes can be applied to each invoice item

Payment Management

Recording Payments

// Create a payment
$payment = Payment::create([
    'invoice_id' => $invoice->id,
    'amount' => 118.00,
    'payment_method' => PaymentMethod::Transfer,
    'payment_date' => now(),
    'bank_account_id' => $bankAccount->id,
    'reference' => 'WIRE-12345',
    'notes' => 'Wire transfer received',
]);

// Invoice status updates automatically
$invoice->updatePaymentStatus();

Payment Methods

Supported payment methods:
  • Cash
  • Bank Transfer
  • Check
  • Credit Card
  • Debit Card
  • Other

Payment Status Updates

The invoice status automatically updates based on payments:
// Automatic status update logic (app/Models/Invoice.php:180)
public function updatePaymentStatus(): void
{
    $totalPaid = $this->payments()->sum('amount');
    
    if ($totalPaid >= $this->total_amount) {
        $this->update(['status' => InvoiceStatus::Paid]);
    } elseif ($totalPaid > 0) {
        $this->update(['status' => InvoiceStatus::PartiallyPaid]);
    } else {
        $this->update(['status' => InvoiceStatus::PendingPayment]);
    }
}

Payment Tracking

// Get all payments for invoice
$payments = $invoice->payments;

// Calculate amounts
$amountPaid = $invoice->amount_paid; // Total received
$amountDue = $invoice->amount_due;   // Remaining balance

// Check if can receive payment
if ($invoice->canRegisterPayment()) {
    // Accept payment
}

PDF Generation

Generate professional PDF invoices:
// Download invoice PDF
use App\Http\Controllers\DownloadInvoicePdfController;

// Route: GET /invoices/{invoice}/pdf
// Controller handles PDF generation with company details and branding

Bulk PDF Download

// Download multiple invoices as ZIP
use App\Http\Controllers\BulkDownloadInvoicePdfController;

// Generate PDFs for selected invoices

Stock Integration

Invoices automatically affect inventory:
// When invoice is created:
// - Stock movements are created for each item
// - Inventory is decreased

// Get stock movements for invoice
$movements = $invoice->stockMovements;

// Each movement tracks:
// - Product sold
// - Quantity
// - Related invoice
// - Workspace
Deleting an invoice restores stock to inventory. Ensure you have proper permissions before allowing invoice deletion.

Invoice Validation

Edit Restrictions

// Check if invoice can be edited
if ($invoice->canBeEdited()) {
    // Only invoices without payments can be edited
}

// Check if invoice can be deleted
if ($invoice->canBeDeleted()) {
    // Cannot delete paid or partially paid invoices
}

// Check if can register payment
if ($invoice->canRegisterPayment()) {
    // Cannot pay draft, cancelled, or fully paid invoices
}

Salesmen & Commissions

Track salespeople on invoices:
// Attach salesmen to invoice
$invoice->salesmen()->attach([1, 2]);

// Get salesmen for invoice
$salesmen = $invoice->salesmen;

Activity Logging

Invoices track all changes:
// Get activity log
$activities = Activity::query()
    ->where('subject_type', 'invoice')
    ->where('subject_id', $invoice->id)
    ->with('causer')
    ->get();

// Logged changes include:
// - Contact changes
// - Document number updates
// - Date modifications
// - Payment term changes

Comments & Notes

Add internal comments to invoices:
// Invoices support comments
$invoice->comments; // All comments

// Add comment
$comment = $invoice->comments()->create([
    'body' => 'Customer requested payment extension',
    'commentator_id' => auth()->id(),
]);

Use Cases

Generate invoices for retail sales with automatic tax calculation, inventory updates, and payment tracking.
Create invoices with net payment terms, multiple items, bulk discounts, and payment tracking for business customers.
Set up recurring invoices for subscription services or regular customers with consistent payment terms.
Track partial payments for large invoices with installment payment arrangements.

Best Practices

Payment Terms

Clearly specify payment terms and due dates on all invoices to set customer expectations.

Sequential Numbering

Use document subtypes to maintain proper sequential numbering for different invoice types.

Stock Validation

Always verify sufficient stock before creating invoices to prevent overselling.

Payment Recording

Record payments promptly to maintain accurate accounts receivable and customer balances.

API Reference

Key invoice model methods:
  • recalculateTotal() - Recalculate total from items
  • updatePaymentStatus() - Update status based on payments
  • canBeEdited() - Check if editable
  • canBeDeleted() - Check if deletable
  • canRegisterPayment() - Check if can receive payment
  • contact() - Relationship to Contact
  • items() - Relationship to InvoiceItem
  • payments() - Relationship to Payment
  • stockMovements() - Relationship to StockMovement
  • salesmen() - Relationship to Salesman
Model location: app/Models/Invoice.php:1 Controller location: app/Http/Controllers/InvoiceController.php:1

Build docs developers (and LLMs) love