api/src/Solidary/Admin/Service/SolidaryBeneficiaryManager.php
<?php
/**
* Copyright (c) 2021, 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\Admin\Service;
use ApiPlatform\Core\DataProvider\PaginatorInterface;
use App\Action\Entity\Diary;
use App\Auth\Entity\AuthItem;
use App\Auth\ServiceAdmin\AuthManager;
use App\Paginator\MobicoopPaginator;
use App\Service\FormatDataManager;
use App\Solidary\Admin\Event\BeneficiaryStatusChangedEvent;
use App\Solidary\Admin\Exception\SolidaryException;
use App\Solidary\Entity\Proof;
use App\Solidary\Entity\Solidary;
use App\Solidary\Entity\SolidaryBeneficiary;
use App\Solidary\Entity\SolidaryUser;
use App\Solidary\Entity\SolidaryUserStructure;
use App\Solidary\Entity\Structure;
use App\Solidary\Repository\SolidaryUserRepository;
use App\Solidary\Repository\SolidaryUserStructureRepository;
use App\User\Service\PseudonymizationManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Solidary beneficiary manager in admin context.
*
* @author Sylvain Briat <sylvain.briat@mobicoop.org>
*/
class SolidaryBeneficiaryManager
{
private $entityManager;
private $solidaryUserRepository;
private $solidaryUserStructureRepository;
private $authManager;
private $formatDataManager;
private $eventDispatcher;
private $fileFolder;
/**
* Constructor.
*/
public function __construct(
EntityManagerInterface $entityManager,
SolidaryUserRepository $solidaryUserRepository,
SolidaryUserStructureRepository $solidaryUserStructureRepository,
AuthManager $authManager,
FormatDataManager $formatDataManager,
EventDispatcherInterface $eventDispatcher,
string $fileFolder
) {
$this->entityManager = $entityManager;
$this->solidaryUserRepository = $solidaryUserRepository;
$this->solidaryUserStructureRepository = $solidaryUserStructureRepository;
$this->authManager = $authManager;
$this->eventDispatcher = $eventDispatcher;
$this->formatDataManager = $formatDataManager;
$this->fileFolder = $fileFolder;
}
/**
* Get Solidary Beneficiary records (transform SolidaryUsers to SolidaryBeneficiaries).
*
* @param PaginatorInterface $solidaryUsers The solidary user objects
*
* @return null|array The solidary beneficiary records
*/
public function getSolidaryBeneficiaries(PaginatorInterface $solidaryUsers)
{
$solidaryBeneficiaries = [];
foreach ($solidaryUsers as $solidaryUser) {
// @var SolidaryUser $solidaryUser
$solidaryBeneficiaries[] = $this->createSolidaryBeneficiaryFromSolidaryUser($solidaryUser);
}
// we need to return a paginator, we already have all informations but we need to build a custom paginator object
return new MobicoopPaginator($solidaryBeneficiaries, $solidaryUsers->getCurrentPage(), $solidaryUsers->getItemsPerPage(), $solidaryUsers->getTotalItems());
}
/**
* Get the proofs for a solidaryUserStructure.
*
* @param SolidaryUserStructure $solidaryUserStructure The solidaryUserStructure
* @param null|Structure $structure The structure if we want the proofs for a given structure only
*
* @return array The proofs
*/
public function getProofsForSolidaryUserStructure(SolidaryUserStructure $solidaryUserStructure, ?Structure $structure = null)
{
$proofs = [];
foreach ($solidaryUserStructure->getProofs() as $proof) {
/**
* @var Proof $proof
*/
if (is_null($structure) || ($proof->getSolidaryUserStructure()->getStructure()->getId() === $structure->getId())) {
// get the real value for checkbox, selectbox, radio
$value = $proof->getValue();
if ($proof->getStructureProof()->isCheckbox()) {
$value = (bool) $proof->getValue();
} elseif ($proof->getStructureProof()->isRadio() || $proof->getStructureProof()->isSelectbox()) {
$options = explode(';', $proof->getStructureProof()->getOptions());
$values = explode(';', $proof->getStructureProof()->getAcceptedValues());
if ($key = array_search($proof->getValue(), $values)) {
$value = $options[$key];
}
}
$proofs[] = [
'proofId' => $proof->getId(),
'structure' => $solidaryUserStructure->getStructure()->getName(),
'structureId' => $solidaryUserStructure->getStructure()->getId(),
'structureProofId' => $proof->getStructureProof()->getId(),
'userStructureId' => $solidaryUserStructure->getId(),
'status' => $solidaryUserStructure->getStatus(),
'label' => $proof->getStructureProof()->getLabel(),
'checkbox' => $proof->getStructureProof()->isCheckbox(),
'input' => $proof->getStructureProof()->isInput(),
'selectbox' => $proof->getStructureProof()->isSelectbox(),
'radio' => $proof->getStructureProof()->isRadio(),
'file' => $proof->getStructureProof()->isFile(),
'value' => $value,
'originalName' => $proof->getStructureProof()->isFile() ? $proof->getOriginalName() : null,
'fileName' => $proof->getStructureProof()->isFile() ? $this->fileFolder.rawurlencode($proof->getFileName()) : null,
'fileSize' => $proof->getStructureProof()->isFile() ? $this->formatDataManager->convertFilesize($proof->getSize()) : null,
];
}
}
return $proofs;
}
/**
* Get a Solidary Beneficiary by its id.
*
* @param int $id The Solidary Beneficiary id
*
* @return null|SolidaryBeneficiary The Solidary Beneficiary or null if not found
*/
public function getSolidaryBeneficiary(int $id)
{
if (!$solidaryUser = $this->solidaryUserRepository->find($id)) {
throw new SolidaryException(sprintf(SolidaryException::BENEFICIARY_NOT_FOUND, $id));
}
if (
PseudonymizationManager::isUserPseudonymized($solidaryUser->getUser())
|| !$solidaryUser->isBeneficiary()
) {
throw new SolidaryException(sprintf(SolidaryException::BENEFICIARY_NOT_FOUND, $id));
}
return $this->createSolidaryBeneficiaryFromSolidaryUser($solidaryUser, true, true);
}
/**
* Patch a solidary beneficiary.
*
* @param int $id The id of the solidaryBeneficiary to update
* @param array $fields The updated fields
*
* @return SolidaryBeneficiary The solidaryBeneficiary updated
*/
public function patchSolidaryBeneficiary(int $id, array $fields)
{
if (!$solidaryUser = $this->solidaryUserRepository->find($id)) {
throw new SolidaryException(sprintf(SolidaryException::BENEFICIARY_NOT_FOUND, $id));
}
// check if a new validation has been made
if (array_key_exists('validation', $fields)) {
return $this->treatValidation($solidaryUser, $fields['validation']);
}
// check if beneficiary informations have been updated
if (isset($fields['givenName']) && $fields['givenName'] != $solidaryUser->getUser()->getGivenName()) {
$solidaryUser->getUser()->setGivenName($fields['givenName']);
}
if (isset($fields['familyName']) && $fields['familyName'] != $solidaryUser->getUser()->getFamilyName()) {
$solidaryUser->getUser()->setFamilyName($fields['familyName']);
}
if (isset($fields['email']) && $fields['email'] != $solidaryUser->getUser()->getEmail()) {
$solidaryUser->getUser()->setEmail($fields['email']);
}
if (isset($fields['telephone']) && $fields['telephone'] != $solidaryUser->getUser()->getTelephone()) {
$solidaryUser->getUser()->setTelephone($fields['telephone']);
}
if (isset($fields['gender']) && $fields['gender'] != $solidaryUser->getUser()->getGender()) {
$solidaryUser->getUser()->setGender($fields['gender']);
}
if (isset($fields['birthDate']) && $fields['birthDate'] != $solidaryUser->getUser()->getBirthDate()) {
$solidaryUser->getUser()->setBirthDate(new \DateTime($fields['birthDate']));
}
if (isset($fields['newsSubscription']) && $fields['newsSubscription'] != $solidaryUser->getUser()->hasNewsSubscription()) {
$solidaryUser->getUser()->setNewsSubscription($fields['newsSubscription']);
}
// check if beneficiary home address has been updated
if (isset($fields['homeAddress'])) {
// we search the original home address
$homeAddress = null;
foreach ($solidaryUser->getUser()->getAddresses() as $address) {
if ($address->isHome()) {
$homeAddress = $address;
break;
}
}
if (!is_null($homeAddress)) {
// we have to update each field...
/**
* @var Address $homeAddress
*/
$updated = false;
if (isset($fields['homeAddress']['streetAddress']) && $homeAddress->getStreetAddress() != $fields['homeAddress']['streetAddress']) {
$updated = true;
$homeAddress->setStreetAddress($fields['homeAddress']['streetAddress']);
}
if (isset($fields['homeAddress']['postalCode']) && $homeAddress->getPostalCode() != $fields['homeAddress']['postalCode']) {
$updated = true;
$homeAddress->setPostalCode($fields['homeAddress']['postalCode']);
}
if (isset($fields['homeAddress']['addressLocality']) && $homeAddress->getAddressLocality() != $fields['homeAddress']['addressLocality']) {
$updated = true;
$homeAddress->setAddressLocality($fields['homeAddress']['addressLocality']);
}
if (isset($fields['homeAddress']['addressCountry']) && $homeAddress->getAddressCountry() != $fields['homeAddress']['addressCountry']) {
$updated = true;
$homeAddress->setAddressCountry($fields['homeAddress']['addressCountry']);
}
if (isset($fields['homeAddress']['latitude']) && $homeAddress->getLatitude() != $fields['homeAddress']['latitude']) {
$updated = true;
$homeAddress->setLatitude($fields['homeAddress']['latitude']);
}
if (isset($fields['homeAddress']['longitude']) && $homeAddress->getLongitude() != $fields['homeAddress']['longitude']) {
$updated = true;
$homeAddress->setLongitude($fields['homeAddress']['longitude']);
}
if (isset($fields['homeAddress']['houseNumber']) && $homeAddress->getHouseNumber() != $fields['homeAddress']['houseNumber']) {
$updated = true;
$homeAddress->setHouseNumber($fields['homeAddress']['houseNumber']);
}
if (isset($fields['homeAddress']['subLocality']) && $homeAddress->getSubLocality() != $fields['homeAddress']['subLocality']) {
$updated = true;
$homeAddress->setSubLocality($fields['homeAddress']['subLocality']);
}
if (isset($fields['homeAddress']['localAdmin']) && $homeAddress->getLocalAdmin() != $fields['homeAddress']['localAdmin']) {
$updated = true;
$homeAddress->setLocalAdmin($fields['homeAddress']['localAdmin']);
}
if (isset($fields['homeAddress']['county']) && $homeAddress->getCounty() != $fields['homeAddress']['county']) {
$updated = true;
$homeAddress->setCounty($fields['homeAddress']['county']);
}
if (isset($fields['homeAddress']['macroCounty']) && $homeAddress->getMacroCounty() != $fields['homeAddress']['macroCounty']) {
$updated = true;
$homeAddress->setMacroCounty($fields['homeAddress']['macroCounty']);
}
if (isset($fields['homeAddress']['region']) && $homeAddress->getRegion() != $fields['homeAddress']['region']) {
$updated = true;
$homeAddress->setRegion($fields['homeAddress']['region']);
}
if (isset($fields['homeAddress']['macroRegion']) && $homeAddress->getMacroRegion() != $fields['homeAddress']['macroRegion']) {
$updated = true;
$homeAddress->setMacroRegion($fields['homeAddress']['macroRegion']);
}
if (isset($fields['homeAddress']['countryCode']) && $homeAddress->getCountryCode() != $fields['homeAddress']['countryCode']) {
$updated = true;
$homeAddress->setCountryCode($fields['homeAddress']['countryCode']);
}
if ($updated) {
$this->entityManager->persist($homeAddress);
}
}
}
// persist the solidary beneficiary
$this->entityManager->persist($solidaryUser->getUser());
$this->entityManager->flush();
return $this->getSolidaryBeneficiary($solidaryUser->getId());
}
public function checkIsBeneficiary(SolidaryUserStructure $solidaryUserStructure)
{
$isBeneficiary = false;
foreach ($solidaryUserStructure->getSolidaryUser()->getSolidaryUserStructures() as $curSolidaryUserStructure) {
if (SolidaryUserStructure::STATUS_ACCEPTED === $curSolidaryUserStructure->getStatus()) {
$isBeneficiary = true;
break;
}
}
if ($isBeneficiary) {
if ($authItem = $this->authManager->getAuthItem(AuthItem::ROLE_SOLIDARY_BENEFICIARY)) {
$this->authManager->grant($solidaryUserStructure->getSolidaryUser()->getUser(), $authItem);
}
if ($authItem = $this->authManager->getAuthItem(AuthItem::ROLE_SOLIDARY_BENEFICIARY_CANDIDATE)) {
$this->authManager->revoke($solidaryUserStructure->getSolidaryUser()->getUser(), $authItem, null);
}
} else {
if ($authItem = $this->authManager->getAuthItem(AuthItem::ROLE_SOLIDARY_BENEFICIARY)) {
$this->authManager->revoke($solidaryUserStructure->getSolidaryUser()->getUser(), $authItem, null);
}
}
}
/**
* Create a SolidaryBeneficiary from a SolidaryUser.
*
* @param SolidaryUser $solidaryUser The SolidaryUser
* @param bool $withDiary Include the diary for the SolidaryUser
* @param bool $withProofs Include the proofs for the SolidaryUser
*
* @return SolidaryBeneficiary The SolidaryBeneficiary
*/
private function createSolidaryBeneficiaryFromSolidaryUser(SolidaryUser $solidaryUser, bool $withDiary = false, bool $withProofs = false): SolidaryBeneficiary
{
$solidaryBeneficiary = new SolidaryBeneficiary();
$solidaryBeneficiary->setId($solidaryUser->getId());
$solidaryBeneficiary->setUserId($solidaryUser->getUser()->getId());
$solidaryBeneficiary->setEmail($solidaryUser->getUser()->getEmail());
$solidaryBeneficiary->setTelephone($solidaryUser->getUser()->getTelephone());
$solidaryBeneficiary->setGivenName($solidaryUser->getUser()->getGivenName());
$solidaryBeneficiary->setFamilyName($solidaryUser->getUser()->getFamilyName());
$solidaryBeneficiary->setGender($solidaryUser->getUser()->getGender());
$solidaryBeneficiary->setBirthDate($solidaryUser->getUser()->getBirthDate());
$solidaryBeneficiary->setNewsSubscription($solidaryUser->getUser()->hasNewsSubscription());
$solidaryBeneficiary->setHomeAddress($solidaryUser->getUser()->getHomeAddress() ? $solidaryUser->getUser()->getHomeAddress()->jsonSerialize() : null);
$solidaryBeneficiary->setAvatar($solidaryUser->getUser()->getAvatar());
// get the status of the beneficiary for each structure attached, and get the diary and proofs if asked
$beneficiaryStructures = [];
$diaries = [];
$proofs = [];
foreach ($solidaryUser->getSolidaryUserStructures() as $solidaryUserStructure) {
// @var SolidaryUserStructure $solidaryUserStructure
$beneficiaryStructures[] = [
'id' => $solidaryUserStructure->getStructure()->getId(),
'name' => $solidaryUserStructure->getStructure()->getName(),
'status' => $solidaryUserStructure->getStatus(),
];
// get the diary
if ($withDiary) {
$diaries = $this->getDiaryForSolidaryUserStructure($solidaryUserStructure);
}
// get the proofs
if ($withProofs) {
$proofs = $this->getProofsForSolidaryUserStructure($solidaryUserStructure);
}
}
$solidaryBeneficiary->setStructures($beneficiaryStructures);
// reorder diaries by date and solidary id
usort($diaries, function ($a, $b) {
if ($a['date'] == $b['date']) {
return $b['solidary'] <=> $a['solidary'];
}
return $b['date'] <=> $a['date'];
});
$solidaryBeneficiary->setDiaries($diaries);
$solidaryBeneficiary->setProofs($proofs);
return $solidaryBeneficiary;
}
/**
* Get the diary entries for a solidaryUserStructure.
*
* @param SolidaryUserStructure $solidaryUserStructure The solidaryUserStructure
*
* @return array The proofs
*/
private function getDiaryForSolidaryUserStructure(SolidaryUserStructure $solidaryUserStructure)
{
$diaries = [];
foreach ($solidaryUserStructure->getSolidaries() as $solidary) {
/**
* @var Solidary $solidary
*/
foreach ($solidary->getDiaries() as $diary) {
// @var Diary $diary
$diaries[] = [
'action' => $diary->getAction()->getName(),
'comment' => $diary->getComment(),
'progression' => $diary->getProgression(),
'authorGivenName' => $diary->getAuthor()->getGivenName(),
'authorFamilyName' => $diary->getAuthor()->getFamilyName(),
'authorAvatar' => $diary->getAuthor()->getAvatar(),
'userId' => $diary->getUser()->getId(),
'givenName' => $diary->getUser()->getGivenName(),
'familyName' => $diary->getUser()->getFamilyName(),
'avatar' => $diary->getUser()->getAvatar(),
'date' => $diary->getCreatedDate(),
'solidary' => $solidary->getId(),
'origin' => $solidary->getAdminorigin()->jsonSerialize(),
'destination' => $solidary->getAdmindestination()->jsonSerialize(),
];
}
}
return $diaries;
}
/**
* Treat a validation for a solidary beneficiary.
*
* @param SolidaryUser $solidaryUser The solidaryUser corresponding to the solidarybeneficiary to update
* @param array $validation The validation fields
*
* @return SolidaryBeneficiary The solidaryBeneficiary updated
*/
private function treatValidation(SolidaryUser $solidaryUser, array $validation)
{
if (!array_key_exists('validate', $validation)) {
throw new SolidaryException(SolidaryException::BENEFICIARY_VALIDATION_VALUE_REQUIRED);
}
if (!array_key_exists('id', $validation)) {
throw new SolidaryException(SolidaryException::BENEFICIARY_VALIDATION_ID_REQUIRED);
}
if (!$solidaryUserStructure = $this->solidaryUserStructureRepository->find($validation['id'])) {
throw new SolidaryException(sprintf(SolidaryException::SOLIDARY_USER_STRUCTURE_NOT_FOUND, $validation['id']));
}
$solidaryUserStructure->setStatus(true === $validation['validate'] ? SolidaryUserStructure::STATUS_ACCEPTED : SolidaryUserStructure::STATUS_REFUSED);
$this->entityManager->persist($solidaryUserStructure);
$this->entityManager->flush();
// dispatch the event
$event = new BeneficiaryStatusChangedEvent($solidaryUserStructure);
$this->eventDispatcher->dispatch($event, BeneficiaryStatusChangedEvent::NAME);
return $this->getSolidaryBeneficiary($solidaryUser->getId());
}
}