azaharizaman/laravel-serial-numbering

A Laravel 12 Composer package for configurable serial number generation with optional audit logging and model integration.

Installs: 7

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/azaharizaman/laravel-serial-numbering

v0.1.0 2025-11-02 13:22 UTC

This package is auto-updated.

Last update: 2025-11-02 13:27:28 UTC


README

Latest Version on Packagist Total Downloads License

A powerful Laravel 12 package for generating configurable serial numbers with dynamic segments, auto-reset rules, and comprehensive audit logging. Perfect for invoices, orders, tickets, and any entity requiring unique sequential identifiers.

Features

  • ๐ŸŽฏ Pattern-Based Generation - Create serial numbers using dynamic segments like {year}, {month}, {number}, and custom model properties
  • ๐Ÿ”„ Auto-Reset Rules - Configure daily, weekly, monthly, yearly, or custom interval resets
  • ๐Ÿ”’ Concurrency Safe - Built-in atomic locks prevent race conditions in high-traffic environments
  • ๐Ÿ“Š Audit Logging - Track every serial number generation with user information and timestamps
  • โœ… Uniqueness Enforcement - Automatic collision detection and prevention
  • ๐Ÿ—‘๏ธ Serial Voiding - Soft-delete approach for cancelled or erroneous serials
  • ๐Ÿงฉ Eloquent Integration - Simple trait for seamless model integration
  • ๐Ÿ”Œ Extensible - Register custom segment resolvers for specialized patterns
  • ๐Ÿงช Well Tested - Comprehensive test suite included

Installation

Install via Composer:

composer require azaharizaman/controlled-number

Publish the configuration file:

php artisan vendor:publish --tag=serial-pattern-config

Run migrations:

php artisan migrate

Quick Start

1. Configure Patterns

Edit config/serial-pattern.php:

'patterns' => [
    'invoice' => [
        'pattern' => 'INV-{year}-{month}-{number}',
        'start' => 1000,
        'digits' => 5,
        'reset' => 'monthly',
        'interval' => 1,
    ],
    'order' => [
        'pattern' => 'ORD-{year}{month}{day}-{number}',
        'start' => 1,
        'digits' => 4,
        'reset' => 'daily',
    ],
],

2. Use in Models

Add the trait to your Eloquent model:

use AzahariZaman\ControlledNumber\Traits\HasSerialNumbering;
use Illuminate\Database\Eloquent\Model;

class Invoice extends Model
{
    use HasSerialNumbering;

    protected $serialPattern = 'invoice';
    protected $serialColumn = 'invoice_number';
    
    protected $fillable = ['invoice_number', 'amount', 'customer_id'];
}

3. Generate Serials

Serials are generated automatically on model creation:

$invoice = Invoice::create([
    'amount' => 1500.00,
    'customer_id' => 1,
]);

echo $invoice->invoice_number; // INV-2024-10-01000

Or generate manually:

use AzahariZaman\ControlledNumber\Services\SerialManager;

$manager = app(SerialManager::class);
$serial = $manager->generate('invoice');

Available Segments

Built-in Date/Time Segments

  • {year} - Four-digit year (e.g., 2024)
  • {year_short} - Two-digit year (e.g., 24)
  • {month} - Two-digit month (01-12)
  • {month_name} - Short month name (Jan-Dec)
  • {day} - Two-digit day (01-31)
  • {hour} - Two-digit hour (00-23)
  • {minute} - Two-digit minute (00-59)
  • {second} - Two-digit second (00-59)
  • {week} - ISO week number (01-53)
  • {quarter} - Quarter number (1-4)
  • {timestamp} - Unix timestamp

Model Property Segments

Use dot notation to access model properties and relationships:

'pattern' => 'INV-{year}-{department.code}-{number}'

Custom Segments

Register custom segment resolvers in config/serial-pattern.php:

'segments' => [
    'custom.branch' => \App\Segments\BranchCodeResolver::class,
],

Create your resolver:

namespace App\Segments;

use AzahariZaman\ControlledNumber\Contracts\SegmentInterface;
use Illuminate\Database\Eloquent\Model;

class BranchCodeResolver implements SegmentInterface
{
    public function resolve(?Model $model = null, array $context = []): string
    {
        return auth()->user()->branch->code ?? 'HQ';
    }

    public function getName(): string
    {
        return 'custom.branch';
    }

    public function validate(): bool
    {
        return true;
    }
}

Reset Types

Configure automatic counter resets:

  • never - Counter never resets
  • daily - Reset at midnight each day
  • weekly - Reset at start of each week
  • monthly - Reset on first day of each month
  • yearly - Reset on January 1st
  • interval - Reset after specified number of days
'invoice' => [
    'pattern' => 'INV-{year}{month}-{number}',
    'reset' => 'monthly',
    'interval' => 1, // Required for 'interval' reset type
],

Advanced Usage

Preview Serial Numbers

Preview the next serial without generating it:

$manager = app(SerialManager::class);
$preview = $manager->preview('invoice');

Or in a model:

$nextSerial = $invoice->previewSerialNumber();

Void Serial Numbers

Mark a serial as void (soft delete for audit purposes):

$manager->void('INV-2024-10-01000', 'Duplicate invoice');

// Or via model
$invoice->voidSerial('Customer cancelled order');

Manual Reset

Reset a sequence counter manually:

$manager->resetSequence('invoice'); // Reset to configured start value
$manager->resetSequence('invoice', 5000); // Reset to specific value

Export Audit Logs

use AzahariZaman\ControlledNumber\Helpers\SerialHelper;

// Export to CSV
$csv = SerialHelper::exportToCsv([
    'pattern' => 'invoice',
    'start_date' => '2024-01-01',
    'end_date' => '2024-12-31',
]);

// Export to JSON
$json = SerialHelper::exportToJson(['is_void' => false]);

Pattern Statistics

$stats = SerialHelper::getPatternStats('invoice');
/*
[
    'pattern' => 'invoice',
    'total' => 1523,
    'active' => 1487,
    'voided' => 36,
    'void_rate' => 2.36,
]
*/

Artisan Commands

Validate Patterns

Check all configured patterns for errors:

php artisan serial:validate-patterns

Validate a specific pattern:

php artisan serial:validate-patterns --pattern=invoice

Show statistics:

php artisan serial:validate-patterns --stats

Query Scopes

The SerialLog model provides useful query scopes:

use AzahariZaman\ControlledNumber\Models\SerialLog;

// Get active serials
$active = SerialLog::active()->get();

// Get voided serials
$voided = SerialLog::voided()->get();

// Filter by pattern
$invoices = SerialLog::forPattern('invoice')->get();

// Filter by user
$userSerials = SerialLog::byUser(auth()->id())->get();

// Filter by date range
$recent = SerialLog::betweenDates('2024-10-01', '2024-10-31')->get();

Configuration

Disable Logging

'logging' => [
    'enabled' => false,
    'track_user' => false,
],

Concurrency Settings

'lock' => [
    'enabled' => true,
    'timeout' => 10, // seconds
    'store' => 'redis', // cache store for locks
],

Testing

Run the test suite:

composer test

Security

Serial logs cannot be deleted for audit trail integrity. Attempting to delete will throw SerialDeletionNotAllowedException.

Changelog

Please see CHANGELOG for more information on recent changes.

Contributing

Contributions are welcome! Please see CONTRIBUTING for details.

Credits

License

The MIT License (MIT). Please see License File for more information.

Support