diff --git a/src/Invoice/Calculator/AbstractMergedCalculator.php b/src/Invoice/Calculator/AbstractMergedCalculator.php index 4a6cfef9..d4e2f238 100644 --- a/src/Invoice/Calculator/AbstractMergedCalculator.php +++ b/src/Invoice/Calculator/AbstractMergedCalculator.php @@ -114,5 +114,11 @@ abstract class AbstractMergedCalculator extends AbstractCalculator if (empty($invoiceItem->getDescription()) && null !== $entry->getActivity()) { $invoiceItem->setDescription($entry->getActivity()->getName()); } + + if ($entry instanceof Timesheet) { + foreach ($entry->getTagsAsArray() as $tag) { + $invoiceItem->addTag($tag); + } + } } } diff --git a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php index 56368a23..2ba3caa9 100644 --- a/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php +++ b/src/Invoice/Hydrator/InvoiceItemDefaultHydrator.php @@ -62,6 +62,7 @@ class InvoiceItemDefaultHydrator implements InvoiceItemHydrator 'entry.description' => $description, 'entry.amount' => $amount, 'entry.type' => $item->getType(), + 'entry.tags' => implode(', ', $item->getTags()), 'entry.category' => $item->getCategory(), 'entry.rate' => $formatter->getFormattedMoney($appliedRate, $currency), 'entry.rate_nc' => $formatter->getFormattedMoney($appliedRate, $currency, false), diff --git a/src/Invoice/InvoiceItem.php b/src/Invoice/InvoiceItem.php index b71efb4a..3e7c0933 100644 --- a/src/Invoice/InvoiceItem.php +++ b/src/Invoice/InvoiceItem.php @@ -75,6 +75,10 @@ final class InvoiceItem * @var string */ private $category; + /** + * @var string[] + */ + private $tags = []; public function addAdditionalField(string $name, ?string $value): InvoiceItem { @@ -269,4 +273,23 @@ final class InvoiceItem return $this; } + + public function addTag(string $tag): void + { + foreach ($this->tags as $t) { + if (strcasecmp($tag, $t) === 0) { + return; + } + } + + $this->tags[] = $tag; + } + + /** + * @return string[] + */ + public function getTags(): array + { + return $this->tags; + } } diff --git a/templates/timesheet/layout-edit.html.twig b/templates/timesheet/layout-edit.html.twig index 90f7ca43..98743770 100644 --- a/templates/timesheet/layout-edit.html.twig +++ b/templates/timesheet/layout-edit.html.twig @@ -288,6 +288,8 @@ if (jQuery(field).data('daterangepicker') !== undefined) { jQuery(field).data('daterangepicker').setStartDate(momentObj); jQuery(field).data('daterangepicker').setEndDate(momentObj); + {# make sure that the project list is reloaded and the dates can be compared against the project end date #} + jQuery('#{{ blockPrefix }}_customer').trigger('change'); } } diff --git a/tests/Invoice/Calculator/DefaultCalculatorTest.php b/tests/Invoice/Calculator/DefaultCalculatorTest.php index 368f1268..3e5103ee 100644 --- a/tests/Invoice/Calculator/DefaultCalculatorTest.php +++ b/tests/Invoice/Calculator/DefaultCalculatorTest.php @@ -12,6 +12,7 @@ namespace App\Tests\Invoice\Calculator; use App\Entity\Activity; use App\Entity\Customer; use App\Entity\InvoiceTemplate; +use App\Entity\Tag; use App\Entity\Timesheet; use App\Invoice\Calculator\DefaultCalculator; use App\Invoice\InvoiceModel; @@ -31,27 +32,31 @@ class DefaultCalculatorTest extends AbstractCalculatorTest public function testWithMultipleEntries() { + $date = new \DateTime(); $customer = new Customer(); $template = new InvoiceTemplate(); $template->setVat(19); $timesheet = new Timesheet(); $timesheet->setDescription('foo 1'); - $timesheet->setBegin(new \DateTime()); + $timesheet->setBegin(clone $date); $timesheet->setDuration(3600); $timesheet->setRate(293.27); $timesheet->setActivity(new Activity()); + $timesheet->addTag((new Tag())->setName('foo')); + $timesheet->addTag((new Tag())->setName('bar')); $timesheet2 = new Timesheet(); $timesheet2->setDescription('foo 2'); - $timesheet2->setBegin(new \DateTime()); + $timesheet2->setBegin(clone $date); $timesheet2->setDuration(400); $timesheet2->setRate(84); $timesheet2->setActivity(new Activity()); + $timesheet2->addTag((new Tag())->setName('bar1')); $timesheet3 = new Timesheet(); $timesheet3->setDescription('foo 3'); - $timesheet3->setBegin(new \DateTime()); + $timesheet3->setBegin(clone $date); $timesheet3->setDuration(1800); $timesheet3->setRate(111.11); $timesheet3->setActivity(new Activity()); diff --git a/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php index 8f77e52a..31a690ac 100644 --- a/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php +++ b/tests/Invoice/Calculator/ShortInvoiceCalculatorTest.php @@ -13,6 +13,7 @@ use App\Entity\Activity; use App\Entity\Customer; use App\Entity\InvoiceTemplate; use App\Entity\Project; +use App\Entity\Tag; use App\Entity\Timesheet; use App\Entity\User; use App\Invoice\Calculator\ShortInvoiceCalculator; @@ -56,6 +57,8 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest ->setProject($project) ->setBegin(new \DateTime()) ->setEnd(new \DateTime()) + ->addTag((new Tag())->setName('foo')) + ->addTag((new Tag())->setName('bar')) ; $timesheet2 = new Timesheet(); @@ -68,6 +71,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest ->setProject($project) ->setBegin(new \DateTime()) ->setEnd(new \DateTime()) + ->addTag((new Tag())->setName('bar1')) ; $timesheet3 = new Timesheet(); @@ -112,6 +116,7 @@ class ShortInvoiceCalculatorTest extends AbstractCalculatorTest $this->assertEquals(472.5, $result->getRate()); $this->assertEquals(5800, $result->getDuration()); $this->assertEquals(3, $result->getAmount()); + $this->assertEquals(['foo', 'bar', 'bar1'], $result->getTags()); } public function testWithMultipleEntriesDifferentRates() diff --git a/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php b/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php index 396a51e6..ba632fc6 100644 --- a/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php +++ b/tests/Invoice/Hydrator/InvoiceItemDefaultHydratorTest.php @@ -79,6 +79,7 @@ class InvoiceItemDefaultHydratorTest extends TestCase 'entry.customer.meta.foo-customer', 'entry.category', 'entry.type', + 'entry.tags', ]; $keys = array_merge($keys, $metaFields); diff --git a/tests/Invoice/InvoiceItemTest.php b/tests/Invoice/InvoiceItemTest.php index 59851baa..09b1c62a 100644 --- a/tests/Invoice/InvoiceItemTest.php +++ b/tests/Invoice/InvoiceItemTest.php @@ -42,5 +42,15 @@ class InvoiceItemTest extends TestCase self::assertEquals(0, $sut->getDuration()); self::assertNull($sut->getCategory()); self::assertNull($sut->getType()); + self::assertEquals([], $sut->getTags()); + $sut->addTag('foo'); + $sut->addTag('foo'); + $sut->addTag('foo1'); + $sut->addTag('BaR'); + $sut->addTag('bar'); + $sut->addTag('FOO'); + $sut->addTag('bar'); + $sut->addTag('foo1'); + self::assertEquals(['foo', 'foo1', 'BaR'], $sut->getTags()); } } diff --git a/tests/Invoice/Renderer/DebugRendererTest.php b/tests/Invoice/Renderer/DebugRendererTest.php index f2934532..0e56869b 100644 --- a/tests/Invoice/Renderer/DebugRendererTest.php +++ b/tests/Invoice/Renderer/DebugRendererTest.php @@ -143,7 +143,7 @@ class DebugRendererTest extends TestCase 'user.title', 'user.meta.hello', 'user.meta.kitty', - 'testFromModelHydrator' + 'testFromModelHydrator', ]; if ($activityCounter > 1) { @@ -242,7 +242,8 @@ class DebugRendererTest extends TestCase 'entry.customer.meta.foo-customer', 'entry.category', 'entry.type', - 'testFromItemHydrator' + 'testFromItemHydrator', + 'entry.tags', ]; $keys = array_merge($keys, $metaFields);