<?php
namespace App\EventListener;
use App\Entity\Assignment;
use App\Entity\AttachmentAssignment;
use App\Entity\Branch;
use App\Entity\Notification;
use App\Entity\Order;
use App\Entity\OrderNumber;
use App\Entity\OrderTimeLine;
use App\Entity\Procedure;
use App\Entity\Task;
use App\Enum\DeadlineType;
use App\Enum\OrderEnum;
use App\Enum\TaskEnum;
use App\Event\AssignmentFinishedEvent;
use App\Manager\TotalDeadlineAssignment;
use App\Message\FinishDesignSendTelegram;
use App\Message\FinishProductOnProductionMessage;
use App\Message\FinishReceiveSendDocumentTelegram;
use App\Message\FinishReceiveSendTelegram;
use App\Message\MessageNotification;
use App\Message\TelegramMessage;
use App\Message\UpdateRelationOrderProductMessage;
use App\Order\ChangeStatusOrderDelivery;
use App\Order\HandleOrderBranch;
use App\Repository\AssignmentRepository;
use App\Repository\AttachmentAssignmentRepository;
use App\Repository\DeadlineGuideRepository;
use App\Repository\OrderRepository;
use App\Repository\TaskRepository;
use App\WareHouse\AutoCreateReceipt;
use Carbon\CarbonImmutable;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\MessageBusInterface;
class AssignmentFinishedListener
{
const STEP_PRIORITY = 1;
const STEP_HALF_PART = 0.5;
private $totalDeadlineAssignment;
/**
* @var TaskRepository
*/
private $taskRepository;
/**
* @var DeadlineGuideRepository
*/
private $deadlineGuideRepository;
/**
* @var AssignmentRepository
*/
private $assignmentRepository;
/**
* @var OrderRepository
*/
private $orderRepository;
/**
* @var MessageBusInterface
*/
private $messageBus;
/**
* @var AttachmentAssignmentRepository
*/
private $attachmentAssignmentRepository;
/**
* @var EntityManagerInterface
*/
private $entityManager;
public function __construct(TotalDeadlineAssignment $totalDeadlineAssignment,
TaskRepository $taskRepository,
AssignmentRepository $assignmentRepository,
DeadlineGuideRepository $deadlineGuideRepository,
OrderRepository $orderRepository,
MessageBusInterface $messageBus,
AttachmentAssignmentRepository $attachmentAssignmentRepository,
EntityManagerInterface $entityManagerInterface
) {
$this->totalDeadlineAssignment = $totalDeadlineAssignment;
$this->taskRepository = $taskRepository;
$this->deadlineGuideRepository = $deadlineGuideRepository;
$this->assignmentRepository = $assignmentRepository;
$this->orderRepository = $orderRepository;
$this->messageBus = $messageBus;
$this->attachmentAssignmentRepository = $attachmentAssignmentRepository;
$this->entityManager= $entityManagerInterface;
}
public function __invoke(AssignmentFinishedEvent $event)
{
$assignment = $event->getAssignment();
/** @var Order $order */
$order = $assignment->getOrder();
if ($this->checkTaskParallelFinished($order)) {
$this->deActiveAssignment($order);
$taskReplacement = $assignment->getTask()->getTaskReplacement()->last();
$mainTask = $this->getMainTask($assignment);
if ($mainTask) {
$this->addLoopTask($assignment, $mainTask);
} elseif ($taskReplacement) {
if ($this->isApproval($event)) {
$this->skipTask($assignment);
} else {
$this->addReason($event);
$this->addLoopTask($assignment, $taskReplacement);
}
} else {
$this->addTaskNormal($assignment);
}
}
}
private function addReason(AssignmentFinishedEvent $event)
{
$assignment = $event->getAssignment();
$approve = $event->getOptions()['approve'];
if (\count($approve) > 0 && !\in_array('1', $approve, true)) {
$reason = '';
foreach ($approve as $key => $item) {
$item = (int) $item;
if ($key > 0) {
$reason .= ',';
}
if (Assignment::REASON_BY_SALE === $item) {
$reason .= 'Do sale';
} elseif (Assignment::REASON_BY_CUSTOMER === $item) {
$reason .= 'Do khách hàng';
} elseif (Assignment::REASON_BY_DESIGN === $item) {
$reason .= 'Do thiết kế';
}
}
$assignment->setReasonReject($reason);
}
}
private function getMainTask(Assignment $assignment)
{
return $assignment->getTask()->getMainTask()->last();
}
private function isApproval(AssignmentFinishedEvent $event)
{
return \in_array('1', $event->getOptions()['approve'], true);
}
private function addTaskNormal(Assignment $assignment)
{
/** @var Order $order */
$order = $assignment->getOrder();
/** @var Procedure $procedure */
$procedure = $order->getProcedure();
/** @var Assignment $lastAssigment */
$codes = $this->findCodeWithNextTask($assignment);
if (\in_array($assignment->getTask()->getCode(), ['thu_coc', 'thu_coc_cus', true], true)) {
/** @var Order $lastOrder */
$orderNumber = (new OrderNumber())->setOrderMain($order)->setPriority(0);
$this->entityManager->persist($orderNumber);
$this->entityManager->flush();
$order->setSort($orderNumber->getId());
(new HandleOrderBranch($this->entityManager,$this->messageBus))->setBranch($assignment);
$attachmentAssignmentDesign = $this
->attachmentAssignmentRepository
->findLastFileDesign($assignment->getOrder()->getId());
$attachmentAssignmentReviews = $this
->attachmentAssignmentRepository
->findLastFileDesignPreview($assignment->getOrder()->getId());
if (\count($attachmentAssignmentReviews) > 0) {
$attachmentFileNames = $attachmentPreviews = [];
/** @var AttachmentAssignment $attachmentAssignment */
foreach ($attachmentAssignmentReviews as $attachmentAssignment) {
$attachmentPreviews[$attachmentAssignment->getAssignment()->getId()->toString()][] = $attachmentAssignment;
}
foreach (array_values($attachmentPreviews)[0] as $attachmentAssignment) {
$attachmentFileNames[] = $attachmentAssignment->getAttachment()->getFileName();
}
$this->messageBus->dispatch(new FinishReceiveSendTelegram(
$assignment->getOrder()->getId(),
$assignment->getId()->toString(),
$attachmentFileNames
));
}
if (\count($attachmentAssignmentDesign) > 0) {
$this->messageBus->dispatch(new FinishReceiveSendDocumentTelegram(
$assignment->getOrder()->getId(),
$assignment->getId()->toString(),
$attachmentAssignmentDesign[0]->getAttachment()->getFileName()
));
}
}
$beforeFinishReceive = [
Task::START_DESIGN,
Task::START_CUSTOMER_DESIGN_APPROVAL_CODE,
Task::FIX_DESIGN,
];
$beforeFinishReceiveCus = [
Task::START_DESIGN_CUS,
Task::CUS_START_CUSTOMER_DESIGN_APPROVAL_CODE,
Task::FIX_DESIGN_CUS,
];
if (0 === \count($codes) && \in_array($assignment->getTask()->getCode(), $beforeFinishReceive, true)) {
$codes[] = Task::FINISH_RECEIVE;
} elseif (0 === \count($codes) && \in_array($assignment->getTask()->getCode(), $beforeFinishReceiveCus, true)) {
$codes[] = Task::CUS_FINISH_RECEIVE;
}
foreach ($codes as $code) {
$previousTaskCode = $procedure->getTasks()[$code];
$nextTask = $this->taskRepository->findOneBy(['code' => $code]);
$this->addTask($assignment, $nextTask, $previousTaskCode);
}
}
private function findCodeWithNextTask(Assignment $assignment)
{
$code = $assignment->getTask()->getCode();
/** @var Procedure $procedure */
$procedure = $assignment->getOrder()->getProcedure();
$codeTasks = [];
foreach ($procedure->getTasks() as $taskCode => $previousTaskCode) {
if (\in_array($code, $previousTaskCode, true)) {
$codeTasks[] = $taskCode;
}
}
return $codeTasks;
}
private function checkTaskParallelFinished(Order $order)
{
/** @var Assignment $assignment */
foreach ($order->getAssignments() as $assignment) {
if ($assignment->isActive() && !$assignment->isFinished()) {
return false;
}
}
return true;
}
private function deActiveAssignment(Order $order)
{
/** @var Assignment $assignment */
foreach ($order->getAssignments() as $assignment) {
$assignment->setActive(false);
}
}
private function skipTask(Assignment $assignment)
{
$code = $this->findNextTask($assignment);
$nextTask = $this->taskRepository->findOneBy(['code' => $code]);
$previousTaskCode = [$assignment->getTask()->getCode()];
$this->addTask($assignment, $nextTask, $previousTaskCode);
}
private function findNextTask(Assignment $assignment)
{
/** @var Procedure $procedure */
$procedure = $assignment->getOrder()->getProcedure();
$taskList = array_keys($procedure->getTasks());
$index = array_search($assignment->getTask()->getCode(), $taskList, true);
$index += 2;
return $taskList[$index];
}
private function addTask(Assignment $assignment, Task $nextTask, array $previousTaskCode)
{
try {
$this->addEventFinish($assignment);
/** @var Order $order */
$order = $assignment->getOrder();
$assignment = new Assignment($order, $nextTask);
$assignment->setActive(true)
->setState(0);
$this->sortAssignment($assignment, $assignment->getTask()->getCode());
/** @var Assignment $assignmentPrevious */
$assignmentPrevious = $order->getAssignments()->last();
$deadLinePrevious = $assignmentPrevious->getWorkedAt();
if (null === $deadLinePrevious) {
$deadLinePrevious = new \DateTimeImmutable();
}
$deadlineGuide = $this->deadlineGuideRepository->findOneBy(['task' => $assignment->getTask(), 'type' => $order->getType()]);
$deadLineTime = null === $deadlineGuide ? 600 : $deadlineGuide->getTime();
$deadLine = $this->totalDeadlineAssignment->total(
new DateTimeImmutable(), $deadLineTime);
$assignment->setDeadline($deadLine);
if ($previousTaskCode) {
$predecessors = $order->getAssignments()->filter(function ($assignment) use ($previousTaskCode) {
/* @var Assignment $assignment */
return \in_array($assignment->getTask()->getCode(), $previousTaskCode, true);
});
foreach ($predecessors as $predecessor) {
$assignment->addPredecessor($predecessor);
}
}
if (Task::FINISH_TASK === $assignment->getTask()->getCode()) {
$assignment->setState(Assignment::STATE_FINISHED);
$assignment->setDeadline(new \DateTimeImmutable());
$assignment->setWaitingAt(new \DateTimeImmutable());
$assignment->deactivate();
$assignment->setFinishedAt(new \DateTimeImmutable());
$order->finish();
}
#TODO ASSIGNEE DESIGN
if (Task::FIX_DESIGN === $assignment->getTask()->getCode()) {
$order = $assignment->getOrder();
$lastAssignment = null;
foreach ($order->getAssignments() as $oldAssignment) {
$task = $oldAssignment->getTask()->getCode();
if(Task::START_DESIGN === $task){
$lastAssignment = $oldAssignment;
}
if (Task::FIX_DESIGN === $task) {
$finishedAt = $oldAssignment->getFinishedAt();
if ($finishedAt !== null && $finishedAt > $lastAssignment->getFinishedAt()) {
$lastAssignment = $oldAssignment;
}
}
}
if ($lastAssignment !== null) {
$assignment->assignTo($lastAssignment->getAssignee());
}
}
if (Task::FIX_DESIGN_CUS === $assignment->getTask()->getCode()) {
$order = $assignment->getOrder();
foreach ($order->getAssignments() as $oldAssignment) {
$task = $oldAssignment->getTask()->getCode();
if (Task::START_DESIGN_CUS === $task) {
$assignment->assignTo($oldAssignment->getAssignee());
}
}
}
$order->addAssignment($assignment);
} catch (\Exception $e) {
}
}
private function addLoopTask(Assignment $assignment, Task $taskReplacement)
{
$previousTaskCode = [$assignment->getTask()->getCode()];
$this->addTask($assignment, $taskReplacement, $previousTaskCode);
}
private function sortAssignment(Assignment $assignment, $taskCode)
{
/** @var array $assignmentLast */
$assignmentLast = $this->assignmentRepository->findLastPriority($taskCode);
$sort = 0;
if ($assignment->getOrder()->getIsNow()) {
$assignments = $this->assignmentRepository->assignmentFindNowTask([$taskCode]);
if (\count($assignments) > 0) {
$sort += $assignments[0]->getSort() ?? 0;
if (\count($assignmentLast) > 0 && $assignmentLast[0]->getSort() === $assignments[0]->getSort()) {
$sort += self::STEP_HALF_PART;
} else {
$sort += self::STEP_PRIORITY;
}
} else {
$assignments = $this->assignmentRepository->assignmentAssignedByTask([$taskCode]);
$sort = \count($assignments) > 0 ? ($assignments[0]->getSort() ?? 1) : 1 - self::STEP_HALF_PART;
}
} else {
$lastPriority = \count($assignmentLast) > 0 ? ($assignmentLast[0]->getSort() ?? 1) : 1;
if ($lastPriority > 0 && $lastPriority / floor($lastPriority) > 1) {
$sort = ceil($lastPriority);
} else {
$sort = $lastPriority + self::STEP_PRIORITY;
}
}
$assignment->setSort($sort);
}
private function addEventFinish(Assignment $assignment)
{
$taskDesign = [Task::START_DESIGN,
Task::START_DESIGN_CUS,
Task::FIX_DESIGN,
Task::FIX_DESIGN_CUS,
];
$orderTimeLine = $assignment->getOrder()->getOrderTimeLine();
if($orderTimeLine === null){
$orderTimeLine = new OrderTimeLine();
}
$assignment->getOrder()->setOrderTimeLine($orderTimeLine);
if (\in_array($assignment->getTask()->getCode(), $taskDesign, true)) {
if(\in_array($assignment->getTask()->getCode(), [Task::START_DESIGN,Task::START_DESIGN_CUS], true)){
$orderTimeLine->setStartDesign(new DateTimeImmutable());
}
$this->messageBus->dispatch(new FinishDesignSendTelegram($assignment));
$notification =
(new Notification())
->setData([
'content' => 'Đơn hàng đã hoàn tất '.$assignment->getTask()->getName(),
'deep_link' => 'https://amac.ctasolution.com/assignment?search='.$assignment->getOrder()->getId()
])
->setName('Cập nhật công việc đơn hàng '.$assignment->getOrder()->getId())
->setType('maket')
->setUser($assignment->getOrder()->getCreator())
;
$this->entityManager->persist($notification);
$this->entityManager->flush();
$this->messageBus->dispatch(new MessageNotification($notification));
}
else if(\in_array($assignment->getTask()->getCode(), ['thu_coc','thu_coc_cus'], true)){
$orderTimeLine->setDepositCompletionTime(new DateTimeImmutable());
}
else if (\in_array($assignment->getTask()->getCode(), ['kiem_thanh_pham'], true)) {
$orderTimeLine->setQualityAssuranceCompleteAt(new DateTimeImmutable());
$notification =
(new Notification())
->setData([
'content' => 'Đơn hàng đã hoàn tất '.$assignment->getTask()->getName(),
'deep_link' => 'https://amac.ctasolution.com/assignment?search='.$assignment->getOrder()->getId()
])
->setName('Cập nhật công việc đơn hàng '.$assignment->getOrder()->getId())
->setType('kiem_thanh_pham')
->setUser($assignment->getOrder()->getCreator())
;
$changeStatusOrderDelivery = new ChangeStatusOrderDelivery($this->entityManager);
$changeStatusOrderDelivery->change($assignment->getOrder());
// $autoCreateReceipt = new AutoCreateReceipt($this->entityManager);
// $autoCreateReceipt->addReceipt($assignment->getOrder());
$this->entityManager->persist($notification);
$this->entityManager->flush();
$this->messageBus->dispatch(new MessageNotification($notification));
$this->messageBus->dispatch(new UpdateRelationOrderProductMessage($assignment->getOrder()->getId()));
$this->messageBus->dispatch(new FinishProductOnProductionMessage($assignment->getOrder()->getId()));
}else if(\in_array($assignment->getTask()->getCode(), ['hoan_tat'], true)){
$orderTimeLine->setFinishedOrderAt(new DateTimeImmutable());
$this->entityManager->flush();
}
}
}