Release 2.13 (#4659)

This commit is contained in:
Kevin Papst
2024-03-10 15:35:59 +01:00
committed by GitHub
parent a78e8ed8c4
commit dee90bb15e
79 changed files with 1019 additions and 932 deletions

View File

@@ -13,7 +13,7 @@ use App\Activity\ActivityStatisticService;
use App\Configuration\LocaleService;
use App\Configuration\SystemConfiguration;
use App\Customer\CustomerStatisticService;
use App\Entity\Timesheet;
use App\Entity\Timesheet as TimesheetEntity;
use App\Model\BudgetStatisticModel;
use App\Project\ProjectStatisticService;
use App\Repository\TimesheetRepository;
@@ -29,53 +29,49 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
final class TimesheetBudgetUsedValidator extends ConstraintValidator
{
public function __construct(
private SystemConfiguration $configuration,
private CustomerStatisticService $customerStatisticService,
private ProjectStatisticService $projectStatisticService,
private ActivityStatisticService $activityStatisticService,
private TimesheetRepository $timesheetRepository,
private RateServiceInterface $rateService,
private AuthorizationCheckerInterface $security,
private LocaleService $localeService
private readonly SystemConfiguration $configuration,
private readonly CustomerStatisticService $customerStatisticService,
private readonly ProjectStatisticService $projectStatisticService,
private readonly ActivityStatisticService $activityStatisticService,
private readonly TimesheetRepository $timesheetRepository,
private readonly RateServiceInterface $rateService,
private readonly AuthorizationCheckerInterface $security,
private readonly LocaleService $localeService
) {
}
/**
* @param Timesheet $timesheet
* @param Constraint $constraint
*/
public function validate(mixed $timesheet, Constraint $constraint): void
public function validate(mixed $value, Constraint $constraint): void
{
if (!($constraint instanceof TimesheetBudgetUsed)) {
throw new UnexpectedTypeException($constraint, TimesheetBudgetUsed::class);
}
if (!\is_object($timesheet) || !($timesheet instanceof Timesheet)) {
throw new UnexpectedTypeException($timesheet, Timesheet::class);
if (!\is_object($value) || !($value instanceof TimesheetEntity)) {
throw new UnexpectedTypeException($value, TimesheetEntity::class);
}
if ($this->configuration->isTimesheetAllowOverbookingBudget()) {
return;
}
$begin = $timesheet->getBegin();
$begin = $value->getBegin();
// we can only work with stopped entries
if ($begin === null || $timesheet->getEnd() === null || $timesheet->getUser() === null) {
if ($begin === null || $value->getEnd() === null || $value->getUser() === null) {
return;
}
// budgets need only be calculated for billable records
if (!$timesheet->isBillable()) {
if (!$value->isBillable()) {
return;
}
// this validator needs a project to calculate the rates
if ($timesheet->getProject() === null) {
if ($value->getProject() === null) {
return;
}
$id = $timesheet->getId();
$id = $value->getId();
// when changing the date via the calendar and/or the API, the duration will not be reset by the
// duration calculator (which runs after validation!) so we manually reset the duration before
@@ -92,9 +88,9 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
// $timesheet->setDuration(null);
// $duration = $timesheet->getDuration();
$duration = $timesheet->getCalculatedDuration();
$duration = $value->getCalculatedDuration();
$timeRate = $this->rateService->calculate($timesheet);
$timeRate = $this->rateService->calculate($value);
$rate = $timeRate->getRate();
$activityDuration = $duration ?? 0;
@@ -116,10 +112,10 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
// this could for example happen when export flag is changed OR if "prevent overbooking" config was recently activated and this is an old entry
if ($duration === $rawData['duration'] &&
$rate === $rawData['rate'] &&
$timesheet->isBillable() === $rawData['billable'] &&
$value->isBillable() === $rawData['billable'] &&
$begin->format('Y.m.d') === $rawData['begin']->format('Y.m.d') &&
$timesheet->getProject()->getId() === $projectId &&
($timesheet->getActivity() === null || $timesheet->getActivity()->getId() === $activityId)
$value->getProject()->getId() === $projectId &&
($value->getActivity() === null || $value->getActivity()->getId() === $activityId)
) {
return;
}
@@ -129,18 +125,18 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
// only subtract the previously logged data in case the record was billable
// if it wasn't billable, then its values are not included in the statistic models used later on
if ($rawData['billable']) {
if (null !== $timesheet->getActivity() && $activityId === $timesheet->getActivity()->getId()) {
if (null !== $value->getActivity() && $activityId === $value->getActivity()->getId()) {
$activityDuration -= $rawData['duration'];
$activityRate -= $rawData['rate'];
}
if (null !== $timesheet->getProject()) {
if ($projectId === $timesheet->getProject()->getId()) {
if (null !== $value->getProject()) {
if ($projectId === $value->getProject()->getId()) {
$projectDuration -= $rawData['duration'];
$projectRate -= $rawData['rate'];
}
if ($customerId === $timesheet->getProject()->getCustomer()->getId()) {
if ($customerId === $value->getProject()->getCustomer()->getId()) {
$customerDuration -= $rawData['duration'];
$customerRate -= $rawData['rate'];
}
@@ -153,23 +149,23 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
$now = new DateTime('now', $begin->getTimezone());
$recordDate = $begin;
if (null !== ($activity = $timesheet->getActivity()) && $activity->hasBudgets()) {
if (null !== ($activity = $value->getActivity()) && $activity->hasBudgets()) {
$dateTime = $activity->isMonthlyBudget() ? $recordDate : $now;
if ($activity->isMonthlyBudget() && $monthWasChanged) {
$activityDuration = $duration;
}
$stat = $this->activityStatisticService->getBudgetStatisticModel($activity, $dateTime);
$this->checkBudgets($constraint, $stat, $timesheet, $activityDuration, $activityRate, 'activity');
$this->checkBudgets($constraint, $stat, $value, $activityDuration, $activityRate, 'activity');
}
if (null !== ($project = $timesheet->getProject())) {
if (null !== ($project = $value->getProject())) {
if ($project->hasBudgets()) {
$dateTime = $project->isMonthlyBudget() ? $recordDate : $now;
if ($project->isMonthlyBudget() && $monthWasChanged) {
$projectDuration = $duration;
}
$stat = $this->projectStatisticService->getBudgetStatisticModel($project, $dateTime);
$this->checkBudgets($constraint, $stat, $timesheet, $projectDuration, $projectRate, 'project');
$this->checkBudgets($constraint, $stat, $value, $projectDuration, $projectRate, 'project');
}
if (null !== ($customer = $project->getCustomer()) && $customer->hasBudgets()) {
$dateTime = $customer->isMonthlyBudget() ? $recordDate : $now;
@@ -177,12 +173,12 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
$customerDuration = $duration;
}
$stat = $this->customerStatisticService->getBudgetStatisticModel($customer, $dateTime);
$this->checkBudgets($constraint, $stat, $timesheet, $customerDuration, $customerRate, 'customer');
$this->checkBudgets($constraint, $stat, $value, $customerDuration, $customerRate, 'customer');
}
}
}
private function checkBudgets(TimesheetBudgetUsed $constraint, BudgetStatisticModel $stat, Timesheet $timesheet, int $duration, float $rate, string $field): bool
private function checkBudgets(TimesheetBudgetUsed $constraint, BudgetStatisticModel $stat, TimesheetEntity $timesheet, int $duration, float $rate, string $field): bool
{
$fullRate = ($stat->getBudgetSpent() + $rate);
@@ -203,7 +199,7 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator
return false;
}
private function addBudgetViolation(TimesheetBudgetUsed $constraint, Timesheet $timesheet, string $field, float $budget, float $rate): void
private function addBudgetViolation(TimesheetBudgetUsed $constraint, TimesheetEntity $timesheet, string $field, float $budget, float $rate): void
{
// using the locale of the assigned user is not the best solution, but allows to be independent of the request stack
$helper = new LocaleFormatter($this->localeService, $timesheet->getUser()?->getLocale() ?? 'en');