improve error handling during invoice generation (#2932)
* prevent that items will be marked as exported if invoice can not be generated * check for existing invoice number to prevent that invalid invoices will be generated * allow to add a unique id to invoice preview files on batch export * hide delete invoice action with deactivated permission * try to automatically fix duplicate invoice id
This commit is contained in:
18
src/Invoice/DuplicateInvoiceNumberException.php
Normal file
18
src/Invoice/DuplicateInvoiceNumberException.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Invoice;
|
||||
|
||||
final class DuplicateInvoiceNumberException extends \Exception
|
||||
{
|
||||
public function __construct(string $invoiceNumber)
|
||||
{
|
||||
parent::__construct('Invoice number "' . $invoiceNumber . '" already existing');
|
||||
}
|
||||
}
|
||||
@@ -58,21 +58,29 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface
|
||||
{
|
||||
$format = $this->configuration->find('invoice.number_format');
|
||||
$invoiceDate = $this->model->getInvoiceDate();
|
||||
$result = $format;
|
||||
|
||||
preg_match_all('/{[^}]*?}/', $format, $matches);
|
||||
foreach ($matches[0] as $part) {
|
||||
$partialResult = $this->parseReplacer($invoiceDate, $part);
|
||||
$result = str_replace($part, $partialResult, $result);
|
||||
}
|
||||
$loops = 0;
|
||||
$increaseBy = 0;
|
||||
|
||||
do {
|
||||
$result = $format;
|
||||
|
||||
preg_match_all('/{[^}]*?}/', $format, $matches);
|
||||
|
||||
foreach ($matches[0] as $part) {
|
||||
$partialResult = $this->parseReplacer($invoiceDate, $part, $increaseBy);
|
||||
$result = str_replace($part, $partialResult, $result);
|
||||
}
|
||||
|
||||
$increaseBy++;
|
||||
} while ($this->repository->hasInvoice($result) && $loops++ < 99);
|
||||
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
private function parseReplacer(\DateTime $invoiceDate, string $originalFormat): string
|
||||
private function parseReplacer(\DateTime $invoiceDate, string $originalFormat, int $increaseBy): string
|
||||
{
|
||||
$formatterLength = null;
|
||||
$increaseBy = 0;
|
||||
$formatPattern = str_replace(['{', '}'], '', $originalFormat);
|
||||
|
||||
$parts = preg_split('/([+\-,])+/', $formatPattern, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
@@ -366,12 +366,12 @@ final class ServiceInvoice
|
||||
if ($renderer->supports($document)) {
|
||||
$dispatcher->dispatch(new InvoicePreRenderEvent($model, $document, $renderer));
|
||||
|
||||
$response = $renderer->render($document, $model);
|
||||
|
||||
if ($model->getQuery()->isMarkAsExported()) {
|
||||
$this->markEntriesAsExported($model->getEntries());
|
||||
if ($this->invoiceRepository->hasInvoice($model->getInvoiceNumber())) {
|
||||
throw new DuplicateInvoiceNumberException($model->getInvoiceNumber());
|
||||
}
|
||||
|
||||
$response = $renderer->render($document, $model);
|
||||
|
||||
$event = new InvoicePostRenderEvent($model, $document, $renderer, $response);
|
||||
$dispatcher->dispatch($event);
|
||||
|
||||
@@ -382,6 +382,10 @@ final class ServiceInvoice
|
||||
$invoice->setFilename($invoiceFilename);
|
||||
$this->invoiceRepository->saveInvoice($invoice);
|
||||
|
||||
if ($model->getQuery()->isMarkAsExported()) {
|
||||
$this->markEntriesAsExported($model->getEntries());
|
||||
}
|
||||
|
||||
$dispatcher->dispatch(new InvoiceCreatedEvent($invoice));
|
||||
|
||||
return $invoice;
|
||||
|
||||
Reference in New Issue
Block a user