allow to decrement invoice counter (#1947)
* added increment for cc, ccy, ccn, ccd
This commit is contained in:
@@ -62,103 +62,135 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface
|
|||||||
|
|
||||||
preg_match_all('/{[^}]*?}/', $format, $matches);
|
preg_match_all('/{[^}]*?}/', $format, $matches);
|
||||||
foreach ($matches[0] as $part) {
|
foreach ($matches[0] as $part) {
|
||||||
$formatterLength = null;
|
$partialResult = $this->parseReplacer($invoiceDate, $part);
|
||||||
$increaseBy = 1;
|
|
||||||
|
|
||||||
$tmp = str_replace(['{', '}'], '', $part);
|
|
||||||
|
|
||||||
$parts = preg_split('/[,]+/', $tmp);
|
|
||||||
$tmp = $parts[0];
|
|
||||||
if (\count($parts) === 2) {
|
|
||||||
$formatterLength = \intval($parts[1]);
|
|
||||||
if ((string) $formatterLength !== $parts[1]) {
|
|
||||||
$formatterLength = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$parts = preg_split("/[\+]+/", $tmp);
|
|
||||||
$tmp = $parts[0];
|
|
||||||
if (\count($parts) === 2) {
|
|
||||||
$increaseBy = \intval($parts[1]);
|
|
||||||
if ($increaseBy <= 0) {
|
|
||||||
$increaseBy = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($tmp) {
|
|
||||||
case 'Y':
|
|
||||||
$partialResult = $invoiceDate->format('Y');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'y':
|
|
||||||
$partialResult = $invoiceDate->format('y');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'M':
|
|
||||||
$partialResult = $invoiceDate->format('m');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'm':
|
|
||||||
$partialResult = $invoiceDate->format('n');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'D':
|
|
||||||
$partialResult = $invoiceDate->format('d');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
|
||||||
$partialResult = $invoiceDate->format('j');
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'date':
|
|
||||||
$partialResult = $invoiceDate->format('ymd');
|
|
||||||
break;
|
|
||||||
|
|
||||||
// for customer
|
|
||||||
case 'cc':
|
|
||||||
$partialResult = $this->repository->getCounterForAllTime($invoiceDate, $this->model->getCustomer()) + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ccy':
|
|
||||||
$partialResult = $this->repository->getCounterForYear($invoiceDate, $this->model->getCustomer()) + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ccm':
|
|
||||||
$partialResult = $this->repository->getCounterForMonth($invoiceDate, $this->model->getCustomer()) + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ccd':
|
|
||||||
$partialResult = $this->repository->getCounterForDay($invoiceDate, $this->model->getCustomer()) + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// across all invoices
|
|
||||||
case 'c':
|
|
||||||
$partialResult = $this->repository->getCounterForAllTime($invoiceDate) + $increaseBy;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cy':
|
|
||||||
$partialResult = $this->repository->getCounterForYear($invoiceDate) + $increaseBy;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cm':
|
|
||||||
$partialResult = $this->repository->getCounterForMonth($invoiceDate) + $increaseBy;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'cd':
|
|
||||||
$partialResult = $this->repository->getCounterForDay($invoiceDate) + $increaseBy;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$partialResult = $part;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $formatterLength) {
|
|
||||||
$partialResult = str_pad($partialResult, $formatterLength, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = str_replace($part, $partialResult, $result);
|
$result = str_replace($part, $partialResult, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (string) $result;
|
return (string) $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function parseReplacer(\DateTime $invoiceDate, string $originalFormat): string
|
||||||
|
{
|
||||||
|
$formatterLength = null;
|
||||||
|
$increaseBy = 0;
|
||||||
|
$formatPattern = str_replace(['{', '}'], '', $originalFormat);
|
||||||
|
|
||||||
|
$parts = preg_split('/([+\-,])+/', $formatPattern, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
$format = array_shift($parts);
|
||||||
|
|
||||||
|
if (\count($parts) % 2 !== 0) {
|
||||||
|
throw new \InvalidArgumentException('Invalid configuration found');
|
||||||
|
}
|
||||||
|
|
||||||
|
while (null !== ($tmp = array_shift($parts))) {
|
||||||
|
switch ($tmp) {
|
||||||
|
case '+':
|
||||||
|
$local = array_shift($parts);
|
||||||
|
if (!is_numeric($local)) {
|
||||||
|
throw new \InvalidArgumentException('Unknown increment found');
|
||||||
|
}
|
||||||
|
$increaseBy = $increaseBy + \intval($local);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
$local = array_shift($parts);
|
||||||
|
if (!is_numeric($local)) {
|
||||||
|
throw new \InvalidArgumentException('Unknown decrement found');
|
||||||
|
}
|
||||||
|
$increaseBy = $increaseBy - \intval($local);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
$local = array_shift($parts);
|
||||||
|
if (!is_numeric($local)) {
|
||||||
|
throw new \InvalidArgumentException('Unknown format length found');
|
||||||
|
}
|
||||||
|
$formatterLength = \intval($local);
|
||||||
|
if ((string) $formatterLength !== $local) {
|
||||||
|
throw new \InvalidArgumentException('Unknown format length found');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \InvalidArgumentException('Unknown pattern found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($increaseBy === 0) {
|
||||||
|
$increaseBy = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($format) {
|
||||||
|
case 'Y':
|
||||||
|
$partialResult = $invoiceDate->format('Y');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
$partialResult = $invoiceDate->format('y');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
$partialResult = $invoiceDate->format('m');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
$partialResult = $invoiceDate->format('n');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
$partialResult = $invoiceDate->format('d');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
$partialResult = $invoiceDate->format('j');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
$partialResult = $invoiceDate->format('ymd');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// for customer
|
||||||
|
case 'cc':
|
||||||
|
$partialResult = $this->repository->getCounterForAllTime($invoiceDate, $this->model->getCustomer()) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ccy':
|
||||||
|
$partialResult = $this->repository->getCounterForYear($invoiceDate, $this->model->getCustomer()) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ccm':
|
||||||
|
$partialResult = $this->repository->getCounterForMonth($invoiceDate, $this->model->getCustomer()) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ccd':
|
||||||
|
$partialResult = $this->repository->getCounterForDay($invoiceDate, $this->model->getCustomer()) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// across all invoices
|
||||||
|
case 'c':
|
||||||
|
$partialResult = $this->repository->getCounterForAllTime($invoiceDate) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cy':
|
||||||
|
$partialResult = $this->repository->getCounterForYear($invoiceDate) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cm':
|
||||||
|
$partialResult = $this->repository->getCounterForMonth($invoiceDate) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'cd':
|
||||||
|
$partialResult = $this->repository->getCounterForDay($invoiceDate) + $increaseBy;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$partialResult = $originalFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $formatterLength) {
|
||||||
|
$partialResult = str_pad($partialResult, $formatterLength, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $partialResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
*/
|
*/
|
||||||
class ConfigurableNumberGeneratorTest extends TestCase
|
class ConfigurableNumberGeneratorTest extends TestCase
|
||||||
{
|
{
|
||||||
private function getSut(string $format)
|
private function getSut(string $format, int $counter = 1)
|
||||||
{
|
{
|
||||||
$config = $this->createMock(SystemConfiguration::class);
|
$config = $this->createMock(SystemConfiguration::class);
|
||||||
$config->expects($this->any())
|
$config->expects($this->any())
|
||||||
@@ -33,19 +33,19 @@ class ConfigurableNumberGeneratorTest extends TestCase
|
|||||||
$repository
|
$repository
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('getCounterForAllTime')
|
->method('getCounterForAllTime')
|
||||||
->willReturn(1);
|
->willReturn($counter);
|
||||||
$repository
|
$repository
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('getCounterForYear')
|
->method('getCounterForYear')
|
||||||
->willReturn(1);
|
->willReturn($counter);
|
||||||
$repository
|
$repository
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('getCounterForMonth')
|
->method('getCounterForMonth')
|
||||||
->willReturn(1);
|
->willReturn($counter);
|
||||||
$repository
|
$repository
|
||||||
->expects($this->any())
|
->expects($this->any())
|
||||||
->method('getCounterForDay')
|
->method('getCounterForDay')
|
||||||
->willReturn(1);
|
->willReturn($counter);
|
||||||
|
|
||||||
return new ConfigurableNumberGenerator($repository, $config);
|
return new ConfigurableNumberGenerator($repository, $config);
|
||||||
}
|
}
|
||||||
@@ -68,12 +68,14 @@ class ConfigurableNumberGeneratorTest extends TestCase
|
|||||||
['{cy}', '2', $invoiceDate],
|
['{cy}', '2', $invoiceDate],
|
||||||
['{cm}', '2', $invoiceDate],
|
['{cm}', '2', $invoiceDate],
|
||||||
['{cd}', '2', $invoiceDate],
|
['{cd}', '2', $invoiceDate],
|
||||||
|
['{cc}', '2', $invoiceDate],
|
||||||
|
['{ccy}', '2', $invoiceDate],
|
||||||
|
['{ccm}', '2', $invoiceDate],
|
||||||
|
['{ccd}', '2', $invoiceDate],
|
||||||
// number formatting (not testing the lower case versions, as the tests might break depending on the date)
|
// number formatting (not testing the lower case versions, as the tests might break depending on the date)
|
||||||
['{date,10}', '0000' . $invoiceDate->format('ymd'), $invoiceDate],
|
['{date,10}', '0000' . $invoiceDate->format('ymd'), $invoiceDate],
|
||||||
['{date,a}', $invoiceDate->format('ymd'), $invoiceDate], // invalid formatter length
|
|
||||||
['{Y,6}', '00' . $invoiceDate->format('Y'), $invoiceDate],
|
['{Y,6}', '00' . $invoiceDate->format('Y'), $invoiceDate],
|
||||||
['{M,3}', '0' . $invoiceDate->format('m'), $invoiceDate],
|
['{M,3}', '0' . $invoiceDate->format('m'), $invoiceDate],
|
||||||
['{M,#}', $invoiceDate->format('m'), $invoiceDate], // invalid formatter length
|
|
||||||
['{D,3}', '0' . $invoiceDate->format('d'), $invoiceDate],
|
['{D,3}', '0' . $invoiceDate->format('d'), $invoiceDate],
|
||||||
// counter across all invoices
|
// counter across all invoices
|
||||||
['{c,2}', '02', $invoiceDate],
|
['{c,2}', '02', $invoiceDate],
|
||||||
@@ -86,32 +88,50 @@ class ConfigurableNumberGeneratorTest extends TestCase
|
|||||||
['{ccm,2}', '02', $invoiceDate],
|
['{ccm,2}', '02', $invoiceDate],
|
||||||
['{ccd,2}', '02', $invoiceDate],
|
['{ccd,2}', '02', $invoiceDate],
|
||||||
// with incrementing counter
|
// with incrementing counter
|
||||||
|
['{c+13,3}', '014', $invoiceDate],
|
||||||
['{c+13,2}', '14', $invoiceDate],
|
['{c+13,2}', '14', $invoiceDate],
|
||||||
['{ccy+1,2}', '02', $invoiceDate],
|
|
||||||
['{cm+-1,2}', '02', $invoiceDate], // negative is not allowed and set to 1
|
|
||||||
['{cm+0,2}', '02', $invoiceDate], // zero is not allowed and set to 1
|
|
||||||
['{cd+111,2}', '112', $invoiceDate],
|
|
||||||
['{c+13}', '14', $invoiceDate],
|
['{c+13}', '14', $invoiceDate],
|
||||||
['{cy+1}', '2', $invoiceDate],
|
|
||||||
['{cm+-1}', '2', $invoiceDate], // negative is not allowed and set to 1
|
|
||||||
['{cm+0}', '2', $invoiceDate], // zero is not allowed and set to 1
|
|
||||||
['{cd+111}', '112', $invoiceDate],
|
['{cd+111}', '112', $invoiceDate],
|
||||||
|
['{cd+111,5}', '00112', $invoiceDate],
|
||||||
|
['{cd+111,2}', '113', $invoiceDate, 2],
|
||||||
|
['{cm+0,2}', '03', $invoiceDate, 2], // zero is not allowed and set to 1
|
||||||
|
['{cm+0}', '2', $invoiceDate], // zero is not allowed and set to 1
|
||||||
|
['{cy+2}', '3', $invoiceDate],
|
||||||
|
['{cc+4}', '5', $invoiceDate],
|
||||||
|
['{ccy+2,2}', '03', $invoiceDate],
|
||||||
|
['{ccm+2,2}', '03', $invoiceDate],
|
||||||
|
['{ccd+2,2}', '03', $invoiceDate],
|
||||||
// mixing identifiers
|
// mixing identifiers
|
||||||
['{Y}{cy}', $invoiceDate->format('Y') . '2', $invoiceDate],
|
['{Y}{cy}', $invoiceDate->format('Y') . '2', $invoiceDate],
|
||||||
['{Y}{cy}{m}', $invoiceDate->format('Y') . '2' . $invoiceDate->format('n'), $invoiceDate],
|
['{Y}{cy}{m}', $invoiceDate->format('Y') . '2' . $invoiceDate->format('n'), $invoiceDate],
|
||||||
['{Y}-{cy}/{m}', $invoiceDate->format('Y') . '-2/' . $invoiceDate->format('n'), $invoiceDate],
|
['{Y}-{cy}/{m}', $invoiceDate->format('Y') . '-2/' . $invoiceDate->format('n'), $invoiceDate],
|
||||||
['{Y}-{cy}/{m}', $invoiceDate->format('Y') . '-2/' . $invoiceDate->format('n'), $invoiceDate],
|
['{Y}-{cy}/{m}', $invoiceDate->format('Y') . '-2/' . $invoiceDate->format('n'), $invoiceDate],
|
||||||
['{Y,5}/{cy,5}', '0' . $invoiceDate->format('Y') . '/00002', $invoiceDate],
|
['{Y,5}/{cy,5}', '0' . $invoiceDate->format('Y') . '/00002', $invoiceDate],
|
||||||
['{Y,!}/{cy,o}', $invoiceDate->format('Y') . '/2', $invoiceDate], // invalid formatter length
|
// with decrementing counter
|
||||||
|
['{c-1,2}', '00', $invoiceDate],
|
||||||
|
['{c-2,2}', '-1', $invoiceDate],
|
||||||
|
// with incrementing and decrementing counter
|
||||||
|
['{c-5+13,1}', '9', $invoiceDate],
|
||||||
|
['{c+13-5,2}', '09', $invoiceDate],
|
||||||
|
// undefined behaviour - can change at any time
|
||||||
|
['{cm+-1,2}', '00', $invoiceDate],
|
||||||
|
['{cm+-1}', '0', $invoiceDate],
|
||||||
|
['{cm-+1,2}', '02', $invoiceDate],
|
||||||
|
['{cm-+1}', '2', $invoiceDate],
|
||||||
|
['{a-+1+-1+++2}', '{a-+1+-1+++2}', $invoiceDate],
|
||||||
|
['{date+2+2+2}', $invoiceDate->format('ymd'), $invoiceDate],
|
||||||
|
['{cm+22+2+2-1-13-4}', '9', $invoiceDate],
|
||||||
|
['{cm+22+2-2-22}', '6', $invoiceDate, 5],
|
||||||
|
['{cm-21+22+2-2}', '6', $invoiceDate, 5],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getTestData
|
* @dataProvider getTestData
|
||||||
*/
|
*/
|
||||||
public function testGetInvoiceNumber(string $format, string $expectedInvoiceNumber, \DateTime $invoiceDate)
|
public function testGetInvoiceNumber(string $format, string $expectedInvoiceNumber, \DateTime $invoiceDate, int $counter = 1)
|
||||||
{
|
{
|
||||||
$sut = $this->getSut($format);
|
$sut = $this->getSut($format, $counter);
|
||||||
$model = new InvoiceModel(new DebugFormatter());
|
$model = new InvoiceModel(new DebugFormatter());
|
||||||
$model->setInvoiceDate($invoiceDate);
|
$model->setInvoiceDate($invoiceDate);
|
||||||
$model->setCustomer(new Customer());
|
$model->setCustomer(new Customer());
|
||||||
@@ -120,4 +140,43 @@ class ConfigurableNumberGeneratorTest extends TestCase
|
|||||||
$this->assertEquals($expectedInvoiceNumber, $sut->getInvoiceNumber());
|
$this->assertEquals($expectedInvoiceNumber, $sut->getInvoiceNumber());
|
||||||
$this->assertEquals('default', $sut->getId());
|
$this->assertEquals('default', $sut->getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getInvalidTestData()
|
||||||
|
{
|
||||||
|
$invoiceDate = new \DateTime();
|
||||||
|
|
||||||
|
return [
|
||||||
|
['{cm-}', $invoiceDate, 'decrement'],
|
||||||
|
['{cm+}', $invoiceDate, 'increment'],
|
||||||
|
['{cm+-}', $invoiceDate, 'decrement'],
|
||||||
|
['{cm-+}', $invoiceDate, 'increment'],
|
||||||
|
['{cy,}', $invoiceDate, 'format length'],
|
||||||
|
['{date,a}', $invoiceDate, 'format length'],
|
||||||
|
['{M,#}', $invoiceDate, 'format length'],
|
||||||
|
['{,a}', $invoiceDate, 'format length'],
|
||||||
|
['{Y,!}/{cy,o}', $invoiceDate, 'format length'],
|
||||||
|
['{cm,}', $invoiceDate, 'format length'],
|
||||||
|
['{cd+111,050}', $invoiceDate, 'format length'],
|
||||||
|
['{-+}', $invoiceDate, 'increment'],
|
||||||
|
['{+}', $invoiceDate, 'increment'],
|
||||||
|
['{+-}', $invoiceDate, 'decrement'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getInvalidTestData
|
||||||
|
*/
|
||||||
|
public function testInvalidGetInvoiceNumber(string $format, \DateTime $invoiceDate, string $brokenPart)
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionMessage(sprintf('Unknown %s found', $brokenPart));
|
||||||
|
|
||||||
|
$sut = $this->getSut($format);
|
||||||
|
$model = new InvoiceModel(new DebugFormatter());
|
||||||
|
$model->setInvoiceDate($invoiceDate);
|
||||||
|
$model->setCustomer(new Customer());
|
||||||
|
$sut->setModel($model);
|
||||||
|
|
||||||
|
$sut->getInvoiceNumber();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user