diff --git a/CHANGELOG.md b/CHANGELOG.md index 383afc80b..f7cb2cbc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,23 @@ and this project adheres to ## Unreleased +* [main] change address format in case the country is France, in Address render box and address normalizer * [person] add validator for accompanying period with a test on social issues (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/76) * [activity] fix visibility for location +* [origin] fix origin: use correctly the translatable strings + + * /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');` +* [person] redirect bug fixed. +* [action] add an unrelated issue within action creation. +* [origin] fix origin: use correctly the translatable strings + * /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');` +* [main] change order of civilities in civility fixtures (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] set min attr in the minimum of children field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] add marital status date in person view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] show number of children + allow set number of children to null (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] show acceptSMS option (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) +* [person] add death information in person render box in twig and vue render boxes (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191) + ## Test releases diff --git a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js index 5b6297553..1ca9f9cc2 100644 --- a/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js +++ b/src/Bundle/ChillActivityBundle/Resources/public/vuejs/Activity/store.js @@ -6,17 +6,17 @@ const debug = process.env.NODE_ENV !== 'production'; //console.log('window.activity', window.activity); const addIdToValue = (string, id) => { - let array = string ? string.split(',') : []; - array.push(id.toString()); - let str = array.join(); - return str; + let array = string ? string.split(',') : []; + array.push(id.toString()); + let str = array.join(); + return str; }; const removeIdFromValue = (string, id) => { - let array = string.split(','); - array = array.filter(el => el !== id.toString()); - let str = array.join(); - return str; + let array = string.split(','); + array = array.filter(el => el !== id.toString()); + let str = array.join(); + return str; }; const store = createStore({ @@ -50,9 +50,9 @@ const store = createStore({ return state.activity.activityType.personsVisible === 0 ? [] : state.activity.accompanyingPeriod.participations - .filter((p) => p.endDate === null) - .map((p) => p.person) - .filter((p) => !existingPersonIds.includes(p.id)); + .filter((p) => p.endDate === null) + .map((p) => p.person) + .filter((p) => !existingPersonIds.includes(p.id)); }, suggestedRequestor(state) { if (state.activity.accompanyingPeriod.requestor === null) { @@ -78,8 +78,8 @@ const store = createStore({ return state.activity.activityType.usersVisible === 0 ? [] : [state.activity.accompanyingPeriod.user].filter( - (u) => u !== null && !existingUserIds.includes(u.id) - ); + (u) => u !== null && !existingUserIds.includes(u.id) + ); }, suggestedResources(state) { const resources = state.activity.accompanyingPeriod.resources; diff --git a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php index 21a830bfd..de71daa6f 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php +++ b/src/Bundle/ChillDocGeneratorBundle/Controller/DocGeneratorTemplateController.php @@ -18,6 +18,7 @@ use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface; use Chill\DocGeneratorBundle\Context\Exception\ContextNotFoundException; use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate; use Chill\DocGeneratorBundle\GeneratorDriver\DriverInterface; +use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException; use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository; use Chill\DocStoreBundle\Entity\StoredObject; use Chill\MainBundle\Pagination\PaginatorFactory; @@ -28,10 +29,15 @@ use GuzzleHttp\Exception\TransferException; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\Extension\Core\Type\FileType; +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; // TODO à mettre dans services use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\KernelInterface; @@ -78,6 +84,27 @@ final class DocGeneratorTemplateController extends AbstractController $this->client = $client; } + /** + * @Route( + * "{_locale}/admin/doc/gen/generate/test/from/{template}/for/{entityClassName}/{entityId}", + * name="chill_docgenerator_test_generate_from_template" + * ) + */ + public function adminTestGenerateDocFromTemplateAction( + DocGeneratorTemplate $template, + string $entityClassName, + int $entityId, + Request $request + ): Response { + return $this->generateDocFromTemplate( + $template, + $entityClassName, + $entityId, + $request, + true + ); + } + /** * @Route( * "{_locale}/doc/gen/generate/from/{template}/for/{entityClassName}/{entityId}", @@ -89,6 +116,81 @@ final class DocGeneratorTemplateController extends AbstractController string $entityClassName, int $entityId, Request $request + ): Response { + return $this->generateDocFromTemplate( + $template, + $entityClassName, + $entityId, + $request, + false + ); + } + + /** + * @Route( + * "/api/1.0/docgen/templates/by-entity/{entityClassName}", + * name="chill_docgenerator_templates_for_entity_api" + * ) + */ + public function listTemplateApiAction(string $entityClassName): Response + { + $nb = $this->docGeneratorTemplateRepository->countByEntity($entityClassName); + $paginator = $this->paginatorFactory->create($nb); + $entities = $this->docGeneratorTemplateRepository->findByEntity( + $entityClassName, + $paginator->getCurrentPageFirstItemNumber(), + $paginator->getItemsPerPage() + ); + + return $this->json( + new Collection($entities, $paginator), + Response::HTTP_OK, + [], + [AbstractNormalizer::GROUPS => ['read']] + ); + } + + /** + * @Route( + * "{_locale}/admin/doc/gen/generate/test/redirect", + * name="chill_docgenerator_test_generate_redirect" + * ) + * + * @return void + */ + public function redirectToTestGenerate(Request $request): RedirectResponse + { + $template = $request->query->getInt('template'); + + if (null === $template) { + throw new BadRequestHttpException('template parameter is missing'); + } + + $entityClassName = $request->query->get('entityClassName'); + + if (null === $entityClassName) { + throw new BadRequestHttpException('entityClassName is missing'); + } + + $entityId = $request->query->get('entityId'); + + if (null === $entityId) { + throw new BadRequestHttpException('entityId is missing'); + } + + return $this->redirectToRoute( + 'chill_docgenerator_test_generate_from_template', + ['template' => $template, 'entityClassName' => $entityClassName, 'entityId' => $entityId, + 'returnPath' => $request->query->get('returnPath', '/'), ] + ); + } + + private function generateDocFromTemplate( + DocGeneratorTemplate $template, + string $entityClassName, + int $entityId, + Request $request, + bool $isTest ): Response { try { $context = $this->contextManager->getContextByDocGeneratorTemplate($template); @@ -105,9 +207,29 @@ final class DocGeneratorTemplateController extends AbstractController $contextGenerationData = []; if ($context instanceof DocGeneratorContextWithPublicFormInterface - && $context->hasPublicForm($template, $entity)) { - $builder = $this->createFormBuilder($context->getFormData($template, $entity)); + && $context->hasPublicForm($template, $entity) || $isTest) { + if ($context instanceof DocGeneratorContextWithPublicFormInterface) { + $builder = $this->createFormBuilder( + array_merge( + $context->getFormData($template, $entity), + $isTest ? ['test_file' => null] : [] + ) + ); + } else { + $builder = $this->createFormBuilder( + ['test_file' => null] + ); + } + $context->buildPublicForm($builder, $template, $entity); + + if ($isTest) { + $builder->add('test_file', FileType::class, [ + 'label' => 'Template file', + 'required' => false, + ]); + } + $form = $builder->getForm()->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { @@ -121,43 +243,71 @@ final class DocGeneratorTemplateController extends AbstractController } } - $getUrlGen = $this->tempUrlGenerator->generate( - 'GET', - $template->getFile()->getFilename() - ); + if ($isTest && null !== $contextGenerationData['test_file']) { + /** @var File $file */ + $file = $contextGenerationData['test_file']; + $templateResource = fopen($file->getPathname(), 'rb'); + } else { + $getUrlGen = $this->tempUrlGenerator->generate( + 'GET', + $template->getFile()->getFilename() + ); - $data = $this->client->request('GET', $getUrlGen->url); + $data = $this->client->request('GET', $getUrlGen->url); - $iv = $template->getFile()->getIv(); // iv as an Array - $ivGoodFormat = pack('C*', ...$iv); // iv as a String (ok for openssl_decrypt) + $iv = $template->getFile()->getIv(); // iv as an Array + $ivGoodFormat = pack('C*', ...$iv); // iv as a String (ok for openssl_decrypt) - $method = 'AES-256-CBC'; + $method = 'AES-256-CBC'; - $key = $template->getFile()->getKeyInfos()['k']; - $keyGoodFormat = Base64Url::decode($key); + $key = $template->getFile()->getKeyInfos()['k']; + $keyGoodFormat = Base64Url::decode($key); - $dataDecrypted = openssl_decrypt($data->getContent(), $method, $keyGoodFormat, 1, $ivGoodFormat); + $dataDecrypted = openssl_decrypt($data->getContent(), $method, $keyGoodFormat, 1, $ivGoodFormat); - if (false === $dataDecrypted) { - throw new Exception('Error during Decrypt ', 1); + if (false === $dataDecrypted) { + throw new Exception('Error during Decrypt ', 1); + } + + if (false === $templateResource = fopen('php://memory', 'r+b')) { + $this->logger->error('Could not write data to memory'); + + throw new HttpException(500); + } + fwrite($templateResource, $dataDecrypted); + rewind($templateResource); } - if (false === $templateResource = fopen('php://memory', 'r+b')) { - $this->logger->error('Could not write data to memory'); - - throw new HttpException(500); - } - - fwrite($templateResource, $dataDecrypted); - rewind($templateResource); - $datas = $context->getData($template, $entity, $contextGenerationData); - dump('datas compiled', $datas); - $generatedResource = $this->driver->generateFromResource($templateResource, $template->getFile()->getType(), $datas, $template->getFile()->getFilename()); + try { + $generatedResource = $this->driver->generateFromResource($templateResource, $template->getFile()->getType(), $datas, $template->getFile()->getFilename()); + } catch (TemplateException $e) { + $msg = implode("\n", $e->getErrors()); + + return new Response($msg, 400, [ + 'Content-Type' => 'text/plain', + ]); + } fclose($templateResource); + if ($isTest) { + return new StreamedResponse( + static function () use ($generatedResource) { + fpassthru($generatedResource); + fclose($generatedResource); + }, + Response::HTTP_OK, + [ + 'Content-Transfer-Encoding', 'binary', + 'Content-Type' => 'application/vnd.oasis.opendocument.text', + 'Content-Disposition' => sprintf('attachment; filename="%s.odt"', 'generated'), + 'Content-Length' => fstat($generatedResource)['size'], + ], + ); + } + $genDocName = 'doc_' . sprintf('%010d', mt_rand()) . 'odt'; $getUrlGen = $this->tempUrlGenerator->generate( @@ -206,28 +356,4 @@ final class DocGeneratorTemplateController extends AbstractController throw new Exception('Unable to generate document.'); } - - /** - * @Route( - * "/api/1.0/docgen/templates/by-entity/{entityClassName}", - * name="chill_docgenerator_templates_for_entity_api" - * ) - */ - public function listTemplateApiAction(string $entityClassName): Response - { - $nb = $this->docGeneratorTemplateRepository->countByEntity($entityClassName); - $paginator = $this->paginatorFactory->create($nb); - $entities = $this->docGeneratorTemplateRepository->findByEntity( - $entityClassName, - $paginator->getCurrentPageFirstItemNumber(), - $paginator->getItemsPerPage() - ); - - return $this->json( - new Collection($entities, $paginator), - Response::HTTP_OK, - [], - [AbstractNormalizer::GROUPS => ['read']] - ); - } } diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/Exception/TemplateException.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/Exception/TemplateException.php new file mode 100644 index 000000000..214c77621 --- /dev/null +++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/Exception/TemplateException.php @@ -0,0 +1,35 @@ +errors = $errors; + } + + public function getErrors(): array + { + return $this->errors; + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php index 8889e72b1..7a3e4ac69 100644 --- a/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php +++ b/src/Bundle/ChillDocGeneratorBundle/GeneratorDriver/RelatorioDriver.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\DocGeneratorBundle\GeneratorDriver; +use Chill\DocGeneratorBundle\GeneratorDriver\Exception\TemplateException; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\Mime\Part\DataPart; @@ -54,10 +55,21 @@ class RelatorioDriver implements DriverInterface return $response->toStream(); } catch (HttpExceptionInterface $e) { + $content = $e->getResponse()->getContent(false); + + if (400 === $e->getResponse()->getStatusCode()) { + $content = json_decode($content, true); + $this->logger->error('relatorio: template error', [ + 'error' => $content['message'] ?? '_not defined', + ]); + + throw new TemplateException([$content['message']]); + } + $this->logger->error('relatorio: error while generating document', [ 'msg' => $e->getMessage(), 'response' => $e->getResponse()->getStatusCode(), - 'content' => $e->getResponse()->getContent(false), + 'content' => $content, ]); throw $e; diff --git a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig index 1cdad36ff..29050ce02 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig +++ b/src/Bundle/ChillDocGeneratorBundle/Resources/views/Admin/DocGeneratorTemplate/index.html.twig @@ -6,6 +6,7 @@
{{ address.street }} - {% if address.streetNumber is not empty %} - {{ address.streetNumber }} - {% endif %} -
+{{ streetLine }}
{% endif %} {% if options['extended_infos'] %} {{ _self.extended(address, options) }} @@ -56,7 +54,7 @@ {% endif %} {% endmacro %} -{% macro inline(address, options) %} +{% macro inline(address, options, streetLine) %} {% if options['has_no_address'] == true and address.isNoAddress == true %} {% if address.postCode is not empty %}@@ -70,7 +68,7 @@ {% else %}
{% endif %} {{ _self.validity(address, options) }} @@ -99,7 +97,7 @@ {% if options['with_picto'] %} {% endif %} - {{ _self.inline(address, options) }} + {{ _self.inline(address, options, streetLine) }} {%- endif -%} @@ -108,7 +106,7 @@ {% if options['with_picto'] %} {% endif %} - {{ _self.inline(address, options) }} + {{ _self.inline(address, options, streetLine) }} {%- endif -%} @@ -133,7 +131,7 @@ {% if options['with_picto'] %} {% endif %} - {{ _self.raw(address, options) }} + {{ _self.raw(address, options, streetLine) }} {% endif %} {{ _self.validity(address, options) }} diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php index f9a73cd07..fd250e967 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php @@ -11,55 +11,125 @@ declare(strict_types=1); namespace Chill\MainBundle\Serializer\Normalizer; +use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper; use Chill\MainBundle\Entity\Address; +use DateTimeInterface; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; -use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class AddressNormalizer implements NormalizerAwareInterface, NormalizerInterface +class AddressNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface { use NormalizerAwareTrait; + private const NULL_POSTCODE_COUNTRY = [ + 'id', 'name', 'code', + ]; + + private const NULL_VALUE = [ + 'address_id', + 'text', + 'street', + 'streetNumber', + 'postcode', + 'country', + 'floor', + 'corridor', + 'steps', + 'flat', + 'buildingName', + 'distribution', + 'extra', + 'validFrom' => DateTimeInterface::class, + 'validTo' => DateTimeInterface::class, + ]; + /** * @param Address $address */ public function normalize($address, ?string $format = null, array $context = []) { - return [ - 'address_id' => $address->getId(), - 'text' => $address->isNoAddress() ? '' : $address->getStreetNumber() . ', ' . $address->getStreet(), - 'street' => $address->getStreet(), - 'streetNumber' => $address->getStreetNumber(), - 'postcode' => [ - 'id' => $address->getPostCode()->getId(), - 'name' => $address->getPostCode()->getName(), - 'code' => $address->getPostCode()->getCode(), - ], - 'country' => [ - 'id' => $address->getPostCode()->getCountry()->getId(), - 'name' => $address->getPostCode()->getCountry()->getName(), - 'code' => $address->getPostCode()->getCountry()->getCountryCode(), - ], - 'floor' => $address->getFloor(), - 'corridor' => $address->getCorridor(), - 'steps' => $address->getSteps(), - 'flat' => $address->getFlat(), - 'buildingName' => $address->getBuildingName(), - 'distribution' => $address->getDistribution(), - 'extra' => $address->getExtra(), - 'validFrom' => $address->getValidFrom(), - 'validTo' => $address->getValidTo(), - 'addressReference' => $this->normalizer->normalize( - $address->getAddressReference(), - $format, - [AbstractNormalizer::GROUPS => ['read']] - ), - ]; + if ($address instanceof Address) { + $text = $address->isNoAddress() ? '' : $address->getStreet() . ', ' . $address->getStreetNumber(); + + if (null !== $address->getPostCode()->getCountry()->getCountryCode()) { + if ($address->getPostCode()->getCountry()->getCountryCode() === 'FR') { + $text = $address->isNoAddress() ? '' : $address->getStreetNumber() . ', ' . $address->getStreet(); + } else { + $text = $address->isNoAddress() ? '' : $address->getStreetNumber() . ', ' . $address->getStreet(); + } + } + + $data = [ + 'address_id' => $address->getId(), + 'text' => $text, + 'street' => $address->getStreet(), + 'streetNumber' => $address->getStreetNumber(), + 'postcode' => [ + 'id' => $address->getPostCode()->getId(), + 'name' => $address->getPostCode()->getName(), + 'code' => $address->getPostCode()->getCode(), + ], + 'country' => [ + 'id' => $address->getPostCode()->getCountry()->getId(), + 'name' => $address->getPostCode()->getCountry()->getName(), + 'code' => $address->getPostCode()->getCountry()->getCountryCode(), + ], + 'floor' => $address->getFloor(), + 'corridor' => $address->getCorridor(), + 'steps' => $address->getSteps(), + 'flat' => $address->getFlat(), + 'buildingName' => $address->getBuildingName(), + 'distribution' => $address->getDistribution(), + 'extra' => $address->getExtra(), + ]; + + if ('json' === $format) { + $data['addressReference'] = $this->normalizer->normalize( + $address->getAddressReference(), + $format, + [AbstractNormalizer::GROUPS => ['read']] + ); + $data['validFrom'] = $address->getValidFrom(); + $data['validTo'] = $address->getValidTo(); + } elseif ('docgen' === $format) { + $dateContext = array_merge($context, ['docgen:expects' => DateTimeInterface::class]); + $data['validFrom'] = $this->normalizer->normalize($address->getValidFrom(), $format, $dateContext); + $data['validTo'] = $this->normalizer->normalize($address->getValidTo(), $format, $dateContext); + } + + return $data; + } + + if (null === $address) { + $helper = new NormalizeNullValueHelper($this->normalizer); + + return array_merge( + $helper->normalize(self::NULL_VALUE, $format, $context), + [ + 'postcode' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context), + 'country' => $helper->normalize(self::NULL_POSTCODE_COUNTRY, $format, $context), + ] + ); + } + + throw new UnexpectedValueException(); } - public function supportsNormalization($data, ?string $format = null) + public function supportsNormalization($data, ?string $format = null, array $context = []): bool { - return $data instanceof Address; + if ('json' === $format) { + return $data instanceof Address; + } + + if ('docgen' === $format) { + return + $data instanceof Address + || (null === $data && Address::class === ($context['docgen:expects'] ?? null)); + } + + return false; } } diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php index 22219b108..d8c616d82 100644 --- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php +++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php @@ -11,16 +11,28 @@ declare(strict_types=1); namespace Chill\MainBundle\Serializer\Normalizer; +use Chill\MainBundle\Entity\Center; +use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\User; +use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Templating\Entity\UserRender; +use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; -use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -class UserNormalizer implements NormalizerAwareInterface, NormalizerInterface +class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface { use NormalizerAwareTrait; + public const NULL_USER = [ + 'type' => 'user', + 'id' => '', + 'username' => '', + 'text' => '', + 'label' => '', + 'email' => '', + ]; + private UserRender $userRender; public function __construct(UserRender $userRender) @@ -31,20 +43,50 @@ class UserNormalizer implements NormalizerAwareInterface, NormalizerInterface public function normalize($user, ?string $format = null, array $context = []) { /** @var User $user */ + $userJobContext = array_merge( + $context, + ['docgen:expects' => UserJob::class, 'groups' => 'docgen:read'] + ); + $scopeContext = array_merge( + $context, + ['docgen:expects' => Scope::class, 'groups' => 'docgen:read'] + ); + $centerContext = array_merge( + $context, + ['docgen:expects' => Center::class, 'groups' => 'docgen:read'] + ); + + if (null === $user && 'docgen' === $format) { + return array_merge(self::NULL_USER, [ + 'user_job' => $this->normalizer->normalize(null, $format, $userJobContext), + 'main_center' => $this->normalizer->normalize(null, $format, $centerContext), + 'main_scope' => $this->normalizer->normalize(null, $format, $scopeContext), + ]); + } + return [ 'type' => 'user', 'id' => $user->getId(), 'username' => $user->getUsername(), 'text' => $this->userRender->renderString($user, []), 'label' => $user->getLabel(), - 'user_job' => $this->normalizer->normalize($user->getUserJob(), $format, $context), - 'main_center' => $this->normalizer->normalize($user->getMainCenter(), $format, $context), - 'main_scope' => $this->normalizer->normalize($user->getMainScope(), $format, $context), + 'email' => (string) $user->getEmail(), + 'user_job' => $this->normalizer->normalize($user->getUserJob(), $format, $userJobContext), + 'main_center' => $this->normalizer->normalize($user->getMainCenter(), $format, $centerContext), + 'main_scope' => $this->normalizer->normalize($user->getMainScope(), $format, $scopeContext), ]; } - public function supportsNormalization($data, ?string $format = null): bool + public function supportsNormalization($data, ?string $format = null, array $context = []): bool { - return $data instanceof User && ('json' === $format || 'docgen' === $format); + if ($data instanceof User && ('json' === $format || 'docgen' === $format)) { + return true; + } + + if (null === $data && 'docgen' === $format && User::class === ($context['docgen:expects'] ?? null)) { + return true; + } + + return false; } } diff --git a/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php b/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php index 2a29fb2f3..264f11688 100644 --- a/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php +++ b/src/Bundle/ChillMainBundle/Templating/Entity/AddressRender.php @@ -46,6 +46,7 @@ class AddressRender implements ChillEntityRenderInterface return $this->templating ->render('@ChillMain/Entity/address.html.twig', [ 'address' => $addr, + 'streetLine' => $this->renderStreetLine($addr), 'render' => $options['render'] ?? 'bloc', 'options' => $options, ]); @@ -59,13 +60,7 @@ class AddressRender implements ChillEntityRenderInterface { $lines = []; - if (!empty($addr->getStreet())) { - $lines[0] = $addr->getStreet(); - } - - if (!empty($addr->getStreetNumber())) { - $lines[0] .= ', ' . $addr->getStreetNumber(); - } + $lines[0] = $this->renderStreetLine($addr); if (!empty($addr->getPostcode())) { $lines[1] = strtr('{postcode} {label}', [ @@ -81,4 +76,33 @@ class AddressRender implements ChillEntityRenderInterface { return $entity instanceof Address; } + + private function renderStreetLine($addr): string + { + if (!empty($addr->getStreet())) { + $street = $addr->getStreet(); + } else { + $street = ''; + } + + if (!empty($addr->getStreetNumber())) { + $streetNumber = $addr->getStreetNumber(); + } else { + $streetNumber = ''; + } + + $res = trim($street . ', ' . $streetNumber, ', '); + + if (null !== $addr->getPostCode()->getCountry()->getCountryCode()) { + if ($addr->getPostCode()->getCountry()->getCountryCode() === 'FR') { + $res = trim($streetNumber . ', ' . $street, ', '); + } + } + + if (',' === $res) { + $res = ''; + } + + return $res; + } } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php index 43276c949..9445dc302 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php @@ -34,6 +34,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\Exception\RuntimeException; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Workflow\Registry; use function array_values; @@ -294,4 +296,17 @@ final class AccompanyingCourseApiController extends ApiController return null; } + + protected function validate(string $action, Request $request, string $_format, $entity, array $more = []): ConstraintViolationListInterface + { + if ('work' !== $action) { + return parent::validate($action, $request, $_format, $entity, $more); + } + + if (Request::METHOD_POST === $request->getMethod()) { + return $this->getValidator()->validate($more[0], null); + } + + return new ConstraintViolationList([]); + } } diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php index 012d0fd68..802f9fd6c 100644 --- a/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php +++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdMemberController.php @@ -186,7 +186,7 @@ class HouseholdMemberController extends ApiController $_format, ['groups' => ['read']] ); - } catch (Exception\InvalidArgumentException | Exception\UnexpectedValueException $e) { + } catch (Exception\InvalidArgumentException|Exception\UnexpectedValueException $e) { throw new BadRequestException("Deserialization error: {$e->getMessage()}", 45896, $e); } diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index 6247b92bb..1b79ba867 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -222,9 +222,7 @@ final class PersonController extends AbstractController 'label' => 'Add the person', ])->add('createPeriod', SubmitType::class, [ 'label' => 'Add the person and create an accompanying period', - ])->add('createHousehold', SubmitType::class, [ - 'label' => 'Add the person and create an household', - ]); // TODO createHousehold form action + ]); $form->handleRequest($request); diff --git a/src/Bundle/ChillPersonBundle/Controller/RelationshipApiController.php b/src/Bundle/ChillPersonBundle/Controller/RelationshipApiController.php index 72e92ac92..271b0cff7 100644 --- a/src/Bundle/ChillPersonBundle/Controller/RelationshipApiController.php +++ b/src/Bundle/ChillPersonBundle/Controller/RelationshipApiController.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Repository\Relationships\RelationshipRepository; +use Chill\PersonBundle\Security\Authorization\PersonVoter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Validator\Validator\ValidatorInterface; -use function array_values; class RelationshipApiController extends ApiController { @@ -36,9 +36,10 @@ class RelationshipApiController extends ApiController */ public function getRelationshipsByPerson(Person $person) { - //TODO: add permissions? (voter?) + $this->denyAccessUnlessGranted(PersonVoter::SEE, $person); + $relationships = $this->repository->findByPerson($person); - return $this->json(array_values($relationships), Response::HTTP_OK, [], ['groups' => ['read']]); + return $this->json($relationships, Response::HTTP_OK, [], ['groups' => ['read']]); } } diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 1e4d5022f..65a45c2e6 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -79,6 +79,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac $loader->load('services/serializer.yaml'); $loader->load('services/security.yaml'); $loader->load('services/doctrineEventListener.yaml'); + $loader->load('services/accompanyingPeriodConsistency.yaml'); if ($container->getParameter('chill_person.accompanying_period') !== 'hidden') { $loader->load('services/exports_accompanying_period.yaml'); diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php index d3e1b7f82..8a0cab301 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/Configuration.php @@ -80,6 +80,7 @@ class Configuration implements ConfigurationInterface ->append($this->addFieldNode('memo')) ->append($this->addFieldNode('number_of_children')) ->append($this->addFieldNode('acceptEmail')) + ->append($this->addFieldNode('deathdate')) ->arrayNode('alt_names') ->defaultValue([]) ->arrayPrototype() diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index e8a4e5608..5b4293226 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -789,6 +789,22 @@ class AccompanyingPeriod implements return $this->requestorPerson ?? $this->requestorThirdParty; } + /** + * @return string 'person' if requestor is an instanceof @see{Person::class}, 'thirdparty' if this is an instanceof @see{ThirdParty::class}, or 'none' + */ + public function getRequestorKind(): string + { + if ($this->getRequestor() instanceof ThirdParty) { + return 'thirdparty'; + } + + if ($this->getRequestor() instanceof Person) { + return 'person'; + } + + return 'none'; + } + public function getRequestorPerson(): ?Person { return $this->requestorPerson; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index 0e7b27cef..df4a5f5de 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -14,10 +14,12 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; use Chill\MainBundle\Entity\User; +use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\SocialAction; +use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\ThirdPartyBundle\Entity\ThirdParty; use DateTimeImmutable; use DateTimeInterface; @@ -28,458 +30,470 @@ use LogicException; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Validator\Constraints as Assert; - /** - * @ORM\Entity - * @ORM\Table(name="chill_person_accompanying_period_work") - * @Serializer\DiscriminatorMap( - * typeProperty="type", - * mapping={ - * "accompanying_period_work": AccompanyingPeriodWork::class - * } - * ) - */ - class AccompanyingPeriodWork implements TrackCreationInterface, TrackUpdateInterface - { - /** - * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) - * @Serializer\Groups({"read"}) - */ - private ?AccompanyingPeriod $accompanyingPeriod = null; - - /** - * @ORM\OneToMany( - * targetEntity=AccompanyingPeriodWorkEvaluation::class, - * mappedBy="accompanyingPeriodWork", - * cascade={"remove", "persist"}, - * orphanRemoval=true - * ) - * @Serializer\Groups({"read", "docgen:read"}) - * - * @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer` - */ - private Collection $accompanyingPeriodWorkEvaluations; - - /** - * @ORM\Column(type="datetime_immutable") - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?DateTimeImmutable $createdAt = null; - - /** - * @ORM\Column(type="boolean") - * @Serializer\Groups({"read", "docgen:read"}) - */ - private bool $createdAutomatically = false; - - /** - * @ORM\Column(type="text") - * @Serializer\Groups({"read", "docgen:read"}) - */ - private string $createdAutomaticallyReason = ''; - - /** - * @ORM\ManyToOne(targetEntity=User::class) - * @ORM\JoinColumn(nullable=false) - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?User $createdBy = null; - - /** - * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) - * @Serializer\Groups({"accompanying_period_work:create"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read", "docgen:read"}) - * @Assert\GreaterThan(propertyPath="startDate", - * message="accompanying_course_work.The endDate should be greater than the start date" - * ) - */ - private ?DateTimeImmutable $endDate = null; - - /** - * @ORM\OneToMany( - * targetEntity=AccompanyingPeriodWorkGoal::class, - * mappedBy="accompanyingPeriodWork", - * cascade={"persist"}, - * orphanRemoval=true - * ) - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - */ - private Collection $goals; - - /** - * @ORM\ManyToOne(targetEntity=ThirdParty::class) - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - * - * In schema : traitant - */ - private ?ThirdParty $handlingThierParty = null; - - /** - * @ORM\Id - * @ORM\GeneratedValue - * @ORM\Column(type="integer") - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?int $id = null; - - /** - * @ORM\Column(type="text") - * @Serializer\Groups({"read", "accompanying_period_work:edit", "docgen:read"}) - */ - private string $note = ''; - - /** - * @ORM\ManyToMany(targetEntity=Person::class) - * @ORM\JoinTable(name="chill_person_accompanying_period_work_person") - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"accompanying_period_work:create"}) - */ - private Collection $persons; - - /** - * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks") - * @ORM\JoinTable(name="chill_person_accompanying_period_work_result") - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - */ - private Collection $results; - - /** - * @ORM\ManyToOne(targetEntity=SocialAction::class) - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:create"}) - */ - private ?SocialAction $socialAction = null; - - /** - * @ORM\Column(type="date_immutable") - * @Serializer\Groups({"accompanying_period_work:create"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?DateTimeImmutable $startDate = null; - - /** - * @ORM\ManyToMany(targetEntity=ThirdParty::class) - * @ORM\JoinTable(name="chill_person_accompanying_period_work_third_party") - * - * In schema : intervenants - * @Serializer\Groups({"read", "docgen:read"}) - * @Serializer\Groups({"accompanying_period_work:edit"}) - */ - private Collection $thirdParties; - - /** - * @ORM\Column(type="datetime_immutable") - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?DateTimeImmutable $updatedAt = null; - - /** - * @ORM\ManyToOne(targetEntity=User::class) - * @ORM\JoinColumn(nullable=false) - * @Serializer\Groups({"read", "docgen:read"}) - */ - private ?User $updatedBy = null; - - public function __construct() - { - $this->goals = new ArrayCollection(); - $this->results = new ArrayCollection(); - $this->thirdParties = new ArrayCollection(); - $this->persons = new ArrayCollection(); - $this->accompanyingPeriodWorkEvaluations = new ArrayCollection(); - } - - public function addAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self - { - if (!$this->accompanyingPeriodWorkEvaluations->contains($evaluation)) { - $this->accompanyingPeriodWorkEvaluations[] = $evaluation; - $evaluation->setAccompanyingPeriodWork($this); - } - - return $this; - } - - public function addGoal(AccompanyingPeriodWorkGoal $goal): self - { - if (!$this->goals->contains($goal)) { - $this->goals[] = $goal; - $goal->setAccompanyingPeriodWork($this); - } - - return $this; - } - - public function addPerson(Person $person): self - { - if (!$this->persons->contains($person)) { - $this->persons[] = $person; - } - - return $this; - } - - public function addResult(Result $result): self - { - if (!$this->results->contains($result)) { - $this->results[] = $result; - } - - return $this; - } - - public function addThirdParty(ThirdParty $thirdParty): self - { - if (!$this->thirdParties->contains($thirdParty)) { - $this->thirdParties[] = $thirdParty; - } - - return $this; - } - - public function getAccompanyingPeriod(): ?AccompanyingPeriod - { - return $this->accompanyingPeriod; - } - - /** - * @return Collection - */ - public function getAccompanyingPeriodWorkEvaluations() - { - return $this->accompanyingPeriodWorkEvaluations; - } - - public function getCreatedAt(): ?DateTimeImmutable - { - return $this->createdAt; - } - - public function getCreatedAutomatically(): ?bool - { - return $this->createdAutomatically; - } - - public function getCreatedAutomaticallyReason(): ?string - { - return $this->createdAutomaticallyReason; - } - - public function getCreatedBy(): ?User - { - return $this->createdBy; - } - - public function getEndDate(): ?DateTimeInterface - { - return $this->endDate; - } - - /** - * @return AccompanyingPeriodWorkGoal[]|Collection - */ - public function getGoals(): Collection - { - return $this->goals; - } - - public function getHandlingThierParty(): ?ThirdParty - { - return $this->handlingThierParty; - } - - public function getId(): ?int - { - return $this->id; - } - - public function getNote(): ?string - { - return $this->note; - } - - public function getPersons(): Collection - { - return $this->persons; - } - - /** - * @return Collection|Result[] - */ - public function getResults(): Collection - { - return $this->results; - } - - public function getSocialAction(): ?SocialAction - { - return $this->socialAction; - } - - public function getStartDate(): ?DateTimeInterface - { - return $this->startDate; - } - - /** - * @return Collection|ThirdParty[] - */ - public function getThirdParties(): Collection - { - return $this->thirdParties; - } - - public function getThirdPartys(): Collection - { - return $this->getThirdParties(); - } - - public function getUpdatedAt(): ?DateTimeImmutable - { - return $this->updatedAt; - } - - public function getUpdatedBy(): ?User - { - return $this->updatedBy; - } - - public function removeAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self - { - $this->accompanyingPeriodWorkEvaluations - ->removeElement($evaluation); - $evaluation->setAccompanyingPeriodWork(null); - - return $this; - } - - public function removeGoal(AccompanyingPeriodWorkGoal $goal): self - { - if ($this->goals->removeElement($goal)) { - // set the owning side to null (unless already changed) - if ($goal->getAccompanyingPeriodWork() === $this) { - $goal->setAccompanyingPeriodWork(null); - } - } - - return $this; - } - - public function removePerson(Person $person): self - { - $this->persons->removeElement($person); - - return $this; - } - - public function removeResult(Result $result): self - { - $this->results->removeElement($result); - - return $this; - } - - public function removeThirdParty(ThirdParty $thirdParty): self - { - $this->thirdParties->removeElement($thirdParty); - - return $this; - } - - /** - * Internal: you should use `$accompanyingPeriod->removeWork($work);` or - * `$accompanyingPeriod->addWork($work);`. - */ - public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self - { - if ($this->accompanyingPeriod instanceof AccompanyingPeriod - && $accompanyingPeriod !== $this->accompanyingPeriod) { - throw new LogicException('A work cannot change accompanyingPeriod'); - } - - $this->accompanyingPeriod = $accompanyingPeriod; - - return $this; - } - - public function setCreatedAt(DateTimeInterface $createdAt): self - { - $this->createdAt = $createdAt; - - return $this; - } - - public function setCreatedAutomatically(bool $createdAutomatically): self - { - $this->createdAutomatically = $createdAutomatically; - - return $this; - } - - public function setCreatedAutomaticallyReason(string $createdAutomaticallyReason): self - { - $this->createdAutomaticallyReason = $createdAutomaticallyReason; - - return $this; - } - - public function setCreatedBy(?User $createdBy): self - { - $this->createdBy = $createdBy; +/** + * @ORM\Entity + * @ORM\Table(name="chill_person_accompanying_period_work") + * @Serializer\DiscriminatorMap( + * typeProperty="type", + * mapping={ + * "accompanying_period_work": AccompanyingPeriodWork::class + * } + * ) + */ +class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface, TrackCreationInterface, TrackUpdateInterface +{ + /** + * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) + * @Serializer\Groups({"read"}) + */ + private ?AccompanyingPeriod $accompanyingPeriod = null; + + /** + * @ORM\OneToMany( + * targetEntity=AccompanyingPeriodWorkEvaluation::class, + * mappedBy="accompanyingPeriodWork", + * cascade={"remove", "persist"}, + * orphanRemoval=true + * ) + * @Serializer\Groups({"read", "docgen:read"}) + * + * @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer` + */ + private Collection $accompanyingPeriodWorkEvaluations; + + /** + * @ORM\Column(type="datetime_immutable") + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?DateTimeImmutable $createdAt = null; + + /** + * @ORM\Column(type="boolean") + * @Serializer\Groups({"read", "docgen:read"}) + */ + private bool $createdAutomatically = false; + + /** + * @ORM\Column(type="text") + * @Serializer\Groups({"read", "docgen:read"}) + */ + private string $createdAutomaticallyReason = ''; + + /** + * @ORM\ManyToOne(targetEntity=User::class) + * @ORM\JoinColumn(nullable=false) + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?User $createdBy = null; + + /** + * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) + * @Serializer\Groups({"accompanying_period_work:create"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read", "docgen:read"}) + * @Assert\GreaterThan(propertyPath="startDate", + * message="accompanying_course_work.The endDate should be greater than the start date" + * ) + */ + private ?DateTimeImmutable $endDate = null; + + /** + * @ORM\OneToMany( + * targetEntity=AccompanyingPeriodWorkGoal::class, + * mappedBy="accompanyingPeriodWork", + * cascade={"persist"}, + * orphanRemoval=true + * ) + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + */ + private Collection $goals; + + /** + * @ORM\ManyToOne(targetEntity=ThirdParty::class) + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * + * In schema : traitant + */ + private ?ThirdParty $handlingThierParty = null; + + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?int $id = null; + + /** + * @ORM\Column(type="text") + * @Serializer\Groups({"read", "accompanying_period_work:edit", "docgen:read"}) + */ + private string $note = ''; + + /** + * @ORM\ManyToMany(targetEntity=Person::class) + * @ORM\JoinTable(name="chill_person_accompanying_period_work_person") + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"accompanying_period_work:create"}) + */ + private Collection $persons; + + /** + * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks") + * @ORM\JoinTable(name="chill_person_accompanying_period_work_result") + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + */ + private Collection $results; + + /** + * @ORM\ManyToOne(targetEntity=SocialAction::class) + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:create"}) + */ + private ?SocialAction $socialAction = null; + + /** + * @ORM\Column(type="date_immutable") + * @Serializer\Groups({"accompanying_period_work:create"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?DateTimeImmutable $startDate = null; + + /** + * @ORM\ManyToMany(targetEntity=ThirdParty::class) + * @ORM\JoinTable(name="chill_person_accompanying_period_work_third_party") + * + * In schema : intervenants + * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + */ + private Collection $thirdParties; + + /** + * @ORM\Column(type="datetime_immutable") + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?DateTimeImmutable $updatedAt = null; + + /** + * @ORM\ManyToOne(targetEntity=User::class) + * @ORM\JoinColumn(nullable=false) + * @Serializer\Groups({"read", "docgen:read"}) + */ + private ?User $updatedBy = null; + + public function __construct() + { + $this->goals = new ArrayCollection(); + $this->results = new ArrayCollection(); + $this->thirdParties = new ArrayCollection(); + $this->persons = new ArrayCollection(); + $this->accompanyingPeriodWorkEvaluations = new ArrayCollection(); + } + + public function addAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self + { + if (!$this->accompanyingPeriodWorkEvaluations->contains($evaluation)) { + $this->accompanyingPeriodWorkEvaluations[] = $evaluation; + $evaluation->setAccompanyingPeriodWork($this); + } + + return $this; + } + + public function addGoal(AccompanyingPeriodWorkGoal $goal): self + { + if (!$this->goals->contains($goal)) { + $this->goals[] = $goal; + $goal->setAccompanyingPeriodWork($this); + } + + return $this; + } + + public function addPerson(Person $person): self + { + if (!$this->persons->contains($person)) { + $this->persons[] = $person; + } + + return $this; + } + + public function addResult(Result $result): self + { + if (!$this->results->contains($result)) { + $this->results[] = $result; + } + + return $this; + } + + public function addThirdParty(ThirdParty $thirdParty): self + { + if (!$this->thirdParties->contains($thirdParty)) { + $this->thirdParties[] = $thirdParty; + } + + return $this; + } + + public function getAccompanyingPeriod(): ?AccompanyingPeriod + { + return $this->accompanyingPeriod; + } + + /** + * @return Collection + */ + public function getAccompanyingPeriodWorkEvaluations() + { + return $this->accompanyingPeriodWorkEvaluations; + } + + public function getCreatedAt(): ?DateTimeImmutable + { + return $this->createdAt; + } + + public function getCreatedAutomatically(): ?bool + { + return $this->createdAutomatically; + } + + public function getCreatedAutomaticallyReason(): ?string + { + return $this->createdAutomaticallyReason; + } + + public function getCreatedBy(): ?User + { + return $this->createdBy; + } + + public function getEndDate(): ?DateTimeInterface + { + return $this->endDate; + } + + /** + * @return AccompanyingPeriodWorkGoal[]|Collection + */ + public function getGoals(): Collection + { + return $this->goals; + } + + public function getHandlingThierParty(): ?ThirdParty + { + return $this->handlingThierParty; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getNote(): ?string + { + return $this->note; + } + + public function getPersons(): Collection + { + return $this->persons; + } + + /** + * @return Collection|Result[] + */ + public function getResults(): Collection + { + return $this->results; + } + + public function getSocialAction(): ?SocialAction + { + return $this->socialAction; + } + + public function getSocialIssues(): Collection + { + return new ArrayCollection([$this->getSocialAction()->getIssue()]); + } + + public function getStartDate(): ?DateTimeInterface + { + return $this->startDate; + } + + /** + * @return Collection|ThirdParty[] + */ + public function getThirdParties(): Collection + { + return $this->thirdParties; + } + + public function getThirdPartys(): Collection + { + return $this->getThirdParties(); + } + + public function getUpdatedAt(): ?DateTimeImmutable + { + return $this->updatedAt; + } + + public function getUpdatedBy(): ?User + { + return $this->updatedBy; + } + + public function removeAccompanyingPeriodWorkEvaluation(AccompanyingPeriodWorkEvaluation $evaluation): self + { + $this->accompanyingPeriodWorkEvaluations + ->removeElement($evaluation); + $evaluation->setAccompanyingPeriodWork(null); + + return $this; + } + + public function removeGoal(AccompanyingPeriodWorkGoal $goal): self + { + if ($this->goals->removeElement($goal)) { + // set the owning side to null (unless already changed) + if ($goal->getAccompanyingPeriodWork() === $this) { + $goal->setAccompanyingPeriodWork(null); + } + } + + return $this; + } + + public function removePerson(Person $person): self + { + $this->persons->removeElement($person); + + return $this; + } + + public function removeResult(Result $result): self + { + $this->results->removeElement($result); + + return $this; + } + + public function removeSocialIssue(SocialIssue $issue): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface + { + $this->getSocialIssues()->removeElement($issue); + + return $this; + } + + public function removeThirdParty(ThirdParty $thirdParty): self + { + $this->thirdParties->removeElement($thirdParty); + + return $this; + } + + /** + * Internal: you should use `$accompanyingPeriod->removeWork($work);` or + * `$accompanyingPeriod->addWork($work);`. + */ + public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self + { + if ($this->accompanyingPeriod instanceof AccompanyingPeriod + && $accompanyingPeriod !== $this->accompanyingPeriod) { + throw new LogicException('A work cannot change accompanyingPeriod'); + } + + $this->accompanyingPeriod = $accompanyingPeriod; + + return $this; + } + + public function setCreatedAt(DateTimeInterface $createdAt): self + { + $this->createdAt = $createdAt; + + return $this; + } - return $this; - } + public function setCreatedAutomatically(bool $createdAutomatically): self + { + $this->createdAutomatically = $createdAutomatically; - public function setEndDate(?DateTimeInterface $endDate = null): self - { - $this->endDate = $endDate; + return $this; + } + + public function setCreatedAutomaticallyReason(string $createdAutomaticallyReason): self + { + $this->createdAutomaticallyReason = $createdAutomaticallyReason; - return $this; - } + return $this; + } - public function setHandlingThierParty(?ThirdParty $handlingThierParty): self - { - $this->handlingThierParty = $handlingThierParty; + public function setCreatedBy(?User $createdBy): self + { + $this->createdBy = $createdBy; - return $this; - } + return $this; + } - public function setNote(string $note): self - { - $this->note = $note; + public function setEndDate(?DateTimeInterface $endDate = null): self + { + $this->endDate = $endDate; - return $this; - } + return $this; + } - public function setSocialAction(?SocialAction $socialAction): self - { - $this->socialAction = $socialAction; + public function setHandlingThierParty(?ThirdParty $handlingThierParty): self + { + $this->handlingThierParty = $handlingThierParty; - return $this; - } + return $this; + } - public function setStartDate(DateTimeInterface $startDate): self - { - $this->startDate = $startDate; + public function setNote(string $note): self + { + $this->note = $note; - return $this; - } + return $this; + } - public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface - { - $this->updatedAt = $datetime; + public function setSocialAction(?SocialAction $socialAction): self + { + $this->socialAction = $socialAction; - return $this; - } + return $this; + } - public function setUpdatedBy(User $user): TrackUpdateInterface - { - $this->updatedBy = $user; + public function setStartDate(DateTimeInterface $startDate): self + { + $this->startDate = $startDate; - return $this; - } - } + return $this; + } + + public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface + { + $this->updatedAt = $datetime; + + return $this; + } + + public function setUpdatedBy(User $user): TrackUpdateInterface + { + $this->updatedBy = $user; + + return $this; + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php index b7d4c00b1..aa12f0416 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * ClosingMotive give an explanation why we closed the Accompanying period. @@ -24,22 +25,18 @@ use Doctrine\ORM\Mapping as ORM; class ClosingMotive { /** - * @var bool - * * @ORM\Column(type="boolean") */ - private $active = true; + private bool $active = true; /** * Child Accompanying periods. * - * @var Collection - * * @ORM\OneToMany( * targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive", * mappedBy="parent") */ - private $children; + private Collection $children; /** * @var int @@ -47,22 +44,21 @@ class ClosingMotive * @ORM\Id * @ORM\Column(name="id", type="integer") * @ORM\GeneratedValue(strategy="AUTO") + * @Serializer\Groups({"docgen:read"}) */ - private $id; + private ?int $id = null; /** - * @var array - * * @ORM\Column(type="json") + * @Serializer\Groups({"docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ - private $name; + private array $name = []; /** - * @var float - * * @ORM\Column(type="float") */ - private $ordering = 0.0; + private float $ordering = 0.0; /** * @var self @@ -71,7 +67,7 @@ class ClosingMotive * targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive", * inversedBy="children") */ - private $parent; + private ?ClosingMotive $parent = null; /** * ClosingMotive constructor. diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php index ec22e806e..7a1114b3e 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php @@ -31,15 +31,16 @@ class Origin * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Groups({"read"}) + * @Groups({"read", "docgen:read"}) */ private ?int $id = null; /** * @ORM\Column(type="json") - * @Groups({"read"}) + * @Groups({"read", "docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ - private $label; + private array $label = []; /** * @ORM\Column(type="date_immutable", nullable=true) @@ -52,7 +53,7 @@ class Origin return $this->id; } - public function getLabel() + public function getLabel(): array { return $this->label; } diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php index 829bc3ade..22c33f85f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMember.php @@ -65,7 +65,7 @@ class HouseholdMember * @ORM\Column(type="integer") * @Serializer\Groups({"read", "docgen:read"}) */ - private $id; + private ?int $id = null; /** * @var Person @@ -73,6 +73,7 @@ class HouseholdMember * targetEntity="\Chill\PersonBundle\Entity\Person" * ) * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Context({"docgen:person:with-household": false}) * @Assert\Valid(groups={"household_memberships"}) * @Assert\NotNull(groups={"household_memberships"}) */ diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Position.php b/src/Bundle/ChillPersonBundle/Entity/Household/Position.php index b8f707126..378bc0d9f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Position.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Position.php @@ -35,11 +35,12 @@ class Position * @ORM\Column(type="integer") * @Serializer\Groups({"read", "docgen:read"}) */ - private ?int $id; + private ?int $id = null; /** * @ORM\Column(type="json") * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ private array $label = []; diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index f58cee747..a3524d306 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -244,6 +244,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI * @Assert\Date( * groups={"general", "creation"} * ) + * @Assert\GreaterThan(propertyPath="birthDate") + * @Assert\LessThanOrEqual("today") */ private ?DateTimeImmutable $deathdate = null; @@ -588,8 +590,6 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } - // a period opened and another one after it - /** * Function used for validation that check if the accompanying periods of * the person are not collapsing (i.e. have not shared days) or having @@ -1776,7 +1776,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI return $this; } - public function setNumberOfChildren(int $numberOfChildren): self + public function setNumberOfChildren(?int $numberOfChildren): self { $this->numberOfChildren = $numberOfChildren; diff --git a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php index 3b7c75a06..c78e5df33 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php +++ b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relation.php @@ -41,12 +41,14 @@ class Relation /** * @ORM\Column(type="json", nullable=true) * @Serializer\Groups({"read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ private array $reverseTitle = []; /** * @ORM\Column(type="json", nullable=true) * @Serializer\Groups({"read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ private array $title = []; diff --git a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relationship.php b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relationship.php index 45cd546b0..d51881bb2 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Relationships/Relationship.php +++ b/src/Bundle/ChillPersonBundle/Entity/Relationships/Relationship.php @@ -19,6 +19,7 @@ use DateTimeImmutable; use DateTimeInterface; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\DiscriminatorColumn; +use RuntimeException; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Validator\Constraints as Assert; @@ -116,6 +117,27 @@ class Relationship implements TrackCreationInterface, TrackUpdateInterface return $this->id; } + /** + * Return the opposite person of the @see{counterpart} person. + * + * this is the from person if the given is associated to the To, + * or the To person otherwise. + * + * @throw RuntimeException if the counterpart is neither in the from or to person + */ + public function getOpposite(Person $counterpart): Person + { + if ($this->fromPerson !== $counterpart && $this->toPerson !== $counterpart) { + throw new RuntimeException('the counterpart is neither the from nor to person for this relationship'); + } + + if ($this->fromPerson === $counterpart) { + return $this->toPerson; + } + + return $this->fromPerson; + } + public function getRelation(): ?Relation { return $this->relation; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php index 4c21cd348..aeb8d0a8d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php @@ -55,6 +55,7 @@ class Evaluation /** * @ORM\Column(type="json") * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ private array $title = []; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php index f3d8f5289..30abaa3ef 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php @@ -40,24 +40,25 @@ class Goal * @ORM\Column(type="integer") * @Serializer\Groups({"read", "docgen:read"}) */ - private $id; + private ?int $id = null; /** * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="goals") * @ORM\JoinTable(name="chill_person_social_work_goal_result") */ - private $results; + private Collection $results; /** * @ORM\ManyToMany(targetEntity=SocialAction::class, mappedBy="goals") */ - private $socialActions; + private Collection $socialActions; /** * @ORM\Column(type="json") * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ - private $title = []; + private array $title = []; public function __construct() { diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php index 17d4ddd0a..b4a9b6aa2 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php @@ -13,6 +13,7 @@ namespace Chill\PersonBundle\Entity\SocialWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal; +use DateTime; use DateTimeInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; @@ -34,22 +35,22 @@ class Result /** * @ORM\ManyToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="results") */ - private $accompanyingPeriodWorkGoals; + private Collection $accompanyingPeriodWorkGoals; /** * @ORM\ManyToMany(targetEntity=AccompanyingPeriodWork::class, mappedBy="results") */ - private $accompanyingPeriodWorks; + private Collection $accompanyingPeriodWorks; /** * @ORM\Column(type="datetime", nullable=true) */ - private $desactivationDate; + private DateTime $desactivationDate; /** * @ORM\ManyToMany(targetEntity=Goal::class, mappedBy="results") */ - private $goals; + private Collection $goals; /** * @ORM\Id @@ -57,18 +58,19 @@ class Result * @ORM\Column(type="integer") * @Serializer\Groups({"read", "docgen:read"}) */ - private $id; + private ?int $id = null; /** * @ORM\ManyToMany(targetEntity=SocialAction::class, mappedBy="results") */ - private $socialActions; + private Collection $socialActions; /** * @ORM\Column(type="json") * @Serializer\Groups({"read", "docgen:read"}) + * @Serializer\Context({"is-translatable": true}, groups={"docgen:read"}) */ - private $title = []; + private array $title = []; public function __construct() { diff --git a/src/Bundle/ChillPersonBundle/Form/PersonType.php b/src/Bundle/ChillPersonBundle/Form/PersonType.php index de9b3bbb1..47baafe2b 100644 --- a/src/Bundle/ChillPersonBundle/Form/PersonType.php +++ b/src/Bundle/ChillPersonBundle/Form/PersonType.php @@ -93,6 +93,7 @@ class PersonType extends AbstractType ]) ->add('numberOfChildren', IntegerType::class, [ 'required' => false, + 'attr' => ['min' => 0], ]); if ($this->configAltNamesHelper->hasAltNames()) { diff --git a/src/Bundle/ChillPersonBundle/Repository/Relationships/RelationshipRepository.php b/src/Bundle/ChillPersonBundle/Repository/Relationships/RelationshipRepository.php index ab172e459..9549a4564 100644 --- a/src/Bundle/ChillPersonBundle/Repository/Relationships/RelationshipRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/Relationships/RelationshipRepository.php @@ -11,18 +11,31 @@ declare(strict_types=1); namespace Chill\PersonBundle\Repository\Relationships; +use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Relationships\Relationship; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; class RelationshipRepository implements ObjectRepository { + private EntityManagerInterface $em; + private EntityRepository $repository; public function __construct(EntityManagerInterface $em) { $this->repository = $em->getRepository(Relationship::class); + $this->em = $em; + } + + public function countByPerson(Person $person): int + { + return $this->buildQueryByPerson($person) + ->select('COUNT(p)') + ->getQuery() + ->getSingleScalarResult(); } public function find($id): ?Relationship @@ -40,15 +53,13 @@ class RelationshipRepository implements ObjectRepository return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } - public function findByPerson($personId): array + /** + * @return array|Relationship[] + */ + public function findByPerson(Person $person): array { - // return all relationships of which person is part? or only where person is the fromPerson? - return $this->repository->createQueryBuilder('r') - ->select('r, t') // entity Relationship - ->join('r.relation', 't') - ->where('r.fromPerson = :val') - ->orWhere('r.toPerson = :val') - ->setParameter('val', $personId) + return $this->buildQueryByPerson($person) + ->select('r') ->getQuery() ->getResult(); } @@ -62,4 +73,20 @@ class RelationshipRepository implements ObjectRepository { return Relationship::class; } + + private function buildQueryByPerson(Person $person): QueryBuilder + { + $qb = $this->em->createQueryBuilder(); + $qb + ->from(Relationship::class, 'r') + ->where( + $qb->expr()->orX( + $qb->expr()->eq('r.fromPerson', ':person'), + $qb->expr()->eq('r.toPerson', ':person') + ) + ) + ->setParameter('person', $person); + + return $qb; + } } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js index 4eb6f2a33..8e4134205 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js @@ -22,9 +22,9 @@ const getUsers = () => { }; const getReferrersSuggested = (course) => { - const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`; + const url = `/api/1.0/person/accompanying-course/${course.id}/referrers-suggested.json`; - return fetchResults(url); + return fetchResults(url); } /* diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue index ee5ad469a..c3c33e9ef 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/OriginDemand.vue @@ -4,7 +4,7 @@{{ $t('pick_social_issue_linked_with_action') }}
- -{{ $t('pick_social_issue_linked_with_action') }}
+