<?php
namespace App\Listener\NonMember;
use App\Controller\Ajax\TutorialController;
use App\Controller\MySief\ContractController;
use App\Controller\MySief\SanctionsController;
use App\Controller\MySief\TransferController;
use App\Entity\LeadRegistrant\Agreement;
use App\Entity\LegalEntity;
use App\Entity\LegalEntity\Contract\AbstractContract;
use App\Entity\LegalEntity\Contract\Amendment;
use App\Entity\LegalEntity\Contract\Contract;
use App\Entity\LegalEntity\Contract\LicenseAgreement;
use App\Entity\Notification;
use App\Entity\Notification\NonMember\ContractsAddedNotification;
use App\Entity\Notification\NonMember\ContractsPendingNotification;
use App\Entity\StaffRequest;
use App\Entity\Substance\Definition;
use App\Entity\User;
use App\Entity\Worker;
use App\FormType\TonnageBandType;
use App\Service\DossierCalculator;
use App\Service\StaffTransferCreator;
use App\Workers\AgreementGeneratorWorker;
use App\Workers\AgreementGeneratorWorkerConfig;
use App\Workers\FinishTransferWorker;
use App\Workers\FinishTransferWorkerConfig;
use App\Workers\LicenseAgreementAmendmentWorker;
use App\Workers\LicenseAgreementAmendmentWorkerConfig;
use App\Workers\NonPetroleumWorker;
use App\Workers\NonPetroleumWorkerConfig;
use App\Workers\ScheduleFourWorker;
use App\Workers\ScheduleFourWorkerConfig;
use App\Workers\ScheduleThreeWorker;
use App\Workers\ScheduleThreeWorkerConfig;
use App\Workers\SendAcceptedContractWorker;
use App\Workers\SendAcceptedContractWorkerConfig;
use App\Workers\StatusWorker;
use App\Workers\StatusWorkerConfig;
use App\Workers\TokenRenewalCheckWorker;
use App\Workers\TokenRenewalCheckWorkerConfig;
use App\Workers\TonnageBandWorker;
use App\Workers\TonnageBandWorkerConfig;
use App\Workers\WorkerAlreadyQueuedException;
use Carbon\Carbon;
use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
use Symfony\Component\HttpKernel\Controller\ErrorController;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ContractListener implements EventSubscriberInterface, WarmableInterface
{
/**
* @var TokenStorageInterface
*/
private $storage;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var RouterInterface
*/
private $router;
/**
* @var ContractController
*/
private $controller;
/**
* @var ValidatorInterface
*/
private $validator;
/**
* @var DossierCalculator
*/
private $dossierCalculator;
private StaffTransferCreator $staffTransferCreator;
/**
* @var bool
*/
private static $enabled = true;
public function __construct(TokenStorageInterface $storage, EntityManagerInterface $em, RouterInterface $router, ContractController $controller, ContainerInterface $container, ValidatorInterface $validator, DossierCalculator $dossierCalculator, StaffTransferCreator $staffTransferCreator)
{
$this->storage = $storage;
$this->em = $em;
$this->router = $router;
$this->controller = $controller;
$this->controller->setContainer($container);
$this->validator = $validator;
$this->dossierCalculator = $dossierCalculator;
$this->staffTransferCreator = $staffTransferCreator;
}
private function computeChanges($entity)
{
$uow = $this->em->getUnitOfWork();
$class = $this->em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($class, $entity);
}
public function getSubscribedEvents()
{
return [Events::postPersist, Events::postUpdate, Events::postRemove, Events::onFlush];
}
public function postPersist(LifecycleEventArgs $args)
{
if (!$args->getEntity() instanceof AbstractContract) {
return;
}
$this->processContract($args->getEntityManager(), $args->getEntity());
}
public function postUpdate(LifecycleEventArgs $args)
{
if (!$args->getEntity() instanceof AbstractContract) {
return;
}
$this->processContract($args->getEntityManager(), $args->getEntity());
}
public function postRemove(LifecycleEventArgs $args)
{
if (!$args->getEntity() instanceof AbstractContract) {
return;
}
$this->processContract($args->getEntityManager(), $args->getEntity(), true);
}
public function onFlush(OnFlushEventArgs $args)
{
if (!self::$enabled) {
return;
}
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$md = $em->getClassMetadata(Worker::class);
foreach ($uow->getScheduledEntityUpdates() as $contract) {
if (!$contract instanceof AbstractContract) {
continue;
}
$changes = $uow->getEntityChangeSet($contract);
if (!array_key_exists('accepted', $changes)) {
continue;
}
if ($contract instanceof LicenseAgreement) {
$reason = $changes['accepted'][1]
? 'License agreement marked as accepted'
: 'License agreement marked as not accepted';
foreach ($contract->getLegalEntity()->getOrderLines() as $line) {
/** @var User $user */
$user = $this->storage->getToken()->getUser();
// Status worker (calculate status for all order lines)
$worker = (new Worker(StatusWorker::class, (new StatusWorkerConfig())
->setOrderLineId($line->getId())
->setReason($reason)
->setUserId($user->getId())
))->setDelay(20);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
// Token renewal check worker
try {
$worker = $this->em->getRepository(Worker::class)->createWorker(TokenRenewalCheckWorker::class, (new TokenRenewalCheckWorkerConfig())
->setEcId($line->getSubstance()->getEc()->getId()))
->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate license agreement
try {
$worker = $this->em->getRepository(Worker::class)->createWorker(AgreementGeneratorWorker::class, (new AgreementGeneratorWorkerConfig())
->setType(AgreementGeneratorWorkerConfig::TYPE_NON_MEMBER)
->setImport(false)
->setReset(false)
->setNotify(false)
->setEntityId($contract->getLegalEntity()->getId())
)->setDelay(10);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate schedule 4 amendment
if ($contract instanceof Amendment && Amendment::TYPE_SCHEDULE_4 === $contract->getType()) {
try {
$worker = $this->em->getRepository(Worker::class)->createWorker(ScheduleFourWorker::class, (new ScheduleFourWorkerConfig())
->setId($contract->getId()))
->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate non-petroleum data sharing agreement
if ($contract instanceof LegalEntity\Contract\NonPetroleumAgreement) {
try {
$config = (new NonPetroleumWorkerConfig())
->setId($contract->getId());
if ($contract->isAccepted() && $contract->getAcceptedAt()->format('d-m-Y') === carbon::today()->format('d-m-Y')) {
$config->setAccepted(true);
}
$worker = $this->em->getRepository(Worker::class)->createWorker(NonPetroleumWorker::class, $config)
->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate License agreement amendment
if ($contract instanceof Amendment && Amendment::TYPE_LICENSE_AGREEMENT === $contract->getType()) {
try {
$worker = $em->getRepository(Worker::class)->createWorker(LicenseAgreementAmendmentWorker::class, (new LicenseAgreementAmendmentWorkerConfig())
->setId($contract->getId()))
->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate schedule 3 amendment
if ($contract instanceof Amendment && Amendment::TYPE_SCHEDULE_3 === $contract->getType()) {
/** @var LegalEntity\Contract\Schedule3Amendment $contract */
$linkedContract = $contract->getAmendment();
if ($linkedContract->isAccepted()) {
$request = $this->em->getRepository(StaffRequest::class)->find($contract->getTransferRequest());
$substances = [];
$oldSubstances = [];
if ($request instanceof StaffRequest\TransferRequest) {
foreach ($request->getSubstances() as $substance) {
$substances[] = $em->getRepository(Definition::class)->find($substance);
}
foreach ($request->getLegalEntity()->getSubstances() as $substance) {
$oldSubstances[] = $em->getRepository(Definition::class)->find($substance);
}
if ($substances === $oldSubstances) {
if (!$request->getLegalEntity()->getRemarks()->isEmpty()) {
foreach ($request->getLegalEntity()->getRemarks() as $remark) {
$request->getNewLegalEntity()->getRemarks()->add($remark);
}
}
}
if ($request->getMember()) {
$md = $this->em->getClassMetadata(Worker::class);
// Queue the transfer worker
try {
$worker = $this->em->getRepository(Worker::class)->createWorker(
FinishTransferWorker::class,
(new FinishTransferWorkerConfig())
->setTransferRequest($request->getId())
)->setDelay(2);
$this->em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
} else {
/**
* @var StaffRequest\TransferRequest $request
*/
$steps = $request->getProgress()->getSteps();
$steps[] = 'awaiting-invoice-creation';
$request->getProgress()
->setStatus('primary')
->setPercentage(66)
->setStatusText('Awaiting invoice creation, ')
->setWaiting('staff interaction required')
->setSteps($steps);
if (!$request->getNewLegalEntity()->getLicenseAgreement()->isAccepted()) {
// Queue the license agreement generator
try {
$worker = $em->getRepository(Worker::class)->createWorker(
AgreementGeneratorWorker::class,
(new AgreementGeneratorWorkerConfig())
->setType(AgreementGeneratorWorkerConfig::TYPE_NON_MEMBER)
->setReset(false)
->setEntityId($request->getNewLegalEntity()->getId())
)->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
$this->em->persist($request->getNewLegalEntity());
}
}
}
try {
$config = (new ScheduleThreeWorkerConfig())
->setId($contract->getId());
if ($contract->isAccepted()) {
$config->setAccepted(true);
}
$worker = $this->em->getRepository(Worker::class)->createWorker(ScheduleThreeWorker::class, $config)->setDelay(2);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
try {
$linkedWorker = $this->em->getRepository(Worker::class)->createWorker(ScheduleThreeWorker::class, (new ScheduleThreeWorkerConfig())
->setId($linkedContract->getId()))->setDelay(2);
$em->persist($linkedWorker);
$uow->computeChangeSet($md, $linkedWorker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Regenerate tonnageBandAmendment
if ($contract instanceof Amendment && Amendment::TYPE_TONNAGEBAND === $contract->getType()) {
if ($changes['accepted'][1]) {
$substancemd = $em->getClassMetadata(Definition::class);
$remarkmd = $em->getClassMetadata(LegalEntity\Remark::class);
$amendment = $contract;
$data = $amendment->getData();
foreach ($data['substances'] as $substanceId) {
$substance = $this->em->getRepository(Definition::class)->find($substanceId);
/** @var LegalEntity $entity */
$entity = $substance->getLegalEntity();
$bands = TonnageBandType::getTonnageBands(true);
$remark = new LegalEntity\Remark();
$user = $this->em->getRepository(User::class)->find(3);
$contents = sprintf('Tonnage band change for EC %s from [%s] to [%s]', $substance->getEc()->getEc(), $bands[$substance->getPreviousTonnageBand()], $bands[$substance->getNewTonnageBand()]);
$remark->setContents($contents)
->setDate(carbon::now())
->setLegalEntity($entity)
->setAuthor($user);
$entity->getRemarks()->add($remark);
$this->em->persist($remark);
$uow->computeChangeSet($remarkmd, $remark);
$substance->setTonnageChanged(false);
$substance->setTonnageBand($substance->getNewTonnageBand());
$substance->setNewTonnageBand(null);
$substance->setPreviousTonnageBand(null);
$substance->getTonnageBandProof()->setAmendmentId(null);
$substance->getTonnageBandProof()->setTonnageInvoiceId(null);
$this->em->persist($substance);
$uow->computeChangeSet($substancemd, $substance);
}
}
try {
$worker = $this->em->getRepository(Worker::class)->createWorker(TonnageBandWorker::class, (new TonnageBandWorkerConfig())
->setAmendment($contract->getId()))
->setDelay(10);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
// Send copy of contract to authorized representative
try {
/** @var User $user */
$user = $this->storage->getToken()->getUser();
$worker = ($this->em->getRepository(Worker::class)->createWorker(SendAcceptedContractWorker::class, (new SendAcceptedContractWorkerConfig())
->setUserId($user->getId())
->setContractId($contract->getId())
->setLicenseAgreement($contract instanceof LicenseAgreement)
))->setDelay(300);
$em->persist($worker);
$uow->computeChangeSet($md, $worker);
} catch (WorkerAlreadyQueuedException $e) {
}
}
}
/**
* @param AbstractContract|Contract|LicenseAgreement $contract
*/
private function processContract(EntityManagerInterface $em, AbstractContract $contract, bool $removed = false)
{
if (!self::$enabled) {
return;
}
$repository = $em->getRepository(Notification::class);
if ($contract instanceof Agreement) {
return;
}
if ($contract instanceof LegalEntity\Contract\ModifiedLicenseAgreement) {
/** @var LegalEntity\Contract\ModifiedLicenseAgreement $contract */
$user = $contract->getCompany()->getUser();
} else {
/** @var Amendment $contract */
$user = $contract->getLegalEntity()->getUser();
}
if ($contract instanceof Amendment && Amendment::TYPE_SCHEDULE_3 === $contract->getType()) {
if ($contract instanceof LegalEntity\Contract\Schedule3Amendment) {
$linkedContract = $contract->getAmendment();
if ($contract->isAccepted() && $linkedContract->isAccepted()) {
$request = $this->em->getRepository(StaffRequest::class)->find($contract->getTransferRequest());
$repository = $this->em->getRepository(Notification::class);
/** @var StaffRequest\TransferRequest $request */
if (!$request->isCompleted()) {
if ($request->isTransferStaffRequest()) {
if (!$request->getMember()) {
$notification = (new Notification\Staff\StaffTransferAcceptedNotification())
->setup($request->getLegalEntity()->getUser(), $request);
$repository->removeByInternalCode($notification);
if (!$removed) {
$repository->notifyStaff($notification);
}
}
} else {
$notification = (new Notification\NonMember\TransferAcceptedNotification())
->setup($request->getLegalEntity()->getUser(), $request);
$repository->removeByInternalCode($notification);
if (!$removed) {
$repository->notifyStaff($notification);
}
}
$request->setContractAccepted(true);
$request->setAccepted(true);
}
}
}
}
if ($contract->isAcceptanceRequired()) {
$notification = (new ContractsPendingNotification())->setup($em, $user);
// Remove the existing notification (if there is one)
$repository->removeByInternalCode($notification);
// Create a new one if one or more contracts require acceptance
/** @var ContractsPendingNotification $notification */
if (!$removed && $notification->getPending() > 0) {
$repository->notifyUser($notification);
}
} else {
$notification = (new ContractsAddedNotification())->setup($em, $user);
// Remove the existing notification (if there is one)
$repository->removeByInternalCode($notification);
// Create a new one if one or more contracts require acceptance
/** @var ContractsAddedNotification $notification */
if (!$removed && $notification->getNew() > 0) {
$repository->notifyUser($notification);
}
}
}
public function onKernelController(ControllerEvent $event)
{
if (!self::$enabled) {
return;
}
$token = $this->storage->getToken();
if (!$token instanceof TokenInterface) {
return;
}
$user = $token->getUser();
if (!$user instanceof User) {
return;
}
$entities = $this->em->getRepository(LegalEntity::class)->findBy(['user' => $user]);
if (count($entities) > 0) {
foreach ($entities as $entity) {
$violations = $this->validator->validate($entity);
if ($violations->count() > 0) {
return;
}
}
}
// Count the number of pending contracts
$pending = (int) $this->em->createQueryBuilder()
->from(Contract::class, 'contract')
->select('COUNT(contract)')
->join('contract.legalEntity', 'e')
->where('e.user = :user')
->setParameter('user', $user)
->andWhere('contract.accepted = :false')
->setParameter('false', false)
->andWhere('contract.file.path IS NOT NULL')
->andWhere('contract.forceAcceptance = TRUE')
->andWhere('e.status = :status')
->setParameter('status', LegalEntity::STATUS_APPROVED)
->getQuery()->useQueryCache(true)->getSingleScalarResult();
if (0 === $pending) {
return;
}
$controller = $event->getController();
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof TutorialController) {
return;
}
if ($controller[0] instanceof SanctionsController) {
return;
}
if (!isset($entity)) {
throw new \Exception('entity is not set');
}
if (!$entity->isInactive()) {
if ($controller[0] instanceof ErrorController) {
// We always need to show error pages
$event->getRequest()->attributes->remove('contract_enforcement');
} elseif ($controller[0] instanceof ContractController || $controller[0] instanceof TransferController) {
// Notice needs to be shown, but no redirect required
$event->getRequest()->attributes->set('contract_enforcement', false);
} else {
$redirectUrl = $this->router->generate('my-sief-page.contracts.index');
$event->setController(function () use ($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
}
}
public function onKernelResponse(ResponseEvent $event)
{
}
public static function disable()
{
self::$enabled = false;
}
public static function enable()
{
self::$enabled = true;
}
public function warmUp($cacheDir): array
{
return [TokenStorageInterface::class, EntityManagerInterface::class, RouterInterface::class, ContractController::class, ContainerInterface::class, ValidatorInterface::class, DossierCalculator::class];
}
}