azaharizaman / laravel-serial-numbering
A Laravel 12 Composer package for configurable serial number generation with optional audit logging and model integration.
Installs: 25
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/azaharizaman/laravel-serial-numbering
Requires
- php: ^8.3
- illuminate/console: ^12.0
- illuminate/database: ^12.0
- illuminate/http: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
- laravel/sanctum: ^4.0
- spatie/laravel-activitylog: ^4.8
Requires (Dev)
- pestphp/pest: ^2.36
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2025-12-02 13:42:48 UTC
README
A powerful Laravel 12 package for generating configurable serial numbers with dynamic segments, custom reset strategies, comprehensive audit logging, and RESTful API support. Perfect for invoices, orders, tickets, and any entity requiring unique sequential identifiers.
โจ Features
Core 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, interval, or custom reset strategies
- ๐ 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 - 71 tests with 163 assertions
New in v1.1.0
- ๐ Custom Reset Strategies - Fiscal year resets, business day resets, and pluggable custom logic
- ๏ฟฝ Spatie Activity Log Integration - Rich audit trail with tenant support and activity timeline
- ๐ RESTful API Endpoints - Generate, preview, reset, void, and query serials via REST API
- ๐งช Concurrency Stress Tests - Validated under 100+ concurrent operations
๐ Documentation
- Quick Start - Get started in minutes
- API Documentation - Complete REST API reference
- Custom Reset Strategies - Build custom reset logic
- Examples - Real-world usage examples
- Roadmap - Upcoming features
- Changelog - Version history
- Release Notes - v1.1.0 features
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 resetsdaily- Reset at midnight each dayweekly- Reset at start of each weekmonthly- Reset on first day of each monthyearly- Reset on January 1stinterval- 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
- Documentation: Full documentation
- Issues: GitHub Issues
- Email: azaharizaman@gmail.com