project grouped invoice calculator (#892)

This commit is contained in:
Kevin Papst
2019-07-24 17:45:51 +02:00
committed by GitHub
parent 56e276939b
commit 53eda31179
12 changed files with 346 additions and 97 deletions

View File

@@ -76,6 +76,9 @@
"symfony/thanks": "^1.0"
},
"config": {
"platform": {
"php": "7.2"
},
"preferred-install": {
"*": "dist"
},

120
composer.lock generated
View File

@@ -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"
}
}

View File

@@ -69,7 +69,7 @@ abstract class AbstractCalculator
public function getTax(): float
{
$vat = $this->getVat();
if (0 == $vat) {
if (0 === $vat) {
return 0;
}

View File

@@ -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()) {

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Invoice\CalculatorInterface;
/**
* A calculator that sums up the timesheet records by activity.
*/
abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
{
abstract protected function calculateSumIdentifier(Timesheet $timesheet): string;
/**
* @return Timesheet[]
*/
public function getEntries()
{
$entries = $this->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);
}
}

View File

@@ -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();
}
/**

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Invoice\CalculatorInterface;
/**
* A calculator that sums up the timesheet records by project.
*/
class ProjectInvoiceCalculator extends AbstractSumInvoiceCalculator implements CalculatorInterface
{
protected function calculateSumIdentifier(Timesheet $timesheet): string
{
if (null === $timesheet->getProject()->getId()) {
throw new \Exception('Cannot handle un-persisted projects');
}
return (string) $timesheet->getProject()->getId();
}
/**
* @return string
*/
public function getId(): string
{
return 'project';
}
}

View File

@@ -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);

View File

@@ -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];

View File

@@ -0,0 +1,145 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Invoice\Calculator;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\InvoiceTemplate;
use App\Entity\Project;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Invoice\Calculator\ProjectInvoiceCalculator;
use App\Model\InvoiceModel;
use App\Repository\Query\InvoiceQuery;
/**
* @covers \App\Invoice\Calculator\ProjectInvoiceCalculator
* @covers \App\Invoice\Calculator\AbstractSumInvoiceCalculator
* @covers \App\Invoice\Calculator\AbstractMergedCalculator
* @covers \App\Invoice\Calculator\AbstractCalculator
*/
class ProjectInvoiceCalculatorTest extends AbstractCalculatorTest
{
public function testEmptyModel()
{
$this->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);
}
}

View File

@@ -8,19 +8,23 @@
</trans-unit>
<trans-unit id="default">
<source>default</source>
<target>Eintragsbasiert - ein Eintrag pro Zeitmessung (Standard)</target>
<target>Standard: ein Eintrag pro Zeitmessung</target>
</trans-unit>
<trans-unit id="short">
<source>short</source>
<target>Stunden aufsummiert - nur ein Eintrag</target>
<target>Stunden: Einträge aufsummiert, nur ein Eintrag</target>
</trans-unit>
<trans-unit id="user">
<source>user</source>
<target>Benutzer gruppiert - ein Eintrag pro Benutzer</target>
<target>Benutzer: ein Eintrag pro Benutzer</target>
</trans-unit>
<trans-unit id="activity">
<source>activity</source>
<target>Aktivitäten gruppiert - ein Eintrag pro Aktivität</target>
<target>Aktivität: ein Eintrag pro Aktivität</target>
</trans-unit>
<trans-unit id="project">
<source>project</source>
<target>Projekt: ein Eintrag pro Projekt</target>
</trans-unit>
</body>
</file>

View File

@@ -8,19 +8,23 @@
</trans-unit>
<trans-unit id="default">
<source>default</source>
<target>Entry-based - one entry per time measurement (default)</target>
<target>Default: one entry per time record</target>
</trans-unit>
<trans-unit id="short">
<source>short</source>
<target>Hours added up - only one entry</target>
<target>Hours: records summed up, one entry total</target>
</trans-unit>
<trans-unit id="user">
<source>user</source>
<target>User grouped - one entry per user</target>
<target>User: one entry per user</target>
</trans-unit>
<trans-unit id="activity">
<source>activity</source>
<target>Activity grouped - one entry per activity</target>
<target>Activity: one entry per activity</target>
</trans-unit>
<trans-unit id="project">
<source>project</source>
<target>Project: one entry per project</target>
</trans-unit>
</body>
</file>