From d5f8655827af45750e8538f49814a0cb4499e351 Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sat, 21 May 2022 13:13:56 +0200 Subject: [PATCH] Release 1.20.1 (#3317) - improved timesheet calculator with changesets and priority - calculate and include exported stats (e.g. available in export templates) - added hourly rate column to timesheet listing --- src/Activity/ActivityStatisticService.php | 20 ++++- src/Constants.php | 4 +- src/Customer/CustomerStatisticService.php | 20 ++++- src/Doctrine/TimesheetSubscriber.php | 48 ++++++++---- src/Entity/Timesheet.php | 8 ++ src/Export/Base/RendererTrait.php | 17 +++-- src/Model/TimesheetCountedStatistic.php | 74 +++++++++++++++++++ src/Project/ProjectStatisticService.php | 20 ++++- .../Calculator/BillableCalculator.php | 5 ++ .../Calculator/DurationCalculator.php | 5 ++ src/Timesheet/Calculator/RateCalculator.php | 5 ++ src/Timesheet/CalculatorInterface.php | 14 +++- templates/timesheet/layout-listing.html.twig | 31 ++++++-- tests/Entity/TimesheetTest.php | 20 +++++ .../AbstractTimesheetCountedStatisticTest.php | 29 +++++++- 15 files changed, 277 insertions(+), 43 deletions(-) diff --git a/src/Activity/ActivityStatisticService.php b/src/Activity/ActivityStatisticService.php index 0378e451..b6bc44f6 100644 --- a/src/Activity/ActivityStatisticService.php +++ b/src/Activity/ActivityStatisticService.php @@ -149,10 +149,20 @@ class ActivityStatisticService $statistic->setInternalRate($statistic->getInternalRate() + $resultRow['internalRate']); $statistic->setCounter($statistic->getCounter() + $resultRow['counter']); if ($resultRow['billable']) { - $statistic->setDurationBillable($resultRow['duration']); - $statistic->setRateBillable($resultRow['rate']); - $statistic->setInternalRateBillable($resultRow['internalRate']); - $statistic->setCounterBillable($resultRow['counter']); + $statistic->setDurationBillable($statistic->getDurationBillable() + $resultRow['duration']); + $statistic->setRateBillable($statistic->getRateBillable() + $resultRow['rate']); + $statistic->setInternalRateBillable($statistic->getInternalRateBillable() + $resultRow['internalRate']); + $statistic->setCounterBillable($statistic->getCounterBillable() + $resultRow['counter']); + if ($resultRow['exported']) { + $statistic->setDurationBillableExported($statistic->getDurationBillableExported() + $resultRow['duration']); + $statistic->setRateBillableExported($statistic->getRateBillableExported() + $resultRow['rate']); + } + } + if ($resultRow['exported']) { + $statistic->setDurationExported($statistic->getDurationExported() + $resultRow['duration']); + $statistic->setRateExported($statistic->getRateExported() + $resultRow['rate']); + $statistic->setInternalRateExported($statistic->getInternalRateExported() + $resultRow['internalRate']); + $statistic->setCounterExported($statistic->getCounterExported() + $resultRow['counter']); } } } @@ -170,9 +180,11 @@ class ActivityStatisticService ->addSelect('COALESCE(SUM(t.internalRate), 0) as internalRate') ->addSelect('COUNT(t.id) as counter') ->addSelect('t.billable as billable') + ->addSelect('t.exported as exported') ->andWhere($qb->expr()->isNotNull('t.end')) ->groupBy('id') ->addGroupBy('billable') + ->addGroupBy('exported') ->andWhere($qb->expr()->in('t.activity', ':activity')) ->setParameter('activity', $activities) ; diff --git a/src/Constants.php b/src/Constants.php index dec137da..7854dcae 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -17,11 +17,11 @@ class Constants /** * The current release version */ - public const VERSION = '1.20'; + public const VERSION = '1.20.1'; /** * The current release: major * 10000 + minor * 100 + patch */ - public const VERSION_ID = 12000; + public const VERSION_ID = 12001; /** * The current release status, either "stable" or "dev" */ diff --git a/src/Customer/CustomerStatisticService.php b/src/Customer/CustomerStatisticService.php index 2cef85ec..d2cf5a10 100644 --- a/src/Customer/CustomerStatisticService.php +++ b/src/Customer/CustomerStatisticService.php @@ -97,10 +97,20 @@ class CustomerStatisticService $statistic->setInternalRate($statistic->getInternalRate() + $resultRow['internalRate']); $statistic->setCounter($statistic->getCounter() + $resultRow['counter']); if ($resultRow['billable']) { - $statistic->setDurationBillable($resultRow['duration']); - $statistic->setRateBillable($resultRow['rate']); - $statistic->setInternalRateBillable($resultRow['internalRate']); - $statistic->setCounterBillable($resultRow['counter']); + $statistic->setDurationBillable($statistic->getDurationBillable() + $resultRow['duration']); + $statistic->setRateBillable($statistic->getRateBillable() + $resultRow['rate']); + $statistic->setInternalRateBillable($statistic->getInternalRateBillable() + $resultRow['internalRate']); + $statistic->setCounterBillable($statistic->getCounterBillable() + $resultRow['counter']); + if ($resultRow['exported']) { + $statistic->setDurationBillableExported($statistic->getDurationBillableExported() + $resultRow['duration']); + $statistic->setRateBillableExported($statistic->getRateBillableExported() + $resultRow['rate']); + } + } + if ($resultRow['exported']) { + $statistic->setDurationExported($statistic->getDurationExported() + $resultRow['duration']); + $statistic->setRateExported($statistic->getRateExported() + $resultRow['rate']); + $statistic->setInternalRateExported($statistic->getInternalRateExported() + $resultRow['internalRate']); + $statistic->setCounterExported($statistic->getCounterExported() + $resultRow['counter']); } } } @@ -119,9 +129,11 @@ class CustomerStatisticService ->addSelect('COALESCE(SUM(t.internalRate), 0) as internalRate') ->addSelect('COUNT(t.id) as counter') ->addSelect('t.billable as billable') + ->addSelect('t.exported as exported') ->andWhere($qb->expr()->isNotNull('t.end')) ->groupBy('id') ->addGroupBy('billable') + ->addGroupBy('exported') ->andWhere($qb->expr()->in('p.customer', ':customer')) ->setParameter('customer', $customers) ; diff --git a/src/Doctrine/TimesheetSubscriber.php b/src/Doctrine/TimesheetSubscriber.php index 32646ee7..4972ee59 100644 --- a/src/Doctrine/TimesheetSubscriber.php +++ b/src/Doctrine/TimesheetSubscriber.php @@ -23,7 +23,11 @@ class TimesheetSubscriber implements EventSubscriber /** * @var CalculatorInterface[] */ - protected $calculator; + private $calculator; + /** + * @var CalculatorInterface[] + */ + private $sorted; /** * @param CalculatorInterface[] $calculators @@ -33,20 +37,14 @@ class TimesheetSubscriber implements EventSubscriber $this->calculator = $calculators; } - /** - * @return array - */ - public function getSubscribedEvents() + public function getSubscribedEvents(): array { return [ Events::onFlush, ]; } - /** - * @param OnFlushEventArgs $args - */ - public function onFlush(OnFlushEventArgs $args) + public function onFlush(OnFlushEventArgs $args): void { $em = $args->getEntityManager(); $uow = $em->getUnitOfWork(); @@ -57,7 +55,7 @@ class TimesheetSubscriber implements EventSubscriber continue; } - $this->calculateFields($entity); + $this->calculateFields($entity, $uow->getEntityChangeSet($entity)); $uow->recomputeSingleEntityChangeSet($meta, $entity); } @@ -71,13 +69,31 @@ class TimesheetSubscriber implements EventSubscriber } } - /** - * @param Timesheet $entity - */ - protected function calculateFields(Timesheet $entity) + protected function calculateFields(Timesheet $entity, array $changes = []): void { - foreach ($this->calculator as $calculator) { - $calculator->calculate($entity); + if ($this->sorted === null) { + $this->sorted = []; + + foreach ($this->calculator as $calculator) { + $i = 0; + $prio = 1000; + if (method_exists($calculator, 'getPriority')) { + $prio = $calculator->getPriority(); + } + + do { + $key = $prio + $i++; + } while (\array_key_exists($key, $this->sorted)); + + $this->sorted[$key] = $calculator; + } + + ksort($this->sorted); + } + + foreach ($this->sorted as $calculator) { + /* @phpstan-ignore-next-line */ + $calculator->calculate($entity, $changes); } } } diff --git a/src/Entity/Timesheet.php b/src/Entity/Timesheet.php index 097b2e6e..74292f50 100644 --- a/src/Entity/Timesheet.php +++ b/src/Entity/Timesheet.php @@ -654,6 +654,14 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface return $this; } + public function resetRates(): void + { + $this->rate = 0.00; + $this->internalRate = null; + $this->hourlyRate = null; + $this->fixedRate = null; + } + public function isBillable(): bool { return $this->billable; diff --git a/src/Export/Base/RendererTrait.php b/src/Export/Base/RendererTrait.php index 831cd17e..195a55e8 100644 --- a/src/Export/Base/RendererTrait.php +++ b/src/Export/Base/RendererTrait.php @@ -11,6 +11,7 @@ namespace App\Export\Base; use App\Activity\ActivityStatisticService; use App\Invoice\InvoiceItemInterface; +use App\Model\TimesheetCountedStatistic; use App\Project\ProjectStatisticService; use App\Repository\Query\TimesheetQuery; @@ -172,6 +173,7 @@ trait RendererTrait { $summary = []; $projects = []; + $empty = new TimesheetCountedStatistic(); foreach ($exportItems as $exportItem) { $customer = null; @@ -183,15 +185,14 @@ trait RendererTrait $customer = $project->getCustomer(); $customerId = $customer->getId(); $projectId = $project->getId(); - if ($project->hasBudgets()) { - $projects[] = $project; - } + $projects[] = $project; } $id = $customerId . '_' . $projectId; if (!isset($summary[$id])) { $summary[$id] = [ + 'totals' => $empty->jsonSerialize(), 'time' => $project->getTimeBudget(), 'money' => $project->getBudget(), 'time_left' => null, @@ -209,6 +210,8 @@ trait RendererTrait foreach ($allBudgets as $projectId => $statisticModel) { $project = $statisticModel->getProject(); $id = $project->getCustomer()->getId() . '_' . $projectId; + $total = $statisticModel->getStatisticTotal(); + $summary[$id]['totals'] = $total->jsonSerialize(); if ($statisticModel->hasTimeBudget()) { $summary[$id]['time_left'] = $statisticModel->getTimeBudgetOpenRelative(); $summary[$id]['time_left_total'] = $statisticModel->getTimeBudgetOpen(); @@ -255,6 +258,7 @@ trait RendererTrait { $summary = []; $activities = []; + $empty = new TimesheetCountedStatistic(); foreach ($exportItems as $exportItem) { $customerId = 'none'; @@ -270,9 +274,7 @@ trait RendererTrait continue; } - if ($activity->hasBudgets()) { - $activities[] = $activity; - } + $activities[] = $activity; if (null !== ($project = $exportItem->getProject())) { $projectId = $project->getId(); @@ -289,6 +291,7 @@ trait RendererTrait if (!isset($summary[$id][$activityId])) { $summary[$id][$activityId] = [ + 'totals' => $empty->jsonSerialize(), 'time' => $activity->getTimeBudget(), 'money' => $activity->getBudget(), 'time_left' => null, @@ -306,6 +309,8 @@ trait RendererTrait foreach ($allBudgets as $activityId => $statisticModel) { $project = $statisticModel->getActivity()->getProject(); $id = $project->getCustomer()->getId() . '_' . $project->getId(); + $total = $statisticModel->getStatisticTotal(); + $summary[$id][$activityId]['totals'] = $total->jsonSerialize(); if ($statisticModel->hasTimeBudget()) { $summary[$id][$activityId]['time_left'] = $statisticModel->getTimeBudgetOpenRelative(); $summary[$id][$activityId]['time_left_total'] = $statisticModel->getTimeBudgetOpen(); diff --git a/src/Model/TimesheetCountedStatistic.php b/src/Model/TimesheetCountedStatistic.php index 97b56498..60a24527 100644 --- a/src/Model/TimesheetCountedStatistic.php +++ b/src/Model/TimesheetCountedStatistic.php @@ -21,6 +21,14 @@ class TimesheetCountedStatistic implements \JsonSerializable private $recordRateBillable = 0.0; private $internalRateBillable = 0.0; + private $recordRateBillableExported = 0.0; + private $recordDurationBillableExported = 0; + + private $counterExported = 0; + private $recordDurationExported = 0; + private $recordRateExported = 0.0; + private $internalRateExported = 0.0; + /** * For unified access, used in frontend. * @@ -46,6 +54,16 @@ class TimesheetCountedStatistic implements \JsonSerializable $this->counterBillable = $counter; } + public function getCounterExported(): int + { + return $this->counterExported; + } + + public function setCounterExported(int $counter): void + { + $this->counterExported = $counter; + } + /** * Returns the total amount of included timesheet records. * @@ -178,6 +196,16 @@ class TimesheetCountedStatistic implements \JsonSerializable $this->internalRateBillable = $internalRateBillable; } + public function getInternalRateExported(): float + { + return $this->internalRateExported; + } + + public function setInternalRateExported(float $internalRateExported): void + { + $this->internalRateExported = $internalRateExported; + } + /** * @param float $recordInternalRate * @return $this @@ -217,6 +245,16 @@ class TimesheetCountedStatistic implements \JsonSerializable $this->recordDurationBillable = $recordDuration; } + public function getDurationBillableExported(): int + { + return $this->recordDurationBillableExported; + } + + public function setDurationBillableExported(int $recordDuration): void + { + $this->recordDurationBillableExported = $recordDuration; + } + public function getRateBillable(): float { return $this->recordRateBillable; @@ -227,16 +265,52 @@ class TimesheetCountedStatistic implements \JsonSerializable $this->recordRateBillable = $recordRate; } + public function getRateBillableExported(): float + { + return $this->recordRateBillableExported; + } + + public function setRateBillableExported(float $recordRate): void + { + $this->recordRateBillableExported = $recordRate; + } + + public function getDurationExported(): int + { + return $this->recordDurationExported; + } + + public function setDurationExported(int $recordDuration): void + { + $this->recordDurationExported = $recordDuration; + } + + public function getRateExported(): float + { + return $this->recordRateExported; + } + + public function setRateExported(float $recordRate): void + { + $this->recordRateExported = $recordRate; + } + public function jsonSerialize() { return [ 'duration' => $this->recordDuration, 'duration_billable' => $this->recordDurationBillable, + 'duration_exported' => $this->recordDurationExported, + 'duration_billable_exported' => $this->recordDurationBillableExported, 'rate' => $this->recordRate, 'rate_billable' => $this->recordRateBillable, + 'rate_exported' => $this->recordRateExported, + 'rate_billable_exported' => $this->recordRateBillableExported, 'rate_internal' => $this->recordInternalRate, + 'rate_internal_exported' => $this->internalRateExported, 'amount' => $this->counter, 'amount_billable' => $this->counterBillable, + 'amount_exported' => $this->counterExported, ]; } } diff --git a/src/Project/ProjectStatisticService.php b/src/Project/ProjectStatisticService.php index 66b66cf4..b206c6b0 100644 --- a/src/Project/ProjectStatisticService.php +++ b/src/Project/ProjectStatisticService.php @@ -328,10 +328,12 @@ class ProjectStatisticService ->addSelect('COALESCE(SUM(t.internalRate), 0) as internalRate') ->addSelect('COUNT(t.id) as counter') ->addSelect('t.billable as billable') + ->addSelect('t.exported as exported') ->andWhere($qb->expr()->in('t.project', ':project')) ->andWhere($qb->expr()->isNotNull('t.end')) ->groupBy('id') ->addGroupBy('billable') + ->addGroupBy('exported') ->setParameter('project', array_keys($statistics)) ; @@ -359,10 +361,20 @@ class ProjectStatisticService $statistic->setInternalRate($statistic->getInternalRate() + $resultRow['internalRate']); $statistic->setCounter($statistic->getCounter() + $resultRow['counter']); if ($resultRow['billable']) { - $statistic->setDurationBillable($resultRow['duration']); - $statistic->setRateBillable($resultRow['rate']); - $statistic->setInternalRateBillable($resultRow['internalRate']); - $statistic->setCounterBillable($resultRow['counter']); + $statistic->setDurationBillable($statistic->getDurationBillable() + $resultRow['duration']); + $statistic->setRateBillable($statistic->getRateBillable() + $resultRow['rate']); + $statistic->setInternalRateBillable($statistic->getInternalRateBillable() + $resultRow['internalRate']); + $statistic->setCounterBillable($statistic->getCounterBillable() + $resultRow['counter']); + if ($resultRow['exported']) { + $statistic->setDurationBillableExported($statistic->getDurationBillableExported() + $resultRow['duration']); + $statistic->setRateBillableExported($statistic->getRateBillableExported() + $resultRow['rate']); + } + } + if ($resultRow['exported']) { + $statistic->setDurationExported($statistic->getDurationExported() + $resultRow['duration']); + $statistic->setRateExported($statistic->getRateExported() + $resultRow['rate']); + $statistic->setInternalRateExported($statistic->getInternalRateExported() + $resultRow['internalRate']); + $statistic->setCounterExported($statistic->getCounterExported() + $resultRow['counter']); } } } diff --git a/src/Timesheet/Calculator/BillableCalculator.php b/src/Timesheet/Calculator/BillableCalculator.php index aae187a6..5b30f194 100644 --- a/src/Timesheet/Calculator/BillableCalculator.php +++ b/src/Timesheet/Calculator/BillableCalculator.php @@ -50,4 +50,9 @@ class BillableCalculator implements CalculatorInterface break; } } + + public function getPriority(): int + { + return 100; + } } diff --git a/src/Timesheet/Calculator/DurationCalculator.php b/src/Timesheet/Calculator/DurationCalculator.php index fea75b30..702bd6a5 100644 --- a/src/Timesheet/Calculator/DurationCalculator.php +++ b/src/Timesheet/Calculator/DurationCalculator.php @@ -42,4 +42,9 @@ final class DurationCalculator implements CalculatorInterface $this->roundings->applyRoundings($record); } + + public function getPriority(): int + { + return 200; + } } diff --git a/src/Timesheet/Calculator/RateCalculator.php b/src/Timesheet/Calculator/RateCalculator.php index d5e40ade..8b3ac9ff 100644 --- a/src/Timesheet/Calculator/RateCalculator.php +++ b/src/Timesheet/Calculator/RateCalculator.php @@ -44,4 +44,9 @@ class RateCalculator implements CalculatorInterface $record->setFixedRate($rate->getFixedRate()); } } + + public function getPriority(): int + { + return 300; + } } diff --git a/src/Timesheet/CalculatorInterface.php b/src/Timesheet/CalculatorInterface.php index 15f773bf..26f52aa7 100644 --- a/src/Timesheet/CalculatorInterface.php +++ b/src/Timesheet/CalculatorInterface.php @@ -22,6 +22,18 @@ interface CalculatorInterface * The methods return value will not be evaluated. * * @param Timesheet $record + * @ param array> $changeset + * @return void */ - public function calculate(Timesheet $record); + public function calculate(Timesheet $record/*, array $changeset*/); + + /* + * FIXME use with Kimai 2.0 + * + * Default priority is 1000 (after all system Calculator were executed). + * The higher the priority the later it will be executed. + * + * @return int + */ + //public function getPriority(): int; } diff --git a/templates/timesheet/layout-listing.html.twig b/templates/timesheet/layout-listing.html.twig index b1eceb79..ad90f1ea 100644 --- a/templates/timesheet/layout-listing.html.twig +++ b/templates/timesheet/layout-listing.html.twig @@ -23,7 +23,10 @@ {% endif %} {% set columns = columns|merge({'duration': {'class': 'text-right'}}) %} {% if canSeeRate %} - {% set columns = columns|merge({'rate': {'class': 'text-right'}}) %} + {% set columns = columns|merge({ + 'hourlyRate': {'class': 'text-right hidden'}, + 'rate': {'class': 'text-right'} + }) %} {% endif %} {% set columns = columns|merge({ 'customer': {'class': 'hidden-xs hidden-sm hidden-md'}, @@ -71,17 +74,20 @@ {% set day = null %} {% set dayDuration = 0 %} {% set dayRate = {} %} + {% set dayHourlyRate = 0 %} {% set lastEntry = null %} {% for entry in entries %} {%- set customerCurrency = entry.project.customer.currency -%} + {%- set entryHourlyRate = entry.hourlyRate|money(customerCurrency) -%} {%- if day is same as(null) -%} {% set day = entry.begin|date_short %} {% endif %} {%- if showSummary and day is not same as(entry.begin|date_short) -%} - {{ _self.summary(day, dayDuration, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }} + {{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }} {% set day = entry.begin|date_short %} {% set dayDuration = 0 %} {% set dayRate = {} %} + {% set dayHourlyRate = 0 %} {%- endif -%} {% set class = '' %} {% if checkOverlappingDesc or checkOverlappingAsc %} @@ -124,11 +130,14 @@ {% endif %} {% if canSeeRate %} + + {{ entryHourlyRate }} + {% if not entry.end or not is_granted('view_rate', entry) %} ‐ {% else %} - {{ entry.rate|money(entry.project.customer.currency) }} + {{ entry.rate|money(customerCurrency) }} {% endif %} {% endif %} @@ -179,12 +188,19 @@ {% endif %} {% set dayRate = dayRate|merge({(customerCurrency): dayRate[customerCurrency] + entry.rate}) %} {%- endif -%} + {% if dayHourlyRate is not null %} + {% if dayHourlyRate == 0 %} + {% set dayHourlyRate = entryHourlyRate %} + {% elseif dayHourlyRate != entryHourlyRate %} + {% set dayHourlyRate = null %} + {% endif %} + {% endif %} {%- set dayDuration = dayDuration + entry.duration -%} {% set lastEntry = entry %} {% endfor %} {% if showSummary %} - {{ _self.summary(day, dayDuration, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }} + {{ _self.summary(day, dayDuration, dayHourlyRate, dayRate, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) }} {% endif %} {{ tables.data_table_footer(entries, paginationRoute, multiUpdateForm) }} @@ -192,7 +208,7 @@ {% endblock %} -{% macro summary(day, duration, dayRates, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) %} +{% macro summary(day, duration, dayHourlyRate, dayRates, columns, canSeeRate, canSeeUsername, showStartEndTime, tableName, metaColumns) %} {% import "macros/datatables.html.twig" as tables %} @@ -203,6 +219,11 @@ {% endif %} {{ duration|duration }} {% if canSeeRate %} + + {% if dayHourlyRate is not null and dayHourlyRate != 0 %} + {{ dayHourlyRate }} + {% endif %} + {% for currency, rate in dayRates %} {{ rate|money(currency) }} diff --git a/tests/Entity/TimesheetTest.php b/tests/Entity/TimesheetTest.php index 42325743..adcdece5 100644 --- a/tests/Entity/TimesheetTest.php +++ b/tests/Entity/TimesheetTest.php @@ -157,6 +157,26 @@ class TimesheetTest extends TestCase self::assertTrue($sut->isBillable()); } + public function testResetRates() + { + $sut = new Timesheet(); + self::assertSame(0.00, $sut->getRate()); + self::assertNull($sut->getFixedRate()); + self::assertNull($sut->getInternalRate()); + self::assertNull($sut->getHourlyRate()); + + $sut->setRate(123.45); + $sut->setFixedRate(42.32); + $sut->setInternalRate(212); + $sut->setHourlyRate(123); + $sut->resetRates(); + + self::assertSame(0.00, $sut->getRate()); + self::assertNull($sut->getFixedRate()); + self::assertNull($sut->getInternalRate()); + self::assertNull($sut->getHourlyRate()); + } + public function testCategory() { $sut = new Timesheet(); diff --git a/tests/Model/AbstractTimesheetCountedStatisticTest.php b/tests/Model/AbstractTimesheetCountedStatisticTest.php index e63a03ba..baf2ff1e 100644 --- a/tests/Model/AbstractTimesheetCountedStatisticTest.php +++ b/tests/Model/AbstractTimesheetCountedStatisticTest.php @@ -28,17 +28,25 @@ abstract class AbstractTimesheetCountedStatisticTest extends TestCase self::assertSame(0.0, $sut->getRateBillable()); self::assertSame(0, $sut->getRecordAmountBillable()); self::assertSame(0.0, $sut->getInternalRateBillable()); + self::assertSame(0, $sut->getDurationBillableExported()); + self::assertSame(0, $sut->getDurationExported()); + self::assertSame(0.0, $sut->getRateExported()); + self::assertSame(0.0, $sut->getInternalRateExported()); $json = $sut->jsonSerialize(); $expected = [ 'duration' => 0, 'duration_billable' => 0, + 'duration_exported' => 0, + 'duration_billable_exported' => 0, 'rate' => 0.0, 'rate_billable' => 0.0, + 'rate_exported' => 0.0, 'rate_internal' => 0.0, 'amount' => 0, 'amount_billable' => 0, + 'amount_exported' => 0, ]; foreach ($expected as $key => $value) { @@ -55,12 +63,22 @@ abstract class AbstractTimesheetCountedStatisticTest extends TestCase $sut->setRecordAmountBillable(15); self::assertInstanceOf(TimesheetCountedStatistic::class, $sut->setRecordInternalRate(99.09)); + $sut->setDurationBillableExported(199); + $sut->setDurationExported(299); + $sut->setRateExported(456.48); + $sut->setInternalRateExported(27.15); + self::assertSame(23.97, $sut->getRecordRate()); self::assertSame(21, $sut->getRecordDuration()); self::assertSame(5, $sut->getRecordAmount()); self::assertSame(15, $sut->getRecordAmountBillable()); self::assertSame(99.09, $sut->getRecordInternalRate()); + self::assertSame(199, $sut->getDurationBillableExported()); + self::assertSame(299, $sut->getDurationExported()); + self::assertSame(456.48, $sut->getRateExported()); + self::assertSame(27.15, $sut->getInternalRateExported()); + self::assertSame(21, $sut->getValue()); $sut->setCounter(524); @@ -93,15 +111,24 @@ abstract class AbstractTimesheetCountedStatisticTest extends TestCase $sut->setDurationBillable(1234); $sut->setRecordAmountBillable(4321); + $sut->setDurationBillableExported(199); + $sut->setRateBillableExported(654.23); + $sut->setDurationExported(299); + $sut->setRateExported(456.48); + $sut->setInternalRateExported(27.15); + $json = $sut->jsonSerialize(); - foreach (['duration', 'duration_billable', 'rate', 'rate_billable', 'rate_internal', 'amount', 'amount_billable'] as $key) { + foreach (['duration', 'duration_billable', 'duration_exported', 'rate', 'rate_billable', 'rate_billable_exported', 'rate_exported', 'rate_internal', 'amount', 'amount_billable', 'amount_exported'] as $key) { self::assertArrayHasKey($key, $json); } self::assertSame(21, $json['duration']); self::assertSame(1234, $json['duration_billable']); + self::assertSame(199, $json['duration_billable_exported']); + self::assertSame(299, $json['duration_exported']); self::assertSame(23.97, $json['rate']); self::assertSame(123.456, $json['rate_billable']); + self::assertSame(654.23, $json['rate_billable_exported']); self::assertSame(99.09, $json['rate_internal']); self::assertSame(5, $json['amount']); self::assertSame(4321, $json['amount_billable']);