From dd89363c72332de038cc12f3f2eaae7a903b6591 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Wed, 21 Jun 2023 08:17:54 +0200 Subject: [PATCH] Improve UX for invoice template management (#4121) change translations and to explain that calculator actually group items by fields --- phpstan.neon | 25 --- src/Form/Type/InvoiceCalculatorType.php | 1 + .../AbstractSumInvoiceCalculator.php | 24 ++- .../Calculator/ActivityInvoiceCalculator.php | 10 +- .../ActivityUserInvoiceCalculator.php | 50 +++++ .../Calculator/DateInvoiceCalculator.php | 6 +- .../Calculator/DateUserInvoiceCalculator.php | 40 ++++ .../Calculator/PriceInvoiceCalculator.php | 6 +- .../Calculator/ProjectInvoiceCalculator.php | 16 +- .../ProjectUserInvoiceCalculator.php | 62 ++++++ .../Calculator/ShortInvoiceCalculator.php | 4 +- .../Calculator/UserInvoiceCalculator.php | 8 +- .../Calculator/WeeklyInvoiceCalculator.php | 10 +- templates/invoice/template_edit.html.twig | 94 +++++---- .../Calculator/AbstractCalculatorTest.php | 18 +- .../ActivityInvoiceCalculatorTest.php | 15 +- .../ActivityUserInvoiceCalculatorTest.php | 181 ++++++++++++++++++ .../Calculator/DateInvoiceCalculatorTest.php | 14 +- .../DateUserInvoiceCalculatorTest.php | 143 ++++++++++++++ .../Calculator/DefaultCalculatorTest.php | 9 +- .../Calculator/PriceInvoiceCalculatorTest.php | 14 +- .../ProjectInvoiceCalculatorTest.php | 14 +- .../ProjectUserInvoiceCalculatorTest.php | 142 ++++++++++++++ .../Calculator/ShortInvoiceCalculatorTest.php | 21 +- .../Calculator/UserInvoiceCalculatorTest.php | 15 +- .../WeeklyInvoiceCalculatorTest.php | 13 +- tests/phpstan.neon | 140 -------------- translations/invoice-calculator.de.xlf | 34 +++- translations/invoice-calculator.de_CH.xlf | 40 ++-- translations/invoice-calculator.en.xlf | 34 +++- translations/invoice-renderer.de.xlf | 4 +- translations/invoice-renderer.de_CH.xlf | 2 +- translations/invoice-renderer.en.xlf | 4 +- 33 files changed, 889 insertions(+), 324 deletions(-) create mode 100644 src/Invoice/Calculator/ActivityUserInvoiceCalculator.php create mode 100644 src/Invoice/Calculator/DateUserInvoiceCalculator.php create mode 100644 src/Invoice/Calculator/ProjectUserInvoiceCalculator.php create mode 100644 tests/Invoice/Calculator/ActivityUserInvoiceCalculatorTest.php create mode 100644 tests/Invoice/Calculator/DateUserInvoiceCalculatorTest.php create mode 100644 tests/Invoice/Calculator/ProjectUserInvoiceCalculatorTest.php diff --git a/phpstan.neon b/phpstan.neon index e4998a6a..474c9472 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4091,31 +4091,6 @@ parameters: count: 1 path: src/Invoice/Calculator/AbstractMergedCalculator.php - - - message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\Project\\|null\\.$#" - count: 2 - path: src/Invoice/Calculator/ProjectInvoiceCalculator.php - - - - message: "#^Cannot call method getInvoiceText\\(\\) on App\\\\Entity\\\\Project\\|null\\.$#" - count: 2 - path: src/Invoice/Calculator/ProjectInvoiceCalculator.php - - - - message: "#^Cannot call method getName\\(\\) on App\\\\Entity\\\\Project\\|null\\.$#" - count: 1 - path: src/Invoice/Calculator/ProjectInvoiceCalculator.php - - - - message: "#^Cannot call method getId\\(\\) on App\\\\Entity\\\\User\\|null\\.$#" - count: 2 - path: src/Invoice/Calculator/UserInvoiceCalculator.php - - - - message: "#^Cannot call method format\\(\\) on DateTime\\|null\\.$#" - count: 1 - path: src/Invoice/Calculator/WeeklyInvoiceCalculator.php - - message: "#^Cannot call method format\\(\\) on DateTime\\|null\\.$#" count: 2 diff --git a/src/Form/Type/InvoiceCalculatorType.php b/src/Form/Type/InvoiceCalculatorType.php index 1b965a82..8296b31f 100644 --- a/src/Form/Type/InvoiceCalculatorType.php +++ b/src/Form/Type/InvoiceCalculatorType.php @@ -36,6 +36,7 @@ final class InvoiceCalculatorType extends AbstractType 'choice_label' => function ($renderer) { return $renderer; }, + 'help' => 'invoice_calculator.help', 'translation_domain' => 'invoice-calculator', ]); } diff --git a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php index 89bda645..b6416da2 100644 --- a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php +++ b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php @@ -18,7 +18,29 @@ use App\Invoice\InvoiceItem; */ abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface { - abstract protected function calculateSumIdentifier(ExportableItem $invoiceItem): string; + protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + { + $ids = $this->getIdentifiers($invoiceItem); + + $identifier = ''; + foreach ($ids as $id) { + if ($id === null) { + $id = '__NULL__'; + } + $identifier .= $id; + } + + return $identifier; + } + + /** + * @param ExportableItem $invoiceItem + * @return array + */ + public function getIdentifiers(ExportableItem $invoiceItem): array + { + return []; + } protected function calculateIdentifier(ExportableItem $entry): string { diff --git a/src/Invoice/Calculator/ActivityInvoiceCalculator.php b/src/Invoice/Calculator/ActivityInvoiceCalculator.php index 592b3c31..8c86f30a 100644 --- a/src/Invoice/Calculator/ActivityInvoiceCalculator.php +++ b/src/Invoice/Calculator/ActivityInvoiceCalculator.php @@ -18,13 +18,11 @@ use App\Invoice\InvoiceItem; */ final class ActivityInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { - if (null === $invoiceItem->getActivity()) { - return '__NULL__'; - } - - return (string) $invoiceItem->getActivity()->getId(); + return [ + $invoiceItem->getActivity()?->getId() + ]; } protected function mergeSumInvoiceItem(InvoiceItem $invoiceItem, ExportableItem $entry): void diff --git a/src/Invoice/Calculator/ActivityUserInvoiceCalculator.php b/src/Invoice/Calculator/ActivityUserInvoiceCalculator.php new file mode 100644 index 00000000..e84e2297 --- /dev/null +++ b/src/Invoice/Calculator/ActivityUserInvoiceCalculator.php @@ -0,0 +1,50 @@ +getUser()?->getId() === null) { + throw new \Exception('Cannot handle un-persisted users'); + } + + return [ + $invoiceItem->getActivity()?->getId(), + $invoiceItem->getUser()->getId() + ]; + } + + protected function mergeSumInvoiceItem(InvoiceItem $invoiceItem, ExportableItem $entry): void + { + if (null === $entry->getActivity()) { + return; + } + + if ($entry->getActivity()->getInvoiceText() !== null) { + $invoiceItem->setDescription($entry->getActivity()->getInvoiceText()); + } else { + $invoiceItem->setDescription($entry->getActivity()->getName()); + } + } + + public function getId(): string + { + return 'activity_user'; + } +} diff --git a/src/Invoice/Calculator/DateInvoiceCalculator.php b/src/Invoice/Calculator/DateInvoiceCalculator.php index b510f266..559cabd4 100644 --- a/src/Invoice/Calculator/DateInvoiceCalculator.php +++ b/src/Invoice/Calculator/DateInvoiceCalculator.php @@ -17,13 +17,15 @@ use App\Invoice\CalculatorInterface; */ final class DateInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { if (null === $invoiceItem->getBegin()) { throw new \Exception('Cannot handle invoice items without start date'); } - return $invoiceItem->getBegin()->format('Y-m-d'); + return [ + $invoiceItem->getBegin()->format('Y-m-d') + ]; } public function getId(): string diff --git a/src/Invoice/Calculator/DateUserInvoiceCalculator.php b/src/Invoice/Calculator/DateUserInvoiceCalculator.php new file mode 100644 index 00000000..4856f404 --- /dev/null +++ b/src/Invoice/Calculator/DateUserInvoiceCalculator.php @@ -0,0 +1,40 @@ +getBegin()) { + throw new \Exception('Cannot handle invoice items without start date'); + } + + if ($invoiceItem->getUser()?->getId() === null) { + throw new \Exception('Cannot handle un-persisted users'); + } + + return [ + $invoiceItem->getBegin()->format('Y-m-d'), + $invoiceItem->getUser()->getId() + ]; + } + + public function getId(): string + { + return 'date_user'; + } +} diff --git a/src/Invoice/Calculator/PriceInvoiceCalculator.php b/src/Invoice/Calculator/PriceInvoiceCalculator.php index 9809cb58..bbef2f2f 100644 --- a/src/Invoice/Calculator/PriceInvoiceCalculator.php +++ b/src/Invoice/Calculator/PriceInvoiceCalculator.php @@ -17,13 +17,13 @@ use App\Invoice\CalculatorInterface; */ final class PriceInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { if (null !== $invoiceItem->getFixedRate()) { - return 'fixed_' . $invoiceItem->getFixedRate(); + return ['fixed_' . $invoiceItem->getFixedRate()]; } - return 'hourly_' . $invoiceItem->getHourlyRate(); + return ['hourly_' . $invoiceItem->getHourlyRate()]; } public function getId(): string diff --git a/src/Invoice/Calculator/ProjectInvoiceCalculator.php b/src/Invoice/Calculator/ProjectInvoiceCalculator.php index bbd05df7..291494dc 100644 --- a/src/Invoice/Calculator/ProjectInvoiceCalculator.php +++ b/src/Invoice/Calculator/ProjectInvoiceCalculator.php @@ -18,17 +18,27 @@ use App\Invoice\InvoiceItem; */ final class ProjectInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { - if (null === $invoiceItem->getProject()->getId()) { + if ($invoiceItem->getProject() === null) { + throw new \Exception('Cannot handle invoice items without project'); + } + + if ($invoiceItem->getProject()->getId() === null) { throw new \Exception('Cannot handle un-persisted projects'); } - return (string) $invoiceItem->getProject()->getId(); + return [ + $invoiceItem->getProject()->getId() + ]; } protected function mergeSumInvoiceItem(InvoiceItem $invoiceItem, ExportableItem $entry): void { + if ($entry->getProject() === null) { + return; + } + if ($entry->getProject()->getInvoiceText() !== null) { $invoiceItem->setDescription($entry->getProject()->getInvoiceText()); } else { diff --git a/src/Invoice/Calculator/ProjectUserInvoiceCalculator.php b/src/Invoice/Calculator/ProjectUserInvoiceCalculator.php new file mode 100644 index 00000000..9a2ba6c7 --- /dev/null +++ b/src/Invoice/Calculator/ProjectUserInvoiceCalculator.php @@ -0,0 +1,62 @@ +getProject() === null) { + throw new \Exception('Cannot handle invoice items without project'); + } + + if ($invoiceItem->getProject()->getId() === null) { + throw new \Exception('Cannot handle un-persisted projects'); + } + + if ($invoiceItem->getUser() === null) { + throw new \Exception('Cannot handle invoice items without user'); + } + + if ($invoiceItem->getUser()->getId() === null) { + throw new \Exception('Cannot handle un-persisted users'); + } + + return [ + $invoiceItem->getProject()->getId(), + $invoiceItem->getUser()->getId() + ]; + } + + protected function mergeSumInvoiceItem(InvoiceItem $invoiceItem, ExportableItem $entry): void + { + if ($entry->getProject() === null) { + return; + } + + if ($entry->getProject()->getInvoiceText() !== null) { + $invoiceItem->setDescription($entry->getProject()->getInvoiceText()); + } else { + $invoiceItem->setDescription($entry->getProject()->getName()); + } + } + + public function getId(): string + { + return 'project_user'; + } +} diff --git a/src/Invoice/Calculator/ShortInvoiceCalculator.php b/src/Invoice/Calculator/ShortInvoiceCalculator.php index fe58d2c9..3286ae02 100644 --- a/src/Invoice/Calculator/ShortInvoiceCalculator.php +++ b/src/Invoice/Calculator/ShortInvoiceCalculator.php @@ -32,9 +32,9 @@ final class ShortInvoiceCalculator extends AbstractMergedCalculator implements C $keys = []; foreach ($entries as $entry) { - $key = 'hourly_' . (string) $entry->getHourlyRate(); + $key = 'hourly_' . $entry->getHourlyRate(); if (null !== $entry->getFixedRate()) { - $key = 'fixed_' . (string) $entry->getFixedRate(); + $key = 'fixed_' . $entry->getFixedRate(); } if (!\in_array($key, $keys)) { $keys[] = $key; diff --git a/src/Invoice/Calculator/UserInvoiceCalculator.php b/src/Invoice/Calculator/UserInvoiceCalculator.php index 8f406be5..882b0340 100644 --- a/src/Invoice/Calculator/UserInvoiceCalculator.php +++ b/src/Invoice/Calculator/UserInvoiceCalculator.php @@ -17,13 +17,15 @@ use App\Invoice\CalculatorInterface; */ final class UserInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { - if (null === $invoiceItem->getUser()->getId()) { + if (null === $invoiceItem->getUser()?->getId()) { throw new \Exception('Cannot handle un-persisted user'); } - return (string) $invoiceItem->getUser()->getId(); + return [ + $invoiceItem->getUser()->getId() + ]; } public function getId(): string diff --git a/src/Invoice/Calculator/WeeklyInvoiceCalculator.php b/src/Invoice/Calculator/WeeklyInvoiceCalculator.php index 2e06eb1f..eec9fa12 100644 --- a/src/Invoice/Calculator/WeeklyInvoiceCalculator.php +++ b/src/Invoice/Calculator/WeeklyInvoiceCalculator.php @@ -17,9 +17,15 @@ use App\Invoice\CalculatorInterface; */ final class WeeklyInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface { - protected function calculateSumIdentifier(ExportableItem $invoiceItem): string + public function getIdentifiers(ExportableItem $invoiceItem): array { - return $invoiceItem->getBegin()->format('W'); + if (null === $invoiceItem->getBegin()) { + throw new \Exception('Cannot handle invoice items without start date'); + } + + return [ + $invoiceItem->getBegin()->format('W') + ]; } public function getId(): string diff --git a/templates/invoice/template_edit.html.twig b/templates/invoice/template_edit.html.twig index 5624e219..4943295c 100644 --- a/templates/invoice/template_edit.html.twig +++ b/templates/invoice/template_edit.html.twig @@ -11,55 +11,65 @@ {% embed formEditTemplate with formOptions %} {% block form_body %} {{ form_row(form.name) }} - {{ form_row(form.title) }} -
-
- {{ form_row(form.company) }} +
+
+
+ {{ form_row(form.title) }} +
-
- {{ form_row(form.vatId) }} +
+
+ {{ form_row(form.company) }} +
+
+ {{ form_row(form.vatId) }} +
-
-
-
- {{ form_row(form.address) }} +
+
+ {{ form_row(form.address) }} +
+
+ {{ form_row(form.contact) }} +
-
- {{ form_row(form.contact) }} +
+
+ {{ form_row(form.paymentTerms) }} +
+
+ {{ form_row(form.paymentDetails) }} +
-
-
-
- {{ form_row(form.paymentTerms) }} +
+
+
+
+ {{ form_row(form.dueDays) }} +
+
+ {{ form_row(form.vat) }} +
-
- {{ form_row(form.paymentDetails) }} +
+
+ {{ form_row(form.language) }} +
+
+ {{ form_row(form.numberGenerator) }} +
-
-
-
- {{ form_row(form.dueDays) }} +
+
+
+
+ {{ form_row(form.renderer, {'help_html': true, 'help': '' ~ ('download_invoice_renderer'|trans({}, 'invoice-renderer')) ~ ''}) }} +
+
+ {{ form_row(form.calculator) }} +
-
- {{ form_row(form.vat) }} -
-
-
-
- {{ form_row(form.renderer, {'help_html': true, 'help': '' ~ ('download_invoice_renderer'|trans({}, 'invoice-renderer')) ~ ''}) }} -
-
- {{ form_row(form.language) }} -
-
-
-
- {{ form_row(form.calculator) }} -
-
- {{ form_row(form.numberGenerator) }} -
-
+ {{ form_rest(form) }} {% endblock %} {% endembed %} diff --git a/tests/Invoice/Calculator/AbstractCalculatorTest.php b/tests/Invoice/Calculator/AbstractCalculatorTest.php index c1495a3f..a4b0cc88 100644 --- a/tests/Invoice/Calculator/AbstractCalculatorTest.php +++ b/tests/Invoice/Calculator/AbstractCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\CalculatorInterface; +use App\Invoice\InvoiceModel; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -23,7 +24,18 @@ use PHPUnit\Framework\TestCase; abstract class AbstractCalculatorTest extends TestCase { - protected function assertEmptyModel(CalculatorInterface $sut) + abstract protected function getCalculator(): CalculatorInterface; + + public function testCalculatorInterface(): void + { + $sut = $this->getCalculator(); + + self::assertLessThanOrEqual(20, \strlen($sut->getId())); + + $this->assertEmptyModel($sut); + } + + private function assertEmptyModel(CalculatorInterface $sut): void { $model = $this->getEmptyModel(); $this->assertEquals('EUR', $model->getCurrency()); @@ -38,7 +50,7 @@ abstract class AbstractCalculatorTest extends TestCase $this->assertEquals(0, $sut->getTax()); } - protected function getEmptyModel() + private function getEmptyModel(): InvoiceModel { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -52,7 +64,7 @@ abstract class AbstractCalculatorTest extends TestCase return $model; } - protected function assertDescription(CalculatorInterface $sut, $addProject = false, $addActivity = false) + protected function assertDescription(CalculatorInterface $sut, $addProject = false, $addActivity = false): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); diff --git a/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php index 4e30ce8f..683f3a6e 100644 --- a/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\ActivityInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -28,12 +29,12 @@ use App\Tests\Mocks\InvoiceModelFactoryFactory; */ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new ActivityInvoiceCalculator()); + return new ActivityInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -140,7 +141,7 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new ActivityInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('activity', $sut->getId()); @@ -149,16 +150,16 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals('EUR', $model->getCurrency()); $this->assertEquals(2521.12, $sut->getSubtotal()); $this->assertEquals(6600, $sut->getTimeWorked()); - $this->assertEquals(5, \count($sut->getEntries())); $entries = $sut->getEntries(); + self::assertCount(4, $entries); $this->assertEquals(404.38, $entries[0]->getRate()); $this->assertEquals(2032.74, $entries[1]->getRate()); $this->assertEquals(84, $entries[2]->getRate()); } - public function testDescriptionByActivity() + public function testDescriptionByActivity(): void { - $this->assertDescription(new ActivityInvoiceCalculator(), false, true); + $this->assertDescription($this->getCalculator(), false, true); } } diff --git a/tests/Invoice/Calculator/ActivityUserInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ActivityUserInvoiceCalculatorTest.php new file mode 100644 index 00000000..4de0a168 --- /dev/null +++ b/tests/Invoice/Calculator/ActivityUserInvoiceCalculatorTest.php @@ -0,0 +1,181 @@ +setVat(19); + + $user1 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user1->method('getId')->willReturn(1); + + $user2 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user2->method('getId')->willReturn(2); + + $activity1 = $this->getMockBuilder(Activity::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $activity1->method('getId')->willReturn(1); + + $activity2 = $this->getMockBuilder(Activity::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $activity2->method('getId')->willReturn(2); + + $activity3 = $this->getMockBuilder(Activity::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $activity3->method('getId')->willReturn(3); + + $timesheet = new Timesheet(); + $timesheet + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(3600) + ->setRate(293.27) + ->setUser($user1) + ->setActivity($activity1) + ->setProject((new Project())->setName('bar')); + + $timesheet2 = new Timesheet(); + $timesheet2 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(400) + ->setRate(84.75) + ->setUser($user1) + ->setActivity($activity2) + ->setProject((new Project())->setName('bar')); + + $timesheet3 = new Timesheet(); + $timesheet3 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(1800) + ->setRate(111.11) + ->setUser($user1) + ->setActivity($activity1) + ->setProject((new Project())->setName('bar')); + + $timesheet4 = new Timesheet(); + $timesheet4 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(400) + ->setRate(1947.99) + ->setUser($user1) + ->setActivity($activity2) + ->setProject((new Project())->setName('bar')); + + $timesheet5 = new Timesheet(); + $timesheet5 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(400) + ->setRate(84) + ->setUser($user2) + ->setActivity($activity3) + ->setProject((new Project())->setName('bar')); + + $timesheet5a = new Timesheet(); + $timesheet5a + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(400) + ->setRate(84) + ->setUser($user1) + ->setActivity($activity3) + ->setProject((new Project())->setName('bar')); + + $timesheet6 = new Timesheet(); + $timesheet6 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(0) + ->setRate(0) + ->setUser($user1) + ->setProject((new Project())->setName('bar')); + + $timesheet7 = new Timesheet(); + $timesheet7 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(0) + ->setRate(0) + ->setUser($user2) + ->setActivity(new Activity()) + ->setProject((new Project())->setName('bar')); + + $timesheet8 = new Timesheet(); + $timesheet8 + ->setBegin(new \DateTime()) + ->setEnd(new \DateTime()) + ->setDuration(0) + ->setRate(0) + ->setUser($user2) + ->setProject((new Project())->setName('bar')); + + $entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5, $timesheet5a, $timesheet6, $timesheet7, $timesheet8]; + + $query = new InvoiceQuery(); + $query->addActivity($activity1); + + $model = (new InvoiceModelFactoryFactory($this))->create()->createModel(new DebugFormatter()); + $model->setCustomer($customer); + $model->setTemplate($template); + $model->addEntries($entries); + $model->setQuery($query); + + $sut = $this->getCalculator(); + $sut->setModel($model); + + $this->assertEquals('activity_user', $sut->getId()); + $this->assertEquals(3100.09, $sut->getTotal()); + $this->assertEquals(19, $sut->getVat()); + $this->assertEquals('EUR', $model->getCurrency()); + $this->assertEquals(2605.12, $sut->getSubtotal()); + $this->assertEquals(7000, $sut->getTimeWorked()); + + $entries = $sut->getEntries(); + self::assertCount(6, $entries); + $this->assertEquals(404.38, $entries[0]->getRate()); + $this->assertEquals(2032.74, $entries[1]->getRate()); + $this->assertEquals(84.0, $entries[2]->getRate()); + $this->assertEquals(84.0, $entries[3]->getRate()); + $this->assertEquals(0, $entries[4]->getRate()); + $this->assertEquals(0, $entries[5]->getRate()); + } + + public function testDescriptionByActivity(): void + { + $this->assertDescription($this->getCalculator(), false, true); + } +} diff --git a/tests/Invoice/Calculator/DateInvoiceCalculatorTest.php b/tests/Invoice/Calculator/DateInvoiceCalculatorTest.php index 14074798..5e48f362 100644 --- a/tests/Invoice/Calculator/DateInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/DateInvoiceCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\DateInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -29,12 +30,12 @@ use DateTime; */ class DateInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new DateInvoiceCalculator()); + return new DateInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -113,7 +114,7 @@ class DateInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new DateInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('date', $sut->getId()); @@ -122,7 +123,6 @@ class DateInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals('EUR', $model->getCurrency()); $this->assertEquals(2521.12, $sut->getSubtotal()); $this->assertEquals(6600, $sut->getTimeWorked()); - $this->assertEquals(3, \count($sut->getEntries())); $entries = $sut->getEntries(); self::assertCount(3, $entries); @@ -132,8 +132,8 @@ class DateInvoiceCalculatorTest extends AbstractCalculatorTest self::assertEquals(2521.12, $entries[0]->getRate() + $entries[1]->getRate() + $entries[2]->getRate()); } - public function testDescriptionByTimesheet() + public function testDescriptionByTimesheet(): void { - $this->assertDescription(new DateInvoiceCalculator(), false, false); + $this->assertDescription($this->getCalculator(), false, false); } } diff --git a/tests/Invoice/Calculator/DateUserInvoiceCalculatorTest.php b/tests/Invoice/Calculator/DateUserInvoiceCalculatorTest.php new file mode 100644 index 00000000..885a82d4 --- /dev/null +++ b/tests/Invoice/Calculator/DateUserInvoiceCalculatorTest.php @@ -0,0 +1,143 @@ +setVat(19); + + $user1 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user1->method('getId')->willReturn(1); + + $user2 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user2->method('getId')->willReturn(2); + + $project1 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project1->method('getId')->willReturn(1); + + $project2 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project2->method('getId')->willReturn(2); + + $project3 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project3->method('getId')->willReturn(3); + + $timesheet = new Timesheet(); + $timesheet + ->setBegin(new DateTime('2018-11-29')) + ->setEnd(new DateTime()) + ->setDuration(3600) + ->setRate(293.27) + ->setUser($user1) + ->setActivity((new Activity())->setName('sdsd')) + ->setProject($project1); + + $timesheet2 = new Timesheet(); + $timesheet2 + ->setBegin(new DateTime('2018-11-29')) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(84.75) + ->setUser($user1) + ->setActivity((new Activity())->setName('bar')) + ->setProject($project2); + + $timesheet3 = new Timesheet(); + $timesheet3 + ->setBegin(new DateTime('2018-11-28')) + ->setEnd(new DateTime()) + ->setDuration(1800) + ->setRate(111.11) + ->setUser($user1) + ->setActivity((new Activity())->setName('foo')) + ->setProject($project1); + + $timesheet4 = new Timesheet(); + $timesheet4 + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(1947.99) + ->setUser($user1) + ->setActivity((new Activity())->setName('blub')) + ->setProject($project2); + + $timesheet5 = new Timesheet(); + $timesheet5 + ->setBegin(new DateTime('2018-11-28')) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(84) + ->setUser($user2) + ->setActivity(new Activity()) + ->setProject($project3); + + $entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5]; + + $query = new InvoiceQuery(); + $query->setProjects([$project1]); + + $model = (new InvoiceModelFactoryFactory($this))->create()->createModel(new DebugFormatter()); + $model->setCustomer($customer); + $model->setTemplate($template); + $model->addEntries($entries); + $model->setQuery($query); + + $sut = $this->getCalculator(); + $sut->setModel($model); + + $this->assertEquals('date_user', $sut->getId()); + $this->assertEquals(3000.13, $sut->getTotal()); + $this->assertEquals(19, $sut->getVat()); + $this->assertEquals('EUR', $model->getCurrency()); + $this->assertEquals(2521.12, $sut->getSubtotal()); + $this->assertEquals(6600, $sut->getTimeWorked()); + + $entries = $sut->getEntries(); + self::assertCount(4, $entries); + $this->assertEquals(378.02, $entries[0]->getRate()); + $this->assertEquals(111.11, $entries[1]->getRate()); + $this->assertEquals(1947.99, $entries[2]->getRate()); + $this->assertEquals(84, $entries[3]->getRate()); + self::assertEquals(2521.12, $entries[0]->getRate() + $entries[1]->getRate() + $entries[2]->getRate() + $entries[3]->getRate()); + } + + public function testDescriptionByTimesheet(): void + { + $this->assertDescription($this->getCalculator(), false, false); + } +} diff --git a/tests/Invoice/Calculator/DefaultCalculatorTest.php b/tests/Invoice/Calculator/DefaultCalculatorTest.php index 206523e8..55e51cf0 100644 --- a/tests/Invoice/Calculator/DefaultCalculatorTest.php +++ b/tests/Invoice/Calculator/DefaultCalculatorTest.php @@ -15,6 +15,7 @@ use App\Entity\InvoiceTemplate; use App\Entity\Tag; use App\Entity\Timesheet; use App\Invoice\Calculator\DefaultCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -25,12 +26,12 @@ use App\Tests\Mocks\InvoiceModelFactoryFactory; */ class DefaultCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new DefaultCalculator()); + return new DefaultCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $date = new \DateTime(); $customer = new Customer('foo'); @@ -69,7 +70,7 @@ class DefaultCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery(new InvoiceQuery()); - $sut = new DefaultCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('default', $sut->getId()); diff --git a/tests/Invoice/Calculator/PriceInvoiceCalculatorTest.php b/tests/Invoice/Calculator/PriceInvoiceCalculatorTest.php index c610c771..555ce4ac 100644 --- a/tests/Invoice/Calculator/PriceInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/PriceInvoiceCalculatorTest.php @@ -15,8 +15,8 @@ use App\Entity\InvoiceTemplate; use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; -use App\Invoice\Calculator\DateInvoiceCalculator; use App\Invoice\Calculator\PriceInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -30,12 +30,12 @@ use DateTime; */ class PriceInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new DateInvoiceCalculator()); + return new PriceInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -118,7 +118,7 @@ class PriceInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new PriceInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('price', $sut->getId()); @@ -136,8 +136,8 @@ class PriceInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals(84, $entries[3]->getRate()); } - public function testDescriptionByTimesheet() + public function testDescriptionByTimesheet(): void { - $this->assertDescription(new PriceInvoiceCalculator(), false, false); + $this->assertDescription($this->getCalculator(), false, false); } } diff --git a/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php index 1783f9bb..80c915d8 100644 --- a/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\ProjectInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -29,12 +30,12 @@ use DateTime; */ class ProjectInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new ProjectInvoiceCalculator()); + return new ProjectInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -113,7 +114,7 @@ class ProjectInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new ProjectInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('project', $sut->getId()); @@ -122,7 +123,6 @@ class ProjectInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals('EUR', $model->getCurrency()); $this->assertEquals(2521.12, $sut->getSubtotal()); $this->assertEquals(6600, $sut->getTimeWorked()); - $this->assertEquals(3, \count($sut->getEntries())); $entries = $sut->getEntries(); self::assertCount(3, $entries); @@ -132,8 +132,8 @@ class ProjectInvoiceCalculatorTest extends AbstractCalculatorTest self::assertEquals(2521.12, $entries[0]->getRate() + $entries[1]->getRate() + $entries[2]->getRate()); } - public function testDescriptionByProject() + public function testDescriptionByProject(): void { - $this->assertDescription(new ProjectInvoiceCalculator(), true, false); + $this->assertDescription($this->getCalculator(), true, false); } } diff --git a/tests/Invoice/Calculator/ProjectUserInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ProjectUserInvoiceCalculatorTest.php new file mode 100644 index 00000000..d5db51af --- /dev/null +++ b/tests/Invoice/Calculator/ProjectUserInvoiceCalculatorTest.php @@ -0,0 +1,142 @@ +setVat(19); + + $user1 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user1->method('getId')->willReturn(1); + + $user2 = $this->getMockBuilder(User::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $user2->method('getId')->willReturn(2); + + $project1 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project1->method('getId')->willReturn(1); + + $project2 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project2->method('getId')->willReturn(2); + + $project3 = $this->getMockBuilder(Project::class)->onlyMethods(['getId'])->disableOriginalConstructor()->getMock(); + $project3->method('getId')->willReturn(3); + + $timesheet = new Timesheet(); + $timesheet + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(3600) + ->setRate(293.27) + ->setUser($user1) + ->setActivity((new Activity())->setName('sdsd')) + ->setProject($project1); + + $timesheet2 = new Timesheet(); + $timesheet2 + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(84.75) + ->setUser($user1) + ->setActivity((new Activity())->setName('bar')) + ->setProject($project2); + + $timesheet3 = new Timesheet(); + $timesheet3 + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(1800) + ->setRate(111.11) + ->setUser($user1) + ->setActivity((new Activity())->setName('foo')) + ->setProject($project1); + + $timesheet4 = new Timesheet(); + $timesheet4 + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(1947.99) + ->setUser($user1) + ->setActivity((new Activity())->setName('blub')) + ->setProject($project2); + + $timesheet5 = new Timesheet(); + $timesheet5 + ->setBegin(new DateTime()) + ->setEnd(new DateTime()) + ->setDuration(400) + ->setRate(84) + ->setUser($user2) + ->setActivity(new Activity()) + ->setProject($project3); + + $entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5]; + + $query = new InvoiceQuery(); + $query->setProjects([$project1]); + + $model = (new InvoiceModelFactoryFactory($this))->create()->createModel(new DebugFormatter()); + $model->setCustomer($customer); + $model->setTemplate($template); + $model->addEntries($entries); + $model->setQuery($query); + + $sut = $this->getCalculator(); + $sut->setModel($model); + + $this->assertEquals('project_user', $sut->getId()); + $this->assertEquals(3000.13, $sut->getTotal()); + $this->assertEquals(19, $sut->getVat()); + $this->assertEquals('EUR', $model->getCurrency()); + $this->assertEquals(2521.12, $sut->getSubtotal()); + $this->assertEquals(6600, $sut->getTimeWorked()); + + $entries = $sut->getEntries(); + self::assertCount(3, $entries); + $this->assertEquals(404.38, $entries[0]->getRate()); + $this->assertEquals(2032.74, $entries[1]->getRate()); + $this->assertEquals(84, $entries[2]->getRate()); + self::assertEquals(2521.12, $entries[0]->getRate() + $entries[1]->getRate() + $entries[2]->getRate()); + } + + public function testDescriptionByProject(): void + { + $this->assertDescription($this->getCalculator(), true, false); + } +} diff --git a/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php index c50fa42a..506933c4 100644 --- a/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php @@ -17,6 +17,7 @@ use App\Entity\Tag; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\ShortInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Invoice\InvoiceItem; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; @@ -29,12 +30,12 @@ use App\Tests\Mocks\InvoiceModelFactoryFactory; */ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new ShortInvoiceCalculator()); + return new ShortInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -97,7 +98,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new ShortInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('short', $sut->getId()); @@ -119,7 +120,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals(['foo', 'bar', 'bar1'], $result->getTags()); } - public function testWithMultipleEntriesDifferentRates() + public function testWithMultipleEntriesDifferentRates(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -179,7 +180,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new ShortInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('short', $sut->getId()); @@ -200,7 +201,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals(1, $result->getAmount()); } - public function testWithMixedRateTypes() + public function testWithMixedRateTypes(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -258,7 +259,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new ShortInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('short', $sut->getId()); @@ -279,8 +280,8 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals(1, $result->getAmount()); } - public function testDescriptionByTimesheet() + public function testDescriptionByTimesheet(): void { - $this->assertDescription(new ShortInvoiceCalculator(), false, false); + $this->assertDescription($this->getCalculator(), false, false); } } diff --git a/tests/Invoice/Calculator/UserInvoiceCalculatorTest.php b/tests/Invoice/Calculator/UserInvoiceCalculatorTest.php index 66f13831..699eb460 100644 --- a/tests/Invoice/Calculator/UserInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/UserInvoiceCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\UserInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -27,12 +28,12 @@ use App\Tests\Mocks\InvoiceModelFactoryFactory; */ class UserInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new UserInvoiceCalculator()); + return new UserInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -111,7 +112,7 @@ class UserInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new UserInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('user', $sut->getId()); @@ -120,16 +121,16 @@ class UserInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals('EUR', $model->getCurrency()); $this->assertEquals(2521.12, $sut->getSubtotal()); $this->assertEquals(6600, $sut->getTimeWorked()); - $this->assertEquals(3, \count($sut->getEntries())); $entries = $sut->getEntries(); + self::assertCount(3, $entries); $this->assertEquals(404.38, $entries[0]->getRate()); $this->assertEquals(2032.74, $entries[1]->getRate()); $this->assertEquals(84, $entries[2]->getRate()); } - public function testDescriptionByTimesheet() + public function testDescriptionByTimesheet(): void { - $this->assertDescription(new UserInvoiceCalculator(), false, false); + $this->assertDescription($this->getCalculator(), false, false); } } diff --git a/tests/Invoice/Calculator/WeeklyInvoiceCalculatorTest.php b/tests/Invoice/Calculator/WeeklyInvoiceCalculatorTest.php index ccc764dc..ac34b914 100644 --- a/tests/Invoice/Calculator/WeeklyInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/WeeklyInvoiceCalculatorTest.php @@ -16,6 +16,7 @@ use App\Entity\Project; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\WeeklyInvoiceCalculator; +use App\Invoice\CalculatorInterface; use App\Repository\Query\InvoiceQuery; use App\Tests\Invoice\DebugFormatter; use App\Tests\Mocks\InvoiceModelFactoryFactory; @@ -29,12 +30,12 @@ use DateTime; */ class WeeklyInvoiceCalculatorTest extends AbstractCalculatorTest { - public function testEmptyModel() + protected function getCalculator(): CalculatorInterface { - $this->assertEmptyModel(new WeeklyInvoiceCalculator()); + return new WeeklyInvoiceCalculator(); } - public function testWithMultipleEntries() + public function testWithMultipleEntries(): void { $customer = new Customer('foo'); $template = new InvoiceTemplate(); @@ -116,7 +117,7 @@ class WeeklyInvoiceCalculatorTest extends AbstractCalculatorTest $model->addEntries($entries); $model->setQuery($query); - $sut = new WeeklyInvoiceCalculator(); + $sut = $this->getCalculator(); $sut->setModel($model); $this->assertEquals('weekly', $sut->getId()); @@ -133,8 +134,8 @@ class WeeklyInvoiceCalculatorTest extends AbstractCalculatorTest self::assertEquals(2521.12, $entries[0]->getRate() + $entries[1]->getRate()); } - public function testDescriptionByTimesheet() + public function testDescriptionByTimesheet(): void { - $this->assertDescription(new WeeklyInvoiceCalculator(), false, false); + $this->assertDescription($this->getCalculator(), false, false); } } diff --git a/tests/phpstan.neon b/tests/phpstan.neon index dc6bd7ec..33ce7a4d 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -6547,11 +6547,6 @@ parameters: count: 1 path: Form/Type/TypeTestModel.php - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\AbstractCalculatorTest\\:\\:assertDescription\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/AbstractCalculatorTest.php - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\AbstractCalculatorTest\\:\\:assertDescription\\(\\) has parameter \\$addActivity with no type specified\\.$#" count: 1 @@ -6562,141 +6557,6 @@ parameters: count: 1 path: Invoice/Calculator/AbstractCalculatorTest.php - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\AbstractCalculatorTest\\:\\:assertEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/AbstractCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\AbstractCalculatorTest\\:\\:getEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/AbstractCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ActivityInvoiceCalculatorTest\\:\\:testDescriptionByActivity\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ActivityInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ActivityInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ActivityInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ActivityInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ActivityInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\DateInvoiceCalculatorTest\\:\\:testDescriptionByTimesheet\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/DateInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\DateInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/DateInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\DateInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/DateInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\DefaultCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/DefaultCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\DefaultCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/DefaultCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\PriceInvoiceCalculatorTest\\:\\:testDescriptionByTimesheet\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/PriceInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\PriceInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/PriceInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\PriceInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/PriceInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ProjectInvoiceCalculatorTest\\:\\:testDescriptionByProject\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ProjectInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ProjectInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ProjectInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ProjectInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ProjectInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ShortInvoiceCalculatorTest\\:\\:testDescriptionByTimesheet\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ShortInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ShortInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ShortInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ShortInvoiceCalculatorTest\\:\\:testWithMixedRateTypes\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ShortInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ShortInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ShortInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\ShortInvoiceCalculatorTest\\:\\:testWithMultipleEntriesDifferentRates\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/ShortInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\UserInvoiceCalculatorTest\\:\\:testDescriptionByTimesheet\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/UserInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\UserInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/UserInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\UserInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/UserInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\WeeklyInvoiceCalculatorTest\\:\\:testDescriptionByTimesheet\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/WeeklyInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\WeeklyInvoiceCalculatorTest\\:\\:testEmptyModel\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/WeeklyInvoiceCalculatorTest.php - - - - message: "#^Method App\\\\Tests\\\\Invoice\\\\Calculator\\\\WeeklyInvoiceCalculatorTest\\:\\:testWithMultipleEntries\\(\\) has no return type specified\\.$#" - count: 1 - path: Invoice/Calculator/WeeklyInvoiceCalculatorTest.php - - message: "#^Cannot call method getEntries\\(\\) on App\\\\Invoice\\\\CalculatorInterface\\|null\\.$#" count: 2 diff --git a/translations/invoice-calculator.de.xlf b/translations/invoice-calculator.de.xlf index cad1d99a..a4453f8f 100644 --- a/translations/invoice-calculator.de.xlf +++ b/translations/invoice-calculator.de.xlf @@ -4,39 +4,55 @@ invoice_calculator - Summen Berechnung + Gruppierung der Rechnungszeilen + + + invoice_calculator.help + Anhand welcher Felder sollen Rechnungsposten gruppiert werden? default - Standard: ein Eintrag pro Zeitmessung + Standard (eine Zeile pro Eintrag) short - Stunden: Einträge aufsummiert, nur ein Eintrag + Alle (ein Eintrag insgesamt) user - Benutzer: ein Eintrag pro Benutzer + Benutzer activity - Aktivität: ein Eintrag pro Aktivität + Aktivität + + + activity_user + Aktivität und Benutzer project - Projekt: ein Eintrag pro Projekt + Projekt + + + project_user + Projekt und Benutzer date - Datum: ein Eintrag pro Tag (verwendet Startdatum) + Datum + + + date_user + Datum und Benutzer weekly - Wöchentlich: ein Eintrag pro Woche (verwendet Startdatum) + Kalenderwoche price - Preis: ein Eintrag je Preis + Preis diff --git a/translations/invoice-calculator.de_CH.xlf b/translations/invoice-calculator.de_CH.xlf index 999afa62..968f556f 100644 --- a/translations/invoice-calculator.de_CH.xlf +++ b/translations/invoice-calculator.de_CH.xlf @@ -1,42 +1,58 @@ - + invoice_calculator - Summenberechnung + Gruppierung der Rechnungszeilen - - price - Preis: ein Eintrag pro Preis + + invoice_calculator.help + Anhand welcher Felder sollen Rechnungsposten gruppiert werden? default - Standard: ein Eintrag pro Zeiterfassung + Standard (eine Zeile pro Eintrag) short - Stunden: Aufzeichnungen summiert, ein Eintrag insgesamt + Alle (ein Eintrag insgesamt) user - Benutzer: ein Eintrag pro Benutzer + Benutzer activity - Aktivität: ein Eintrag pro Aktivität + Aktivität + + + activity_user + Aktivität und Benutzer project - Projekt: ein Eintrag pro Projekt + Projekt + + + project_user + Projekt und Benutzer date - Datum: ein Eintrag pro Tag (verwendet Startdatum) + Datum + + + date_user + Datum und Benutzer weekly - Wöchentlich: ein Eintrag pro Woche (verwendet Startdatum) + Kalenderwoche + + + price + Preis diff --git a/translations/invoice-calculator.en.xlf b/translations/invoice-calculator.en.xlf index 93e608c1..d96bf694 100644 --- a/translations/invoice-calculator.en.xlf +++ b/translations/invoice-calculator.en.xlf @@ -4,39 +4,55 @@ invoice_calculator - Sum calculation + Grouping of invoice lines + + + invoice_calculator.help + Select by which fields the invoice items should be grouped default - Default: one entry per time record + Default (one row per entry) short - Hours: records summed up, one entry total + All (one entry total) user - User: one entry per user + User activity - Activity: one entry per activity + Activity + + + activity_user + Activity and user project - Project: one entry per project + Project + + + project_user + Project and user date - Date: one entry per day (uses start date) + Date + + + date_user + Date and user weekly - Weekly: one entry per week (uses start date) + Calendar week price - Price: one entry per price + Price diff --git a/translations/invoice-renderer.de.xlf b/translations/invoice-renderer.de.xlf index cc9f0871..b2ba2415 100644 --- a/translations/invoice-renderer.de.xlf +++ b/translations/invoice-renderer.de.xlf @@ -4,7 +4,7 @@ invoice_renderer - Rechnungsdokument + Rechnungsvorlage help.upload @@ -14,12 +14,10 @@ download_invoice_renderer Sie können weitere Vorlagen hier herunterladen - programmatic Zur Weiterverarbeitung - invoice Rechnung diff --git a/translations/invoice-renderer.de_CH.xlf b/translations/invoice-renderer.de_CH.xlf index 52924b57..d9909b17 100644 --- a/translations/invoice-renderer.de_CH.xlf +++ b/translations/invoice-renderer.de_CH.xlf @@ -4,7 +4,7 @@ invoice_renderer - Rechnungsdokument + Rechnungsvorlage invoice diff --git a/translations/invoice-renderer.en.xlf b/translations/invoice-renderer.en.xlf index 255c4dba..03c1e407 100644 --- a/translations/invoice-renderer.en.xlf +++ b/translations/invoice-renderer.en.xlf @@ -4,7 +4,7 @@ invoice_renderer - Invoice document + Invoice template help.upload @@ -14,12 +14,10 @@ download_invoice_renderer You can download further templates here - programmatic For further processing - invoice Invoice