Skip to content

Commit 98cd8b9

Browse files
committed
feat: Add support for date-based formats in running numbers
- Implemented DatePrefixPresenter, CompactDatePresenter, and YearMonthPresenter for generating running numbers with date components. - Updated documentation to include examples and use cases for date-based formats. - Added tests for date-based presenters to ensure correct formatting and padding behavior. feat: Introduce multiple sequences per type using scopes - Added scope functionality to maintain independent sequences for the same running number type. - Updated database structure to support composite unique constraints on type and scope. - Enhanced documentation with examples for multi-branch systems, department-specific numbering, and category-based sequences. feat: Allow custom starting numbers for running number sequences - Implemented startFrom() method to configure sequences to start from a specific number. - Updated documentation with use cases for system migration and sequential year-based numbering. - Added tests to verify custom starting number functionality. feat: Implement number range management with maximum limits - Introduced maxNumber() method to set upper limits for running number sequences. - Added exception handling for MaxNumberReachedException when limits are exceeded. - Updated documentation with examples for daily ticket systems and limited capacity events. - Added tests to ensure correct behavior when maximum numbers are reached. fix: Update exception handling and logging for max number reached scenarios - Improved exception messages and logging for better monitoring and alerting. - Enhanced model integration examples to demonstrate best practices for handling max number limits. chore: Update README with advanced features overview - Compiled documentation for advanced features including date-based formats, multiple sequences, custom starting numbers, and number range management.
1 parent cb25d0e commit 98cd8b9

18 files changed

+2129
-11
lines changed

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ Generate sequential running numbers for your Laravel application. Perfect for in
1212

1313
- 🔢 **Sequential Generation** - Automatic sequential number generation per type
1414
- 💾 **Database Persistence** - Reliable state storage in your database
15+
- 🔄 **Reset Periods** - Automatic reset (daily, monthly, yearly, or never)
16+
- 🔒 **Thread-Safe** - Race condition protection with database transactions
17+
- 🏢 **Multiple Sequences** - Separate sequences per type using scopes
18+
- 🎯 **Custom Starting Numbers** - Start sequences from any number
19+
- 📊 **Range Management** - Set maximum limits with exception handling
20+
- 📅 **Date-Based Formats** - Built-in date presenters for time-organized numbers
1521
- ⚙️ **Configurable** - Customize padding, formatting, and behavior
1622
- 🆔 **UUID Support** - Built-in UUID support for running number records
1723
- 🏷️ **Native PHP Enums** - Modern PHP 8.1+ enum support with Traitify
1824
- 🔧 **Extensible** - Custom generators and presenters via contracts
1925
- 🚀 **Developer Friendly** - Helper functions, facades, and excellent IDE support
20-
-**Well Tested** - Comprehensive test coverage
26+
-**Well Tested** - 51 tests with 124 assertions
2127
- 📦 **Wide Compatibility** - Laravel 9-12 & PHP 8.1-8.4
2228

2329
## 📦 Installation
@@ -111,15 +117,18 @@ Comprehensive documentation is available in the [docs](docs/) directory:
111117
- **[Getting Started](docs/01-getting-started/)** - Installation, quick start, and core concepts
112118
- **[Configuration](docs/02-configuration/)** - Configuration options, types, enums, and models
113119
- **[Usage](docs/03-usage/)** - Helper functions, facades, model integration, and examples
114-
- **[Advanced Topics](docs/04-advanced/)** - Custom presenters, generators, and integrations
120+
- **[Advanced Features](docs/04-advanced-features/)** - Date formats, scopes, ranges, and more
115121
- **[Development](docs/05-development/)** - Testing, contributing, and development setup
116122

117123
### Quick Links
118124

119125
- [Installation Guide](docs/01-getting-started/01-installation.md)
120126
- [Quick Start](docs/01-getting-started/02-quick-start.md)
121127
- [Common Scenarios](docs/03-usage/05-common-scenarios.md)
122-
- [Custom Presenters](docs/04-advanced/01-custom-presenters.md)
128+
- [Date-Based Formats](docs/04-advanced-features/01-date-based-formats.md)
129+
- [Multiple Sequences](docs/04-advanced-features/02-multiple-sequences.md)
130+
- [Custom Starting Numbers](docs/04-advanced-features/03-custom-starting-numbers.md)
131+
- [Number Range Management](docs/04-advanced-features/04-number-range-management.md)
123132
- [Upgrade Guide](docs/06-upgrade-guide.md)
124133
- [API Reference](docs/README.md)
125134

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up()
10+
{
11+
Schema::table('running_numbers', function (Blueprint $table) {
12+
$table->string('scope')->nullable()->after('type');
13+
14+
// Drop the old unique constraint on type if it exists
15+
// Add composite unique constraint on type + scope
16+
$table->unique(['type', 'scope'], 'running_numbers_type_scope_unique');
17+
});
18+
}
19+
20+
public function down()
21+
{
22+
Schema::table('running_numbers', function (Blueprint $table) {
23+
$table->dropUnique('running_numbers_type_scope_unique');
24+
$table->dropColumn('scope');
25+
});
26+
}
27+
};

docs/03-usage/02-generator-class.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,36 @@ use CleaniqueCoders\RunningNumber\Contracts\Presenter;
6565
$generator->formatter(new CustomPresenter());
6666
```
6767

68+
### `scope(?string $scope)`
69+
70+
Set a scope for multiple sequences per type:
71+
72+
```php
73+
$generator->scope('retail');
74+
```
75+
76+
See [Multiple Sequences](../04-advanced-features/02-multiple-sequences.md) for details.
77+
78+
### `startFrom(int $number)`
79+
80+
Set a custom starting number:
81+
82+
```php
83+
$generator->startFrom(1000);
84+
```
85+
86+
See [Custom Starting Numbers](../04-advanced-features/03-custom-starting-numbers.md) for details.
87+
88+
### `maxNumber(int $number)`
89+
90+
Set a maximum number limit:
91+
92+
```php
93+
$generator->maxNumber(9999);
94+
```
95+
96+
See [Number Range Management](../04-advanced-features/04-number-range-management.md) for details.
97+
6898
### `generate()`
6999

70100
Generate the running number:
@@ -86,6 +116,22 @@ $number = Generator::make()
86116
->generate();
87117
```
88118

119+
### With Advanced Features
120+
121+
```php
122+
use CleaniqueCoders\RunningNumber\Generator;
123+
use CleaniqueCoders\RunningNumber\Presenters\YearMonthPresenter;
124+
125+
$number = Generator::make()
126+
->type('invoice')
127+
->scope('retail')
128+
->startFrom(1000)
129+
->maxNumber(9999)
130+
->formatter(new YearMonthPresenter())
131+
->generate();
132+
// Output: INVOICE-2025-11-1001
133+
```
134+
89135
### Reusable Instance
90136

91137
```php
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Date-Based Formats
2+
3+
Generate running numbers with date components embedded in the format. This is useful for creating human-readable, time-organized identifiers.
4+
5+
## Available Presenters
6+
7+
### DatePrefixPresenter
8+
9+
Adds a full date prefix to the running number.
10+
11+
```php
12+
use CleaniqueCoders\RunningNumber\Generator;
13+
use CleaniqueCoders\RunningNumber\Presenters\DatePrefixPresenter;
14+
15+
$number = Generator::make()
16+
->type('invoice')
17+
->formatter(new DatePrefixPresenter())
18+
->generate();
19+
20+
// Output: INVOICE-2025-11-13-001
21+
```
22+
23+
#### Custom Date Format
24+
25+
```php
26+
$presenter = new DatePrefixPresenter('Y/m/d', '/');
27+
28+
$number = Generator::make()
29+
->type('order')
30+
->formatter($presenter)
31+
->generate();
32+
33+
// Output: ORDER/2025/11/13/001
34+
```
35+
36+
**Constructor Parameters:**
37+
- `$dateFormat` (string): PHP date format string (default: 'Y-m-d')
38+
- `$separator` (string): Separator between parts (default: '-')
39+
40+
### CompactDatePresenter
41+
42+
Creates compact date formats without separators in the date portion.
43+
44+
```php
45+
use CleaniqueCoders\RunningNumber\Presenters\CompactDatePresenter;
46+
47+
$number = Generator::make()
48+
->type('receipt')
49+
->formatter(new CompactDatePresenter())
50+
->generate();
51+
52+
// Output: RECEIPT-20251113001
53+
```
54+
55+
#### Custom Format
56+
57+
```php
58+
$presenter = new CompactDatePresenter('Ym', '_');
59+
60+
$number = Generator::make()
61+
->type('ticket')
62+
->formatter($presenter)
63+
->generate();
64+
65+
// Output: TICKET_202511001
66+
```
67+
68+
**Constructor Parameters:**
69+
- `$dateFormat` (string): PHP date format string (default: 'Ymd')
70+
- `$separator` (string): Separator between type and date (default: '-')
71+
72+
### YearMonthPresenter
73+
74+
Formats with separate year and month components.
75+
76+
```php
77+
use CleaniqueCoders\RunningNumber\Presenters\YearMonthPresenter;
78+
79+
$number = Generator::make()
80+
->type('invoice')
81+
->formatter(new YearMonthPresenter())
82+
->generate();
83+
84+
// Output: INVOICE-2025-11-001
85+
```
86+
87+
#### Custom Separator
88+
89+
```php
90+
$presenter = new YearMonthPresenter('/');
91+
92+
$number = Generator::make()
93+
->type('order')
94+
->formatter($presenter)
95+
->generate();
96+
97+
// Output: ORDER/2025/11/001
98+
```
99+
100+
**Constructor Parameters:**
101+
- `$separator` (string): Separator between parts (default: '-')
102+
103+
## Use Cases
104+
105+
### Monthly Invoice Numbering
106+
107+
```php
108+
use CleaniqueCoders\RunningNumber\Presenters\YearMonthPresenter;
109+
110+
$invoice = Generator::make()
111+
->type('invoice')
112+
->formatter(new YearMonthPresenter())
113+
->generate();
114+
115+
// January 2025: INVOICE-2025-01-001
116+
// February 2025: INVOICE-2025-02-001
117+
```
118+
119+
### Daily Ticket System
120+
121+
```php
122+
use CleaniqueCoders\RunningNumber\Presenters\DatePrefixPresenter;
123+
124+
$ticket = Generator::make()
125+
->type('ticket')
126+
->formatter(new DatePrefixPresenter('Y-m-d', '-'))
127+
->generate();
128+
129+
// Output: TICKET-2025-11-13-001
130+
```
131+
132+
### Compact Receipt Numbers
133+
134+
```php
135+
use CleaniqueCoders\RunningNumber\Presenters\CompactDatePresenter;
136+
137+
$receipt = Generator::make()
138+
->type('receipt')
139+
->formatter(new CompactDatePresenter('Ymd', ''))
140+
->generate();
141+
142+
// Output: RECEIPT20251113001
143+
```
144+
145+
## Combining with Reset Periods
146+
147+
Date-based formats work seamlessly with reset periods:
148+
149+
```php
150+
// Configure in config/running-number.php
151+
'reset_period' => [
152+
'types' => [
153+
'invoice' => 'monthly',
154+
],
155+
],
156+
157+
// Usage
158+
use CleaniqueCoders\RunningNumber\Presenters\YearMonthPresenter;
159+
160+
$invoice = Generator::make()
161+
->type('invoice')
162+
->formatter(new YearMonthPresenter())
163+
->generate();
164+
165+
// Automatically resets each month
166+
// Jan 2025: INVOICE-2025-01-001, INVOICE-2025-01-002, ...
167+
// Feb 2025: INVOICE-2025-02-001, INVOICE-2025-02-002, ... (reset)
168+
```
169+
170+
## Configuration
171+
172+
### Padding
173+
174+
The number padding configuration still applies:
175+
176+
```php
177+
// config/running-number.php
178+
'padding' => 5,
179+
180+
// Usage
181+
$number = Generator::make()
182+
->type('order')
183+
->formatter(new DatePrefixPresenter())
184+
->generate();
185+
186+
// Output: ORDER-2025-11-13-00001
187+
```
188+
189+
## Creating Custom Date Presenters
190+
191+
You can create your own date-based presenter by implementing the `Presenter` contract:
192+
193+
```php
194+
use CleaniqueCoders\RunningNumber\Contracts\Presenter;
195+
196+
class CustomDatePresenter implements Presenter
197+
{
198+
public function format($type, $number): string
199+
{
200+
$padding = config('running-number.padding', 3);
201+
$paddedNumber = str_pad($number, $padding, '0', STR_PAD_LEFT);
202+
203+
$year = date('Y');
204+
$week = date('W');
205+
206+
return "{$type}-W{$week}-{$year}-{$paddedNumber}";
207+
// Example: INVOICE-W46-2025-001
208+
}
209+
}
210+
```

0 commit comments

Comments
 (0)