Covivo/mobicoop

View on GitHub
api/src/Incentive/Service/Manager/SubscriptionManager.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace App\Incentive\Service\Manager;

use App\Carpool\Entity\CarpoolProof;
use App\Carpool\Entity\Proposal;
use App\Carpool\Repository\CarpoolProofRepository;
use App\Communication\Service\NotificationManager;
use App\Incentive\Entity\Log\Log;
use App\Incentive\Entity\LongDistanceSubscription;
use App\Incentive\Entity\ShortDistanceSubscription;
use App\Incentive\Entity\Subscription;
use App\Incentive\Entity\Subscription\SpecificFields;
use App\Incentive\Event\SubscriptionNotReadyToVerifyEvent;
use App\Incentive\Interfaces\SubscriptionDefinitionInterface;
use App\Incentive\Repository\LongDistanceJourneyRepository;
use App\Incentive\Repository\LongDistanceSubscriptionRepository;
use App\Incentive\Repository\ShortDistanceSubscriptionRepository;
use App\Incentive\Resource\EecEligibility;
use App\Incentive\Resource\EecInstance;
use App\Incentive\Service\LoggerService;
use App\Incentive\Service\Provider\JourneyProvider;
use App\Incentive\Service\Provider\SubscriptionProvider;
use App\Incentive\Service\Stage\AutoRecommitSubscription;
use App\Incentive\Service\Stage\CreateSubscription;
use App\Incentive\Service\Stage\PatchSubscription;
use App\Incentive\Service\Stage\ProofInvalidate;
use App\Incentive\Service\Stage\ProofRecovery;
use App\Incentive\Service\Stage\ResetSubscription;
use App\Incentive\Service\Stage\VerifySubscription;
use App\Incentive\Service\Validation\SubscriptionValidation;
use App\Incentive\Service\Validation\UserValidation;
use App\Incentive\Validator\CarpoolProofValidator;
use App\Incentive\Validator\SubscriptionValidator;
use App\Payment\Entity\CarpoolPayment;
use App\Payment\Repository\CarpoolItemRepository;
use App\User\Entity\User;
use App\User\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class SubscriptionManager extends MobConnectManager
{
    public const VERIFICATION_STATUS_PENDING = 0;
    public const VERIFICATION_STATUS_ENDED = 1;

    /**
     * @var EecInstance
     */
    protected $_eecInstance;

    /**
     * @var EventDispatcherInterface
     */
    protected $_eventDispatcher;

    /**
     * @var CarpoolItemRepository
     */
    protected $_carpoolItemRepository;

    /**
     * @var CarpoolProofRepository
     */
    protected $_carpoolProofRepository;

    /**
     * @var NotificationManager
     */
    private $_notificationManager;

    /**
     * @var LongDistanceSubscriptionRepository
     */
    private $_longDistanceSubscriptionRepository;

    /**
     * @var ShortDistanceSubscriptionRepository
     */
    private $_shortDistanceSubscriptionRepository;

    /**
     * @var UserRepository
     */
    private $_userRepository;

    /**
     * @var SubscriptionValidation
     */
    private $_subscriptionValidation;

    public function __construct(
        EntityManagerInterface $em,
        EventDispatcherInterface $eventDispatcher,
        NotificationManager $notificationManager,
        SubscriptionValidation $subscriptionValidation,
        UserValidation $userValidation,
        LoggerService $loggerService,
        InstanceManager $instanceManager,
        TimestampTokenManager $timestampTokenManager,
        CarpoolItemRepository $carpoolItemRepository,
        CarpoolProofRepository $carpoolProofRepository,
        LongDistanceJourneyRepository $longDistanceJourneyRepository,
        LongDistanceSubscriptionRepository $longDistanceSubscriptionRepository,
        ShortDistanceSubscriptionRepository $shortDistanceSubscriptionRepository,
        UserRepository $userRepository
    ) {
        parent::__construct($em, $instanceManager, $loggerService);

        $this->_eventDispatcher = $eventDispatcher;
        $this->_notificationManager = $notificationManager;

        $this->_timestampTokenManager = $timestampTokenManager;
        $this->_carpoolItemRepository = $carpoolItemRepository;
        $this->_carpoolProofRepository = $carpoolProofRepository;
        $this->_longDistanceJourneyRepository = $longDistanceJourneyRepository;
        $this->_longDistanceSubscriptionRepository = $longDistanceSubscriptionRepository;
        $this->_shortDistanceSubscriptionRepository = $shortDistanceSubscriptionRepository;
        $this->_userRepository = $userRepository;
        $this->_subscriptionValidation = $subscriptionValidation;
        $this->_userValidation = $userValidation;
        $this->_eecInstance = $instanceManager->getEecInstance();
    }

    /**
     * STEP 5 - Creating incentives requests.
     *
     * For the authenticated user, if needed, creates the CEE sheets.
     */
    public function createSubscriptions(User $user)
    {
        if (!$this->_instanceManager->isEecServiceAvailable()) {
            return;
        }

        $this->setDriver($user);

        $this->_createSubscription(Subscription::TYPE_SHORT);
        $this->_createSubscription(Subscription::TYPE_LONG);
    }

    public function getUserEECEligibility(User $user): EecEligibility
    {
        $this->setDriver($user);

        $userEligibility = new EecEligibility($user);

        $userEligibility->setLongDistanceJourneysNumber(count($this->getEECCompliantProofsObtainedSinceDate(LongDistanceSubscription::SUBSCRIPTION_TYPE)));
        $userEligibility->setShortDistanceJourneysNumber(count($this->getEECCompliantProofsObtainedSinceDate(ShortDistanceSubscription::SUBSCRIPTION_TYPE)));
        $userEligibility->setLongDistanceDrivingLicenceNumberDoublon($this->_longDistanceSubscriptionRepository->getDuplicatePropertiesNumber('drivingLicenceNumber', $user->getDrivingLicenceNumber()));
        $userEligibility->setLongDistancePhoneDoublon($this->_longDistanceSubscriptionRepository->getDuplicatePropertiesNumber('telephone', $user->getTelephone()));
        $userEligibility->setShortDistanceDrivingLicenceNumberDoublon($this->_shortDistanceSubscriptionRepository->getDuplicatePropertiesNumber('drivingLicenceNumber', $user->getDrivingLicenceNumber()));
        $userEligibility->setShortDistancePhoneDoublon($this->_shortDistanceSubscriptionRepository->getDuplicatePropertiesNumber('telephone', $user->getTelephone()));

        return $userEligibility;
    }

    /**
     * Set EEC subscription timestamps.
     */
    public function setUserSubscriptionTimestamps(string $subscriptionType, int $subscriptionId)
    {
        $subscription = self::LONG_SUBSCRIPTION_TYPE === $subscriptionType
            ? $this->_em->getRepository(LongDistanceSubscription::class)->find($subscriptionId)
            : $this->_em->getRepository(ShortDistanceSubscription::class)->find($subscriptionId);

        if (is_null($subscription)) {
            throw new \LogicException('The subscription was not found');
        }

        if (!$this->_subscriptionValidation->isSubscriptionValidForTimestampsProcess($subscription)) {
            throw new \LogicException('Subscription cannot be processed at this time');
        }

        $this->_loggerService->log('Performing the timestamping process');
        $this->setDriver($subscription->getUser());

        $this->_timestampTokenManager->setMissingSubscriptionTimestampTokens($subscription, Log::TYPE_VERIFY);

        $this->_em->flush();

        $response = 'The timestamping process is complete';

        $this->_loggerService->log($response);

        return $response;
    }

    public function updateSubscriptionsAddress(User $user)
    {
        $this->setDriver($user);

        if (!is_null($this->getDriver()->getLongDistanceSubscription())) {
            $this->getDriver()->getLongDistanceSubscription()->updateAddress();
        }

        if (!is_null($this->getDriver()->getShortDistanceSubscription())) {
            $this->getDriver()->getShortDistanceSubscription()->updateAddress();
        }

        $this->_em->flush();
    }

    public function updateSubscriptionDrivingLicenceNumber(User $user)
    {
        $stage = new PatchSubscription($this->_em, $this->_eecInstance, $user, SpecificFields::DRIVING_LICENCE_NUMBER);
        $stage->execute();
    }

    public function updateSubscriptionPhone(User $user)
    {
        $stage = new PatchSubscription($this->_em, $this->_eecInstance, $user, SpecificFields::PHONE_NUMBER);
        $stage->execute();
    }

    public function updateTimestampTokens(User $user): User
    {
        $this->setDriver($user);

        if (!is_null($this->getDriver()->getLongDistanceSubscription())) {
            $this->_timestampTokenManager->setSubscriptionTimestampTokens($this->getDriver()->getLongDistanceSubscription());
        }
        if (!is_null($this->getDriver()->getShortDistanceSubscription())) {
            $this->_timestampTokenManager->setSubscriptionTimestampTokens($this->getDriver()->getShortDistanceSubscription());
        }

        $this->_em->flush();

        return $this->getDriver();
    }

    /**
     * Set missing subscription timestamps.
     *
     * @param LongDistanceSubscription|ShortDistanceSubscription $subscription
     *
     * @return bool Returns if getting tokens was successful
     */
    public function setTimestamps($subscription): bool
    {
        $this->setDriver($subscription->getUser());

        $this->_timestampTokenManager->setMissingSubscriptionTimestampTokens($subscription, Log::TYPE_VERIFY);

        $this->_em->flush();

        return false;
    }

    public function processingVersionTransitionalPeriods()
    {
        /**
         * @var SubscriptionDefinitionInterface[]
         */
        $definitions = array_merge(LongDistanceSubscription::getAvailableDefinitions(), ShortDistanceSubscription::getAvailableDefinitions());

        foreach ($definitions as $definition) {
            if (!$definition::isDeadlineOver()) {
                continue;
            }

            $definition::manageTransition($this->_em, $this->_longDistanceSubscriptionRepository);
        }
    }

    /**
     * @param LongDistanceSubscription|ShortDistanceSubscription $subscription
     */
    public function resetSubscription($subscription): void
    {
        $stage = new ResetSubscription($this->_em, $subscription);
        $stage->execute();
    }

    /**
     * STEP 9 - Commit a subscription.
     *
     * @param CarpoolProof|Proposal                              $referenceObject
     * @param LongDistanceSubscription|ShortDistanceSubscription $subscription
     */
    public function commitSubscription($subscription, $referenceObject, bool $pushOnlyMode = false, bool $noResetMode = false): void
    {
        if (!$noResetMode && $subscription->isCommited()) {
            $stage = new ResetSubscription($this->_em, $subscription);
            $stage->execute();
        }

        $commitClass = $subscription instanceof LongDistanceSubscription
            ? 'App\\Incentive\\Service\\Stage\\CommitLDSubscription'
            : 'App\\Incentive\\Service\\Stage\\CommitSDSubscription';

        $stage = new $commitClass($this->_em, $this->_timestampTokenManager, $this->_eecInstance, $subscription, $referenceObject, $pushOnlyMode);
        $stage->execute();
    }

    public function autoRecommitSubscriptions(): void
    {
        // Processing subscriptions that simply need to be reset
        $sdSubscriptions = SubscriptionProvider::getSubscriptionsCanBeReset($this->_shortDistanceSubscriptionRepository->getSubscriptionsReadyToBeRecommited(), true);
        $ldSubscriptions = SubscriptionProvider::getSubscriptionsCanBeReset($this->_longDistanceSubscriptionRepository->getSubscriptionsReadyToBeRecommited(), true);

        foreach (array_merge($sdSubscriptions, $ldSubscriptions) as $subscription) {
            $this->resetSubscription($subscription);
        }

        // Processing subscriptions that need to be recommit
        $sdSubscriptions = SubscriptionProvider::getSubscriptionsCanBeReset($this->_shortDistanceSubscriptionRepository->getSubscriptionsReadyToBeRecommited());
        $ldSubscriptions = SubscriptionProvider::getSubscriptionsCanBeReset($this->_longDistanceSubscriptionRepository->getSubscriptionsReadyToBeRecommited());

        foreach (array_merge($sdSubscriptions, $ldSubscriptions) as $subscription) {
            $this->recommitSubscription($subscription);
        }
    }

    public function recommitSubscription($subscription): void
    {
        $stage = new AutoRecommitSubscription($this->_em, $this->_timestampTokenManager, $this->_eecInstance, $subscription);
        $stage->execute();
    }

    /**
     * STEP 17 - Validate a subscription.
     *
     * @param CarpoolPayment|CarpoolProof $referenceObject
     */
    public function validateSubscription($referenceObject, bool $pushOnlyMode = false): void
    {
        $validateClass = $referenceObject instanceof CarpoolPayment
            ? 'App\\Incentive\\Service\\Stage\\ValidateLDSubscription'
            : 'App\\Incentive\\Service\\Stage\\ProofValidate';

        $stage = new $validateClass($this->_em, $this->_longDistanceJourneyRepository, $this->_timestampTokenManager, $this->_eecInstance, $referenceObject, $pushOnlyMode);
        $stage->execute();
    }

    /**
     * Step 17 - Unvalidate proof.
     */
    public function invalidateProof(CarpoolProof $carpoolProof): void
    {
        if (CarpoolProofValidator::isEecCompliant($carpoolProof)) {
            return;
        }

        $journeyProvider = new JourneyProvider($this->_longDistanceJourneyRepository);
        $journey = $journeyProvider->getJourneyFromCarpoolProof($carpoolProof);

        if (is_null($journey)) {
            return;
        }

        $stage = new ProofInvalidate($this->_em, $this->_timestampTokenManager, $this->_eecInstance, $journey);
        $stage->execute();
    }

    public function proofsRecover(string $subscriptionType, ?int $userId): void
    {
        if (!is_null($userId)) {
            $user = $this->_em->getRepository(User::class)->find($userId);

            if (is_null($user)) {
                throw new NotFoundHttpException('The requested user was not found');
            }

            $stage = new ProofRecovery($this->_em, $this->_carpoolItemRepository, $this->_carpoolProofRepository, $this->_longDistanceJourneyRepository, $this->_timestampTokenManager, $this->_eecInstance, $user, $subscriptionType);
            $stage->execute();

            return;
        }
    }

    /**
     * STEP 20 - Verify ready subscriptions.
     */
    public function verifySubscriptions()
    {
        $subscriptions = array_merge(
            $this->_shortDistanceSubscriptionRepository->getReadyForVerify(),
            $this->_longDistanceSubscriptionRepository->getReadyForVerify()
        );

        foreach ($subscriptions as $key => $subscription) {
            $this->_verifySubscription($subscription);
        }
    }

    /**
     * STEP 20 - Vérify a subscription from it's type and ID.
     */
    public function verifySubscriptionFromType(?string $subscriptionType, ?string $subscriptionId)
    {
        if (is_null($subscriptionType) || is_null($subscriptionId)) {
            return $this->verifySubscriptions();
        }

        $this->_subscriptionValidation->checkSubscriptionTypeValidity($subscriptionType);

        $this->_subscriptionValidation->checkSubscriptionIdValidity($subscriptionId);

        $subscriptionId = intval($subscriptionId);

        switch ($subscriptionType) {
            case 'long':
                $repository = $this->_em->getRepository(LongDistanceSubscription::class);

                break;

            case 'short':
                $repository = $this->_em->getRepository(ShortDistanceSubscription::class);

                break;
        }

        $subscription = $repository->find($subscriptionId);

        if (is_null($subscription)) {
            throw new NotFoundHttpException('The requested subscription was not found');
        }

        $this->_verifySubscription($subscription);
    }

    /**
     * @param LongDistanceSubscription|ShortDistanceSubscription $subscription
     */
    public function subscriptionNotReadyToVerify($subscription)
    {
        if (
            !SubscriptionValidator::isAddressValid($subscription)
            || !SubscriptionValidator::isPhoneNumberValid($subscription)
            || !SubscriptionValidator::isDrivingLicenceNumberValid($subscription)
        ) {
            $this->_notificationManager->notifies('eec_subscription_not_ready_to_verify', $subscription->getUser(), $subscription);
        }
    }

    /**
     * STEP 5 - Create a subscription from it's type.
     *
     * @throws \LogicException
     */
    protected function _createSubscription(string $subscriptionType): void
    {
        if (!Subscription::isTypeAllowed($subscriptionType)) {
            throw new \LogicException('eec_subscriptionType_unallowed');
        }

        if (!is_null($this->getDriver()->{'get'.ucfirst($subscriptionType).'DistanceSubscription'}())) {
            throw new \LogicException('eec_subscriptionType_'.$subscriptionType.'_allready.subscribed');
        }

        if (
            !$this->_instanceManager->{'is'.ucfirst($subscriptionType).'SubscriptionAvailable'}()
            || !$this->isDriverAccountReadyForSubscription($subscriptionType)
        ) {
            return;
        }

        $stage = new CreateSubscription($this->_em, $this->_timestampTokenManager, $this->_loggerService, $this->_eecInstance, $this->_driver, $subscriptionType);
        $stage->execute();
    }

    /**
     * STEP 20 - Vérify a subscription from it's ID.
     *
     * @param LongDistanceSubscription|ShortDistanceSubscription $subscription
     */
    protected function _verifySubscription($subscription): void
    {
        if (SubscriptionValidator::isReadyToVerify($subscription)) {
            $stage = new VerifySubscription($this->_em, $this->_timestampTokenManager, $this->_eecInstance, $subscription);
            $stage->execute();

            return;
        }

        $event = new SubscriptionNotReadyToVerifyEvent($subscription);
        $this->_eventDispatcher->dispatch(SubscriptionNotReadyToVerifyEvent::NAME, $event);
    }
}