Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -57,7 +57,7 @@ class ActivityStatistic extends BudgetStatistic implements \JsonSerializable
|
||||
return $this->activity->getName();
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return array_merge(parent::jsonSerialize(), [
|
||||
'name' => $this->getName(),
|
||||
|
||||
@@ -19,22 +19,11 @@ use App\Model\Statistic\BudgetStatistic;
|
||||
*/
|
||||
class BudgetStatisticModel implements BudgetStatisticModelInterface
|
||||
{
|
||||
/**
|
||||
* @var EntityWithBudget
|
||||
*/
|
||||
private $entity;
|
||||
/**
|
||||
* @var BudgetStatistic
|
||||
*/
|
||||
private $statistic;
|
||||
/**
|
||||
* @var BudgetStatistic
|
||||
*/
|
||||
private $statisticTotal;
|
||||
private ?BudgetStatistic $statistic = null;
|
||||
private ?BudgetStatistic $statisticTotal = null;
|
||||
|
||||
public function __construct(EntityWithBudget $entity)
|
||||
public function __construct(private EntityWithBudget $entity)
|
||||
{
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
public function getEntity(): EntityWithBudget
|
||||
|
||||
@@ -11,56 +11,6 @@ namespace App\Model;
|
||||
|
||||
use App\Model\Statistic\BudgetStatistic;
|
||||
|
||||
class CustomerStatistic extends BudgetStatistic
|
||||
final class CustomerStatistic extends BudgetStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $activityAmount = 0;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $projectAmount = 0;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @return int
|
||||
*/
|
||||
public function getActivityAmount(): int
|
||||
{
|
||||
return $this->activityAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @param int $activityAmount
|
||||
* @return $this
|
||||
*/
|
||||
public function setActivityAmount(int $activityAmount): CustomerStatistic
|
||||
{
|
||||
$this->activityAmount = $activityAmount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @return int
|
||||
*/
|
||||
public function getProjectAmount(): int
|
||||
{
|
||||
return $this->projectAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @param int $projectAmount
|
||||
* @return $this
|
||||
*/
|
||||
public function setProjectAmount(int $projectAmount): CustomerStatistic
|
||||
{
|
||||
$this->projectAmount = $projectAmount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
29
src/Model/FavoriteTimesheet.php
Normal file
29
src/Model/FavoriteTimesheet.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
|
||||
final class FavoriteTimesheet
|
||||
{
|
||||
public function __construct(private Timesheet $timesheet, private bool $isFavorite)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTimesheet(): Timesheet
|
||||
{
|
||||
return $this->timesheet;
|
||||
}
|
||||
|
||||
public function isFavorite(): bool
|
||||
{
|
||||
return $this->isFavorite;
|
||||
}
|
||||
}
|
||||
54
src/Model/InvoiceDocument.php
Normal file
54
src/Model/InvoiceDocument.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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\Model;
|
||||
|
||||
final class InvoiceDocument
|
||||
{
|
||||
public function __construct(private \SplFileInfo $file)
|
||||
{
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
$file = $this->file->getFilename();
|
||||
|
||||
return substr($file, 0, strpos($file, '.'));
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return basename($this->getFilename());
|
||||
}
|
||||
|
||||
public function getFilename(): string
|
||||
{
|
||||
$path = $this->file->getRealPath();
|
||||
if ($path === false) {
|
||||
throw new \Exception('Invoice template got deleted from filesystem: ' . $this->file->getFilename());
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function getFileExtension(): string
|
||||
{
|
||||
return $this->file->getExtension();
|
||||
}
|
||||
|
||||
public function getLastChange(): int
|
||||
{
|
||||
$modified = $this->file->getMTime();
|
||||
if ($modified === false) {
|
||||
throw new \Exception('Invoice template got deleted from filesystem: ' . $this->file->getFilename());
|
||||
}
|
||||
|
||||
return $modified;
|
||||
}
|
||||
}
|
||||
@@ -11,19 +11,8 @@ namespace App\Model;
|
||||
|
||||
class PermissionSection implements PermissionSectionInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $filter;
|
||||
|
||||
public function __construct(string $title, string $strposFilter)
|
||||
public function __construct(private string $title, private string $filter)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->filter = $strposFilter;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
@@ -33,6 +22,6 @@ class PermissionSection implements PermissionSectionInterface
|
||||
|
||||
public function filter(string $permission): bool
|
||||
{
|
||||
return strpos($permission, $this->filter) !== false;
|
||||
return str_contains($permission, $this->filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,31 +11,6 @@ namespace App\Model;
|
||||
|
||||
use App\Model\Statistic\BudgetStatistic;
|
||||
|
||||
class ProjectStatistic extends BudgetStatistic
|
||||
final class ProjectStatistic extends BudgetStatistic
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $activityAmount = 0;
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @return int
|
||||
*/
|
||||
public function getActivityAmount(): int
|
||||
{
|
||||
return $this->activityAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 - will be removed with 2.0
|
||||
* @param int $activityAmount
|
||||
* @return $this
|
||||
*/
|
||||
public function setActivityAmount(int $activityAmount): ProjectStatistic
|
||||
{
|
||||
$this->activityAmount = $activityAmount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,14 @@ use App\Entity\User;
|
||||
*/
|
||||
class QuickEntryModel
|
||||
{
|
||||
private $user;
|
||||
private $project;
|
||||
private $activity;
|
||||
private $prototype = false;
|
||||
private bool $prototype = false;
|
||||
/**
|
||||
* @var Timesheet[]
|
||||
*/
|
||||
private $timesheets = [];
|
||||
private array $timesheets = [];
|
||||
|
||||
public function __construct(?User $user = null, ?Project $project = null, ?Activity $activity = null)
|
||||
public function __construct(private ?User $user = null, private ?Project $project = null, private ?Activity $activity = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->project = $project;
|
||||
$this->activity = $activity;
|
||||
}
|
||||
|
||||
public function markAsPrototype(): void
|
||||
|
||||
@@ -18,12 +18,13 @@ use App\Entity\User;
|
||||
*/
|
||||
class QuickEntryWeek
|
||||
{
|
||||
private $startDate;
|
||||
private $rows = [];
|
||||
/**
|
||||
* @var array<QuickEntryModel>
|
||||
*/
|
||||
private array $rows = [];
|
||||
|
||||
public function __construct(\DateTime $startDate)
|
||||
public function __construct(private \DateTime $startDate)
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
}
|
||||
|
||||
public function addRow(?User $user = null, ?Project $project = null, ?Activity $activity = null): QuickEntryModel
|
||||
@@ -65,8 +66,8 @@ class QuickEntryWeek
|
||||
|
||||
private function sortByProjectName(QuickEntryModel $a, QuickEntryModel $b): int
|
||||
{
|
||||
$aName = $a->getProject() !== null ? $a->getProject()->getName() : null;
|
||||
$bName = $b->getProject() !== null ? $b->getProject()->getName() : null;
|
||||
$aName = $a->getProject()?->getName();
|
||||
$bName = $b->getProject()?->getName();
|
||||
|
||||
if ($aName === null && $bName === null) {
|
||||
$result = 0;
|
||||
@@ -75,7 +76,7 @@ class QuickEntryWeek
|
||||
} elseif ($aName !== null && $bName === null) {
|
||||
$result = -1;
|
||||
} else {
|
||||
$result = strcmp($aName, $bName);
|
||||
$result = strcmp((string) $aName, (string) $bName);
|
||||
}
|
||||
|
||||
return $result < 0 ? -1 : 1;
|
||||
|
||||
27
src/Model/Revenue.php
Normal file
27
src/Model/Revenue.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\Model;
|
||||
|
||||
final class Revenue
|
||||
{
|
||||
public function __construct(private readonly string $currency, private readonly float $amount)
|
||||
{
|
||||
}
|
||||
|
||||
public function getCurrency(): string
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
public function getAmount(): float
|
||||
{
|
||||
return $this->amount;
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,6 @@ namespace App\Model\Statistic;
|
||||
|
||||
use App\Model\TimesheetCountedStatistic;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BudgetStatistic extends TimesheetCountedStatistic
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,24 +11,13 @@ namespace App\Model\Statistic;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class Day extends Timesheet
|
||||
final class Day extends Timesheet
|
||||
{
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
private $totalDurationBillable = 0;
|
||||
/**
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $day;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $details = [];
|
||||
private int $totalDurationBillable = 0;
|
||||
private array $details = [];
|
||||
|
||||
public function __construct(DateTime $day, int $duration, float $rate)
|
||||
public function __construct(private DateTime $day, int $duration, float $rate)
|
||||
{
|
||||
$this->day = $day;
|
||||
$this->setTotalDuration($duration);
|
||||
$this->setTotalRate($rate);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ use InvalidArgumentException;
|
||||
|
||||
final class Month extends Timesheet
|
||||
{
|
||||
private $month;
|
||||
private $billableDuration = 0;
|
||||
private $billableRate = 0.00;
|
||||
private string $month;
|
||||
private int $billableDuration = 0;
|
||||
private float $billableRate = 0.00;
|
||||
|
||||
/**
|
||||
* @param string|int $month
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace App\Model\Statistic;
|
||||
|
||||
final class StatisticDate extends Timesheet
|
||||
{
|
||||
private $date;
|
||||
private $billableDuration = 0;
|
||||
private $billableRate = 0.00;
|
||||
private \DateTimeInterface $date;
|
||||
private int $billableDuration = 0;
|
||||
private float $billableRate = 0.00;
|
||||
|
||||
public function __construct(\DateTimeInterface $date)
|
||||
{
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace App\Model\Statistic;
|
||||
|
||||
class Timesheet
|
||||
{
|
||||
private $totalDuration = 0;
|
||||
private $totalRate = 0.00;
|
||||
private $totalInternalRate = 0.00;
|
||||
private int $totalDuration = 0;
|
||||
private float $totalRate = 0.00;
|
||||
private float $totalInternalRate = 0.00;
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
|
||||
@@ -13,19 +13,8 @@ use App\Entity\User;
|
||||
|
||||
final class UserYear
|
||||
{
|
||||
/**
|
||||
* @var Year
|
||||
*/
|
||||
private $year;
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user, Year $year)
|
||||
public function __construct(private User $user, private Year $year)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->year = $year;
|
||||
}
|
||||
|
||||
public function getYear(): Year
|
||||
|
||||
@@ -11,17 +11,15 @@ namespace App\Model\Statistic;
|
||||
|
||||
final class Year extends Timesheet
|
||||
{
|
||||
private $year;
|
||||
/**
|
||||
* @var Month[]
|
||||
*/
|
||||
private $months = [];
|
||||
private $billableDuration = 0;
|
||||
private $billableRate = 0.00;
|
||||
private array $months = [];
|
||||
private int $billableDuration = 0;
|
||||
private float $billableRate = 0.00;
|
||||
|
||||
public function __construct(string $year)
|
||||
public function __construct(private string $year)
|
||||
{
|
||||
$this->year = $year;
|
||||
}
|
||||
|
||||
public function getYear(): string
|
||||
|
||||
@@ -9,25 +9,28 @@
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class TimesheetCountedStatistic implements \JsonSerializable
|
||||
{
|
||||
private $counter = 0;
|
||||
private $recordDuration = 0;
|
||||
private $recordRate = 0.0;
|
||||
private $recordInternalRate = 0.0;
|
||||
private int $counter = 0;
|
||||
private int $recordDuration = 0;
|
||||
private float $recordRate = 0.0;
|
||||
private float $internalRate = 0.0;
|
||||
|
||||
private $counterBillable = 0;
|
||||
private $recordDurationBillable = 0;
|
||||
private $recordRateBillable = 0.0;
|
||||
private $internalRateBillable = 0.0;
|
||||
private int $counterBillable = 0;
|
||||
private int $recordDurationBillable = 0;
|
||||
private float $recordRateBillable = 0.0;
|
||||
private float $internalRateBillable = 0.0;
|
||||
|
||||
private $recordRateBillableExported = 0.0;
|
||||
private $recordDurationBillableExported = 0;
|
||||
private float $recordRateBillableExported = 0.0;
|
||||
private int $recordDurationBillableExported = 0;
|
||||
|
||||
private $counterExported = 0;
|
||||
private $recordDurationExported = 0;
|
||||
private $recordRateExported = 0.0;
|
||||
private $internalRateExported = 0.0;
|
||||
private int $counterExported = 0;
|
||||
private int $recordDurationExported = 0;
|
||||
private float $recordRateExported = 0.0;
|
||||
private float $internalRateExported = 0.0;
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
@@ -64,28 +67,6 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->counterExported = $counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of included timesheet records.
|
||||
*
|
||||
* @return int
|
||||
* @deprecated since 1.15 use getCounter() instead
|
||||
*/
|
||||
public function getRecordAmount()
|
||||
{
|
||||
return $this->getCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $recordAmount
|
||||
* @return $this
|
||||
*/
|
||||
public function setRecordAmount($recordAmount)
|
||||
{
|
||||
$this->setCounter((int) $recordAmount);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
*
|
||||
@@ -111,27 +92,6 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
return $this->recordDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total duration of all included timesheet records.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRecordDuration(): int
|
||||
{
|
||||
return $this->getDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $recordDuration
|
||||
* @return $this
|
||||
*/
|
||||
public function setRecordDuration($recordDuration)
|
||||
{
|
||||
$this->setDuration((int) $recordDuration);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* For unified access, used in frontend.
|
||||
*
|
||||
@@ -142,40 +102,11 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
return $this->recordRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total rate of all included timesheet records.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getRecordRate(): float
|
||||
{
|
||||
return $this->getRate();
|
||||
}
|
||||
|
||||
public function setRate(float $rate): void
|
||||
{
|
||||
$this->recordRate = $rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $recordRate
|
||||
* @return $this
|
||||
*/
|
||||
public function setRecordRate($recordRate)
|
||||
{
|
||||
$this->setRate((float) $recordRate);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 use getInternalRate() instead
|
||||
*/
|
||||
public function getRecordInternalRate(): float
|
||||
{
|
||||
return $this->getInternalRate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total internal rate of all included timesheet records.
|
||||
*
|
||||
@@ -183,7 +114,7 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
*/
|
||||
public function getInternalRate(): float
|
||||
{
|
||||
return $this->recordInternalRate;
|
||||
return $this->internalRate;
|
||||
}
|
||||
|
||||
public function getInternalRateBillable(): float
|
||||
@@ -206,34 +137,9 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->internalRateExported = $internalRateExported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $recordInternalRate
|
||||
* @return $this
|
||||
* @deprecated since 1.15 use setInternalRate() instead
|
||||
*/
|
||||
public function setRecordInternalRate($recordInternalRate)
|
||||
{
|
||||
$this->setInternalRate((float) $recordInternalRate);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInternalRate(float $internalRate): void
|
||||
{
|
||||
$this->recordInternalRate = $internalRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 use getCounterBillable() instead
|
||||
*/
|
||||
public function getRecordAmountBillable(): int
|
||||
{
|
||||
return $this->getCounterBillable();
|
||||
}
|
||||
|
||||
public function setRecordAmountBillable(int $recordAmount): void
|
||||
{
|
||||
$this->setCounterBillable($recordAmount);
|
||||
$this->internalRate = $internalRate;
|
||||
}
|
||||
|
||||
public function getDurationBillable(): int
|
||||
@@ -296,8 +202,7 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
$this->recordRateExported = $recordRate;
|
||||
}
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'duration' => $this->recordDuration,
|
||||
@@ -308,7 +213,7 @@ class TimesheetCountedStatistic implements \JsonSerializable
|
||||
'rate_billable' => $this->recordRateBillable,
|
||||
'rate_exported' => $this->recordRateExported,
|
||||
'rate_billable_exported' => $this->recordRateBillableExported,
|
||||
'rate_internal' => $this->recordInternalRate,
|
||||
'rate_internal' => $this->internalRate,
|
||||
'rate_internal_exported' => $this->internalRateExported,
|
||||
'amount' => $this->counter,
|
||||
'amount_billable' => $this->counterBillable,
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Timesheet statistics for one user.
|
||||
*/
|
||||
@@ -19,14 +17,14 @@ class TimesheetStatistic
|
||||
/**
|
||||
* @var \DateTime|null
|
||||
*/
|
||||
protected $firstEntry;
|
||||
protected $durationThisMonth = 0;
|
||||
protected $durationTotal = 0;
|
||||
protected $amountThisMonth = 0.0;
|
||||
protected $amountTotal = 0.0;
|
||||
protected $amountThisMonthBillable = 0.0;
|
||||
protected $amountTotalBillable = 0.0;
|
||||
protected $recordsTotal = 0;
|
||||
protected ?\DateTime $firstEntry = null;
|
||||
protected int $durationThisMonth = 0;
|
||||
protected int $durationTotal = 0;
|
||||
protected float $amountThisMonth = 0.0;
|
||||
protected float $amountTotal = 0.0;
|
||||
protected float $amountThisMonthBillable = 0.0;
|
||||
protected float $amountTotalBillable = 0.0;
|
||||
protected int $recordsTotal = 0;
|
||||
|
||||
public function getDurationThisMonth(): int
|
||||
{
|
||||
@@ -111,19 +109,6 @@ class TimesheetStatistic
|
||||
$this->amountThisMonthBillable = $amountThisMonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.15 use TimesheetStatisticService::findFirstRecordDate() instead, will be removed with 2.0
|
||||
*/
|
||||
public function getFirstEntry(): ?\DateTime
|
||||
{
|
||||
return $this->firstEntry;
|
||||
}
|
||||
|
||||
public function setFirstEntry(DateTime $firstEntry): void
|
||||
{
|
||||
$this->firstEntry = $firstEntry;
|
||||
}
|
||||
|
||||
public function getRecordsTotal(): int
|
||||
{
|
||||
return $this->recordsTotal;
|
||||
|
||||
@@ -14,14 +14,8 @@ use App\Model\Statistic\Month;
|
||||
|
||||
class UserStatistic extends TimesheetCountedStatistic
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
public function __construct(User $user)
|
||||
public function __construct(private User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getUser(): User
|
||||
|
||||
Reference in New Issue
Block a user