<?php
namespace App\Entity\TimeNormalization;
use ApiPlatform\Core\Annotation\ApiFilter;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\ExistsFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use App\Entity\Unit;
use App\Entity\User;
use App\Repository\WorkPositionRepository;
use App\Validator\IntersectionOfPeriods;
use App\Validator\OpenPeriodExist;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
collectionOperations: [
'get' => ['security' => "is_granted('ROLE_HR') or is_granted('ROLE_MASTER_MANAGER')"],
'post' => ['security' => "is_granted('ROLE_HR') or is_granted('ROLE_MASTER_MANAGER')",
'openapi_context' => WorkPosition::OPENAPI_DOC,
],
],
itemOperations: [
'get' => ['security' => "is_granted('ROLE_HR') or is_granted('ROLE_MASTER_MANAGER')"],
'put' => ['security' => "is_granted('ROLE_HR') or is_granted('ROLE_MASTER_MANAGER')",
'openapi_context' => WorkPosition::OPENAPI_DOC,
],
'delete' => ['security' => "is_granted('ROLE_HR') or is_granted('ROLE_MASTER_MANAGER')",
'openapi_context' => WorkPosition::OPENAPI_DOC,
],
],
order: ['datePosition' => 'ASC'],
)]
#[ApiFilter(filterClass: SearchFilter::class, properties: [
'user.slug' => SearchFilter::STRATEGY_EXACT,
'unit.slug' => SearchFilter::STRATEGY_EXACT,
'contractType' => SearchFilter::STRATEGY_EXACT,
])]
#[ApiFilter(ExistsFilter::class, properties: ['datePositionEnd'])]
#[ORM\Entity(repositoryClass: WorkPositionRepository::class)]
#[ORM\Table(name: 'work_position')]
#[ORM\HasLifecycleCallbacks]
#[ORM\Index(name: 'idx_date_position', columns: ['date_position'])]
#[ORM\Index(name: 'idx_contract_type', columns: ['contract_type'])]
#[ORM\Index(name: 'idx_date_position_end', columns: ['date_position_end'])]
#[OpenPeriodExist(toDate: 'endBefore', filter: ['user'])]
#[IntersectionOfPeriods(fromDate: 'datePosition', toDate: 'endBefore', filter: ['user'])]
class WorkPosition
{
public const CONTRACT_TYPE_LABOR = 'labor_contract';
public const CONTRACT_TYPE_SELF = 'self_employed_contract';
public const CONTRACT_TYPE_FOREIGNER = 'foreigner_contract';
public const STATUS_IN_WORK = 'in_work';
public const STATUS_DISSMISSES = 'dismissed';
public const STATUS_UNPAID_LEAVE = 'unpaid_leave';
public const STATUS_MATERNITY_LEAVE = 'maternity_leave';
public const READ_GROUP = 'WorkPosition:read';
public const WRITE_GROUP = 'WorkPosition:write';
public const AUTOMATED_STATUSES = [
self::STATUS_IN_WORK,
self::STATUS_DISSMISSES,
];
public const CONTRACT_TYPES = [
'labor_contract' => self::CONTRACT_TYPE_LABOR,
'self_employed_contract' => self::CONTRACT_TYPE_SELF,
'foreigner_contract' => self::CONTRACT_TYPE_FOREIGNER,
];
public const STATUSES = [
'in_work' => self::STATUS_IN_WORK,
'dismissed' => self::STATUS_DISSMISSES,
'unpaid_leave' => self::STATUS_UNPAID_LEAVE,
'maternity_leave' => self::STATUS_MATERNITY_LEAVE,
];
private const OPENAPI_DOC = [
'summary' => 'recalculate PlanDays for WorkPosition',
'description' => 'recalculate PlanDays for WorkPosition',
'parameters' => [['in' => 'query', 'type' => 'bool', 'name' => 'recalculatePlanDays', 'required' => false]],
];
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[Assert\NotBlank]
#[Groups([self::READ_GROUP, self::WRITE_GROUP])]
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'workPositions', cascade: ['persist'])]
#[ORM\JoinColumn(nullable: false)]
private User $user;
#[Assert\NotBlank]
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[ORM\ManyToOne(targetEntity: Unit::class, cascade: ['persist'])]
#[ORM\JoinColumn(nullable: false)]
private Unit $unit;
#[Assert\NotBlank]
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[ORM\Column(type: 'date')]
private ?\DateTimeInterface $datePosition = null;
#[Assert\NotBlank]
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[ORM\Column(type: 'string', length: 255)]
private ?string $jobTitle = null;
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[Assert\Choice(choices: [self::CONTRACT_TYPE_LABOR, self::CONTRACT_TYPE_SELF, self::CONTRACT_TYPE_FOREIGNER])]
#[ORM\Column(type: 'string', length: 40, options: ['default' => 'labor_contract'])]
private string $contractType = self::CONTRACT_TYPE_LABOR;
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[ORM\Column(type: 'float', nullable: true)]
private ?float $normFullDay = 8;
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[ORM\Column(type: 'float', nullable: true)]
private ?float $normShortDay = 7;
/**
* дата последнего дня периода работы (вычисляется, не хранится в БД).
*/
#[Assert\GreaterThan(propertyPath: 'datePosition', message: 'datePositionEnd must be greater than datePosition')]
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
private ?\DateTimeInterface $datePositionEnd = null;
/**
* дата окончания (не включается в период работы).
*/
#[ORM\Column(name: 'date_position_end', type: 'date', nullable: true)]
private ?\DateTimeInterface $endBefore = null;
#[Groups([self::READ_GROUP, self::WRITE_GROUP, User::READ_GROUP])]
#[Assert\Choice(choices: self::STATUSES)]
#[ORM\Column(type: 'string', length: 40, options: ['default' => 'in_work'])]
private string $workingStatus = self::STATUS_IN_WORK;
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
/**
* @param User|null $user
*/
public function setUser(User $user): WorkPosition
{
$this->user = $user;
return $this;
}
public function getUnit(): ?Unit
{
return $this->unit;
}
/**
* @param Unit|null $unit
*/
public function setUnit(Unit $unit): WorkPosition
{
$this->unit = $unit;
return $this;
}
public function getDatePosition(): ?\DateTimeInterface
{
return $this->datePosition;
}
public function setDatePosition(\DateTimeInterface $datePosition): self
{
$this->datePosition = $datePosition;
return $this;
}
public function getJobTitle(): ?string
{
return $this->jobTitle;
}
public function setJobTitle(string $jobTitle): self
{
$this->jobTitle = $jobTitle;
return $this;
}
public function getContractType(): ?string
{
return $this->contractType;
}
public function setContractType(string $contractType): self
{
$this->contractType = $contractType;
return $this;
}
public function getNormFullDay(): ?float
{
return $this->normFullDay;
}
public function setNormFullDay(?float $normFullDay): self
{
$this->normFullDay = $normFullDay;
return $this;
}
public function getNormShortDay(): ?float
{
return $this->normShortDay;
}
public function setNormShortDay(?float $normShortDay): self
{
$this->normShortDay = $normShortDay;
return $this;
}
public function getDatePositionEnd(): ?\DateTimeInterface
{
return $this->endBefore ? \DateTime::createFromInterface($this->endBefore)->modify('yesterday midnight') : null;
}
public function setDatePositionEnd(?\DateTimeInterface $datePositionEnd): self
{
$this->endBefore = $datePositionEnd ? \DateTime::createFromInterface($datePositionEnd)->modify('tomorrow midnight') : null;
return $this;
}
public function getEndBefore(): ?\DateTimeInterface
{
return $this->endBefore;
}
public function setEndBefore(?\DateTimeInterface $endBefore): self
{
$this->endBefore = $endBefore;
return $this;
}
public function getWorkingStatus(): string
{
return $this->workingStatus;
}
public function setWorkingStatus(string $workingStatus): WorkPosition
{
$this->workingStatus = $workingStatus;
return $this;
}
#[ORM\PreUpdate]
#[ORM\PrePersist]
public function workingStatusCheck(): void
{
if (\in_array($this->workingStatus, self::AUTOMATED_STATUSES)) {
$this->workingStatus = $this->endBefore ? self::STATUS_DISSMISSES : self::STATUS_IN_WORK;
}
}
}