diff --git a/composer.json b/composer.json
index 827e8691..e72a250c 100644
--- a/composer.json
+++ b/composer.json
@@ -76,6 +76,9 @@
"symfony/thanks": "^1.0"
},
"config": {
+ "platform": {
+ "php": "7.2"
+ },
"preferred-install": {
"*": "dist"
},
diff --git a/composer.lock b/composer.lock
index 2be03bbe..7db77e9f 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "67746acda4894c88c47e3afffab130e4",
+ "content-hash": "689eb3aa452e83c4ad8a67a1bae462d0",
"packages": [
{
"name": "beberlei/doctrineextensions",
@@ -400,16 +400,16 @@
},
{
"name": "doctrine/data-fixtures",
- "version": "v1.3.1",
+ "version": "v1.3.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/data-fixtures.git",
- "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a"
+ "reference": "09b16943b27f3d80d63988d100ff256148c2f78b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a",
- "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a",
+ "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/09b16943b27f3d80d63988d100ff256148c2f78b",
+ "reference": "09b16943b27f3d80d63988d100ff256148c2f78b",
"shasum": ""
},
"require": {
@@ -456,7 +456,7 @@
"keywords": [
"database"
],
- "time": "2018-03-20T09:06:36+00:00"
+ "time": "2019-07-10T18:30:35+00:00"
},
{
"name": "doctrine/dbal",
@@ -1479,16 +1479,16 @@
},
{
"name": "egulias/email-validator",
- "version": "2.1.9",
+ "version": "2.1.10",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
- "reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25"
+ "reference": "a6c8d7101b19a451c1707b1b79bbbc56e4bdb7ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/128cc721d771ec2c46ce59698f4ca42b73f71b25",
- "reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/a6c8d7101b19a451c1707b1b79bbbc56e4bdb7ec",
+ "reference": "a6c8d7101b19a451c1707b1b79bbbc56e4bdb7ec",
"shasum": ""
},
"require": {
@@ -1498,7 +1498,8 @@
"require-dev": {
"dominicsayers/isemail": "dev-master",
"phpunit/phpunit": "^4.8.35||^5.7||^6.0",
- "satooshi/php-coveralls": "^1.0.1"
+ "satooshi/php-coveralls": "^1.0.1",
+ "symfony/phpunit-bridge": "^4.4@dev"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
@@ -1532,7 +1533,7 @@
"validation",
"validator"
],
- "time": "2019-06-23T10:14:27+00:00"
+ "time": "2019-07-19T20:52:08+00:00"
},
{
"name": "erusev/parsedown",
@@ -3765,16 +3766,16 @@
},
{
"name": "pagerfanta/pagerfanta",
- "version": "v2.1.2",
+ "version": "v2.1.3",
"source": {
"type": "git",
"url": "https://github.com/whiteoctober/Pagerfanta.git",
- "reference": "45a85ad426316ae37f2d007022e5b4c95bc3aef4"
+ "reference": "a53ff01d521648d9dbca19b93ac6bc75a59b0972"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/45a85ad426316ae37f2d007022e5b4c95bc3aef4",
- "reference": "45a85ad426316ae37f2d007022e5b4c95bc3aef4",
+ "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/a53ff01d521648d9dbca19b93ac6bc75a59b0972",
+ "reference": "a53ff01d521648d9dbca19b93ac6bc75a59b0972",
"shasum": ""
},
"require": {
@@ -3830,7 +3831,7 @@
"paginator",
"paging"
],
- "time": "2019-04-02T08:50:39+00:00"
+ "time": "2019-07-17T20:56:16+00:00"
},
{
"name": "paragonie/random_compat",
@@ -4127,16 +4128,16 @@
},
{
"name": "phpoffice/phpspreadsheet",
- "version": "1.8.1",
+ "version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
- "reference": "352c7002fefe4e4037d02654d853a1b09520946f"
+ "reference": "0c1346a1956347590b7db09533966307d20cb7cc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/352c7002fefe4e4037d02654d853a1b09520946f",
- "reference": "352c7002fefe4e4037d02654d853a1b09520946f",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/0c1346a1956347590b7db09533966307d20cb7cc",
+ "reference": "0c1346a1956347590b7db09533966307d20cb7cc",
"shasum": ""
},
"require": {
@@ -4217,7 +4218,7 @@
"xls",
"xlsx"
],
- "time": "2019-07-01T20:46:51+00:00"
+ "time": "2019-07-08T21:21:25+00:00"
},
{
"name": "phpoffice/phpword",
@@ -5634,16 +5635,16 @@
},
{
"name": "symfony/flex",
- "version": "v1.4.1",
+ "version": "v1.4.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/flex.git",
- "reference": "a388cacccf6e3c5e5a395e020c5aa05849ea4ccc"
+ "reference": "4467ab35c82edebac58fe58c22cea166a805eb1f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/flex/zipball/a388cacccf6e3c5e5a395e020c5aa05849ea4ccc",
- "reference": "a388cacccf6e3c5e5a395e020c5aa05849ea4ccc",
+ "url": "https://api.github.com/repos/symfony/flex/zipball/4467ab35c82edebac58fe58c22cea166a805eb1f",
+ "reference": "4467ab35c82edebac58fe58c22cea166a805eb1f",
"shasum": ""
},
"require": {
@@ -5679,7 +5680,7 @@
}
],
"description": "Composer plugin for Symfony",
- "time": "2019-06-28T18:01:09+00:00"
+ "time": "2019-07-19T08:59:18+00:00"
},
{
"name": "symfony/form",
@@ -6160,16 +6161,16 @@
},
{
"name": "symfony/maker-bundle",
- "version": "v1.11.6",
+ "version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/maker-bundle.git",
- "reference": "d262c2cace4d9bca99137a84f6fc6ba909a17e02"
+ "reference": "201d0e050dca336c44f93657ce1f34f3b3868432"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/d262c2cace4d9bca99137a84f6fc6ba909a17e02",
- "reference": "d262c2cace4d9bca99137a84f6fc6ba909a17e02",
+ "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/201d0e050dca336c44f93657ce1f34f3b3868432",
+ "reference": "201d0e050dca336c44f93657ce1f34f3b3868432",
"shasum": ""
},
"require": {
@@ -6185,12 +6186,14 @@
"symfony/http-kernel": "^3.4|^4.0"
},
"require-dev": {
- "allocine/twigcs": "^3.0",
"doctrine/doctrine-bundle": "^1.8",
"doctrine/orm": "^2.3",
"friendsofphp/php-cs-fixer": "^2.8",
- "symfony/phpunit-bridge": "^3.4|^4.0",
+ "friendsoftwig/twigcs": "^3.1.2",
+ "symfony/http-client": "^4.3",
+ "symfony/phpunit-bridge": "^3.4.19|^4.0",
"symfony/process": "^3.4|^4.0",
+ "symfony/security-core": "^3.4|^4.0",
"symfony/yaml": "^3.4|^4.0"
},
"type": "symfony-bundle",
@@ -6222,7 +6225,7 @@
"scaffold",
"scaffolding"
],
- "time": "2019-04-19T17:26:45+00:00"
+ "time": "2019-07-09T22:19:18+00:00"
},
{
"name": "symfony/monolog-bridge",
@@ -9266,16 +9269,16 @@
},
{
"name": "nette/php-generator",
- "version": "v3.2.2",
+ "version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/nette/php-generator.git",
- "reference": "acff8b136fad84b860a626d133e791f95781f9f5"
+ "reference": "aea6e81437bb238e5f0e5b5ce06337433908e63b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nette/php-generator/zipball/acff8b136fad84b860a626d133e791f95781f9f5",
- "reference": "acff8b136fad84b860a626d133e791f95781f9f5",
+ "url": "https://api.github.com/repos/nette/php-generator/zipball/aea6e81437bb238e5f0e5b5ce06337433908e63b",
+ "reference": "aea6e81437bb238e5f0e5b5ce06337433908e63b",
"shasum": ""
},
"require": {
@@ -9321,7 +9324,7 @@
"php",
"scaffolding"
],
- "time": "2019-03-15T03:41:13+00:00"
+ "time": "2019-07-05T13:01:56+00:00"
},
{
"name": "nette/robot-loader",
@@ -9783,16 +9786,16 @@
},
{
"name": "phpstan/phpstan",
- "version": "0.11.9",
+ "version": "0.11.12",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
- "reference": "6771e622ad93f0aff16a100547811e1e50781acb"
+ "reference": "56b3eb2a371b60537fd20794e24af9e7e8ed4e30"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6771e622ad93f0aff16a100547811e1e50781acb",
- "reference": "6771e622ad93f0aff16a100547811e1e50781acb",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/56b3eb2a371b60537fd20794e24af9e7e8ed4e30",
+ "reference": "56b3eb2a371b60537fd20794e24af9e7e8ed4e30",
"shasum": ""
},
"require": {
@@ -9854,7 +9857,7 @@
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
- "time": "2019-07-03T21:25:16+00:00"
+ "time": "2019-07-08T06:55:18+00:00"
},
{
"name": "phpstan/phpstan-doctrine",
@@ -10251,16 +10254,16 @@
},
{
"name": "phpunit/php-token-stream",
- "version": "3.0.1",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18"
+ "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18",
- "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c",
+ "reference": "c4a66b97f040e3e20b3aa2a243230a1c3a9f7c8c",
"shasum": ""
},
"require": {
@@ -10296,20 +10299,20 @@
"keywords": [
"tokenizer"
],
- "time": "2018-10-30T05:52:18+00:00"
+ "time": "2019-07-08T05:24:54+00:00"
},
{
"name": "phpunit/phpunit",
- "version": "7.5.13",
+ "version": "7.5.14",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "b9278591caa8630127f96c63b598712b699e671c"
+ "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b9278591caa8630127f96c63b598712b699e671c",
- "reference": "b9278591caa8630127f96c63b598712b699e671c",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2834789aeb9ac182ad69bfdf9ae91856a59945ff",
+ "reference": "2834789aeb9ac182ad69bfdf9ae91856a59945ff",
"shasum": ""
},
"require": {
@@ -10369,8 +10372,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "role": "lead",
+ "email": "sebastian@phpunit.de"
}
],
"description": "The PHP Unit Testing framework.",
@@ -10380,7 +10383,7 @@
"testing",
"xunit"
],
- "time": "2019-06-19T12:01:51+00:00"
+ "time": "2019-07-15T06:24:08+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@@ -11278,5 +11281,8 @@
"ext-pdo": "*",
"ext-zip": "*"
},
- "platform-dev": []
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "7.2"
+ }
}
diff --git a/src/Invoice/Calculator/AbstractCalculator.php b/src/Invoice/Calculator/AbstractCalculator.php
index e2c2c64a..30705912 100644
--- a/src/Invoice/Calculator/AbstractCalculator.php
+++ b/src/Invoice/Calculator/AbstractCalculator.php
@@ -69,7 +69,7 @@ abstract class AbstractCalculator
public function getTax(): float
{
$vat = $this->getVat();
- if (0 == $vat) {
+ if (0 === $vat) {
return 0;
}
diff --git a/src/Invoice/Calculator/AbstractMergedCalculator.php b/src/Invoice/Calculator/AbstractMergedCalculator.php
index 5dc86932..0418b5a2 100644
--- a/src/Invoice/Calculator/AbstractMergedCalculator.php
+++ b/src/Invoice/Calculator/AbstractMergedCalculator.php
@@ -20,9 +20,15 @@ abstract class AbstractMergedCalculator extends AbstractCalculator
protected function mergeTimesheets(Timesheet $timesheet, Timesheet $entry)
{
$timesheet->setUser($entry->getUser());
- $timesheet->setFixedRate($entry->getFixedRate()); // FIXME invoice
- $timesheet->setHourlyRate($entry->getHourlyRate()); // FIXME invoice
$timesheet->setRate($timesheet->getRate() + $entry->getRate());
+
+ if (null !== $timesheet->getFixedRate() || null !== $entry->getFixedRate()) {
+ $timesheet->setFixedRate($timesheet->getRate());
+ }
+ if (null === $timesheet->getHourlyRate() && null !== $entry->getHourlyRate() && $timesheet->getHourlyRate() !== $entry->getHourlyRate()) {
+ $timesheet->setHourlyRate($entry->getHourlyRate());
+ }
+
$timesheet->setDuration($timesheet->getDuration() + $entry->getDuration());
if (null === $timesheet->getBegin() || $timesheet->getBegin()->getTimestamp() > $entry->getBegin()->getTimestamp()) {
diff --git a/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php
new file mode 100644
index 00000000..5dc8f74d
--- /dev/null
+++ b/src/Invoice/Calculator/AbstractSumInvoiceCalculator.php
@@ -0,0 +1,46 @@
+model->getEntries();
+ if (empty($entries)) {
+ return [];
+ }
+
+ /** @var Timesheet[] $timesheets */
+ $timesheets = [];
+
+ foreach ($entries as $entry) {
+ $id = $this->calculateSumIdentifier($entry);
+ if (!isset($timesheets[$id])) {
+ $timesheets[$id] = new Timesheet();
+ }
+ $timesheet = $timesheets[$id];
+ $this->mergeTimesheets($timesheet, $entry);
+ }
+
+ return array_values($timesheets);
+ }
+}
diff --git a/src/Invoice/Calculator/ActivityInvoiceCalculator.php b/src/Invoice/Calculator/ActivityInvoiceCalculator.php
index 22a23229..e6a9beea 100644
--- a/src/Invoice/Calculator/ActivityInvoiceCalculator.php
+++ b/src/Invoice/Calculator/ActivityInvoiceCalculator.php
@@ -15,30 +15,15 @@ use App\Invoice\CalculatorInterface;
/**
* A calculator that sums up the timesheet records by activity.
*/
-class ActivityInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
+class ActivityInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface
{
- /**
- * @return Timesheet[]
- */
- public function getEntries()
+ protected function calculateSumIdentifier(Timesheet $timesheet): string
{
- $entries = $this->model->getEntries();
- if (empty($entries)) {
- return [];
+ if (null === $timesheet->getActivity()->getId()) {
+ throw new \Exception('Cannot handle un-persisted activities');
}
- /** @var Timesheet[] $timesheets */
- $timesheets = [];
-
- foreach ($entries as $entry) {
- if (!isset($timesheets[$entry->getActivity()->getId()])) {
- $timesheets[$entry->getActivity()->getId()] = new Timesheet();
- }
- $timesheet = $timesheets[$entry->getActivity()->getId()];
- $this->mergeTimesheets($timesheet, $entry);
- }
-
- return array_values($timesheets);
+ return (string) $timesheet->getActivity()->getId();
}
/**
diff --git a/src/Invoice/Calculator/ProjectInvoiceCalculator.php b/src/Invoice/Calculator/ProjectInvoiceCalculator.php
new file mode 100644
index 00000000..87fd875a
--- /dev/null
+++ b/src/Invoice/Calculator/ProjectInvoiceCalculator.php
@@ -0,0 +1,36 @@
+getProject()->getId()) {
+ throw new \Exception('Cannot handle un-persisted projects');
+ }
+
+ return (string) $timesheet->getProject()->getId();
+ }
+
+ /**
+ * @return string
+ */
+ public function getId(): string
+ {
+ return 'project';
+ }
+}
diff --git a/tests/Invoice/Calculator/AbstractCalculatorTest.php b/tests/Invoice/Calculator/AbstractCalculatorTest.php
index 167ed7b1..523244cc 100644
--- a/tests/Invoice/Calculator/AbstractCalculatorTest.php
+++ b/tests/Invoice/Calculator/AbstractCalculatorTest.php
@@ -50,13 +50,23 @@ abstract class AbstractCalculatorTest extends TestCase
$template = new InvoiceTemplate();
$template->setVat(19);
- $project = new Project();
- $project->setName('project description');
- $project->setCustomer($customer);
+ $project = $this->getMockBuilder(Project::class)->setMethods(['getId', 'getCustomer', 'getName'])->disableOriginalConstructor()->getMock();
+ $project->method('getId')->willReturn(1);
+ $project->method('getCustomer')->willReturn($customer);
+ $project->method('getName')->willReturn('project description');
- $activity = new Activity();
- $activity->setName('activity description');
- $activity->setProject($project);
+ $project1 = $this->getMockBuilder(Project::class)->setMethods(['getId', 'getName'])->disableOriginalConstructor()->getMock();
+ $project1->method('getId')->willReturn(1);
+ $project1->method('getName')->willReturn('bar');
+
+ $activity = $this->getMockBuilder(Activity::class)->setMethods(['getId', 'getProject', 'getName'])->disableOriginalConstructor()->getMock();
+ $activity->method('getId')->willReturn(1);
+ $activity->method('getProject')->willReturn($project);
+ $activity->method('getName')->willReturn('activity description');
+
+ $activity1 = $this->getMockBuilder(Activity::class)->setMethods(['getId', 'getName'])->disableOriginalConstructor()->getMock();
+ $activity1->method('getId')->willReturn(1);
+ $activity1->method('getName')->willReturn('foo');
$query = new InvoiceQuery();
if ($addProject === true) {
@@ -72,8 +82,8 @@ abstract class AbstractCalculatorTest extends TestCase
->setDuration(3600)
->setRate(293.27)
->setUser(new User())
- ->setActivity((new Activity())->setName('foo'))
- ->setProject((new Project())->setName('bar'));
+ ->setActivity($activity1)
+ ->setProject($project1);
$model = new InvoiceModel();
$model->setCustomer($customer);
diff --git a/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php
index 466a1246..f2c65d3b 100644
--- a/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php
+++ b/tests/Invoice/Calculator/ActivityInvoiceCalculatorTest.php
@@ -21,6 +21,7 @@ use App\Repository\Query\InvoiceQuery;
/**
* @covers \App\Invoice\Calculator\ActivityInvoiceCalculator
+ * @covers \App\Invoice\Calculator\AbstractSumInvoiceCalculator
* @covers \App\Invoice\Calculator\AbstractMergedCalculator
* @covers \App\Invoice\Calculator\AbstractCalculator
*/
@@ -46,6 +47,9 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
$activity2 = $this->getMockBuilder(Activity::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
$activity2->method('getId')->willReturn(2);
+ $activity3 = $this->getMockBuilder(Activity::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
+ $activity3->method('getId')->willReturn(3);
+
$timesheet = new Timesheet();
$timesheet
->setBegin(new \DateTime())
@@ -93,7 +97,7 @@ class ActivityInvoiceCalculatorTest extends AbstractCalculatorTest
->setDuration(400)
->setRate(84)
->setUser(new User())
- ->setActivity(new Activity())
+ ->setActivity($activity3)
->setProject((new Project())->setName('bar'));
$entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5];
diff --git a/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php b/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php
new file mode 100644
index 00000000..47c1d130
--- /dev/null
+++ b/tests/Invoice/Calculator/ProjectInvoiceCalculatorTest.php
@@ -0,0 +1,145 @@
+assertEmptyModel(new ProjectInvoiceCalculator());
+ }
+
+ public function testWithMultipleEntries()
+ {
+ $customer = new Customer();
+ $template = new InvoiceTemplate();
+ $template->setVat(19);
+
+ $user = $this->getMockBuilder(User::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
+ $user->method('getId')->willReturn(1);
+
+ $project1 = $this->getMockBuilder(Project::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
+ $project1->method('getId')->willReturn(1);
+
+ $project2 = $this->getMockBuilder(Project::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
+ $project2->method('getId')->willReturn(2);
+
+ $project3 = $this->getMockBuilder(Project::class)->setMethods(['getId'])->disableOriginalConstructor()->getMock();
+ $project3->method('getId')->willReturn(3);
+
+ $timesheet = new Timesheet();
+ $timesheet
+ ->setBegin(new \DateTime())
+ ->setEnd(new \DateTime())
+ ->setDuration(3600)
+ ->setRate(293.27)
+ ->setUser($user)
+ ->setActivity((new Activity())->setName('sdsd'))
+ ->setProject($project1);
+
+ $timesheet2 = new Timesheet();
+ $timesheet2
+ ->setBegin(new \DateTime())
+ ->setEnd(new \DateTime())
+ ->setDuration(400)
+ ->setRate(84.75)
+ ->setUser($user)
+ ->setActivity((new Activity())->setName('bar'))
+ ->setProject($project2);
+
+ $timesheet3 = new Timesheet();
+ $timesheet3
+ ->setBegin(new \DateTime())
+ ->setEnd(new \DateTime())
+ ->setDuration(1800)
+ ->setRate(111.11)
+ ->setUser($user)
+ ->setActivity((new Activity())->setName('foo'))
+ ->setProject($project1);
+
+ $timesheet4 = new Timesheet();
+ $timesheet4
+ ->setBegin(new \DateTime())
+ ->setEnd(new \DateTime())
+ ->setDuration(400)
+ ->setRate(1947.99)
+ ->setUser($user)
+ ->setActivity((new Activity())->setName('blub'))
+ ->setProject($project2);
+
+ $timesheet5 = new Timesheet();
+ $timesheet5
+ ->setBegin(new \DateTime())
+ ->setEnd(new \DateTime())
+ ->setDuration(400)
+ ->setRate(84)
+ ->setUser(new User())
+ ->setActivity(new Activity())
+ ->setProject($project3);
+
+ $entries = [$timesheet, $timesheet2, $timesheet3, $timesheet4, $timesheet5];
+
+ $query = new InvoiceQuery();
+ $query->setProject($project1);
+
+ $model = new InvoiceModel();
+ $model->setCustomer($customer);
+ $model->setTemplate($template);
+ $model->setEntries($entries);
+ $model->setQuery($query);
+
+ $sut = new ProjectInvoiceCalculator();
+ $sut->setModel($model);
+
+ $this->assertEquals('project', $sut->getId());
+ $this->assertEquals(3000.13, $sut->getTotal());
+ $this->assertEquals(19, $sut->getVat());
+ $this->assertEquals('EUR', $sut->getCurrency());
+ $this->assertEquals(2521.12, $sut->getSubtotal());
+ $this->assertEquals(6600, $sut->getTimeWorked());
+ $this->assertEquals(3, count($sut->getEntries()));
+
+ $entries = $sut->getEntries();
+ $this->assertEquals(404.38, $entries[0]->getRate());
+ $this->assertEquals(2032.74, $entries[1]->getRate());
+ $this->assertEquals(84, $entries[2]->getRate());
+ }
+
+ public function testDescriptionByTimesheet()
+ {
+ $this->assertDescription(new ProjectInvoiceCalculator(), false, false);
+ }
+
+ public function testDescriptionByActivity()
+ {
+ $this->assertDescription(new ProjectInvoiceCalculator(), false, true);
+ }
+
+ public function testDescriptionByProject()
+ {
+ $this->assertDescription(new ProjectInvoiceCalculator(), true, false);
+ }
+}
diff --git a/translations/invoice-calculator.de.xliff b/translations/invoice-calculator.de.xliff
index dba779a0..ae834fc6 100644
--- a/translations/invoice-calculator.de.xliff
+++ b/translations/invoice-calculator.de.xliff
@@ -8,19 +8,23 @@
default
- Eintragsbasiert - ein Eintrag pro Zeitmessung (Standard)
+ Standard: ein Eintrag pro Zeitmessung
short
- Stunden aufsummiert - nur ein Eintrag
+ Stunden: Einträge aufsummiert, nur ein Eintrag
user
- Benutzer gruppiert - ein Eintrag pro Benutzer
+ Benutzer: ein Eintrag pro Benutzer
activity
- Aktivitäten gruppiert - ein Eintrag pro Aktivität
+ Aktivität: ein Eintrag pro Aktivität
+
+
+ project
+ Projekt: ein Eintrag pro Projekt