From 3ea05cb705336d6d9c56167c5d77334a05ce91c8 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Wed, 4 Sep 2019 22:50:53 +0200 Subject: [PATCH] support multiple invoice repositories (#1084) --- UPGRADING.md | 1 + .../{app.dacf60f7.js => app.75bdc057.js} | 0 public/build/entrypoints.json | 4 +- public/build/manifest.json | 2 +- src/Controller/InvoiceController.php | 81 +++++++++++++------ .../Compiler/InvoiceServiceCompilerPass.php | 5 ++ src/Entity/Timesheet.php | 10 +-- .../Calculator/AbstractMergedCalculator.php | 11 +++ .../AbstractSumInvoiceCalculator.php | 14 ++-- .../Calculator/ActivityInvoiceCalculator.php | 12 +-- .../Calculator/DateInvoiceCalculator.php | 12 +-- src/Invoice/Calculator/DefaultCalculator.php | 2 +- .../Calculator/ProjectInvoiceCalculator.php | 12 +-- .../Calculator/ShortInvoiceCalculator.php | 5 +- .../Calculator/UserInvoiceCalculator.php | 10 +-- src/Invoice/InvoiceItemInterface.php | 43 ++++++++++ .../InvoiceItemRepositoryInterface.php | 26 ++++++ src/Invoice/InvoiceModel.php | 21 ++++- .../Renderer/AbstractSpreadsheetRenderer.php | 21 ++--- src/Invoice/Renderer/DocxRenderer.php | 2 +- src/Invoice/Renderer/RendererTrait.php | 10 +++ src/Invoice/ServiceInvoice.php | 81 ++++++++----------- src/Kernel.php | 3 + src/Repository/Query/ActivityQuery.php | 8 +- src/Repository/Query/BaseQuery.php | 12 +-- .../TimesheetInvoiceItemRepository.php | 51 ++++++++++++ src/Repository/TimesheetRepository.php | 4 +- translations/messages.de.xliff | 2 +- translations/messages.en.xliff | 2 +- 29 files changed, 323 insertions(+), 144 deletions(-) rename public/build/{app.dacf60f7.js => app.75bdc057.js} (100%) create mode 100644 src/Invoice/InvoiceItemInterface.php create mode 100644 src/Invoice/InvoiceItemRepositoryInterface.php create mode 100644 src/Repository/TimesheetInvoiceItemRepository.php diff --git a/UPGRADING.md b/UPGRADING.md index 3fbb1477..cbe20b8a 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -13,6 +13,7 @@ Perform EACH version specific task between your version and the new one, otherwi ### Possible BC breaks - Refactored toolbars and search, plugins needs to be checked +- Invoices now supports multiple repositories, some method signatures had to be changed (eg. `calculateSumIdentifier()`) ## [1.2](https://github.com/kevinpapst/kimai2/releases/tag/1.2) diff --git a/public/build/app.dacf60f7.js b/public/build/app.75bdc057.js similarity index 100% rename from public/build/app.dacf60f7.js rename to public/build/app.75bdc057.js diff --git a/public/build/entrypoints.json b/public/build/entrypoints.json index 26ab6888..a7a6329e 100644 --- a/public/build/entrypoints.json +++ b/public/build/entrypoints.json @@ -5,7 +5,7 @@ "build/runtime.4ee6be68.js", "build/0.a87622f3.js", "build/1.c1bee41f.js", - "build/app.dacf60f7.js" + "build/app.75bdc057.js" ], "css": [ "build/app.eb4ed947.css" @@ -35,7 +35,7 @@ "build/runtime.4ee6be68.js": "sha384-xNNrNinl64G3nCUrIskgSjU0mUXXCB9lj6XCSInBTwxSKXk8uTMafnLHtdWdIGtd", "build/0.a87622f3.js": "sha384-ncT/BKhCsqH6jhxwdsSG95m1ei7ZZjeZtzH1262h+OPUU80TSFFE3dt+abcHHMok", "build/1.c1bee41f.js": "sha384-7UVWcP6Hefp2k/CrtGSITKXx4dSZqtvpAiU8WX7dClETkzMewrUjoCRtVXQ5j3KI", - "build/app.dacf60f7.js": "sha384-a93v/mUzZDopA6To/YJEjOAxgJ6T4H6gmXnGf0ja6Bq4M7ZVDQqqKMX7/9hIhRPM", + "build/app.75bdc057.js": "sha384-a93v/mUzZDopA6To/YJEjOAxgJ6T4H6gmXnGf0ja6Bq4M7ZVDQqqKMX7/9hIhRPM", "build/app.eb4ed947.css": "sha384-TQ5nEns/+JBxjuHlqGXkiS1qt9Zdzb7tREhc4DPunQkTib/iuv8MfwELt8OPhF10", "build/2.7be60d8d.js": "sha384-txR0QG+838LKYtPQ99Gx4OU7WmgN9J3joZEyGwIskSz74EN1T4/IBVnmNaKiFN1q", "build/chart.0af3f813.js": "sha384-I57c9DtU3AOG2kzKqIZkIu0hi1aGYHRZ5QG4LKC9+9slzJnAMttPGXoL2cQG3m6y", diff --git a/public/build/manifest.json b/public/build/manifest.json index 4f52e5b8..88bfa984 100644 --- a/public/build/manifest.json +++ b/public/build/manifest.json @@ -3,7 +3,7 @@ "build/1.c1bee41f.js": "build/1.c1bee41f.js", "build/2.7be60d8d.js": "build/2.7be60d8d.js", "build/app.css": "build/app.eb4ed947.css", - "build/app.js": "build/app.dacf60f7.js", + "build/app.js": "build/app.75bdc057.js", "build/calendar.css": "build/calendar.b0551848.css", "build/calendar.js": "build/calendar.5839778f.js", "build/chart.js": "build/chart.0af3f813.js", diff --git a/src/Controller/InvoiceController.php b/src/Controller/InvoiceController.php index ff228854..4739eec3 100644 --- a/src/Controller/InvoiceController.php +++ b/src/Controller/InvoiceController.php @@ -10,15 +10,14 @@ namespace App\Controller; use App\Entity\InvoiceTemplate; -use App\Entity\Timesheet; use App\Form\InvoiceTemplateForm; use App\Form\Toolbar\InvoiceToolbarForm; +use App\Invoice\InvoiceItemInterface; use App\Invoice\InvoiceModel; use App\Invoice\ServiceInvoice; use App\Repository\InvoiceTemplateRepository; use App\Repository\Query\BaseQuery; use App\Repository\Query\InvoiceQuery; -use App\Repository\TimesheetRepository; use App\Timesheet\UserDateTimeFactory; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Form\FormInterface; @@ -43,10 +42,6 @@ class InvoiceController extends AbstractController * @var InvoiceTemplateRepository */ protected $invoiceRepository; - /** - * @var TimesheetRepository - */ - protected $timesheetRepository; /** * @var UserDateTimeFactory */ @@ -62,12 +57,8 @@ class InvoiceController extends AbstractController /** * @Route(path="/", name="invoice", methods={"GET", "POST"}) * @Security("is_granted('view_invoice')") - * - * @param Request $request - * @return \Symfony\Component\HttpFoundation\Response - * @throws \Exception */ - public function indexAction(Request $request, TimesheetRepository $repository) + public function indexAction(Request $request): Response { if (!$this->invoiceRepository->hasTemplate()) { if ($this->isGranted('manage_invoice_template')) { @@ -90,7 +81,7 @@ class InvoiceController extends AbstractController /** @var SubmitButton $createButton */ $createButton = $form->get('create'); if ($createButton->isClicked()) { - return $this->renderInvoice($query, $repository); + return $this->renderInvoice($query); } /** @var SubmitButton $previewButton */ @@ -98,12 +89,15 @@ class InvoiceController extends AbstractController if ($previewButton->isClicked()) { $showPreview = true; $query->setPageSize($maxItemsPreview); - $entries = $this->getEntries($query, $repository); + $entries = $this->getPreviewEntries($query); } } } - $model = $this->prepareModel($query, $entries); + $model = $this->prepareModel($query); + if (!empty($entries)) { + $model->addEntries($entries); + } return $this->render('invoice/index.html.twig', [ 'model' => $model, @@ -129,10 +123,13 @@ class InvoiceController extends AbstractController return $query; } - protected function renderInvoice(InvoiceQuery $query, TimesheetRepository $repository) + protected function renderInvoice(InvoiceQuery $query) { - $entries = $this->getEntries($query, $repository); - $model = $this->prepareModel($query, $entries); + $entries = $this->getEntries($query); + $model = $this->prepareModel($query); + foreach ($entries as $repo => $items) { + $model->addEntries($items); + } $document = $this->service->getDocumentByName($model->getTemplate()->getRenderer()); if (null === $document) { @@ -143,7 +140,7 @@ class InvoiceController extends AbstractController if ($renderer->supports($document)) { $response = $renderer->render($document, $model); if ($query->isMarkAsExported()) { - $repository->setExported($entries); + $this->markEntriesAsExported($entries); } return $response; @@ -158,11 +155,26 @@ class InvoiceController extends AbstractController } /** - * @param InvoiceQuery $query - * @param TimesheetRepository $repository - * @return Timesheet[] + * @param InvoiceItemInterface[] $entries */ - protected function getEntries(InvoiceQuery $query, TimesheetRepository $repository): iterable + private function markEntriesAsExported(iterable $entries) + { + $repositories = $this->service->getInvoiceItemRepositories(); + + foreach ($entries as $repo => $items) { + foreach ($repositories as $repository) { + if (get_class($repository) === $repo) { + $repository->setExported($items); + } + } + } + } + + /** + * @param InvoiceQuery $query + * @return InvoiceItemInterface[] + */ + protected function getEntries(InvoiceQuery $query): array { // customer needs to be defined, as we need the currency for the invoice if (null === $query->getCustomer()) { @@ -178,21 +190,38 @@ class InvoiceController extends AbstractController $query->getBegin()->setTime(0, 0, 0); $query->getEnd()->setTime(23, 59, 59); - return $repository->getTimesheetsForQuery($query); + $repositories = $this->service->getInvoiceItemRepositories(); + $items = []; + + foreach ($repositories as $repository) { + $items[get_class($repository)] = $repository->getInvoiceItemsForQuery($query); + } + + return $items; + } + + protected function getPreviewEntries(InvoiceQuery $query): array + { + $entries = []; + $temp = $this->getEntries($query); + + foreach ($temp as $repo => $items) { + $entries = array_merge($entries, $items); + } + + return array_slice($entries, 0, $query->getPageSize()); } /** * @param InvoiceQuery $query - * @param Timesheet[] $entries * @return InvoiceModel * @throws \Exception */ - protected function prepareModel(InvoiceQuery $query, array $entries): InvoiceModel + protected function prepareModel(InvoiceQuery $query): InvoiceModel { $model = new InvoiceModel(); $model ->setQuery($query) - ->setEntries($entries) ->setCustomer($query->getCustomer()) ; diff --git a/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php b/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php index 90181b3c..62f9672b 100644 --- a/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php +++ b/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php @@ -47,5 +47,10 @@ class InvoiceServiceCompilerPass implements CompilerPassInterface foreach ($taggedCalculator as $id => $tags) { $definition->addMethodCall('addCalculator', [new Reference($id)]); } + + $taggedRepository = $container->findTaggedServiceIds(Kernel::TAG_INVOICE_REPOSITORY); + foreach ($taggedRepository as $id => $tags) { + $definition->addMethodCall('addInvoiceItemRepository', [new Reference($id)]); + } } } diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php index deaffeeb..867329de 100644 --- a/src/Entity/Timesheet.php +++ b/src/Entity/Timesheet.php @@ -9,6 +9,7 @@ namespace App\Entity; +use App\Invoice\InvoiceItemInterface; use DateTime; use DateTimeZone; use Doctrine\Common\Collections\ArrayCollection; @@ -38,7 +39,7 @@ use Symfony\Component\Validator\Constraints as Assert; * columns={"start_time","end_time"} => IDX_4F60C6B1502DF58741561401 => ??? * columns={"start_time","end_time","user"} => IDX_4F60C6B1502DF587415614018D93D649 => ??? */ -class Timesheet implements EntityWithMetaFields +class Timesheet implements EntityWithMetaFields, InvoiceItemInterface { /** * @var int @@ -260,7 +261,7 @@ class Timesheet implements EntityWithMetaFields * * @return int */ - public function getDuration() + public function getDuration(): int { return $this->duration; } @@ -340,10 +341,7 @@ class Timesheet implements EntityWithMetaFields return $this; } - /** - * @return float - */ - public function getRate() + public function getRate(): float { return $this->rate; } diff --git a/src/Invoice/Calculator/AbstractMergedCalculator.php b/src/Invoice/Calculator/AbstractMergedCalculator.php index 44513833..e130b78a 100644 --- a/src/Invoice/Calculator/AbstractMergedCalculator.php +++ b/src/Invoice/Calculator/AbstractMergedCalculator.php @@ -11,10 +11,21 @@ namespace App\Invoice\Calculator; use App\Entity\Timesheet; use App\Invoice\InvoiceItem; +use App\Invoice\InvoiceItemInterface; abstract class AbstractMergedCalculator extends AbstractCalculator { + /** + * @deprecated since 1.3 - will be removed with 2.0 + */ protected function mergeTimesheets(InvoiceItem $invoiceItem, Timesheet $entry) + { + @trigger_error('mergeTimesheets() is deprecated and will be removed with 2.0', E_USER_DEPRECATED); + + $this->mergeInvoiceItems($invoiceItem, $entry); + } + + protected function mergeInvoiceItems(InvoiceItem $invoiceItem, InvoiceItemInterface $entry) { $invoiceItem->setAmount($invoiceItem->getAmount() + 1); $invoiceItem->setUser($entry->getUser()); diff --git a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php index de16c037..f50209b4 100644 --- a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php +++ b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php @@ -9,16 +9,16 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; use App\Invoice\InvoiceItem; +use App\Invoice\InvoiceItemInterface; /** - * An abstract calculator that sums up the timesheet records. + * An abstract calculator that sums up the invoice item records. */ abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface { - abstract protected function calculateSumIdentifier(Timesheet $timesheet): string; + abstract protected function calculateSumIdentifier(InvoiceItemInterface $invoiceItem): string; /** * @return InvoiceItem[] @@ -45,15 +45,15 @@ abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator imp if (!isset($invoiceItems[$id])) { $invoiceItems[$id] = new InvoiceItem(); } - $timesheet = $invoiceItems[$id]; - $this->mergeTimesheets($timesheet, $entry); - $this->mergeSumTimesheet($timesheet, $entry); + $invoiceItem = $invoiceItems[$id]; + $this->mergeInvoiceItems($invoiceItem, $entry); + $this->mergeSumTimesheet($invoiceItem, $entry); } return array_values($invoiceItems); } - protected function mergeSumTimesheet(InvoiceItem $invoiceItem, Timesheet $entry) + protected function mergeSumTimesheet(InvoiceItem $invoiceItem, InvoiceItemInterface $entry) { // allows to set values per calculator after merging the timesheet } diff --git a/src/Invoice/Calculator/ActivityInvoiceCalculator.php b/src/Invoice/Calculator/ActivityInvoiceCalculator.php index 4702f025..acc04fec 100644 --- a/src/Invoice/Calculator/ActivityInvoiceCalculator.php +++ b/src/Invoice/Calculator/ActivityInvoiceCalculator.php @@ -9,25 +9,25 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; use App\Invoice\InvoiceItem; +use App\Invoice\InvoiceItemInterface; /** - * A calculator that sums up the timesheet records by activity. + * A calculator that sums up the invoice item records by activity. */ class ActivityInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(Timesheet $timesheet): string + protected function calculateSumIdentifier(InvoiceItemInterface $invoiceItem): string { - if (null === $timesheet->getActivity()->getId()) { + if (null === $invoiceItem->getActivity()->getId()) { throw new \Exception('Cannot handle un-persisted activities'); } - return (string) $timesheet->getActivity()->getId(); + return (string) $invoiceItem->getActivity()->getId(); } - protected function mergeSumTimesheet(InvoiceItem $invoiceItem, Timesheet $entry) + protected function mergeSumTimesheet(InvoiceItem $invoiceItem, InvoiceItemInterface $entry) { $invoiceItem->setActivity($entry->getActivity()); $invoiceItem->setDescription($entry->getActivity()->getName()); diff --git a/src/Invoice/Calculator/DateInvoiceCalculator.php b/src/Invoice/Calculator/DateInvoiceCalculator.php index 029fbf72..173f02d2 100644 --- a/src/Invoice/Calculator/DateInvoiceCalculator.php +++ b/src/Invoice/Calculator/DateInvoiceCalculator.php @@ -9,21 +9,21 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; +use App\Invoice\InvoiceItemInterface; /** - * A calculator that sums up the timesheet records for each day. + * A calculator that sums up the invoice item records for each day. */ class DateInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(Timesheet $timesheet): string + protected function calculateSumIdentifier(InvoiceItemInterface $invoiceItem): string { - if (null === $timesheet->getBegin()) { - throw new \Exception('Cannot handle timesheets without start date'); + if (null === $invoiceItem->getBegin()) { + throw new \Exception('Cannot handle invoice items without start date'); } - return $timesheet->getBegin()->format('Y-m-d'); + return $invoiceItem->getBegin()->format('Y-m-d'); } /** diff --git a/src/Invoice/Calculator/DefaultCalculator.php b/src/Invoice/Calculator/DefaultCalculator.php index 4a6cc5c0..83bc29a4 100644 --- a/src/Invoice/Calculator/DefaultCalculator.php +++ b/src/Invoice/Calculator/DefaultCalculator.php @@ -29,7 +29,7 @@ class DefaultCalculator extends AbstractMergedCalculator implements CalculatorIn foreach ($this->model->getEntries() as $entry) { $item = new InvoiceItem(); - $this->mergeTimesheets($item, $entry); + $this->mergeInvoiceItems($item, $entry); foreach ($entry->getVisibleMetaFields() as $field) { $item->addAdditionalField($field->getName(), $field->getValue()); } diff --git a/src/Invoice/Calculator/ProjectInvoiceCalculator.php b/src/Invoice/Calculator/ProjectInvoiceCalculator.php index 05106229..465dfd32 100644 --- a/src/Invoice/Calculator/ProjectInvoiceCalculator.php +++ b/src/Invoice/Calculator/ProjectInvoiceCalculator.php @@ -9,25 +9,25 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; use App\Invoice\InvoiceItem; +use App\Invoice\InvoiceItemInterface; /** - * A calculator that sums up the timesheet records by project. + * A calculator that sums up the invoice item records by project. */ class ProjectInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(Timesheet $timesheet): string + protected function calculateSumIdentifier(InvoiceItemInterface $invoiceItem): string { - if (null === $timesheet->getProject()->getId()) { + if (null === $invoiceItem->getProject()->getId()) { throw new \Exception('Cannot handle un-persisted projects'); } - return (string) $timesheet->getProject()->getId(); + return (string) $invoiceItem->getProject()->getId(); } - protected function mergeSumTimesheet(InvoiceItem $invoiceItem, Timesheet $entry) + protected function mergeSumTimesheet(InvoiceItem $invoiceItem, InvoiceItemInterface $entry) { $invoiceItem->setProject($entry->getProject()); $invoiceItem->setDescription($entry->getProject()->getName()); diff --git a/src/Invoice/Calculator/ShortInvoiceCalculator.php b/src/Invoice/Calculator/ShortInvoiceCalculator.php index 96e74f30..d28049c1 100644 --- a/src/Invoice/Calculator/ShortInvoiceCalculator.php +++ b/src/Invoice/Calculator/ShortInvoiceCalculator.php @@ -9,12 +9,11 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; use App\Invoice\InvoiceItem; /** - * A calculator that sums up all timesheet records from the model and returns only one + * A calculator that sums up all invoice item records from the model and returns only one * entry for a compact invoice version. */ class ShortInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface @@ -40,7 +39,7 @@ class ShortInvoiceCalculator extends AbstractMergedCalculator implements Calcula if (!in_array($key, $keys)) { $keys[] = $key; } - $this->mergeTimesheets($invoiceItem, $entry); + $this->mergeInvoiceItems($invoiceItem, $entry); } if (count($keys) > 1) { diff --git a/src/Invoice/Calculator/UserInvoiceCalculator.php b/src/Invoice/Calculator/UserInvoiceCalculator.php index 26c8d173..2d0fd059 100644 --- a/src/Invoice/Calculator/UserInvoiceCalculator.php +++ b/src/Invoice/Calculator/UserInvoiceCalculator.php @@ -9,21 +9,21 @@ namespace App\Invoice\Calculator; -use App\Entity\Timesheet; use App\Invoice\CalculatorInterface; +use App\Invoice\InvoiceItemInterface; /** - * A calculator that sums up the timesheet records by user. + * A calculator that sums up the invoice item records by user. */ class UserInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(Timesheet $timesheet): string + protected function calculateSumIdentifier(InvoiceItemInterface $invoiceItem): string { - if (null === $timesheet->getUser()->getId()) { + if (null === $invoiceItem->getUser()->getId()) { throw new \Exception('Cannot handle un-persisted user'); } - return (string) $timesheet->getUser()->getId(); + return (string) $invoiceItem->getUser()->getId(); } /** diff --git a/src/Invoice/InvoiceItemInterface.php b/src/Invoice/InvoiceItemInterface.php new file mode 100644 index 00000000..cd5d2f03 --- /dev/null +++ b/src/Invoice/InvoiceItemInterface.php @@ -0,0 +1,43 @@ +getEntries() instead. * - * @return Timesheet[] + * @return InvoiceItemInterface[] */ public function getEntries(): array { @@ -90,16 +89,30 @@ class InvoiceModel } /** - * @param Timesheet[] $entries + * @deprecated since 1.3 - will be removed with 2.0 + * @param InvoiceItemInterface[] $entries * @return InvoiceModel */ public function setEntries(array $entries): InvoiceModel { + @trigger_error('setEntries() is deprecated and will be removed with 2.0', E_USER_DEPRECATED); + $this->entries = $entries; return $this; } + /** + * @param InvoiceItemInterface[] $entries + * @return InvoiceModel + */ + public function addEntries(array $entries): InvoiceModel + { + $this->entries = array_merge($this->entries, $entries); + + return $this; + } + public function getTemplate(): ?InvoiceTemplate { return $this->template; diff --git a/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php b/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php index f05452f5..7cb1e554 100644 --- a/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php +++ b/src/Invoice/Renderer/AbstractSpreadsheetRenderer.php @@ -41,9 +41,9 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer $worksheet = $spreadsheet->getActiveSheet(); $entries = $model->getCalculator()->getEntries(); $replacer = $this->modelToReplacer($model); - $timesheetAmount = count($entries); - if ($timesheetAmount > 1) { - $this->addTemplateRows($worksheet, $timesheetAmount); + $invoiceItemCount = count($entries); + if ($invoiceItemCount > 1) { + $this->addTemplateRows($worksheet, $invoiceItemCount); } $worksheet->setTitle($model->getTemplate()->getTitle()); @@ -51,13 +51,13 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer $entryRow = 0; foreach ($worksheet->getRowIterator() as $row) { - $timesheet = $entries[$entryRow]; + $invoiceItem = $entries[$entryRow]; $sheetValues = false; foreach ($row->getCellIterator() as $cell) { $value = $cell->getValue(); if (stripos($value, '${entry.') !== false) { if ($sheetValues === false) { - $sheetValues = $this->timesheetToArray($timesheet); + $sheetValues = $this->invoiceItemToArray($invoiceItem); } $searcher = str_replace('${', '', $value); $searcher = str_replace('}', '', $searcher); @@ -73,7 +73,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer } } - if ($sheetValues !== false && $entryRow < $timesheetAmount - 1) { + if ($sheetValues !== false && $entryRow < $invoiceItemCount - 1) { $entryRow++; } } @@ -85,9 +85,10 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer /** * @param Worksheet $worksheet - * @param int $timesheets + * @param int $invoiceItemCount + * @throws \PhpOffice\PhpSpreadsheet\Exception */ - protected function addTemplateRows(Worksheet $worksheet, int $timesheets) + protected function addTemplateRows(Worksheet $worksheet, int $invoiceItemCount) { $startRow = null; $rowCounter = 0; @@ -98,7 +99,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer $value = $cell->getValue(); if (stripos($value, '${entry.') !== false) { $startRow = $row->getRowIndex(); - $worksheet->insertNewRowBefore($row->getRowIndex(), $timesheets - 1); + $worksheet->insertNewRowBefore($row->getRowIndex(), $invoiceItemCount - 1); break 2; } @@ -117,7 +118,7 @@ abstract class AbstractSpreadsheetRenderer extends AbstractRenderer } // fill up all new rows with template values - $templateRow = $timesheets + $startRow; + $templateRow = $invoiceItemCount + $startRow; $iterator = $worksheet->getRowIterator($templateRow - 1, $templateRow); $templateColumns = []; foreach ($iterator->current()->getCellIterator() as $cell) { diff --git a/src/Invoice/Renderer/DocxRenderer.php b/src/Invoice/Renderer/DocxRenderer.php index 465b519c..b219fdf3 100644 --- a/src/Invoice/Renderer/DocxRenderer.php +++ b/src/Invoice/Renderer/DocxRenderer.php @@ -50,7 +50,7 @@ class DocxRenderer extends AbstractRenderer implements RendererInterface $i = 1; foreach ($model->getCalculator()->getEntries() as $entry) { - $values = $this->timesheetToArray($entry); + $values = $this->invoiceItemToArray($entry); foreach ($values as $search => $replace) { $replace = $xmlEscaper->escape($replace); $replace = str_replace(PHP_EOL, '', $replace); diff --git a/src/Invoice/Renderer/RendererTrait.php b/src/Invoice/Renderer/RendererTrait.php index 581318c6..2ebf63e3 100644 --- a/src/Invoice/Renderer/RendererTrait.php +++ b/src/Invoice/Renderer/RendererTrait.php @@ -176,7 +176,17 @@ trait RendererTrait return $values; } + /** + * @deprecated since 1.3 - will be removed with 2.0 + */ protected function timesheetToArray(InvoiceItem $invoiceItem): array + { + @trigger_error('timesheetToArray() is deprecated and will be removed with 2.0', E_USER_DEPRECATED); + + return $this->invoiceItemToArray($invoiceItem); + } + + protected function invoiceItemToArray(InvoiceItem $invoiceItem): array { $rate = $invoiceItem->getRate(); $hourlyRate = $invoiceItem->getHourlyRate(); diff --git a/src/Invoice/ServiceInvoice.php b/src/Invoice/ServiceInvoice.php index 7398a1b5..3872e33a 100644 --- a/src/Invoice/ServiceInvoice.php +++ b/src/Invoice/ServiceInvoice.php @@ -13,43 +13,37 @@ use App\Entity\InvoiceDocument; use App\Repository\InvoiceDocumentRepository; /** - * A service to manage invoice dependencies. + * Service to manage invoice dependencies. */ -class ServiceInvoice +final class ServiceInvoice { /** * @var CalculatorInterface[] */ - protected $calculator = []; - + private $calculator = []; /** * @var RendererInterface[] */ - protected $renderer = []; - + private $renderer = []; /** * @var NumberGeneratorInterface[] */ - protected $numberGenerator = []; - + private $numberGenerator = []; + /** + * @var array InvoiceItemRepositoryInterface[] + */ + private $invoiceItemRepositories = []; /** * @var InvoiceDocumentRepository */ - protected $documents; + private $documents; - /** - * @param InvoiceDocumentRepository $repository - */ public function __construct(InvoiceDocumentRepository $repository) { $this->documents = $repository; } - /** - * @param NumberGeneratorInterface $generator - * @return $this - */ - public function addNumberGenerator(NumberGeneratorInterface $generator) + public function addNumberGenerator(NumberGeneratorInterface $generator): ServiceInvoice { $this->numberGenerator[] = $generator; @@ -59,16 +53,12 @@ class ServiceInvoice /** * @return NumberGeneratorInterface[] */ - public function getNumberGenerator() + public function getNumberGenerator(): array { return $this->numberGenerator; } - /** - * @param string $name - * @return NumberGeneratorInterface|null - */ - public function getNumberGeneratorByName(string $name) + public function getNumberGeneratorByName(string $name): ?NumberGeneratorInterface { foreach ($this->getNumberGenerator() as $generator) { if ($generator->getId() === $name) { @@ -79,11 +69,7 @@ class ServiceInvoice return null; } - /** - * @param CalculatorInterface $calculator - * @return $this - */ - public function addCalculator(CalculatorInterface $calculator) + public function addCalculator(CalculatorInterface $calculator): ServiceInvoice { $this->calculator[] = $calculator; @@ -93,16 +79,12 @@ class ServiceInvoice /** * @return CalculatorInterface[] */ - public function getCalculator() + public function getCalculator(): array { return $this->calculator; } - /** - * @param string $name - * @return CalculatorInterface|null - */ - public function getCalculatorByName(string $name) + public function getCalculatorByName(string $name): ?CalculatorInterface { foreach ($this->getCalculator() as $calculator) { if ($calculator->getId() === $name) { @@ -113,11 +95,7 @@ class ServiceInvoice return null; } - /** - * @param string $name - * @return InvoiceDocument|null - */ - public function getDocumentByName(string $name) + public function getDocumentByName(string $name): ?InvoiceDocument { return $this->documents->findByName($name); } @@ -127,16 +105,12 @@ class ServiceInvoice * * @return InvoiceDocument[] */ - public function getDocuments() + public function getDocuments(): array { return $this->documents->findAll(); } - /** - * @param RendererInterface $renderer - * @return $this - */ - public function addRenderer(RendererInterface $renderer) + public function addRenderer(RendererInterface $renderer): ServiceInvoice { $this->renderer[] = $renderer; @@ -148,8 +122,23 @@ class ServiceInvoice * * @return RendererInterface[] */ - public function getRenderer() + public function getRenderer(): array { return $this->renderer; } + + /** + * @return InvoiceItemRepositoryInterface[] + */ + public function getInvoiceItemRepositories(): array + { + return $this->invoiceItemRepositories; + } + + public function addInvoiceItemRepository(InvoiceItemRepositoryInterface $invoiceItemRepository): ServiceInvoice + { + $this->invoiceItemRepositories[] = $invoiceItemRepository; + + return $this; + } } diff --git a/src/Kernel.php b/src/Kernel.php index 3ba8d980..06b5bcb3 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -16,6 +16,7 @@ use App\DependencyInjection\Compiler\TwigContextCompilerPass; use App\DependencyInjection\Compiler\WidgetCompilerPass; use App\Export\RendererInterface as ExportRendererInterface; use App\Invoice\CalculatorInterface as InvoiceCalculator; +use App\Invoice\InvoiceItemRepositoryInterface; use App\Invoice\NumberGeneratorInterface; use App\Invoice\RendererInterface as InvoiceRendererInterface; use App\Ldap\FormLoginLdapFactory; @@ -46,6 +47,7 @@ class Kernel extends BaseKernel public const TAG_INVOICE_RENDERER = 'invoice.renderer'; public const TAG_INVOICE_NUMBER_GENERATOR = 'invoice.number_generator'; public const TAG_INVOICE_CALCULATOR = 'invoice.calculator'; + public const TAG_INVOICE_REPOSITORY = 'invoice.repository'; public const TAG_TIMESHEET_CALCULATOR = 'timesheet.calculator'; public function getCacheDir() @@ -65,6 +67,7 @@ class Kernel extends BaseKernel $container->registerForAutoconfiguration(InvoiceRendererInterface::class)->addTag(self::TAG_INVOICE_RENDERER); $container->registerForAutoconfiguration(NumberGeneratorInterface::class)->addTag(self::TAG_INVOICE_NUMBER_GENERATOR); $container->registerForAutoconfiguration(InvoiceCalculator::class)->addTag(self::TAG_INVOICE_CALCULATOR); + $container->registerForAutoconfiguration(InvoiceItemRepositoryInterface::class)->addTag(self::TAG_INVOICE_REPOSITORY); $container->registerForAutoconfiguration(PluginInterface::class)->addTag(self::TAG_PLUGIN); $container->registerForAutoconfiguration(WidgetRendererInterface::class)->addTag(self::TAG_WIDGET_RENDERER); $container->registerForAutoconfiguration(WidgetInterface::class)->addTag(self::TAG_WIDGET); diff --git a/src/Repository/Query/ActivityQuery.php b/src/Repository/Query/ActivityQuery.php index d96ba9de..6847b523 100644 --- a/src/Repository/Query/ActivityQuery.php +++ b/src/Repository/Query/ActivityQuery.php @@ -41,9 +41,9 @@ class ActivityQuery extends ProjectQuery /** * @param bool $globalsOnly - * @return ActivityQuery + * @return self */ - public function setGlobalsOnly($globalsOnly): ActivityQuery + public function setGlobalsOnly($globalsOnly): self { $this->globalsOnly = (bool) $globalsOnly; @@ -60,9 +60,9 @@ class ActivityQuery extends ProjectQuery /** * @param Project|int|null $project - * @return ActivityQuery + * @return self */ - public function setProject($project = null): ActivityQuery + public function setProject($project = null): self { $this->project = $project; diff --git a/src/Repository/Query/BaseQuery.php b/src/Repository/Query/BaseQuery.php index b2e68d5f..8b151513 100644 --- a/src/Repository/Query/BaseQuery.php +++ b/src/Repository/Query/BaseQuery.php @@ -83,7 +83,7 @@ class BaseQuery /** * @param User $user - * @return $this + * @return self */ public function setCurrentUser(User $user) { @@ -102,7 +102,7 @@ class BaseQuery /** * @param int $page - * @return $this + * @return self */ public function setPage($page) { @@ -118,7 +118,7 @@ class BaseQuery /** * @param int $pageSize - * @return $this + * @return self */ public function setPageSize($pageSize) { @@ -138,7 +138,7 @@ class BaseQuery * You need to validate carefully if this value is used from a user-input. * * @param string $orderBy - * @return $this + * @return self */ public function setOrderBy($orderBy) { @@ -154,7 +154,7 @@ class BaseQuery /** * @param string $order - * @return $this + * @return self */ public function setOrder($order) { @@ -205,7 +205,7 @@ class BaseQuery /** * @param SearchTerm|null $searchTerm - * @return BaseQuery + * @return self */ public function setSearchTerm(?SearchTerm $searchTerm) { diff --git a/src/Repository/TimesheetInvoiceItemRepository.php b/src/Repository/TimesheetInvoiceItemRepository.php new file mode 100644 index 00000000..c437424d --- /dev/null +++ b/src/Repository/TimesheetInvoiceItemRepository.php @@ -0,0 +1,51 @@ +repository = $repository; + } + + /** + * @param InvoiceQuery $query + * @return InvoiceItemInterface[] + */ + public function getInvoiceItemsForQuery(InvoiceQuery $query): iterable + { + return $this->repository->getTimesheetsForQuery($query); + } + + /** + * @param InvoiceItemInterface[] $invoiceItems + */ + public function setExported(array $invoiceItems) + { + foreach ($invoiceItems as $item) { + if (!$item instanceof Timesheet) { + throw new \InvalidArgumentException('TimesheetInvoiceItemRepository only supports Timesheet entities'); + } + } + + $this->repository->setExported($invoiceItems); + } +} diff --git a/src/Repository/TimesheetRepository.php b/src/Repository/TimesheetRepository.php index 30ee302f..dcfcc0fc 100644 --- a/src/Repository/TimesheetRepository.php +++ b/src/Repository/TimesheetRepository.php @@ -609,9 +609,9 @@ class TimesheetRepository extends EntityRepository } if ($query->getExported() === TimesheetQuery::STATE_EXPORTED) { - $qb->andWhere('t.exported = :exported')->setParameter('exported', true); + $qb->andWhere('t.exported = :exported')->setParameter('exported', true, \PDO::PARAM_BOOL); } elseif ($query->getExported() === TimesheetQuery::STATE_NOT_EXPORTED) { - $qb->andWhere('t.exported = :exported')->setParameter('exported', false); + $qb->andWhere('t.exported = :exported')->setParameter('exported', false, \PDO::PARAM_BOOL); } if (null !== $query->getActivity()) { diff --git a/translations/messages.de.xliff b/translations/messages.de.xliff index 761c5e26..2859787e 100644 --- a/translations/messages.de.xliff +++ b/translations/messages.de.xliff @@ -19,7 +19,7 @@ both - + Beides This is a mandatory field diff --git a/translations/messages.en.xliff b/translations/messages.en.xliff index 329ce17b..62c8b2ac 100644 --- a/translations/messages.en.xliff +++ b/translations/messages.en.xliff @@ -19,7 +19,7 @@ both - + Both This is a mandatory field