api/src/Solidary/Service/SolidarySolutionManager.php
<?php
/**
* Copyright (c) 2020, MOBICOOP. All rights reserved.
* This project is dual licensed under AGPL and proprietary licence.
***************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <gnu.org/licenses>.
***************************
* Licence MOBICOOP described in the file
* LICENSE
**************************/
namespace App\Solidary\Service;
use App\Carpool\Entity\Ask;
use App\Carpool\Entity\AskHistory;
use App\Carpool\Entity\Criteria;
use App\Solidary\Entity\SolidaryAsk;
use App\Solidary\Entity\SolidaryAskHistory;
use App\Solidary\Entity\SolidaryFormalRequest;
use App\Solidary\Entity\SolidarySolution;
use App\Solidary\Exception\SolidaryException;
use App\Solidary\Repository\SolidaryMatchingRepository;
use App\Solidary\Repository\SolidarySolutionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
/**
* @author Maxime Bardot <maxime.bardot@mobicoop.org>
*/
class SolidarySolutionManager
{
private $entityManager;
private $security;
private $solidarySolutionRepository;
public function __construct(EntityManagerInterface $entityManager, Security $security, SolidarySolutionRepository $solidarySolutionRepository)
{
$this->entityManager = $entityManager;
$this->security = $security;
$this->solidarySolutionRepository = $solidarySolutionRepository;
}
/**
* Create a SolidarySolution
*
* @param SolidarySolution $solidarySolution
* @return SolidarySolution|null
*/
public function createSolidarySolution(SolidarySolution $solidarySolution): ?SolidarySolution
{
// If the solidaryMatching have already a solidarySolution we throw an error because a solidaryMatching can only have ONE solidarySolution
if (!is_null($solidarySolution->getSolidaryMatching()->getSolidarySolution())) {
throw new SolidaryException(SolidaryException::SOLIDARY_MATCHING_ALREADY_USED);
}
// If there is a SolidaryUser, it has to be a volunteer
if (!is_null($solidarySolution->getSolidaryMatching()->getSolidaryUser()) && !$solidarySolution->getSolidaryMatching()->getSolidaryUser()->isVolunteer()) {
throw new SolidaryException(SolidaryException::IS_NOT_VOLUNTEER);
}
// Can't have both matching et solidaryUser
if (!is_null($solidarySolution->getSolidaryMatching()->getSolidaryUser()) && !is_null($solidarySolution->getSolidaryMatching()->getMatching())) {
throw new SolidaryException(SolidaryException::CANT_HAVE_BOTH);
}
// We get the Solidary of this SolidaryMatching and set it for the SolidarySolution ((yeah, it a shortcut for the model)
$solidarySolution->setSolidary($solidarySolution->getSolidaryMatching()->getSolidary());
$this->entityManager->persist($solidarySolution);
$this->entityManager->flush();
return $solidarySolution;
}
/**
* Make a formal request for a SolidarySolution
*
* @param SolidaryFormalRequest $solidarySolution
* @return SolidaryFormalRequest|null
*/
public function makeFormalRequest(SolidaryFormalRequest $solidaryFormalRequest) : ?SolidaryFormalRequest
{
$solidarySolution = $solidaryFormalRequest->getSolidarySolution();
// Get the solidaryAsk
// Check if there is a SolidaryAsk
$solidaryAsk = $solidarySolution->getSolidaryAsk();
if (is_null($solidaryAsk)) {
throw new SolidaryException(SolidaryException::NO_SOLIDARY_ASK);
}
// Check if the SolidaryAsk has the right status
if ($solidaryAsk->getStatus()!==SolidaryAsk::STATUS_ASKED) {
throw new SolidaryException(SolidaryException::BAD_SOLIDARY_ASK_STATUS_FOR_FORMAL);
}
// Update the SolidaryAsk Criteria
$solidaryAskCriteria = $this->updateCriteriaFromFormalRequest($solidaryFormalRequest, $solidaryAsk->getCriteria());
$this->entityManager->persist($solidaryAskCriteria);
$this->entityManager->flush();
// Update the status of the SolidaryAsk and add a SolidaryAskHistory
$solidaryAsk->setStatus(SolidaryAsk::STATUS_PENDING);
$this->entityManager->persist($solidaryAsk);
$this->entityManager->flush();
$solidaryAskHistory = new SolidaryAskHistory();
$solidaryAskHistory->setStatus($solidaryAsk->getStatus());
$solidaryAskHistory->setSolidaryAsk($solidaryAsk);
$this->entityManager->persist($solidaryAskHistory);
$this->entityManager->flush();
// If it's a Carpool Ask, we need to treat also the real Ask
// Ask Criteria if it exists
if (!is_null($solidaryAsk->getAsk())) {
// Update the Criteria
$askCriteria = $this->updateCriteriaFromFormalRequest($solidaryFormalRequest, $solidaryAsk->getAsk()->getCriteria());
$this->entityManager->persist($askCriteria);
$this->entityManager->flush();
// Update the status of the Ask and add a AskHistory
$ask = $solidaryAsk->getAsk();
$ask->setStatus(Ask::STATUS_PENDING_AS_PASSENGER);
$this->entityManager->persist($ask);
$this->entityManager->flush();
$askHistory = new AskHistory();
$askHistory->setStatus($ask->getStatus());
$askHistory->setType($ask->getType());
$askHistory->setAsk($ask);
$this->entityManager->persist($askHistory);
$this->entityManager->flush();
// If there is an Ask linked we update it (the return)
if ($solidaryAsk->getAsk()->getAskLinked()) {
$askLinked = $solidaryAsk->getAsk()->getAskLinked();
// Update the Criteria
$askLinkedCriteria = $this->updateCriteriaFromFormalRequest($solidaryFormalRequest, $askLinked->getCriteria(), "return");
$this->entityManager->persist($askLinkedCriteria);
$this->entityManager->flush();
// For now, we don't handle AskHistories on AskLinked. If we do, we need to write the code here
}
}
return $solidaryFormalRequest;
}
/**
* Get a SolidaryFormalRequest
*
* @param integer $solidarySolutionId The SolidarySolutionId the SolidaryFormalRequest is based on
* @return SolidaryFormalRequest
*/
public function getSolidaryFormalRequest(int $solidarySolutionId): SolidaryFormalRequest
{
$solidarySolution = $this->solidarySolutionRepository->find($solidarySolutionId);
if (is_null($solidarySolution)) {
throw new SolidaryException(SolidaryException::NO_SOLIDARY_SOLUTION);
}
$solidaryFormalRequest = new SolidaryFormalRequest();
$solidaryFormalRequest->setSolidarySolution($solidarySolution);
// We get the good criteria. If there is a solidaryAsk, we take it, if not, we take the Proposal Criteria
$criteria = $criteriaReturn = null;
if (!is_null($solidarySolution->getSolidaryAsk())) {
$criteria = $solidarySolution->getSolidaryAsk()->getCriteria();
// Return ? Only carpool
if (!is_null($solidarySolution->getSolidaryAsk()->getAsk()) && !is_null($solidarySolution->getSolidaryAsk()->getAsk()->getAskLinked())) {
$criteriaReturn = $solidarySolution->getSolidaryAsk()->getAsk()->getAskLinked()->getCriteria();
}
} else {
$criteria = $solidarySolution->getSolidary()->getProposal()->getCriteria();
if (!is_null($solidarySolution->getSolidary()->getProposal()->getProposalLinked())) {
$criteriaReturn = $solidarySolution->getSolidary()->getProposal()->getProposalLinked()->getCriteria();
}
}
// Dates
$solidaryFormalRequest->setOutwardDate($criteria->getFromDate());
$solidaryFormalRequest->setOutwardLimitDate($criteria->getFromDate());
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$solidaryFormalRequest->setOutwardLimitDate($criteria->getToDate());
// Days
$solidaryFormalRequest->setOutwardSchedule($this->buildScheduleFromCriteria($criteria, "outward"));
} else {
// For a punctual, we generate only with one day
$outwardSchedule[] = [
"outwardTime" => $criteria->getFromTime()->format("H:i"),
lcfirst($criteria->getFromDate()->format("D"))=>1
];
$solidaryFormalRequest->setOutwardSchedule($outwardSchedule);
}
// Return dates and schedule ? Only carpool
if (!is_null($criteriaReturn)) {
// Dates
$solidaryFormalRequest->setReturnDate($criteriaReturn->getFromDate());
$solidaryFormalRequest->setReturnLimitDate($criteriaReturn->getFromDate());
if ($criteriaReturn->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$solidaryFormalRequest->setReturnLimitDate($criteriaReturn->getToDate());
// Days
$solidaryFormalRequest->setReturnSchedule($this->buildScheduleFromCriteria($criteriaReturn, "return"));
} else {
// For a punctual, we generate only with one day
$returnSchedule[] = [
"returnTime" => $criteriaReturn->getFromTime()->format("H:i"),
lcfirst($criteriaReturn->getFromDate()->format("D"))=>1
];
$solidaryFormalRequest->setReturnSchedule($returnSchedule);
}
}
return $solidaryFormalRequest;
}
private function buildScheduleFromCriteria($criteria, $way)
{
$schedule = [];
if ($criteria->isMonCheck() && !is_null($criteria->getMonTime())) {
$schedule = $this->buildDaySchedule($schedule, "mon", $criteria->getMonTime()->format("H:i"), $way);
}
if ($criteria->isTueCheck() && !is_null($criteria->getTueTime())) {
$schedule = $this->buildDaySchedule($schedule, "tue", $criteria->getTueTime()->format("H:i"), $way);
}
if ($criteria->isWedCheck() && !is_null($criteria->getWedTime())) {
$schedule = $this->buildDaySchedule($schedule, "wed", $criteria->getWedTime()->format("H:i"), $way);
}
if ($criteria->isThuCheck() && !is_null($criteria->getThuTime())) {
$schedule = $this->buildDaySchedule($schedule, "thu", $criteria->getThuTime()->format("H:i"), $way);
}
if ($criteria->isFriCheck() && !is_null($criteria->getFriTime())) {
$schedule = $this->buildDaySchedule($schedule, "fri", $criteria->getFriTime()->format("H:i"), $way);
}
if ($criteria->isSatCheck() && !is_null($criteria->getSatTime())) {
$schedule = $this->buildDaySchedule($schedule, "sat", $criteria->getSatTime()->format("H:i"), $way);
}
if ($criteria->isSunCheck() && !is_null($criteria->getSunTime())) {
$schedule = $this->buildDaySchedule($schedule, "sun", $criteria->getSunTime()->format("H:i"), $way);
}
return $schedule;
}
private function buildDaySchedule($schedule, $day, $time, $way)
{
$found = false;
foreach ($schedule as $key => $currentSchedule) {
if ($currentSchedule[$way.'Time']==$time) {
$schedule[$key][$day] = 1;
$found = true;
break;
}
}
if (!$found) {
$newSchedule = ["mon"=>0,"tue"=>0,"wed"=>0,"thu"=>0,"fri"=>0,"sat"=>0,"sun"=>0];
$newSchedule[$way.'Time'] = $time;
$newSchedule[$day] = 1;
$schedule[] = $newSchedule;
}
return $schedule;
}
/**
* Update a Criteria based on the SolidaryFormalRequest data
*
* @param SolidaryFormalRequest $solidaryFormalRequest The solidary formal request
* @param Criteria $criteria The Criteria to update
* @param string $way Outward or Return
* @return Criteria The updated Criteria
*/
private function updateCriteriaFromFormalRequest(SolidaryFormalRequest $solidaryFormalRequest, Criteria $criteria, string $way="outward"): Criteria
{
$criteria->setFromDate($solidaryFormalRequest->getOutwardDate());
// Treat the schedule
$schedules = $solidaryFormalRequest->getOutwardSchedule();
foreach ($schedules as $schedule) {
if (isset($schedule["mon"]) && $schedule["mon"]==1) {
$criteria->setMonCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setMonTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["tue"]) && $schedule["tue"]==1) {
$criteria->setTueCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setTueTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["wed"]) && $schedule["wed"]==1) {
$criteria->setWedCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setWedTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["thu"]) && $schedule["thu"]==1) {
$criteria->setThuCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setThuTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["fri"]) && $schedule["fri"]==1) {
$criteria->setFriCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setFriTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["sat"]) && $schedule["sat"]==1) {
$criteria->setSatCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setSatTime(new \DateTime($schedule[$way.'Time']));
}
}
if (isset($schedule["sun"]) && $schedule["sun"]==1) {
$criteria->setSunCheck(true);
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setSunTime(new \DateTime($schedule[$way.'Time']));
}
}
}
// The toDate is only for regular
if ($criteria->getFrequency()==Criteria::FREQUENCY_REGULAR) {
$criteria->setToDate($solidaryFormalRequest->getOutwardLimitDate());
} else {
// Punctual journey we update fromTime
$criteria->setFromTime(new \DateTime($schedules[0][$way.'Time']));
}
return $criteria;
}
}