Skip to content

Commit

Permalink
fix serial number generation
Browse files Browse the repository at this point in the history
  • Loading branch information
QuentinGab committed Jan 3, 2025
1 parent d63b967 commit c1a867b
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 40 deletions.
104 changes: 81 additions & 23 deletions src/SerialNumberGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,21 @@ public function generate(
return preg_replace_callback_array(
[
'/S+/' => function ($matches) use ($serie) {
if (! $matches[0]) {
$slot = $matches[0] ?? '';
$slotLength = strlen($slot);
$valueLength = strlen($serie);

if ($slotLength < 1) {
return '';
}
$slotLength = strlen($matches[0]);
throw_if(! $serie, "The serial Number format includes a $slotLength long Serie (S), but no serie has been passed");

$serieLength = strlen(strval($serie));
throw_if(
$serieLength > $slotLength,
"The Serial Number can't be formatted: Serie ($serie) is ($serieLength) digits long while the format has only $slotLength slots."
);
if (! $serie) {
throw new \Exception("The serial Number format includes a $slotLength long Serie (S), but no serie has been specified.");
}

if ($valueLength > $slotLength) {
throw new \Exception("The Serial Number can't be formatted: Serie ($serie) is $valueLength digits long while the format has only $slotLength slots.");
}

return str_pad(
(string) $serie,
Expand All @@ -39,16 +43,60 @@ public function generate(
STR_PAD_LEFT
);
},
'/M+/' => fn ($matches) => $matches[0] && $month ? substr((string) $month, -strlen($matches[0])) : '',
'/Y+/' => fn ($matches) => $matches[0] && $year ? substr((string) $year, -strlen($matches[0])) : '',
'/C+/' => function ($matches) use ($count) {
if (! $matches[0]) {
'/M+/' => function ($matches) use ($month) {
$slot = $matches[0] ?? '';
$slotLength = strlen($slot);

if ($slotLength < 1) {
return '';
}
throw_if(
($countLength = strlen(strval($count))) > $slotLength = strlen($matches[0]),
"The Serial Number can't be formatted: Count ($count) is ($countLength) digit long while the format has only $slotLength slots."

if (! $month) {
throw new \Exception("The serial Number format includes a $slotLength long Month (M), but no month has been specified.");
}

return str_pad(
(string) substr($month, -$slotLength),
$slotLength,
'0',
STR_PAD_LEFT
);
},
'/Y+/' => function ($matches) use ($year) {
$slot = $matches[0] ?? '';
$slotLength = strlen($slot);

if ($slotLength < 1) {
return '';
}

if (! $year) {
throw new \Exception("The serial Number format includes a $slotLength long Year (Y), but no year has been specified.");
}

return str_pad(
(string) substr($year, -$slotLength),
$slotLength,
'0',
STR_PAD_LEFT
);
},
'/C+/' => function ($matches) use ($count) {
$slot = $matches[0] ?? '';
$slotLength = strlen($slot);
$valueLength = strlen((string) $count);

if ($slotLength < 1) {
return '';
}

if (! $count) {
throw new \Exception("The serial Number format includes a {$slotLength} long Count (C), but no count has been specified.");
}

if ($valueLength > $slotLength) {
throw new \Exception("The Serial Number can't be formatted: Count ({$count}) is {$valueLength} digits long while the format has only $slotLength slots.");
}

return str_pad(
(string) $count,
Expand All @@ -59,18 +107,28 @@ public function generate(
},
// Must be kept last to avoid interfering with other callbacks
'/P+/' => function ($matches) use ($prefix) {
if (! $matches[0]) {
$slot = $matches[0] ?? '';
$slotLength = strlen($slot);
$valueLength = strlen($prefix);

if ($slotLength < 1) {
return '';
}
$slotLength = strlen($matches[0]);
$prefixLength = strlen($prefix);

throw_if(
$prefixLength < $slotLength,
"The serial Number can't be formatted, the prefix provided is $prefixLength letters long ({$prefix}), while the format require at minimum a $slotLength letters long prefix"
);
if (! $prefix) {
throw new \Exception("The serial Number format includes a {$slotLength} long Prefix (S), but no prefix has been specified.");
}

if ($valueLength > $slotLength) {
throw new \Exception("The Serial Number can't be formatted: Prefix ({$prefix}) is {$valueLength} digits long while the format has only $slotLength slots.");
}

return substr($prefix, 0, strlen($matches[0]));
return str_pad(
(string) $prefix,
$slotLength,
'0',
STR_PAD_LEFT
);
},
],
$this->format
Expand Down
31 changes: 14 additions & 17 deletions tests/Unit/SerialNumberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,31 @@

use Finller\Invoice\SerialNumberGenerator;

it('can generate serial number from format', function ($format, $prefix, $serie, $count, $expected) {
it('can generate serial number from format', function ($format, $prefix, $serie, $year, $month, $count, $expected) {
$generator = new SerialNumberGenerator(
format: $format,
);

$serialNumber = $generator->generate(
prefix: $prefix,
count: $count,
prefix: $prefix,
serie: $serie,
year: '2022',
month: '01'
year: $year,
month: $month
);

expect($serialNumber)->toBe($expected);
})->with([
['PP-YYCCCC', 'IN', null, 2, 'IN-220002'],
['SSSS-YYCCCC', null, 1, 2, '0001-220002'],
['SSSS-CCCC', null, 1, 2, '0001-0002'],
['SSCC', null, 1, 2, '0102'],
['CCCC', null, 1, 2, '0002'],
['SSSSPP-YYCCCC', 'IN', 1, 2, '0001IN-220002'],
['YYSSSSPPCCCC', 'IN', 1, 2, '220001IN0002'],
['YYCCCCSSSSPP', 'IN', 1, 2, '2200020001IN'],
['PPSSSS-YYYYCCCC', 'IN', 1, 2, 'IN0001-20220002'],
['PPSSSS-YYYCCCC', 'IN', 1, 2, 'IN0001-0220002'],
['PPCCCC', 'IN', null, 102, 'IN0102'],
['PPSSSS-YYCCCC', 'YC', 1, 2, 'YC0001-220002'],
['PPSSSS-YYCCCC', 'PS', 1, 2, 'PS0001-220002'],
['PPPSSSSSSS-YYMMCCCC', 'INV', 1, 2025, 1, 1, 'INV0000001-25010001'],
['PPPSSSSSSS-YYMMCCCC', 'INV', 1, 25, 1, 1, 'INV0000001-25010001'],
['PPPSSSSSSS-YYMMCCCC', 'INV', 1000001, 25, 12, 1001, 'INV1000001-25121001'],
['PPP-YYMMCCCC', 'INV', null, 25, 12, 1001, 'INV-25121001'],
['PPP-MMCCCC', 'INV', null, null, 12, 1001, 'INV-121001'],
['PPP-CCCC', 'INV', null, null, null, 1001, 'INV-1001'],
['CCCC', null, null, null, null, 1001, '1001'],
['PPPSSSSSSS-CCCC', 'INV', 1000001, null, null, 1001, 'INV1000001-1001'],
['PPPSSSSSSS-YYCCCC', 'INV', 1000001, 25, null, 1001, 'INV1000001-251001'],
['PPP-YYCCCC', 'INV', 1000001, 25, null, 1001, 'INV-251001'],
]);

it('can parse serial number from format', function ($format, $serialNumber, $prefix, $serie, $year, $count) {
Expand Down

0 comments on commit c1a867b

Please sign in to comment.