From 711139ad4c21db1ea9f3e7433e4dfa406b63765a Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Thu, 23 Apr 2020 12:19:07 +0200 Subject: [PATCH] delete invoices (#1652) * include all meta fields as template variables * support invoice preview via command * support deletion of generated invoices --- UPGRADING.md | 1 + src/Command/InvoiceCreateCommand.php | 73 ++++++++++++++++++- src/Controller/InvoiceController.php | 21 +++++- src/Entity/InvoiceTemplate.php | 2 +- .../Hydrator/InvoiceItemDefaultHydrator.php | 6 +- .../Hydrator/InvoiceModelCustomerHydrator.php | 2 +- .../Hydrator/InvoiceModelDefaultHydrator.php | 19 +++-- src/Invoice/ServiceInvoice.php | 9 +++ src/Repository/InvoiceRepository.php | 7 ++ src/Utils/FileHelper.php | 5 ++ templates/invoice/actions.html.twig | 7 +- templates/invoice/renderer/text.txt.twig | 11 +-- tests/Command/InvoiceCreateCommandTest.php | 18 +++++ tests/Controller/InvoiceControllerTest.php | 7 +- tests/Entity/InvoiceTemplateTest.php | 9 +++ tests/Entity/UserTest.php | 10 ++- .../InvoiceModelDefaultHydratorTest.php | 11 ++- tests/Invoice/Renderer/DebugRendererTest.php | 9 +++ tests/Invoice/Renderer/RendererTestTrait.php | 1 + tests/Invoice/Renderer/TextRendererTest.php | 14 +++- 20 files changed, 206 insertions(+), 36 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 7e63c495..e9b1bded 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -17,6 +17,7 @@ Perform EACH version specific task between your version and the new one, otherwi - HTML invoice templates are now treated like other files and offered as download. If you are using relative URLs for including assets (CSS, images) you need to either inline them (see the default templates) or use absolute URLs. - Invoice templates that use the templates variables `${activity.X}` or `${project.X}` should be checked and possibly adapted, as multi-select is now possible for filtering +- Invoice templates have access to all meta-fields as variables, not only the ones marked as visible Permission changes: - `history_invoice` - NEW: grants all features for the new invoice archive (by default for all admins) diff --git a/src/Command/InvoiceCreateCommand.php b/src/Command/InvoiceCreateCommand.php index e0af29b0..76986985 100644 --- a/src/Command/InvoiceCreateCommand.php +++ b/src/Command/InvoiceCreateCommand.php @@ -27,6 +27,9 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class InvoiceCreateCommand extends Command @@ -55,6 +58,10 @@ class InvoiceCreateCommand extends Command * @var EventDispatcherInterface */ private $eventDispatcher; + /** + * @var string|null + */ + private $previewDirectory; public function __construct( ServiceInvoice $serviceInvoice, @@ -94,6 +101,7 @@ class InvoiceCreateCommand extends Command ->addOption('template-meta', null, InputOption::VALUE_OPTIONAL, 'Fetch invoice template from a meta-field', null) ->addOption('search', null, InputOption::VALUE_OPTIONAL, 'Search term to filter invoice entries', null) ->addOption('exported', null, InputOption::VALUE_OPTIONAL, 'Exported filter for invoice entries (possible values: exported, all), by default only "not exported" items are fetched', null) + ->addOption('preview', null, InputOption::VALUE_OPTIONAL, 'Absolute path for a rendered preview of the invoice, which will neither be saved nor the items be marked as exported.', null) ; } @@ -208,7 +216,14 @@ class InvoiceCreateCommand extends Command } $markAsExported = false; - if ($input->getOption('set-exported')) { + if ($input->getOption('preview') !== null) { + $this->previewDirectory = rtrim($input->getOption('preview'), '/') . '/'; + if (!is_dir($this->previewDirectory) || !is_writable($this->previewDirectory)) { + $io->error('Invalid preview directory given'); + + return 1; + } + } elseif ($input->getOption('set-exported')) { $markAsExported = true; } @@ -283,7 +298,11 @@ class InvoiceCreateCommand extends Command $query->setTemplate($tpl); try { - $invoices[] = $this->serviceInvoice->createInvoice($query, $this->eventDispatcher); + if (null !== $this->previewDirectory) { + $invoices[] = $this->saveInvoicePreview($this->serviceInvoice->renderInvoice($query, $this->eventDispatcher)); + } else { + $invoices[] = $this->serviceInvoice->createInvoice($query, $this->eventDispatcher); + } } catch (\Exception $ex) { $io->error(sprintf('Failed to create invoice for project "%s" with: %s', $project->getName(), $ex->getMessage())); } @@ -292,6 +311,34 @@ class InvoiceCreateCommand extends Command return $invoices; } + private function saveInvoicePreview(Response $response) + { + $filename = uniqid('invoice_'); + + if ($response->headers->has('Content-Disposition')) { + $disposition = $response->headers->get('Content-Disposition'); + $parts = explode(';', $disposition); + foreach ($parts as $part) { + if (stripos($part, 'filename=') === false) { + continue; + } + $filename = explode('filename=', $part); + if (\count($filename) > 1) { + $filename = $filename[1]; + } + } + } + + if ($response instanceof BinaryFileResponse) { + $file = $response->getFile(); + $file->move($this->previewDirectory, $filename); + } else { + (new Filesystem())->dumpFile($this->previewDirectory . $filename, $response->getContent()); + } + + return $this->previewDirectory . $filename; + } + /** * @param Customer[] $customers * @param InvoiceQuery $defaultQuery @@ -318,7 +365,11 @@ class InvoiceCreateCommand extends Command $query->setTemplate($tpl); try { - $invoices[] = $this->serviceInvoice->createInvoice($query, $this->eventDispatcher); + if (null !== $this->previewDirectory) { + $invoices[] = $this->saveInvoicePreview($this->serviceInvoice->renderInvoice($query, $this->eventDispatcher)); + } else { + $invoices[] = $this->serviceInvoice->createInvoice($query, $this->eventDispatcher); + } } catch (\Exception $ex) { $io->error(sprintf('Failed to create invoice for customer "%s" with: %s', $customer->getName(), $ex->getMessage())); } @@ -343,6 +394,22 @@ class InvoiceCreateCommand extends Command return 0; } + if (null !== $this->previewDirectory) { + $columns = ['Filename']; + + $table = new Table($output); + $table->setHeaderTitle(sprintf('Created %s invoice(s)', \count($invoices))); + $table->setHeaders($columns); + + foreach ($invoices as $invoiceFile) { + $table->addRow([$invoiceFile]); + } + + $table->render(); + + return 0; + } + $columns = ['ID', 'Customer', 'Total', 'Filename']; $table = new Table($output); diff --git a/src/Controller/InvoiceController.php b/src/Controller/InvoiceController.php index c903b462..49f1ac28 100644 --- a/src/Controller/InvoiceController.php +++ b/src/Controller/InvoiceController.php @@ -189,11 +189,26 @@ final class InvoiceController extends AbstractController { try { $this->service->changeInvoiceStatus($invoice, $status); - } catch (\InvalidArgumentException $ex) { - throw $this->createNotFoundException($ex->getMessage()); + $this->flashSuccess('action.update.success'); + } catch (\Exception $ex) { + $this->flashError('action.update.error'); } - $this->flashSuccess('action.update.success'); + return $this->redirectToRoute('admin_invoice_list'); + } + + /** + * @Route(path="/delete/{id}", name="admin_invoice_delete", methods={"GET"}) + * @Security("is_granted('history_invoice')") + */ + public function deleteInvoiceAction(Invoice $invoice): Response + { + try { + $this->service->deleteInvoice($invoice); + $this->flashSuccess('action.delete.success'); + } catch (\Exception $ex) { + $this->flashError('action.delete.error'); + } return $this->redirectToRoute('admin_invoice_list'); } diff --git a/src/Entity/InvoiceTemplate.php b/src/Entity/InvoiceTemplate.php index c020f4f3..5719b4d1 100644 --- a/src/Entity/InvoiceTemplate.php +++ b/src/Entity/InvoiceTemplate.php @@ -145,7 +145,7 @@ class InvoiceTemplate private $decimalDuration = false; /** - * Used when rendering HTML templates. + * Used for translations and locale dependent number and date formats. * * @var string * diff --git a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php index 32c409fb..bc9f7940 100644 --- a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php +++ b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php @@ -95,7 +95,7 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator 'entry.activity_id' => $activity->getId(), ]); - foreach ($activity->getVisibleMetaFields() as $metaField) { + foreach ($activity->getMetaFields() as $metaField) { $values = array_merge($values, [ 'entry.activity.meta.' . $metaField->getName() => $metaField->getValue(), ]); @@ -108,7 +108,7 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator 'entry.project_id' => $project->getId(), ]); - foreach ($project->getVisibleMetaFields() as $metaField) { + foreach ($project->getMetaFields() as $metaField) { $values = array_merge($values, [ 'entry.project.meta.' . $metaField->getName() => $metaField->getValue(), ]); @@ -121,7 +121,7 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator 'entry.customer_id' => $customer->getId(), ]); - foreach ($customer->getVisibleMetaFields() as $metaField) { + foreach ($customer->getMetaFields() as $metaField) { $values = array_merge($values, [ 'entry.customer.meta.' . $metaField->getName() => $metaField->getValue(), ]); diff --git a/src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php b/src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php index cad891dc..44c17db7 100644 --- a/src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php +++ b/src/Invoice/Hydrator/InvoiceModelCustomerHydrator.php @@ -46,7 +46,7 @@ class InvoiceModelCustomerHydrator implements InvoiceModelHydrator // remaining time-budget? ]; - foreach ($customer->getVisibleMetaFields() as $metaField) { + foreach ($customer->getMetaFields() as $metaField) { $values = array_merge($values, [ 'customer.meta.' . $metaField->getName() => $metaField->getValue(), ]); diff --git a/src/Invoice/Hydrator/InvoiceModelDefaultHydrator.php b/src/Invoice/Hydrator/InvoiceModelDefaultHydrator.php index f92bc063..245024ab 100644 --- a/src/Invoice/Hydrator/InvoiceModelDefaultHydrator.php +++ b/src/Invoice/Hydrator/InvoiceModelDefaultHydrator.php @@ -27,6 +27,7 @@ class InvoiceModelDefaultHydrator implements InvoiceModelHydrator 'invoice.date' => $formatter->getFormattedDateTime($model->getInvoiceDate()), 'invoice.number' => $model->getInvoiceNumber(), 'invoice.currency' => $currency, + 'invoice.language' => $model->getTemplate()->getLanguage(), // since 1.9 'invoice.currency_symbol' => $formatter->getCurrencySymbol($currency), 'invoice.vat' => $model->getCalculator()->getVat(), 'invoice.tax' => $formatter->getFormattedMoney($tax, $currency), @@ -52,11 +53,19 @@ class InvoiceModelDefaultHydrator implements InvoiceModelHydrator 'template.payment_details' => $model->getTemplate()->getPaymentDetails(), 'query.begin' => $formatter->getFormattedDateTime($model->getQuery()->getBegin()), - 'query.day' => $model->getQuery()->getBegin()->format('d'), - 'query.end' => $formatter->getFormattedDateTime($model->getQuery()->getEnd()), - 'query.month' => $formatter->getFormattedMonthName($model->getQuery()->getBegin()), - 'query.month_number' => $model->getQuery()->getBegin()->format('m'), - 'query.year' => $model->getQuery()->getBegin()->format('Y'), + 'query.day' => $model->getQuery()->getBegin()->format('d'), // @deprecated + 'query.month' => $formatter->getFormattedMonthName($model->getQuery()->getBegin()), // @deprecated + 'query.month_number' => $model->getQuery()->getBegin()->format('m'), // @deprecated + 'query.year' => $model->getQuery()->getBegin()->format('Y'), // @deprecated + 'query.begin_day' => $model->getQuery()->getBegin()->format('d'), + 'query.begin_month' => $formatter->getFormattedMonthName($model->getQuery()->getBegin()), + 'query.begin_month_number' => $model->getQuery()->getBegin()->format('m'), + 'query.begin_year' => $model->getQuery()->getBegin()->format('Y'), + 'query.end' => $formatter->getFormattedDateTime($model->getQuery()->getEnd()), // since 1.9 + 'query.end_day' => $model->getQuery()->getEnd()->format('d'), // since 1.9 + 'query.end_month' => $formatter->getFormattedMonthName($model->getQuery()->getEnd()), // since 1.9 + 'query.end_month_number' => $model->getQuery()->getEnd()->format('m'), // since 1.9 + 'query.end_year' => $model->getQuery()->getEnd()->format('Y'), // since 1.9 ]; return $values; diff --git a/src/Invoice/ServiceInvoice.php b/src/Invoice/ServiceInvoice.php index fb954a7a..cbc32cca 100644 --- a/src/Invoice/ServiceInvoice.php +++ b/src/Invoice/ServiceInvoice.php @@ -396,6 +396,15 @@ final class ServiceInvoice ); } + public function deleteInvoice(Invoice $invoice) + { + $invoiceDirectory = $this->getInvoicesDirectory(); + if (is_file($invoiceDirectory . $invoice->getInvoiceFilename())) { + $this->fileHelper->removeFile($invoiceDirectory . $invoice->getInvoiceFilename()); + } + $this->invoiceRepository->deleteInvoice($invoice); + } + /** * @param InvoiceQuery $query * @return InvoiceModel diff --git a/src/Repository/InvoiceRepository.php b/src/Repository/InvoiceRepository.php index 17200981..a885add8 100644 --- a/src/Repository/InvoiceRepository.php +++ b/src/Repository/InvoiceRepository.php @@ -28,6 +28,13 @@ class InvoiceRepository extends EntityRepository $entityManager->flush(); } + public function deleteInvoice(Invoice $invoice) + { + $entityManager = $this->getEntityManager(); + $entityManager->remove($invoice); + $entityManager->flush(); + } + private function getCounterFor(\DateTime $start, \DateTime $end): int { $qb = $this->getEntityManager()->createQueryBuilder(); diff --git a/src/Utils/FileHelper.php b/src/Utils/FileHelper.php index 8a803d0e..142bcd49 100644 --- a/src/Utils/FileHelper.php +++ b/src/Utils/FileHelper.php @@ -58,4 +58,9 @@ final class FileHelper { $this->filesystem->dumpFile($filename, $data); } + + public function removeFile(string $filename) + { + $this->filesystem->remove($filename); + } } diff --git a/templates/invoice/actions.html.twig b/templates/invoice/actions.html.twig index b43c6b3a..69b6491f 100644 --- a/templates/invoice/actions.html.twig +++ b/templates/invoice/actions.html.twig @@ -57,11 +57,8 @@ {% set actions = actions|merge({'invoice.paid': path('admin_invoice_status', {'id': invoice.id, 'status': 'paid'})}) %} {% endif %} - {% if actions|length > 0 %} - {% set actions = actions|merge({'divider': null}) %} - {% endif %} - {% set actions = actions|merge({'download': {'url': path('admin_invoice_download', {'id': invoice.id}), 'target': '_blank'}}) %} + {% set actions = actions|merge({'trash': {'url': path('admin_invoice_delete', {'id' : invoice.id}), 'class': 'confirmation-link', 'attr': {'data-question': 'confirm.delete'}}}) %} {% endif %} {% set event = trigger('actions.invoice', {'actions': actions, 'invoice': invoice}) %} @@ -107,7 +104,7 @@ {% if is_granted('manage_invoice_template') %} {% set actions = actions|merge({'edit': {'url': path('admin_invoice_template_edit', {'id' : template.id}), 'class': 'modal-ajax-form'}}) %} {% set actions = actions|merge({'copy': path('admin_invoice_template_copy', {'id' : template.id})}) %} - {% set actions = actions|merge({'trash': path('admin_invoice_template_delete', {'id' : template.id})}) %} + {% set actions = actions|merge({'trash': {'url': path('admin_invoice_template_delete', {'id' : template.id}), 'class': 'confirmation-link', 'attr': {'data-question': 'confirm.delete'}}}) %} {% endif %} {% set event = trigger('actions.invoice_template', {'actions': actions, 'view': view, 'template': template}) %} diff --git a/templates/invoice/renderer/text.txt.twig b/templates/invoice/renderer/text.txt.twig index 7e8b3077..046b030d 100644 --- a/templates/invoice/renderer/text.txt.twig +++ b/templates/invoice/renderer/text.txt.twig @@ -1,19 +1,16 @@ {% for key, value in model.toArray %} -{{ key }}: +{{ key }} {% if value is not empty %} -{{ value|multiline_indent(' ') }} +{{ value|multiline_indent(' ') }} {% endif %} - {% endfor %} {% for entry in model.calculator.entries %} --- - {% set items = model.itemToArray(entry) %} {% for key, value in items %} -{{ key }}: +{{ key }} {% if value is not empty %} -{{ value|multiline_indent(' ') }} +{{ value|multiline_indent(' ') }} {% endif %} - {% endfor %} {% endfor %} diff --git a/tests/Command/InvoiceCreateCommandTest.php b/tests/Command/InvoiceCreateCommandTest.php index a3c61b7c..eb6742f3 100644 --- a/tests/Command/InvoiceCreateCommandTest.php +++ b/tests/Command/InvoiceCreateCommandTest.php @@ -157,6 +157,11 @@ class InvoiceCreateCommandTest extends KernelTestCase $this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1, '--template' => 'x', '--start' => '2020-01-01', '--end' => 'öäüß'], 'Invalid end date given'); } + public function testCreateWithInvalidPreviewDirectory() + { + $this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 1, '--template' => 'x', '--start' => '2020-01-01', '--end' => '2020-01-02', '--preview' => '/kjhg/'], 'Invalid preview directory given'); + } + public function testCreateWithInvalidCustomer() { $this->assertCommandErrors(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--customer' => 3, '--template' => 'x'], 'Unknown customer ID: 3'); @@ -245,4 +250,17 @@ class InvoiceCreateCommandTest extends KernelTestCase $output = $commandTester->getDisplay(); $this->assertStringContainsString('Created 1 invoice(s) ', $output); } + + public function testCreateInvoiceByProjectWithPreview() + { + $start = new \DateTime('-2 months'); + $end = new \DateTime(); + + $this->prepareFixtures($start); + + $commandTester = $this->createInvoice(['--user' => UserFixtures::USERNAME_SUPER_ADMIN, '--exported' => 'all', '--preview' => sys_get_temp_dir(), '--by-project' => null, '--template-meta' => 'template', '--start' => $start->format('Y-m-d'), '--end' => $end->format('Y-m-d')]); + + $output = $commandTester->getDisplay(); + $this->assertStringContainsString('Created 1 invoice(s) ', $output); + } } diff --git a/tests/Controller/InvoiceControllerTest.php b/tests/Controller/InvoiceControllerTest.php index 012f48d2..6c642ec4 100644 --- a/tests/Controller/InvoiceControllerTest.php +++ b/tests/Controller/InvoiceControllerTest.php @@ -266,7 +266,7 @@ class InvoiceControllerTest extends ControllerBaseTest $this->assertEquals('invoice_print', $node->getIterator()[0]->getAttribute('class')); } - public function testCreateActionAsAdminWithDownloadAndStatusChange() + public function testCreateActionAsAdminWithDownloadAndStatusChangeAndDelete() { $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); /** @var EntityManager $em */ @@ -350,6 +350,11 @@ class InvoiceControllerTest extends ControllerBaseTest $this->assertIsRedirect($client, '/invoice/show'); $client->followRedirect(); $this->assertTrue($client->getResponse()->isSuccessful()); + + $this->request($client, '/invoice/delete/1'); + $this->assertIsRedirect($client, '/invoice/show'); + $client->followRedirect(); + $this->assertTrue($client->getResponse()->isSuccessful()); } public function testEditTemplateAction() diff --git a/tests/Entity/InvoiceTemplateTest.php b/tests/Entity/InvoiceTemplateTest.php index 97532ed1..61a11585 100644 --- a/tests/Entity/InvoiceTemplateTest.php +++ b/tests/Entity/InvoiceTemplateTest.php @@ -76,6 +76,15 @@ class InvoiceTemplateTest extends TestCase self::assertInstanceOf(InvoiceTemplate::class, $sut->setLanguage('de')); self::assertEquals('de', $sut->getLanguage()); + self::assertInstanceOf(InvoiceTemplate::class, $sut->setNumberGenerator('foo')); + self::assertEquals('foo', $sut->getNumberGenerator()); + + self::assertInstanceOf(InvoiceTemplate::class, $sut->setRenderer('bar')); + self::assertEquals('bar', $sut->getRenderer()); + + self::assertInstanceOf(InvoiceTemplate::class, $sut->setCalculator('fooBar')); + self::assertEquals('fooBar', $sut->getCalculator()); + self::assertEquals($sut, clone $sut); } diff --git a/tests/Entity/UserTest.php b/tests/Entity/UserTest.php index 260b6431..4f482929 100644 --- a/tests/Entity/UserTest.php +++ b/tests/Entity/UserTest.php @@ -184,13 +184,15 @@ class UserTest extends TestCase { $sut = new User(); $sut->setAlias('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); - self::assertEquals(60, \strlen($sut->getAlias())); + self::assertEquals(60, mb_strlen($sut->getAlias())); $sut->setAlias('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAAAAA'); - self::assertEquals(60, \strlen($sut->getAlias())); + self::assertEquals(60, mb_strlen($sut->getAlias())); + $sut->setAlias('万政提質打録施熟活者韓症写気当。規談表有部確暑将回優隊見竜能南事。竹阪板府入違護究兵厚能提。済伸知題熱正写場京誉事週在複今徳際供。審利世連手阿量携泉指済像更映刊政病世。熱楽時予資方賀月改洋者職原桜提増脚職。気公誌荒原輝文治察専及唱戦白廃模書。着授健出山力集出止員捉害実載措明国無今。棋出陶供供知機使協物確講最新両。'); + self::assertEquals(60, mb_strlen($sut->getAlias())); $sut->setTitle('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'); - self::assertEquals(50, \strlen($sut->getTitle())); + self::assertEquals(50, mb_strlen($sut->getTitle())); $sut->setTitle('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAAAAAA'); - self::assertEquals(50, \strlen($sut->getTitle())); + self::assertEquals(50, mb_strlen($sut->getTitle())); } public function testPreferencesCollectionIsCreatedOnBrokenUser() diff --git a/tests/Invoice/Hydrator/InvoiceModelDefaultHydratorTest.php b/tests/Invoice/Hydrator/InvoiceModelDefaultHydratorTest.php index 1a179f2f..36fd8169 100644 --- a/tests/Invoice/Hydrator/InvoiceModelDefaultHydratorTest.php +++ b/tests/Invoice/Hydrator/InvoiceModelDefaultHydratorTest.php @@ -39,6 +39,7 @@ class InvoiceModelDefaultHydratorTest extends TestCase 'invoice.currency', 'invoice.currency_symbol', 'invoice.vat', + 'invoice.language', 'invoice.tax', 'invoice.tax_nc', 'invoice.tax_plain', @@ -60,11 +61,19 @@ class InvoiceModelDefaultHydratorTest extends TestCase 'template.contact', 'template.payment_details', 'query.begin', - 'query.day', 'query.end', + 'query.day', 'query.month', 'query.month_number', 'query.year', + 'query.begin_day', + 'query.begin_month', + 'query.begin_month_number', + 'query.begin_year', + 'query.end_day', + 'query.end_month', + 'query.end_month_number', + 'query.end_year', ]; $givenKeys = array_keys($model); diff --git a/tests/Invoice/Renderer/DebugRendererTest.php b/tests/Invoice/Renderer/DebugRendererTest.php index e6034430..d14a5cb9 100644 --- a/tests/Invoice/Renderer/DebugRendererTest.php +++ b/tests/Invoice/Renderer/DebugRendererTest.php @@ -84,6 +84,7 @@ class DebugRendererTest extends TestCase 'invoice.currency_symbol', 'invoice.vat', 'invoice.tax', + 'invoice.language', 'invoice.tax_nc', 'invoice.tax_plain', 'invoice.total_time', @@ -109,6 +110,14 @@ class DebugRendererTest extends TestCase 'query.month', 'query.month_number', 'query.year', + 'query.begin_day', + 'query.begin_month', + 'query.begin_month_number', + 'query.begin_year', + 'query.end_day', + 'query.end_month', + 'query.end_month_number', + 'query.end_year', 'customer.id', 'customer.address', 'customer.name', diff --git a/tests/Invoice/Renderer/RendererTestTrait.php b/tests/Invoice/Renderer/RendererTestTrait.php index 4a1ee665..928a416c 100644 --- a/tests/Invoice/Renderer/RendererTestTrait.php +++ b/tests/Invoice/Renderer/RendererTestTrait.php @@ -115,6 +115,7 @@ trait RendererTestTrait $template = new InvoiceTemplate(); $template->setTitle('a very *long* test invoice / template title with [special] character'); $template->setVat(19); + $template->setLanguage('en'); $project = new Project(); $project->setName('project name'); diff --git a/tests/Invoice/Renderer/TextRendererTest.php b/tests/Invoice/Renderer/TextRendererTest.php index fd49fd3e..e22c703a 100644 --- a/tests/Invoice/Renderer/TextRendererTest.php +++ b/tests/Invoice/Renderer/TextRendererTest.php @@ -69,9 +69,19 @@ class TextRendererTest extends KernelTestCase foreach ($model->toArray() as $key => $value) { if (null === $value || '' === $value) { - self::assertStringContainsString(sprintf("%s:\n\n", $key), $content); + self::assertStringContainsString(sprintf("%s\n", $key), $content); } else { - self::assertStringContainsString(sprintf("%s:\n %s", $key, explode("\n", $value)[0]), $content); + self::assertStringContainsString(sprintf("%s\n %s", $key, explode("\n", $value)[0]), $content); + } + } + + foreach ($model->getCalculator()->getEntries() as $entry) { + foreach ($model->itemToArray($entry) as $key => $value) { + if (null === $value || '' === $value) { + self::assertStringContainsString(sprintf("%s\n", $key), $content); + } else { + self::assertStringContainsString(sprintf("%s\n %s", $key, explode("\n", $value)[0]), $content); + } } } self::assertEquals(\count($model->getCalculator()->getEntries()), substr_count($content, PHP_EOL . '---' . PHP_EOL));