PHPackages                             aiarmada/docs - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. aiarmada/docs

ActiveLibrary

aiarmada/docs
=============

Doc generation package for Laravel with PDF support for invoices, receipts, shipping labels, and customizable templates

v1.4.7(5mo ago)01003MITPHPPHP ^8.4

Since Nov 6Pushed 1mo agoCompare

[ Source](https://github.com/AIArmada/docs)[ Packagist](https://packagist.org/packages/aiarmada/docs)[ Docs](https://github.com/aiarmada/commerce)[ RSS](/packages/aiarmada-docs/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (2)Versions (34)Used By (3)

Docs Package for Laravel
========================

[](#docs-package-for-laravel)

Modern Laravel package for generating professional documents (invoices, receipts, and more) with PDF support using Spatie Laravel PDF and Tailwind CSS.

Features
--------

[](#features)

- 📄 Generate professional PDF documents with Blade templates
- 🎨 Tailwind CSS support for beautiful, customizable designs
- 📊 Support for multiple document types: invoices, receipts (expandable)
- 🔢 Automatic document numbering with configurable formats
- 📝 Multiple templates support for each document type
- 📱 Status tracking (Draft, Pending, Sent, Paid, etc.)
- 💾 Store PDFs on any Laravel filesystem disk
- 📧 Email document capabilities
- 🔄 Status history tracking
- 🏢 Configurable company and customer data

Supported Document Types
------------------------

[](#supported-document-types)

### Currently Supported

[](#currently-supported)

- **Invoices** - Full invoice generation with line items, taxes, discounts
- **Receipts** - Payment receipts (coming soon)

### Expandable Architecture

[](#expandable-architecture)

The package is designed to easily support additional document types such as quotations, purchase orders, delivery notes, and more.

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require aiarmada/docs
```

Publish the configuration file:

```
php artisan vendor:publish --tag=docs-config
```

Run the migrations:

```
php artisan migrate
```

Configuration
-------------

[](#configuration)

The package configuration is located in `config/docs.php`. After publishing, you can customize all aspects of document generation.

### Document Types

[](#document-types)

Configure multiple document types (invoices, receipts, tickets, etc.) with type-specific settings:

```
'types' => [
    'invoice' => [
        'default_template' => 'doc-default',
        'number_format' => [
            'prefix' => 'INV',           // Document number prefix
            'year_format' => 'y',        // Year format (Y = 2025, y = 25)
            'separator' => '-',           // Separator between parts
            'suffix_length' => 6,        // Random suffix length
        ],
        'storage' => [
            'disk' => 'local',           // Storage disk (local, s3, etc.)
            'path' => 'docs/invoices',   // Path within disk
        ],
        'defaults' => [
            'currency' => 'MYR',         // Default currency
            'tax_rate' => 0,             // Default tax rate (0 = 0%, 0.06 = 6%)
            'due_days' => 30,            // Days until due
        ],
    ],
    // Add more types as needed (receipt, ticket, quotation, etc.)
],
```

**Example Document Numbers:**

- `INV25-A1B2C3` (Invoice)
- `RCP25-D4E5F6` (Receipt)
- `TKT25-G7H8I9` (Ticket)

### PDF Configuration

[](#pdf-configuration)

Control PDF generation settings globally:

```
'pdf' => [
    'format' => 'a4',              // Paper size: a4, letter, legal, a3, a5
    'orientation' => 'portrait',   // portrait or landscape
    'margin' => [
        'top' => 10,               // Margin in millimeters
        'right' => 10,
        'bottom' => 10,
        'left' => 10,
    ],
    'full_bleed' => false,         // Set to true for borderless PDFs
    'print_background' => true,    // Enable background colors/gradients
],
```

**Override PDF Settings Per Template:**

You can override these settings in your template configuration:

```
DocTemplate::create([
    'name' => 'Borderless Template',
    'settings' => [
        'pdf' => [
            'format' => 'a4',
            'orientation' => 'landscape',
            'full_bleed' => true,      // Removes all margins
            'margin' => [
                'top' => 0,
                'right' => 0,
                'bottom' => 0,
                'left' => 0,
            ],
        ],
    ],
]);
```

**Override PDF Settings Per Document:**

You can also override settings when creating individual documents:

```
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'pdf_options' => [
        'format' => 'letter',
        'orientation' => 'landscape',
        'margin' => [
            'top' => 5,
            'right' => 5,
            'bottom' => 5,
            'left' => 5,
        ],
    ],
    // ... other data
]));
```

**Setting Precedence:** Config defaults &lt; Template settings &lt; Per-document options

### Company Information

[](#company-information)

Set default company details that appear on all documents:

```
'company' => [
    'name' => env('DOCS_COMPANY_NAME', config('app.name')),
    'address' => env('DOCS_COMPANY_ADDRESS'),
    'city' => env('DOCS_COMPANY_CITY'),
    'state' => env('DOCS_COMPANY_STATE'),
    'postcode' => env('DOCS_COMPANY_POSTCODE'),
    'country' => env('DOCS_COMPANY_COUNTRY'),
    'phone' => env('DOCS_COMPANY_PHONE'),
    'email' => env('DOCS_COMPANY_EMAIL'),
    'website' => env('DOCS_COMPANY_WEBSITE'),
    'tax_id' => env('DOCS_COMPANY_TAX_ID'),
],
```

**Environment Variables:**

Add these to your `.env` file:

```
DOCS_COMPANY_NAME="Your Company Name"
DOCS_COMPANY_ADDRESS="123 Business Street"
DOCS_COMPANY_CITY="Kuala Lumpur"
DOCS_COMPANY_STATE="Federal Territory"
DOCS_COMPANY_POSTCODE="50000"
DOCS_COMPANY_COUNTRY="Malaysia"
DOCS_COMPANY_PHONE="+60 3-1234-5678"
DOCS_COMPANY_EMAIL="billing@yourcompany.com"
DOCS_COMPANY_WEBSITE="https://yourcompany.com"
DOCS_COMPANY_TAX_ID="123456789"

DOCS_STORAGE_DISK=local
DOCS_STORAGE_PATH=docs/invoices
DOCS_CURRENCY=MYR
DOCS_TAX_RATE=0.06
DOCS_DUE_DAYS=30
```

### Database Configuration

[](#database-configuration)

Control JSON column types for the documents:

```
'database' => [
    'json_column_type' => env('DOCS_JSON_COLUMN_TYPE', 'json'), // or 'jsonb' for PostgreSQL
],
```

Usage
-----

[](#usage)

### Basic Document Creation

[](#basic-document-creation)

```
use AIArmada\Docs\Services\DocService;
use AIArmada\Docs\DataObjects\DocData;
use AIArmada\Docs\Enums\DocStatus;

$docService = app(DocService::class);

$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [
        [
            'name' => 'Web Development Service',
            'description' => 'Custom website development',
            'quantity' => 1,
            'price' => 2500.00,
        ],
        [
            'name' => 'Hosting (Annual)',
            'quantity' => 1,
            'price' => 500.00,
        ],
    ],
    'customer_data' => [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'address' => '123 Main St',
        'city' => 'Kuala Lumpur',
        'state' => 'Federal Territory',
        'postcode' => '50000',
        'country' => 'Malaysia',
    ],
    'notes' => 'Thank you for your business!',
    'terms' => 'Payment due within 30 days.',
    'generate_pdf' => true,
]));
```

### Advanced Document Creation

[](#advanced-document-creation)

Create documents with custom numbering, dates, taxes, and discounts:

```
$document = $docService->createDoc(DocData::from([
    'doc_number' => 'INV-2025-001',        // Optional: Auto-generated if not provided
    'doc_type' => 'invoice',
    'template_slug' => 'modern',           // Optional: Use specific template
    'status' => DocStatus::PENDING,        // Optional: Defaults to DRAFT

    // Dates
    'issue_date' => now(),
    'due_date' => now()->addDays(30),

    // Financial details
    'currency' => 'USD',
    'tax_rate' => 0.06,                    // 6% tax
    'discount_amount' => 50.00,            // Fixed discount

    // Items
    'items' => [
        [
            'name' => 'Consulting Service',
            'description' => 'Business strategy consultation (2 hours)',
            'quantity' => 2,
            'price' => 150.00,
        ],
        [
            'name' => 'Report Writing',
            'quantity' => 1,
            'price' => 200.00,
        ],
    ],

    // Customer information
    'customer_data' => [
        'name' => 'ACME Corporation',
        'email' => 'billing@acme.com',
        'address' => '456 Corporate Blvd',
        'city' => 'Singapore',
        'postcode' => '018956',
        'country' => 'Singapore',
        'phone' => '+65 6123 4567',
    ],

    // Optional: Override company data for this document
    'company_data' => [
        'name' => 'My Company Ltd',
        'address' => '789 Business Ave',
        'city' => 'Kuala Lumpur',
        // ... other fields
    ],

    // Optional: PDF generation settings
    'generate_pdf' => true,
    'pdf_options' => [
        'format' => 'a4',
        'orientation' => 'portrait',
        'margin' => [
            'top' => 20,
            'right' => 20,
            'bottom' => 20,
            'left' => 20,
        ],
    ],

    // Optional: Additional metadata
    'metadata' => [
        'project_id' => 'PRJ-123',
        'department' => 'Sales',
        'custom_field' => 'Custom value',
    ],
]));
```

### Linking Documents to Models

[](#linking-documents-to-models)

Link documents to orders, tickets, or any other model using polymorphic relationships:

```
use App\Models\Order;

$order = Order::find($orderId);

$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'docable_type' => Order::class,
    'docable_id' => $order->id,
    'items' => [
        [
            'name' => 'Product from Order',
            'quantity' => $order->quantity,
            'price' => $order->price,
        ],
    ],
    'customer_data' => [
        'name' => $order->customer_name,
        'email' => $order->customer_email,
        // ... populate from order
    ],
]));

// Later, access the linked model
$order = $document->docable;  // Returns the Order model
```

### Automatic Calculations

[](#automatic-calculations)

The package automatically calculates totals:

```
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [
        ['name' => 'Item 1', 'quantity' => 2, 'price' => 100],  // $200
        ['name' => 'Item 2', 'quantity' => 1, 'price' => 150],  // $150
    ],
    'tax_rate' => 0.06,           // 6% tax
    'discount_amount' => 25,      // $25 discount
]));

// Automatically calculated:
// Subtotal: $350
// Tax: $21 (6% of $350)
// Discount: -$25
// Total: $346
```

### PDF Generation

[](#pdf-generation)

Generate PDFs from documents with full control over saving and output:

```
$docService = app(DocService::class);

// Generate and save to disk
$pdfPath = $docService->generatePdf($document, save: true);
// Returns: "docs/invoices/inv25-abc123.pdf"

// Generate without saving (returns PDF content as string)
$pdfContent = $docService->generatePdf($document, save: false);
// Use for streaming, attaching to emails, etc.

// Example: Stream to browser
return response($pdfContent)
    ->header('Content-Type', 'application/pdf')
    ->header('Content-Disposition', 'inline; filename="'.$document->doc_number.'.pdf"');
```

### PDF Storage

[](#pdf-storage)

Configure where PDFs are stored in `config/docs.php`:

```
'types' => [
    'invoice' => [
        'storage' => [
            'disk' => 's3',                    // Use S3, local, or any Laravel disk
            'path' => 'documents/invoices',    // Path within the disk
        ],
    ],
],
```

Access stored PDFs:

```
use Illuminate\Support\Facades\Storage;

$disk = config('docs.types.invoice.storage.disk');
$path = $document->pdf_path;

// Get URL (if disk supports it)
$url = Storage::disk($disk)->url($path);

// Download
return Storage::disk($disk)->download($path);

// Check if exists
if (Storage::disk($disk)->exists($path)) {
    // PDF exists
}
```

### Download Document PDF

[](#download-document-pdf)

```
// Generates PDF if not already generated, or returns existing path
$pdfPath = $docService->downloadPdf($document);
```

### Document Status Management

[](#document-status-management)

Track document lifecycle with built-in status management:

```
use AIArmada\Docs\Enums\DocStatus;

// Update status with notes
$docService->updateDocStatus(
    $document,
    DocStatus::PAID,
    'Payment received via bank transfer on '.now()->format('Y-m-d')
);

// Convenience methods on the model
$document->markAsPaid();    // Sets status to PAID
$document->markAsSent();    // Sets status to SENT

// Check current status
if ($document->status === DocStatus::PAID) {
    // Document is paid
}

// Access status label
echo $document->status->label();  // "Paid", "Pending", etc.
```

### Status History

[](#status-history)

View the complete history of status changes:

```
// Get all status changes
$history = $document->statusHistories()
    ->orderBy('created_at', 'desc')
    ->get();

foreach ($history as $entry) {
    echo $entry->status->label();      // Status
    echo $entry->notes;                 // Change notes
    echo $entry->created_at->format('Y-m-d H:i'); // When
}
```

### Available Statuses

[](#available-statuses)

```
DocStatus::DRAFT           // Initial state
DocStatus::PENDING         // Awaiting approval or action
DocStatus::SENT            // Delivered to customer
DocStatus::PAID            // Payment received
DocStatus::PARTIALLY_PAID  // Partial payment received
DocStatus::OVERDUE         // Past due date
DocStatus::CANCELLED       // Cancelled
DocStatus::REFUNDED        // Payment refunded
```

Creating Custom Templates
-------------------------

[](#creating-custom-templates)

Templates are Blade views that define how your documents look. The package uses a structured path convention for template views.

### Template View Paths

[](#template-view-paths)

Templates are automatically resolved using the following path convention:

```
docs::templates.

```

**Examples:**

- `docs::templates.doc-default` → Default template
- `docs::templates.modern` → Custom modern template
- `docs::templates.minimal` → Custom minimal template

### Template Resolution Priority

[](#template-resolution-priority)

When creating a document, the package resolves templates in this order:

1. **By UUID** (if `doc_template_id` provided)

    ```
    $template = DocTemplate::find($data->docTemplateId);
    ```
2. **By Slug** (if `template_slug` provided)

    ```
    $template = DocTemplate::where('slug', $data->templateSlug)->first();
    ```
3. **Database Default** (if no template found)

    ```
    $template = DocTemplate::where('is_default', true)
        ->where('doc_type', $docType)
        ->first();
    ```
4. **Config Fallback** (during PDF generation if template still null)

    ```
    $viewName = config("docs.types.{$docType}.default_template", "{$docType}-default");
    ```

**Best Practice:** Explicitly specify `template_slug` when creating documents to ensure deterministic template selection:

```
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'template_slug' => 'nusavue-invoice',  // ✅ Explicit and predictable
    // ... other data
]));
```

**Why explicit is better:**

- ✅ **Predictable** - Always uses the template you specify
- ✅ **Independent** - Not affected by database default changes
- ✅ **Testable** - Tests don't depend on database state
- ❌ Without it - Depends on database having correct default template seeded

### View Resolution

[](#view-resolution)

The `DocService` automatically normalizes view names. You can reference templates in multiple ways:

```
// All of these resolve to: docs::templates.modern
'view_name' => 'modern'
'view_name' => 'templates.modern'
'view_name' => 'docs.templates.modern'
'view_name' => 'docs::templates.modern'
```

The `normalizeViewName()` method handles various input formats and ensures they resolve to the canonical `docs::templates.` format, which Laravel uses to find views in:

- Package views: `packages/docs/resources/views/templates/`
- Published views: `resources/views/vendor/docs/templates/` (takes precedence)
- Application views: `resources/views/docs/templates/`

### Creating a New Template

[](#creating-a-new-template)

**1. Create the Blade View**

Create your template file in the package or publish views to your application:

```
# Publish views to customize
php artisan vendor:publish --tag=docs-views
```

This publishes templates to: `resources/views/vendor/docs/templates/`

Create a new template file:

```

    {{ $doc->doc_type === 'invoice' ? 'Invoice' : 'Document' }} {{ $doc->doc_number }}

                {{ strtoupper($doc->doc_type) }}

            {{ $doc->doc_number }}

            @if($doc->company_data)

                From
                {{ $doc->company_data['name'] ?? '' }}

                    @if(!empty($doc->company_data['address']))
                        {{ $doc->company_data['address'] }}
                    @endif
                    @if(!empty($doc->company_data['city']))
                        {{ $doc->company_data['city'] }}{{ !empty($doc->company_data['state']) ? ', '.$doc->company_data['state'] : '' }} {{ $doc->company_data['postcode'] ?? '' }}
                    @endif
                    @if(!empty($doc->company_data['email']))
                        {{ $doc->company_data['email'] }}
                    @endif

            @endif

            @if($doc->customer_data)

                Bill To
                {{ $doc->customer_data['name'] ?? '' }}

                    @if(!empty($doc->customer_data['email']))
                        {{ $doc->customer_data['email'] }}
                    @endif
                    @if(!empty($doc->customer_data['address']))
                        {{ $doc->customer_data['address'] }}
                    @endif
                    @if(!empty($doc->customer_data['city']))
                        {{ $doc->customer_data['city'] }}{{ !empty($doc->customer_data['state']) ? ', '.$doc->customer_data['state'] : '' }} {{ $doc->customer_data['postcode'] ?? '' }}
                    @endif

            @endif

                    Item
                    Qty
                    Price
                    Total

                @foreach($doc->items as $item)

                        {{ $item['name'] ?? $item['description'] ?? '' }}
                        @if(!empty($item['description']) && isset($item['name']))
                            {{ $item['description'] }}
                        @endif

                    {{ $item['quantity'] ?? 1 }}
                    {{ $doc->currency }} {{ number_format($item['price'] ?? 0, 2) }}
                    {{ $doc->currency }} {{ number_format(($item['quantity'] ?? 1) * ($item['price'] ?? 0), 2) }}

                @endforeach

                        Subtotal:
                        {{ $doc->currency }} {{ number_format($doc->subtotal, 2) }}

                    @if($doc->tax_amount > 0)

                        Tax:
                        {{ $doc->currency }} {{ number_format($doc->tax_amount, 2) }}

                    @endif
                    @if($doc->discount_amount > 0)

                        Discount:
                        -{{ $doc->currency }} {{ number_format($doc->discount_amount, 2) }}

                    @endif

                        Total:
                        {{ $doc->currency }} {{ number_format($doc->total, 2) }}

        @if($doc->notes || $doc->terms)

            @if($doc->notes)

                Notes
                {{ $doc->notes }}

            @endif
            @if($doc->terms)

                Terms & Conditions
                {{ $doc->terms }}

            @endif

        @endif

```

**2. Create a Template Record**

Register your template in the database:

```
use AIArmada\Docs\Models\DocTemplate;

DocTemplate::create([
    'name' => 'Modern Template',
    'slug' => 'modern',
    'description' => 'A modern design with gradient background',
    'view_name' => 'modern',           // Will resolve to docs::templates.modern
    'doc_type' => 'invoice',
    'is_default' => false,
    'settings' => [
        'show_logo' => true,
        'primary_color' => '#4f46e5',
        'pdf' => [
            'format' => 'a4',
            'orientation' => 'portrait',
            'margin' => [
                'top' => 15,
                'right' => 15,
                'bottom' => 15,
                'left' => 15,
            ],
            'print_background' => true,  // Important for gradient backgrounds
        ],
    ],
]);
```

**3. Use Your Template**

Reference your template when creating documents:

```
// By template slug
$document = $docService->createDoc(DocData::from([
    'template_slug' => 'modern',
    'doc_type' => 'invoice',
    // ... other data
]));

// By template ID
$document = $docService->createDoc(DocData::from([
    'doc_template_id' => $template->id,
    'doc_type' => 'invoice',
    // ... other data
]));
```

### Available Template Variables

[](#available-template-variables)

All templates have access to the `$doc` object with these properties:

```
$doc->doc_number          // Document number (e.g., INV25-ABC123)
$doc->doc_type            // Document type (invoice, receipt, ticket)
$doc->status              // DocStatus enum
$doc->issue_date          // Carbon instance
$doc->due_date            // Carbon instance (nullable)
$doc->subtotal            // Subtotal amount
$doc->tax_amount          // Tax amount
$doc->discount_amount     // Discount amount
$doc->total               // Total amount
$doc->currency            // Currency code (e.g., MYR, USD)
$doc->notes               // Customer notes
$doc->terms               // Terms and conditions
$doc->customer_data       // Array of customer information
$doc->company_data        // Array of company information
$doc->items               // Array of line items
$doc->metadata            // Array of additional data
$doc->template            // DocTemplate model instance
$doc->docable             // Polymorphic relation (Order, Ticket, etc.)
```

**Line Item Structure:**

```
[
    'name' => 'Product Name',
    'description' => 'Optional description',
    'quantity' => 1,
    'price' => 100.00,
]
```

**Customer/Company Data Structure:**

```
[
    'name' => 'Customer Name',
    'email' => 'customer@example.com',
    'address' => '123 Main St',
    'city' => 'Kuala Lumpur',
    'state' => 'Federal Territory',
    'postcode' => '50000',
    'country' => 'Malaysia',
    'phone' => '+60 3-1234-5678',
]
```

### Template Best Practices

[](#template-best-practices)

1. **Use Tailwind CDN for Styling**: Include `` in the `` for easy styling.
2. **Enable Background Printing**: Set `print_background: true` in PDF settings if using colored backgrounds or gradients.
3. **Handle Optional Data**: Always check if data exists before displaying:

    ```
    @if(!empty($doc->customer_data['address']))
        {{ $doc->customer_data['address'] }}
    @endif
    ```
4. **Format Dates**: Use Carbon methods for consistent date formatting:

    ```
    {{ $doc->issue_date->format('M d, Y') }}
    ```
5. **Format Currency**: Always include currency code and format numbers:

    ```
    {{ $doc->currency }} {{ number_format($doc->total, 2) }}
    ```
6. **Test PDF Output**: PDFs may render differently than HTML. Always test your templates with PDF generation:

    ```
    $docService->generatePdf($document, save: true);
    ```
7. **Optimize for Print**: Use appropriate margins and avoid content near page edges unless using `full_bleed: true`.

### Default Template

[](#default-template)

The package includes a default template (`doc-default`) that supports:

- Invoice, receipt, and ticket types
- Company and customer information
- Line items with descriptions
- Tax, discounts, and totals
- Voucher summaries
- Notes and terms
- Status badges
- Responsive design with Tailwind CSS

You can use this as a reference when creating your own templates.

Document Status
---------------

[](#document-status)

The package includes predefined statuses for documents:

- **Draft** - Initial state
- **Pending** - Awaiting approval or action
- **Sent** - Delivered to customer
- **Paid** - Payment received
- **Partially Paid** - Partial payment received
- **Overdue** - Past due date
- **Cancelled** - Cancelled
- **Refunded** - Payment refunded

Querying Documents
------------------

[](#querying-documents)

Use Eloquent to query documents:

```
use AIArmada\Docs\Models\Doc;
use AIArmada\Docs\Enums\DocStatus;

// Get all paid invoices
$paidInvoices = Doc::where('doc_type', 'invoice')
    ->where('status', DocStatus::PAID)
    ->get();

// Get overdue invoices
$overdueInvoices = Doc::where('doc_type', 'invoice')
    ->where('status', DocStatus::OVERDUE)
    ->where('due_date', 'email', 'customer@example.com')
    ->orderBy('issue_date', 'desc')
    ->get();

// Get documents linked to a model
$order = Order::find($orderId);
$orderDocs = Doc::where('docable_type', Order::class)
    ->where('docable_id', $order->id)
    ->get();

// Eager load relationships
$docs = Doc::with(['template', 'statusHistories', 'docable'])
    ->get();

// Get total amount for paid invoices
$totalRevenue = Doc::where('doc_type', 'invoice')
    ->where('status', DocStatus::PAID)
    ->sum('total');
```

Working with Templates
----------------------

[](#working-with-templates)

### Query Templates

[](#query-templates)

```
use AIArmada\Docs\Models\DocTemplate;

// Get default template for a doc type
$defaultTemplate = DocTemplate::where('doc_type', 'invoice')
    ->where('is_default', true)
    ->first();

// Get template by slug
$template = DocTemplate::where('slug', 'modern')->first();

// Get all templates for a doc type
$invoiceTemplates = DocTemplate::where('doc_type', 'invoice')->get();
```

### Update Template Settings

[](#update-template-settings)

```
$template->update([
    'settings' => [
        'primary_color' => '#10b981',
        'show_logo' => true,
        'pdf' => [
            'format' => 'a4',
            'margin' => ['top' => 10, 'right' => 10, 'bottom' => 10, 'left' => 10],
        ],
    ],
]);
```

### Set Default Template

[](#set-default-template)

```
// Make a template the default for its doc type
$template->update(['is_default' => true]);

// This will automatically set other templates of the same type to non-default
```

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 12.0+
- Spatie Laravel PDF 1.5+
- Node.js 18+ and npm
- Puppeteer (for PDF generation)

### Installing Dependencies

[](#installing-dependencies)

After installing the package, you need to install Node.js dependencies for PDF generation:

```
# Install npm packages (includes puppeteer)
npm install

# Or if dependencies are already defined in package.json
npm install puppeteer
```

> **Note:** Puppeteer is required for PDF generation. The package uses Spatie Laravel PDF which relies on Puppeteer/Chromium to render PDFs from HTML.

Advanced Usage
--------------

[](#advanced-usage)

### Custom Document Number Generation

[](#custom-document-number-generation)

Override the default document numbering:

```
use AIArmada\Docs\Services\DocService;

class CustomDocService extends DocService
{
    public function generateDocNumber(string $docType = 'invoice'): string
    {
        // Your custom logic
        $prefix = match($docType) {
            'invoice' => 'INV',
            'receipt' => 'RCP',
            'ticket' => 'TKT',
            default => 'DOC',
        };

        $year = now()->year;
        $sequence = Doc::where('doc_type', $docType)
            ->whereYear('created_at', $year)
            ->count() + 1;

        return sprintf('%s-%d-%05d', $prefix, $year, $sequence);
        // Returns: INV-2025-00001, INV-2025-00002, etc.
    }
}

// Register in a service provider
$this->app->bind(DocService::class, CustomDocService::class);
```

### Metadata and Custom Fields

[](#metadata-and-custom-fields)

Store additional data in the metadata field:

```
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [...],
    'metadata' => [
        'project_id' => 'PRJ-123',
        'project_name' => 'Website Redesign',
        'sales_rep' => 'Jane Smith',
        'payment_link' => 'https://payment.example.com/inv-123',
        'custom_fields' => [
            'po_number' => 'PO-2025-456',
            'contract_id' => 'CNT-789',
        ],
    ],
]));

// Access metadata
$projectId = $document->metadata['project_id'];
$poNumber = $document->metadata['custom_fields']['po_number'];
```

### Voucher Summary Support

[](#voucher-summary-support)

The default template includes voucher summary support. Include voucher data in metadata:

```
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [...],
    'discount_amount' => 50.00,
    'metadata' => [
        'voucher_summary' => [
            'voucher_codes' => ['SUMMER2025', 'LOYALTY10'],
            'total_discount_cents' => 5000,  // $50.00
            'total_charge_cents' => 0,
            'vouchers' => [
                [
                    'code' => 'SUMMER2025',
                    'name' => 'Summer Sale',
                    'amount_cents' => -3000,  // -$30.00 (discount)
                ],
                [
                    'code' => 'LOYALTY10',
                    'name' => 'Loyalty Discount',
                    'amount_cents' => -2000,  // -$20.00 (discount)
                ],
            ],
        ],
    ],
]));
```

### Conditional PDF Generation

[](#conditional-pdf-generation)

Control when PDFs are generated:

```
// Generate PDF immediately
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [...],
    'generate_pdf' => true,  // PDF generated on creation
]));

// Generate PDF later (e.g., after approval)
$document = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'items' => [...],
    'generate_pdf' => false,  // No PDF yet
]));

// Later, when ready
if ($document->status === DocStatus::APPROVED) {
    $docService->generatePdf($document, save: true);
}
```

### Multiple Document Types

[](#multiple-document-types)

Create different document types with specific configurations:

```
// Invoice
$invoice = $docService->createDoc(DocData::from([
    'doc_type' => 'invoice',
    'due_date' => now()->addDays(30),
    // ... invoice data
]));

// Receipt (no due date)
$receipt = $docService->createDoc(DocData::from([
    'doc_type' => 'receipt',
    // ... receipt data
]));

// Ticket
$ticket = $docService->createDoc(DocData::from([
    'doc_type' => 'ticket',
    'docable_type' => Ticket::class,
    'docable_id' => $ticket->id,
    // ... ticket data
]));
```

### Testing

[](#testing)

Example test for document creation:

```
use AIArmada\Docs\Services\DocService;
use AIArmada\Docs\Data\DocData;
use AIArmada\Docs\Models\Doc;

test('creates invoice document', function () {
    $docService = app(DocService::class);

    $document = $docService->createDoc(DocData::from([
        'doc_type' => 'invoice',
        'items' => [
            ['name' => 'Service', 'quantity' => 1, 'price' => 100],
        ],
        'customer_data' => [
            'name' => 'Test Customer',
            'email' => 'test@example.com',
        ],
    ]));

    expect($document)->toBeInstanceOf(Doc::class)
        ->and($document->doc_type)->toBe('invoice')
        ->and($document->subtotal)->toBe(100.0)
        ->and($document->total)->toBe(100.0);

    $this->assertDatabaseHas('docs', [
        'id' => $document->id,
        'doc_type' => 'invoice',
    ]);
});
```

Development Tools
-----------------

[](#development-tools)

The package includes the following development tools as specified:

- **Spatie Laravel Package Tools** - For package scaffolding and structure
- **Larastan** - PHPStan wrapper for Laravel, level 6 static analysis
- **Laravel Rector** - Automated code refactoring and upgrades
- **Laravel Pint** - Opinionated PHP code style fixer

Run these tools with:

```
composer format    # Run Laravel Pint
composer analyse   # Run Larastan
vendor/bin/rector  # Run Rector
```

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE.md) for details.

###  Health Score

43

—

FairBetter than 91% of packages

Maintenance81

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity64

Established project with proven stability

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Recently: every ~30 days

Total

33

Last Release

61d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/726da4efcb731bc0ebcdd0b7ce64759e1f8dd63f6f771eab335458f6a2f2d3af?d=identicon)[sairiz](/maintainers/sairiz)

### Embed Badge

![Health badge](/badges/aiarmada-docs/health.svg)

```
[![Health](https://phpackages.com/badges/aiarmada-docs/health.svg)](https://phpackages.com/packages/aiarmada-docs)
```

###  Alternatives

[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1225.0k10](/packages/fleetbase-core-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
