Configurable activity and project number (#4729)
* added configurable activity number * added configurable project number * fix deprecations * added some tests for entity exporter * better configuration of dropdown pattern for customer, project and activity
This commit is contained in:
29
src/Validator/Constraints/Activity.php
Normal file
29
src/Validator/Constraints/Activity.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\Validator\Constraints;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class Activity extends Constraint
|
||||
{
|
||||
public const ACTIVITY_NUMBER_EXISTING = 'kimai-activity-00';
|
||||
|
||||
protected const ERROR_NAMES = [
|
||||
self::ACTIVITY_NUMBER_EXISTING => 'The number %number% is already used.',
|
||||
];
|
||||
|
||||
public string $message = 'This activity has invalid settings.';
|
||||
|
||||
public function getTargets(): string
|
||||
{
|
||||
return self::CLASS_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
55
src/Validator/Constraints/ActivityValidator.php
Normal file
55
src/Validator/Constraints/ActivityValidator.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Validator\Constraints;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Activity as ActivityEntity;
|
||||
use App\Repository\ActivityRepository;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class ActivityValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SystemConfiguration $systemConfiguration,
|
||||
private readonly ActivityRepository $activityRepository
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ActivityEntity|mixed $value
|
||||
*/
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
if (!($constraint instanceof Activity)) {
|
||||
throw new UnexpectedTypeException($constraint, Activity::class);
|
||||
}
|
||||
|
||||
if (!($value instanceof ActivityEntity)) {
|
||||
throw new UnexpectedTypeException($value, ActivityEntity::class);
|
||||
}
|
||||
|
||||
if ((bool) $this->systemConfiguration->find('activity.allow_duplicate_number') === false && (($number = $value->getNumber()) !== null)) {
|
||||
foreach ($this->activityRepository->findBy(['number' => $number]) as $tmp) {
|
||||
if ($tmp->getId() !== $value->getId()) {
|
||||
$this->context->buildViolation(Activity::getErrorName(Activity::ACTIVITY_NUMBER_EXISTING))
|
||||
->setParameter('%number%', $number)
|
||||
->atPath('number')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(Activity::ACTIVITY_NUMBER_EXISTING)
|
||||
->addViolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ final class Customer extends Constraint
|
||||
public const CUSTOMER_NUMBER_EXISTING = 'kimai-customer-00';
|
||||
|
||||
protected const ERROR_NAMES = [
|
||||
self::CUSTOMER_NUMBER_EXISTING => 'The account number %number% is already used.',
|
||||
self::CUSTOMER_NUMBER_EXISTING => 'The number %number% is already used.',
|
||||
];
|
||||
|
||||
public string $message = 'This customer has invalid settings.';
|
||||
|
||||
@@ -18,13 +18,15 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class CustomerValidator extends ConstraintValidator
|
||||
{
|
||||
public function __construct(private SystemConfiguration $systemConfiguration, private CustomerRepository $customerRepository)
|
||||
public function __construct(
|
||||
private readonly SystemConfiguration $systemConfiguration,
|
||||
private readonly CustomerRepository $customerRepository
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CustomerEntity|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
@@ -37,14 +39,16 @@ final class CustomerValidator extends ConstraintValidator
|
||||
}
|
||||
|
||||
if ((bool) $this->systemConfiguration->find('customer.rules.allow_duplicate_number') === false && (($number = $value->getNumber()) !== null)) {
|
||||
$tmp = $this->customerRepository->findOneBy(['number' => $number]);
|
||||
if ($tmp !== null && $tmp->getId() !== $value->getId()) {
|
||||
$this->context->buildViolation(Customer::getErrorName(Customer::CUSTOMER_NUMBER_EXISTING))
|
||||
->setParameter('%number%', $number)
|
||||
->atPath('number')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(Customer::CUSTOMER_NUMBER_EXISTING)
|
||||
->addViolation();
|
||||
foreach ($this->customerRepository->findBy(['number' => $number]) as $tmp) {
|
||||
if ($tmp->getId() !== $value->getId()) {
|
||||
$this->context->buildViolation(Customer::getErrorName(Customer::CUSTOMER_NUMBER_EXISTING))
|
||||
->setParameter('%number%', $number)
|
||||
->atPath('number')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(Customer::CUSTOMER_NUMBER_EXISTING)
|
||||
->addViolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@ use Symfony\Component\Validator\Constraint;
|
||||
final class Project extends Constraint
|
||||
{
|
||||
public const END_BEFORE_BEGIN_ERROR = 'kimai-project-00';
|
||||
public const PROJECT_NUMBER_EXISTING = 'kimai-project-01';
|
||||
|
||||
protected const ERROR_NAMES = [
|
||||
self::END_BEFORE_BEGIN_ERROR => 'End date must not be earlier then start date.',
|
||||
self::PROJECT_NUMBER_EXISTING => 'The number %number% is already used.',
|
||||
];
|
||||
|
||||
public string $message = 'This project has invalid settings.';
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
|
||||
namespace App\Validator\Constraints;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\Project as ProjectEntity;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Validator\Constraints\Project as ProjectEntityConstraint;
|
||||
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
||||
final class ProjectValidator extends ConstraintValidator
|
||||
@@ -23,6 +24,8 @@ final class ProjectValidator extends ConstraintValidator
|
||||
* @param ProjectConstraint[] $constraints
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly SystemConfiguration $systemConfiguration,
|
||||
private readonly ProjectRepository $projectRepository,
|
||||
#[TaggedIterator(ProjectConstraint::class)]
|
||||
private iterable $constraints = []
|
||||
)
|
||||
@@ -31,7 +34,6 @@ final class ProjectValidator extends ConstraintValidator
|
||||
|
||||
/**
|
||||
* @param Project|mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate(mixed $value, Constraint $constraint): void
|
||||
{
|
||||
@@ -39,11 +41,31 @@ final class ProjectValidator extends ConstraintValidator
|
||||
throw new UnexpectedTypeException($constraint, ProjectEntityConstraint::class);
|
||||
}
|
||||
|
||||
if (!\is_object($value) || !($value instanceof Project)) {
|
||||
if (!\is_object($value) || !($value instanceof ProjectEntity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validateProject($value, $this->context);
|
||||
if (null !== $value->getStart() && null !== $value->getEnd() && $value->getStart()->getTimestamp() > $value->getEnd()->getTimestamp()) {
|
||||
$this->context->buildViolation(ProjectEntityConstraint::getErrorName(ProjectEntityConstraint::END_BEFORE_BEGIN_ERROR))
|
||||
->atPath('end')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(ProjectEntityConstraint::END_BEFORE_BEGIN_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ((bool) $this->systemConfiguration->find('project.allow_duplicate_number') === false && (($number = $value->getNumber()) !== null)) {
|
||||
foreach ($this->projectRepository->findBy(['number' => $number]) as $tmp) {
|
||||
if ($tmp->getId() !== $value->getId()) {
|
||||
$this->context->buildViolation(Project::getErrorName(Project::PROJECT_NUMBER_EXISTING))
|
||||
->setParameter('%number%', $number)
|
||||
->atPath('number')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(Project::PROJECT_NUMBER_EXISTING)
|
||||
->addViolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->constraints as $innerConstraint) {
|
||||
$this->context
|
||||
@@ -52,15 +74,4 @@ final class ProjectValidator extends ConstraintValidator
|
||||
->validate($value, $innerConstraint, [Constraint::DEFAULT_GROUP]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateProject(Project $project, ExecutionContextInterface $context): void
|
||||
{
|
||||
if (null !== $project->getStart() && null !== $project->getEnd() && $project->getStart()->getTimestamp() > $project->getEnd()->getTimestamp()) {
|
||||
$context->buildViolation(ProjectEntityConstraint::getErrorName(ProjectEntityConstraint::END_BEFORE_BEGIN_ERROR))
|
||||
->atPath('end')
|
||||
->setTranslationDomain('validators')
|
||||
->setCode(ProjectEntityConstraint::END_BEFORE_BEGIN_ERROR)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user