Merge branch 'master' into notification/completion

This commit is contained in:
Mathieu Jaumotte 2022-01-05 11:15:49 +01:00
commit 1e0c62b09d
45 changed files with 474 additions and 217 deletions

View File

@ -11,6 +11,15 @@ and this project adheres to
## Unreleased ## Unreleased
<!-- write down unreleased development here --> <!-- write down unreleased development here -->
* [household] change translations (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [household] add address i18n in household component (champs-libres/departement-de-la-vendee/accent-suivi-developpement#158)
* [household] add on the fly i18n in household component
* [household] redirect to the household page when a household is created from a person (champs-libres/departement-de-la-vendee/accent-suivi-developpement#175)
* [household] household member editor: display alert if some members have already an household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#172)
* [household] household member editor: do not add in new members if the member is included in the members of household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#123)
* [household] household member editor: remove markNoAddress button (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [person]: ordering fields in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* [person]: Add email and alt names in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* AddAddress: optimize loading: wait for the user finish typing; * AddAddress: optimize loading: wait for the user finish typing;
* UserPicker: fix bug with deprecated role * UserPicker: fix bug with deprecated role
* docgen: add base context + tests * docgen: add base context + tests
@ -25,6 +34,7 @@ and this project adheres to
* Household: show date validFrom and validTo when moving * Household: show date validFrom and validTo when moving
* address reference: add index for refid * address reference: add index for refid
* [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time) * [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time)
* [accompanyingCourse] improve masonry on resume page, add origin
## Test releases ## Test releases

View File

@ -1,22 +1,22 @@
{ {
"name": "chill-project/chill-bundles", "name": "chill-project/chill-bundles",
"type": "library",
"description": "Most used bundles for chill-project", "description": "Most used bundles for chill-project",
"license": "AGPL-3.0-only",
"type": "library",
"keywords": [ "keywords": [
"chill", "chill",
"social worker" "social worker"
], ],
"license": "AGPL-3.0-only",
"require": { "require": {
"php": "^7.4", "php": "^7.4",
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290", "champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
"champs-libres/wopi-bundle": "dev-master#59b468503b9413f8d588ef9e626e7675560db3d8", "champs-libres/wopi-bundle": "dev-master#59b468503b9413f8d588ef9e626e7675560db3d8",
"champs-libres/wopi-lib": "dev-master#0e1da19bb6de820080b8651867a7e475be590060",
"doctrine/doctrine-bundle": "^2.1", "doctrine/doctrine-bundle": "^2.1",
"doctrine/doctrine-migrations-bundle": "^3.0", "doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.7", "doctrine/orm": "^2.7",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"graylog2/gelf-php": "^1.5", "graylog2/gelf-php": "^1.5",
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0", "knplabs/knp-menu-bundle": "^3.0",
"knplabs/knp-time-bundle": "^1.12", "knplabs/knp-time-bundle": "^1.12",
"league/csv": "^9.7.1", "league/csv": "^9.7.1",
@ -31,6 +31,7 @@
"symfony/css-selector": "^4.4", "symfony/css-selector": "^4.4",
"symfony/expression-language": "^4.4", "symfony/expression-language": "^4.4",
"symfony/form": "^4.4", "symfony/form": "^4.4",
"symfony/framework-bundle": "^4.4",
"symfony/intl": "^4.4", "symfony/intl": "^4.4",
"symfony/mailer": "^5.4", "symfony/mailer": "^5.4",
"symfony/mime": "^4.4", "symfony/mime": "^4.4",
@ -67,8 +68,7 @@
"symfony/phpunit-bridge": "^4.4", "symfony/phpunit-bridge": "^4.4",
"symfony/stopwatch": "^4.4", "symfony/stopwatch": "^4.4",
"symfony/var-dumper": "^4.4", "symfony/var-dumper": "^4.4",
"symfony/web-profiler-bundle": "^4.4", "symfony/web-profiler-bundle": "^4.4"
"vimeo/psalm": "^4.15"
}, },
"config": { "config": {
"bin-dir": "bin", "bin-dir": "bin",
@ -107,8 +107,19 @@
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests" "Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
} }
}, },
"minimum-stability": "dev", "config": {
"prefer-stable": true, "allow-plugins": {
"composer/package-versions-deprecated": true,
"phpstan/extension-installer": true,
"ergebnis/composer-normalize": true,
"phpro/grumphp": true,
"ocramius/package-versions": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"vendor-dir": "tests/app/vendor"
},
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
"cache:clear": "symfony-cmd", "cache:clear": "symfony-cmd",

View File

@ -859,11 +859,6 @@ parameters:
count: 1 count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverEvent.php
-
message: "#^Call to deprecated method setTimeout\\(\\) of class Redis\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/PasswordRecover/PasswordRecoverLocker.php
- -
message: message:
""" """

View File

@ -66,7 +66,7 @@ final class ActivityReasonCategoryControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class ActivityReasonControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class ActivityTypeControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -33,11 +33,11 @@ final class ConfigCustomizablesEntitiesTest extends KernelTestCase
$customizableEntities = self::$kernel->getContainer() $customizableEntities = self::$kernel->getContainer()
->getParameter('chill_custom_fields.customizables_entities'); ->getParameter('chill_custom_fields.customizables_entities');
$this->assertInternalType('array', $customizableEntities); $this->assertIsArray($customizableEntities);
$this->assertCount(2, $customizableEntities); $this->assertCount(2, $customizableEntities);
foreach ($customizableEntities as $key => $config) { foreach ($customizableEntities as $key => $config) {
$this->assertInternalType('array', $config); $this->assertIsArray($config);
$this->assertArrayHasKey('name', $config); $this->assertArrayHasKey('name', $config);
$this->assertArrayHasKey('class', $config); $this->assertArrayHasKey('class', $config);
} }
@ -56,7 +56,7 @@ final class ConfigCustomizablesEntitiesTest extends KernelTestCase
$customizableEntities = self::$kernel->getContainer() $customizableEntities = self::$kernel->getContainer()
->getParameter('chill_custom_fields.customizables_entities'); ->getParameter('chill_custom_fields.customizables_entities');
$this->assertInternalType('array', $customizableEntities); $this->assertIsArray($customizableEntities);
$this->assertCount(1, $customizableEntities); $this->assertCount(1, $customizableEntities);
} }
} }

View File

@ -61,7 +61,7 @@ final class CustomFieldControllerTest_TODO extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class EventControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class EventTypeControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class RoleControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -66,7 +66,7 @@ final class StatusControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -140,8 +140,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase
{ {
$filter = $this->getAggregator(); $filter = $this->getAggregator();
$this->assertInternalType( $this->assertIsString(
'string',
$filter->applyOn(), $filter->applyOn(),
'test that the internal type of "applyOn" is a string' 'test that the internal type of "applyOn" is a string'
); );
@ -160,8 +159,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase
{ {
$queryKeys = $this->getAggregator()->getQueryKeys($data); $queryKeys = $this->getAggregator()->getQueryKeys($data);
$this->assertInternalType( $this->assertIsArray(
'array',
$queryKeys, $queryKeys,
'test that the query keys returned are an array' 'test that the query keys returned are an array'
); );
@ -252,7 +250,7 @@ abstract class AbstractAggregatorTest extends KernelTestCase
{ {
$title = $this->getAggregator()->getTitle(); $title = $this->getAggregator()->getTitle();
$this->assertInternalType('string', $title); $this->assertIsString($title);
$this->assertNotEmpty( $this->assertNotEmpty(
$title, $title,
'test that the title is not empty' 'test that the title is not empty'

View File

@ -151,8 +151,7 @@ abstract class AbstractExportTest extends WebTestCase
{ {
$export = $this->getExport(); $export = $this->getExport();
$this->assertInternalType( $this->assertIsString(
'string',
$export->getDescription(), $export->getDescription(),
'Assert that the `getDescription` method return a string' 'Assert that the `getDescription` method return a string'
); );
@ -214,8 +213,7 @@ abstract class AbstractExportTest extends WebTestCase
$results = $this->getExport()->getResult($query, $data); $results = $this->getExport()->getResult($query, $data);
$this->assertInternalType( $this->assertIsArray(
'array',
$results, $results,
'assert that the returned result is an array' 'assert that the returned result is an array'
); );
@ -271,8 +269,7 @@ abstract class AbstractExportTest extends WebTestCase
{ {
$export = $this->getExport(); $export = $this->getExport();
$this->assertInternalType( $this->assertIsString(
'string',
$export->getType(), $export->getType(),
'Assert that the `getType` method return a string' 'Assert that the `getType` method return a string'
); );

View File

@ -126,7 +126,7 @@ abstract class AbstractFilterTest extends KernelTestCase
{ {
$filter = $this->getFilter(); $filter = $this->getFilter();
$this->assertInternalType('string', $filter->applyOn()); $this->assertIsString($filter->applyOn());
} }
/** /**
@ -149,8 +149,7 @@ abstract class AbstractFilterTest extends KernelTestCase
'test that the description is not empty' 'test that the description is not empty'
); );
} elseif (is_array($description)) { } elseif (is_array($description)) {
$this->assertInternalType( $this->assertIsString(
'string',
$description[0], $description[0],
'test that the first element in the description array is a string' 'test that the first element in the description array is a string'
); );
@ -191,7 +190,7 @@ abstract class AbstractFilterTest extends KernelTestCase
{ {
$title = $this->getFilter()->getTitle(); $title = $this->getFilter()->getTitle();
$this->assertInternalType('string', $title); $this->assertIsString($title);
$this->assertNotEmpty( $this->assertNotEmpty(
$title, $title,
'test that the title is not empty' 'test that the title is not empty'

View File

@ -72,6 +72,6 @@ final class CenterControllerTest extends WebTestCase
$crawler = $client->request('GET', '/fr/admin/center/'); $crawler = $client->request('GET', '/fr/admin/center/');
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertMatchesRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
} }

View File

@ -40,7 +40,7 @@ final class LoginControllerTest extends WebTestCase
$this->assertTrue($client->getResponse()->isRedirect()); $this->assertTrue($client->getResponse()->isRedirect());
//the response is not a login page, but on a new page //the response is not a login page, but on a new page
$this->assertNotRegExp('/\/login$/', $client->getResponse() $this->assertDoesNotMatchRegularExpression('/\/login$/', $client->getResponse()
->headers ->headers
->get('location')); ->get('location'));
@ -48,7 +48,7 @@ final class LoginControllerTest extends WebTestCase
$client->followRedirects(true); $client->followRedirects(true);
$crawler = $client->request('GET', '/'); $crawler = $client->request('GET', '/');
$this->assertRegExp('/center a_social/', $client->getResponse() $this->assertMatchesRegularExpression('/center a_social/', $client->getResponse()
->getContent()); ->getContent());
$logoutLinkFilter = $crawler->filter('a:contains("Se déconnecter")'); $logoutLinkFilter = $crawler->filter('a:contains("Se déconnecter")');
@ -63,7 +63,7 @@ final class LoginControllerTest extends WebTestCase
$client->followRedirect(); //redirect to login page $client->followRedirect(); //redirect to login page
//check we are back on login page //check we are back on login page
$this->assertRegExp('/\/login$/', $client->getResponse() $this->assertMatchesRegularExpression('/\/login$/', $client->getResponse()
->headers ->headers
->get('location')); ->get('location'));
} }

View File

@ -66,7 +66,7 @@ final class PermissionsGroupControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
// Check the entity has been delete on the list // Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); $this->assertDoesNotMatchRegularExpression('/Foo/', $client->getResponse()->getContent());
} }
*/ */

View File

@ -48,7 +48,7 @@ final class TokenManagerTest extends KernelTestCase
$tokens = $tokenManager->generate($user, $expiration); $tokens = $tokenManager->generate($user, $expiration);
$this->assertInternalType('array', $tokens); $this->assertIsArray($tokens);
$this->assertArrayHasKey('h', $tokens); $this->assertArrayHasKey('h', $tokens);
$this->assertArrayHasKey('t', $tokens); $this->assertArrayHasKey('t', $tokens);
$this->assertNotEmpty($tokens['h']); $this->assertNotEmpty($tokens['h']);

View File

@ -48,6 +48,6 @@ final class MenuComposerTest extends KernelTestCase
$routes = $this->menuComposer->getRoutesFor('dummy0'); $routes = $this->menuComposer->getRoutesFor('dummy0');
$this->assertInternalType('array', $routes); $this->assertIsArray($routes);
} }
} }

View File

@ -11,8 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Config; namespace Chill\PersonBundle\Config;
use function count;
/** /**
* Give help to interact with the config for alt names. * Give help to interact with the config for alt names.
*/ */
@ -69,6 +67,6 @@ class ConfigPersonAltNamesHelper
*/ */
public function hasAltNames(): bool public function hasAltNames(): bool
{ {
return count($this->config) > 0; return [] !== $this->config;
} }
} }

View File

@ -14,23 +14,52 @@ namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper; use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use function in_array;
use function array_filter;
use function array_values;
class PersonApiController extends ApiController class PersonApiController extends ApiController
{ {
private AuthorizationHelper $authorizationHelper; private AuthorizationHelper $authorizationHelper;
public function __construct(AuthorizationHelper $authorizationHelper) private ConfigPersonAltNamesHelper $configPersonAltNameHelper;
{
public function __construct(
AuthorizationHelper $authorizationHelper,
ConfigPersonAltNamesHelper $configPersonAltNameHelper
) {
$this->authorizationHelper = $authorizationHelper; $this->authorizationHelper = $authorizationHelper;
$this->configPersonAltNameHelper = $configPersonAltNameHelper;
}
/**
* @Route("/api/1.0/person/config/alt_names.{_format}",
* name="chill_person_config_alt_names",
* requirements={
* "_format": "json"
* }
* )
*/
public function configAltNames(Request $request, string $_format): Response
{
$configAltNamesChoices = $this->configPersonAltNameHelper->getChoices();
return $this->json(
array_map(
static fn (array $data, string $key): array => ['key' => $key, 'labels' => $data],
$configAltNamesChoices,
array_keys($configAltNamesChoices)
),
Response::HTTP_OK,
[],
['groups' => ['read']]
);
} }
public function personAddressApi($id, Request $request, string $_format): Response public function personAddressApi($id, Request $request, string $_format): Response
@ -51,22 +80,51 @@ class PersonApiController extends ApiController
{ {
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person); $this->denyAccessUnlessGranted(PersonVoter::SEE, $person);
$addresses = []; $seenAddressIds = [];
// collect addresses from location in courses // collect addresses from location in courses
foreach ($person->getAccompanyingPeriodParticipations() as $participation) { $addresses = $person
if (null !== $participation->getAccompanyingPeriod()->getAddressLocation()) { ->getAccompanyingPeriodParticipations()
$a = $participation->getAccompanyingPeriod()->getAddressLocation(); ->filter(
$addresses[$a->getId()] = $a; static function (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): bool {
return null !== $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation();
} }
)
->map(
static function (AccompanyingPeriodParticipation $accompanyingPeriodParticipation): ?Address {
return $accompanyingPeriodParticipation->getAccompanyingPeriod()->getAddressLocation();
} }
)
->filter(
// We remove potential null addresses.
static fn (?Address $address): bool => null !== $address
)
->filter(
static function (Address $address) use (&$seenAddressIds): bool {
$id = $address->getId();
if (in_array($id, $seenAddressIds, true)) {
return false;
}
$seenAddressIds[] = $id;
return true;
}
);
// remove the actual address // remove the actual address
$actual = $person->getCurrentHouseholdAddress(); $actual = $person->getCurrentHouseholdAddress();
if (null !== $actual) { if (null !== $actual) {
$addresses = array_filter($addresses, static fn ($a) => $a !== $actual); $addresses = $addresses->filter(static fn (Address $address): bool => $address !== $actual);
} }
return $this->json(array_values($addresses), Response::HTTP_OK, [], ['groups' => ['read']]); return $this->json(
$addresses->getValues(),
Response::HTTP_OK,
[],
['groups' => ['read']]
);
} }
} }

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Entity; namespace Chill\PersonBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/** /**
* PersonAltName. * PersonAltName.
@ -34,6 +35,7 @@ class PersonAltName
* @var string * @var string
* *
* @ORM\Column(name="key", type="string", length=255) * @ORM\Column(name="key", type="string", length=255)
* @Groups({"write"})
*/ */
private $key; private $key;
@ -41,6 +43,7 @@ class PersonAltName
* @var string * @var string
* *
* @ORM\Column(name="label", type="text") * @ORM\Column(name="label", type="text")
* @Groups({"write"})
*/ */
private $label; private $label;

View File

@ -254,3 +254,24 @@ abbr.referrer { // still used ?
border: 1px solid black; border: 1px solid black;
padding: 10px; padding: 10px;
} }
/// Masonry blocs on AccompanyingCourse resume page
div#dashboards {
div.mbloc {
& > div:not(.warnings) {
border: 1px solid $chill-light-gray;
background-color: $chill-llight-gray;
border-radius: 0.35rem;
padding: 1rem;
}
& > div.warnings .alert {
margin-bottom: 0;
}
blockquote.chill-user-quote {
margin: -1.2em;
p {
margin: 0;
}
}
}
}

View File

@ -33,7 +33,7 @@ const fetchHouseholdSuggestionByAccompanyingPeriod = (personId) => {
throw Error ({m: 'Error while fetching household suggestion', status: response.status}); throw Error ({m: 'Error while fetching household suggestion', status: response.status});
}).then(data => Promise.resolve(data.results)) }).then(data => Promise.resolve(data.results))
.catch(e => console.err(e)); .catch(e => console.error(e));
; ;
}; };

View File

@ -9,7 +9,7 @@
<div v-else> <div v-else>
<p> <p>
{{ $t('household_members_editor.concerned.persons_will_be_moved') }}&nbsp;: {{ $t('household_members_editor.concerned.persons_will_be_moved') }}&nbsp;:
<span v-for="c in concerned"> <span v-for="c in concerned" :key=c.person.id>
<person-render-box render="badge" :options="{addLink: false}" :person="c.person"></person-render-box> <person-render-box render="badge" :options="{addLink: false}" :person="c.person"></person-render-box>
<button class="btn" @click="removePerson(c.person)" v-if="c.allowRemove" style="padding-left:0;"> <button class="btn" @click="removePerson(c.person)" v-if="c.allowRemove" style="padding-left:0;">
<span class="fa-stack fa-lg" :title="$t('household_members_editor.concerned.remove_concerned')"> <span class="fa-stack fa-lg" :title="$t('household_members_editor.concerned.remove_concerned')">
@ -19,6 +19,17 @@
</button> </button>
</span> </span>
</p> </p>
<div class="alert alert-info" v-if="concernedPersonsWithHouseholds.length > 0">
<p>{{ $t('household_members_editor.concerned.persons_with_household') }}</p>
<ul v-for="c in concernedPersonsWithHouseholds" :key=c.person.id>
<li>
{{ c.person.text }}
{{ $t('household_members_editor.concerned.already_belongs_to_household') }}
<a target="_blank" :href="this.makeHouseholdLink(c.person.current_household_id)">{{c.person.current_household_id}}</a>.
</li>
</ul>
</div>
</div> </div>
<ul class="record_actions"> <ul class="record_actions">
@ -59,7 +70,7 @@ export default {
}, },
computed: { computed: {
...mapState([ ...mapState([
'concerned' 'concerned', 'household'
]), ]),
...mapGetters([ ...mapGetters([
'persons', 'persons',
@ -67,6 +78,15 @@ export default {
noPerson () { noPerson () {
return this.$store.getters.persons.length === 0; return this.$store.getters.persons.length === 0;
}, },
concernedPersonsWithHouseholds () {
if (this.$store.state.household) {
return this.$store.state.concerned.filter(c =>
c.person.current_household_id !== null && c.person.current_household_id !== this.$store.state.household.id
)
} else {
return [];
}
}
}, },
data() { data() {
return { return {
@ -92,6 +112,9 @@ export default {
console.log('remove person in concerned', person); console.log('remove person in concerned', person);
this.$store.dispatch('removePerson', person); this.$store.dispatch('removePerson', person);
}, },
makeHouseholdLink(id) {
return `/fr/person/household/${id}/summary`
}
} }
} }
</script> </script>

View File

@ -2,11 +2,11 @@
<current-household></current-household> <current-household></current-household>
<ul class="record_actions"> <ul class="record_actions">
<li v-if="!hasHouseholdAddress && !isHouseholdForceAddress"> <!-- <li v-if="!hasHouseholdAddress && !isHouseholdForceAddress">
<button class="btn btn-misc" @click="markNoAddress"> <button class="btn btn-misc" @click="markNoAddress">
{{ $t('household_members_editor.household_address.mark_no_address') }} {{ $t('household_members_editor.household_address.mark_no_address') }}
</button> </button>
</li> </li> -->
<li v-if="!hasHouseholdAddress"> <li v-if="!hasHouseholdAddress">
<add-address <add-address
:context="getAddressContext" :context="getAddressContext"

View File

@ -1,3 +1,4 @@
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'; import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n';
import { ontheflyMessages } from 'ChillMainAssets/vuejs/OnTheFly/i18n'; import { ontheflyMessages } from 'ChillMainAssets/vuejs/OnTheFly/i18n';
import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n'; import { addressMessages } from 'ChillMainAssets/vuejs/Address/i18n';
@ -35,7 +36,7 @@ const appMessages = {
create_new_address: "Créer une nouvelle adresse", create_new_address: "Créer une nouvelle adresse",
}, },
concerned: { concerned: {
title: "Usagers déplacés", title: "Usager(s) à (re)positionner dans un ménage",
persons_will_be_moved: "Les usagers suivants vont être déplacés", persons_will_be_moved: "Les usagers suivants vont être déplacés",
add_at_least_onePerson: "Indiquez au moins un usager à déplacer", add_at_least_onePerson: "Indiquez au moins un usager à déplacer",
remove_concerned: "Ne plus transférer", remove_concerned: "Ne plus transférer",
@ -45,6 +46,8 @@ const appMessages = {
move_to: "Déplacer vers", move_to: "Déplacer vers",
persons_leaving: "Usagers quittant leurs ménages", persons_leaving: "Usagers quittant leurs ménages",
no_person_in_position: "Aucun usager ne sera ajouté à cette position", no_person_in_position: "Aucun usager ne sera ajouté à cette position",
persons_with_household: "Les usagers suivants sont associés à ces ménages:",
already_belongs_to_household: "est associé au ménage"
}, },
positioning: { positioning: {
persons_to_positionnate: 'Usagers à positionner', persons_to_positionnate: 'Usagers à positionner',

View File

@ -224,6 +224,7 @@ const store = createStore({
} }
state.concerned.forEach((c, index) => { state.concerned.forEach((c, index) => {
if (!(h.members.map((m) => m.person.id)).includes(c.person.id)) {
let m = { let m = {
id: index * -1, id: index * -1,
person: c.person, person: c.person,
@ -236,6 +237,8 @@ const store = createStore({
} }
} }
h.new_members.push(m); h.new_members.push(m);
}
}) })
console.log('fake household', h); console.log('fake household', h);
@ -307,7 +310,7 @@ const store = createStore({
comment: "", comment: "",
}); });
} else { } else {
console.err("person already included"); console.error("person already included");
} }
}, },
markPosition(state, { person_id, position_id}) { markPosition(state, { person_id, position_id}) {
@ -533,11 +536,15 @@ const store = createStore({
// nothing to do anymore here, bye-bye ! // nothing to do anymore here, bye-bye !
let params = new URLSearchParams(window.location.search); let params = new URLSearchParams(window.location.search);
if (params.has('followAfter')) {
window.location.replace(`/fr/person/household/${household_id}/summary`);
} else {
if (params.has('returnPath')) { if (params.has('returnPath')) {
window.location.replace(params.get('returnPath')); window.location.replace(params.get('returnPath'));
} else { } else {
window.location.replace(`/fr/person/household/${household_id}/summary`); window.location.replace(`/fr/person/household/${household_id}/summary`);
} }
}
} else { } else {
// we assume the answer was 422... // we assume the answer was 422...
error = household; error = household;

View File

@ -10,6 +10,13 @@ const getPerson = (id) => {
}); });
}; };
const getPersonAltNames = () =>
fetch('/api/1.0/person/config/alt_names.json').then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});;
/* /*
* POST a new person * POST a new person
*/ */
@ -48,6 +55,7 @@ const patchPerson = (id, body) => {
export { export {
getPerson, getPerson,
getPersonAltNames,
postPerson, postPerson,
patchPerson patchPerson
}; };

View File

@ -21,14 +21,19 @@
<div v-else-if="action === 'edit' || action === 'create'"> <div v-else-if="action === 'edit' || action === 'create'">
<div class="form-floating mb-3">
<input class="form-control form-control-lg" id="lastname" v-model="lastName" v-bind:placeholder="$t('person.lastname')" />
<label for="lastname">{{ $t('person.lastname') }}</label>
</div>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
<input class="form-control form-control-lg" id="firstname" v-model="firstName" v-bind:placeholder="$t('person.firstname')" /> <input class="form-control form-control-lg" id="firstname" v-model="firstName" v-bind:placeholder="$t('person.firstname')" />
<label for="firstname">{{ $t('person.firstname') }}</label> <label for="firstname">{{ $t('person.firstname') }}</label>
</div> </div>
<div class="form-floating mb-3"> <div v-for="(a) in config.altNames" :key="a.key" class="form-floating mb-3">
<input class="form-control form-control-lg" id="lastname" v-model="lastName" v-bind:placeholder="$t('person.lastname')" /> <input class="form-control form-control-lg" :id="a.key" @input="onAltNameInput" />
<label for="lastname">{{ $t('person.lastname') }}</label> <label :for="a.key">{{ a.labels.fr }}</label>
</div> </div>
<!-- TODO fix placeholder if undefined <!-- TODO fix placeholder if undefined
@ -71,11 +76,20 @@
aria-describedby="mobilenumber" /> aria-describedby="mobilenumber" />
</div> </div>
<div class="input-group mb-3">
<span class="input-group-text" id="email"><i class="fa fa-fw fa-at"></i></span>
<input class="form-control form-control-lg"
v-model="email"
v-bind:placeholder="$t('person.email')"
v-bind:aria-label="$t('person.email')"
aria-describedby="email" />
</div>
</div> </div>
</template> </template>
<script> <script>
import { getPerson } from '../../_api/OnTheFly'; import { getPerson, getPersonAltNames } from '../../_api/OnTheFly';
import PersonRenderBox from '../Entity/PersonRenderBox.vue'; import PersonRenderBox from '../Entity/PersonRenderBox.vue';
export default { export default {
@ -88,13 +102,19 @@ export default {
data() { data() {
return { return {
person: { person: {
type: 'person' type: 'person',
} altNames: []
},
config: {
altNames: []
},
} }
}, },
computed: { computed: {
firstName: { firstName: {
set(value) { this.person.firstName = value; }, set(value) {
this.person.firstName = value;
},
get() { return this.person.firstName; } get() { return this.person.firstName; }
}, },
lastName: { lastName: {
@ -125,6 +145,10 @@ export default {
set(value) { this.person.mobilenumber = value; }, set(value) { this.person.mobilenumber = value; },
get() { return this.person.mobilenumber; } get() { return this.person.mobilenumber; }
}, },
email: {
set(value) { this.person.email = value; },
get() { return this.person.email; }
},
genderClass() { genderClass() {
switch (this.person.gender) { switch (this.person.gender) {
case 'woman': case 'woman':
@ -150,6 +174,10 @@ export default {
} }
}, },
mounted() { mounted() {
getPersonAltNames()
.then(altNames => {
this.config.altNames = altNames;
});
if (this.action !== 'create') { if (this.action !== 'create') {
this.loadData(); this.loadData();
} }
@ -162,7 +190,16 @@ export default {
console.log('get person', this.person); console.log('get person', this.person);
resolve(); resolve();
})); }));
} },
onAltNameInput(event) {
const key = event.target.id;
const label = event.target.value;
let updateAltNames = this.person.altNames.filter((a) => a.key !== key);
updateAltNames.push(
{'key': key, 'label': label}
)
this.person.altNames = updateAltNames;
},
} }
} }
</script> </script>

View File

@ -30,6 +30,7 @@ const personMessages = {
phonenumber: "Téléphone", phonenumber: "Téléphone",
mobilenumber: "Mobile", mobilenumber: "Mobile",
altnames: "Autres noms", altnames: "Autres noms",
email: "Courriel",
gender: { gender: {
title: "Genre", title: "Genre",
placeholder: "Choisissez le genre de l'usager", placeholder: "Choisissez le genre de l'usager",

View File

@ -29,29 +29,37 @@
{{ chill_list_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) }} {{ chill_list_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) }}
<div id="dashboards" class="row" data-masonry='{"percentPosition": true }'> <div id="dashboards" class="row g-3" data-masonry='{"percentPosition": true }'>
{% if 'DRAFT' == accompanyingCourse.step %} {% if 'DRAFT' == accompanyingCourse.step %}
<div class="col-4 warnings mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="warnings">
{% include '@ChillPerson/AccompanyingCourse/_still_draft.html.twig' %} {% include '@ChillPerson/AccompanyingCourse/_still_draft.html.twig' %}
</div> </div>
</div>
{% endif %} {% endif %}
{% if 'DRAFT' != accompanyingCourse.step %} {% if 'DRAFT' != accompanyingCourse.step %}
{% if withoutHousehold|length > 0 %} {% if withoutHousehold|length > 0 %}
<div class="col-4 warnings mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="warnings">
{% include '@ChillPerson/AccompanyingCourse/_join_household.html.twig' %} {% include '@ChillPerson/AccompanyingCourse/_join_household.html.twig' %}
</div> </div>
</div>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if accompanyingCourse.locationStatus == 'address' {% if accompanyingCourse.locationStatus == 'address'
or accompanyingCourse.locationStatus == 'none' %} or accompanyingCourse.locationStatus == 'none' %}
<div class="col-4 warnings mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="warnings">
{% include '@ChillPerson/AccompanyingCourse/_warning_address.html.twig' %} {% include '@ChillPerson/AccompanyingCourse/_warning_address.html.twig' %}
</div> </div>
</div>
{% endif %} {% endif %}
<div class="col col-sm-6 col-lg-4 location mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="location">
{% if accompanyingCourse.locationStatus == 'person' %} {% if accompanyingCourse.locationStatus == 'person' %}
<h2>{{ 'This course is located by'|trans }}</h2> <h2>{{ 'This course is located by'|trans }}</h2>
<h4>{{ accompanyingCourse.personLocation|chill_entity_render_string }}</h4> <h4>{{ accompanyingCourse.personLocation|chill_entity_render_string }}</h4>
@ -63,34 +71,15 @@
{{ accompanyingCourse.location|chill_entity_render_box }} {{ accompanyingCourse.location|chill_entity_render_box }}
{% endif %} {% endif %}
</div> </div>
{% if accompanyingCourse.participations is not empty %}
<div class="col col-sm-6 col-lg-4 persons mb-4">
<h4 class="item-key">{{ 'Persons associated'|trans }}</h4>
{% for r in accompanyingCourse.participations %}
{{ _self.insert_onthefly('person', r.person) }}
{% endfor %}
</div> </div>
{% endif %}
{% if accompanyingCourse.resources is not empty %}
<div class="col col-sm-6 col-lg-4 resources mb-4">
<h4 class="item-key">{{ 'Resources'|trans }}</h4>
{% for r in accompanyingCourse.resources %}
{% if r.person is not null %}
{{ _self.insert_onthefly('person', r.person) }}
{% elseif r.thirdParty is not null %}
{{ _self.insert_onthefly('thirdparty', r.thirdParty) }}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if accompanyingCourse.pinnedComment is not empty %} {% if accompanyingCourse.pinnedComment is not empty %}
<div class="col col-sm-6 col-lg-4 comment mb-4"> <div class="mbloc col col-sm-6 col-lg-8">
<h4 class="item-key">{{ 'Pinned comment'|trans }}</h4> <div class="comment">
<h4 class="item-key visually-hidden">{{ 'Pinned comment'|trans }}</h4>
<blockquote class="chill-user-quote"> <blockquote class="chill-user-quote">
{{ accompanyingCourse.pinnedComment.content }} <i class="fa fa-flag float-end text-chill-gray" title="{{ 'pinned'|trans }}"></i>
{{ accompanyingCourse.pinnedComment.content|chill_markdown_to_html }}
<div class="metadata"> <div class="metadata">
{{ 'Last updated by'| trans }} {{ 'Last updated by'| trans }}
<span class="user"> <span class="user">
@ -103,10 +92,38 @@
</div> </div>
</blockquote> </blockquote>
</div> </div>
</div>
{% endif %}
{% if accompanyingCourse.participations is not empty %}
<div class="mbloc col col-sm-6 col-lg-4">
<div class="persons">
<h4 class="item-key">{{ 'Persons associated'|trans }}</h4>
{% for r in accompanyingCourse.participations %}
{{ _self.insert_onthefly('person', r.person) }}
{% endfor %}
</div>
</div>
{% endif %}
{% if accompanyingCourse.resources is not empty %}
<div class="mbloc col col-sm-6 col-lg-4">
<div class="resources">
<h4 class="item-key">{{ 'Resources'|trans }}</h4>
{% for r in accompanyingCourse.resources %}
{% if r.person is not null %}
{{ _self.insert_onthefly('person', r.person) }}
{% elseif r.thirdParty is not null %}
{{ _self.insert_onthefly('thirdparty', r.thirdParty) }}
{% endif %}
{% endfor %}
</div>
</div>
{% endif %} {% endif %}
{% if accompanyingCourse.scopes is not empty %} {% if accompanyingCourse.scopes is not empty %}
<div class="col col-sm-6 col-lg-4 scopes mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="scopes">
<h4 class="item-key">{{ 'Scopes'|trans }}</h4> <h4 class="item-key">{{ 'Scopes'|trans }}</h4>
<div> <div>
{% for s in accompanyingCourse.scopes %} {% for s in accompanyingCourse.scopes %}
@ -114,6 +131,7 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</div>
{% endif %} {% endif %}
<div class="col col-sm-4 col-lg-4 notify mb-4"> <div class="col col-sm-4 col-lg-4 notify mb-4">
@ -121,7 +139,8 @@
</div> </div>
{% if accompanyingCourse.requestorPerson is not null or accompanyingCourse.requestorThirdParty is not null %} {% if accompanyingCourse.requestorPerson is not null or accompanyingCourse.requestorThirdParty is not null %}
<div class="col col-sm-6 col-lg-4 requestor mb-4"> <div class="mbloc col col-sm-6 col-lg-4">
<div class="requestor">
{% if accompanyingCourse.requestorPerson is not null %} {% if accompanyingCourse.requestorPerson is not null %}
<h4 class="item-key">{{ 'Requestor'|trans }}</h4> <h4 class="item-key">{{ 'Requestor'|trans }}</h4>
{{ _self.insert_onthefly('person', accompanyingCourse.requestorPerson) }} {{ _self.insert_onthefly('person', accompanyingCourse.requestorPerson) }}
@ -130,7 +149,18 @@
{{ _self.insert_onthefly('thirdparty', accompanyingCourse.requestorThirdParty) }} {{ _self.insert_onthefly('thirdparty', accompanyingCourse.requestorThirdParty) }}
{% endif %} {% endif %}
</div> </div>
</div>
{% endif %} {% endif %}
{% if accompanyingCourse.origin is not empty %}
<div class="mbloc col col-sm-6 col-lg-4">
<div class="origin">
<h4 class="item-key">{{ 'Origin'|trans }}</h4>
{{ accompanyingCourse.origin.label|localize_translatable_string|capitalize }}
</div>
</div>
{% endif %}
</div> </div>
<div class="social-actions my-4"> <div class="social-actions my-4">

View File

@ -124,7 +124,7 @@
{% if not person.isSharingHousehold() %} {% if not person.isSharingHousehold() %}
<ul class="record_actions"> <ul class="record_actions">
<li> <li>
<a class="btn btn-misc" href="{{ chill_path_add_return_path('chill_person_household_members_editor', { 'persons': [ person.id ]}) }}"> <a class="btn btn-misc" href="{{chill_path_add_return_path('chill_person_household_members_editor', { 'persons': [ person.id ], 'followAfter': 'true'}) }}">
<i class="fa fa-sign-in fa-fw"></i> <i class="fa fa-sign-in fa-fw"></i>
{{ 'household.Join'|trans }} {{ 'household.Join'|trans }}
</a> </a>

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer; namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper; use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\Scope; use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher; use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
@ -65,6 +66,8 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
'requestorPerson' => Person::class, 'requestorPerson' => Person::class,
'requestorThirdParty' => ThirdParty::class, 'requestorThirdParty' => ThirdParty::class,
'resources' => Collection::class, 'resources' => Collection::class,
'location' => Address::class,
'locationPerson' => Person::class,
]; ];
private ClosingMotiveRender $closingMotiveRender; private ClosingMotiveRender $closingMotiveRender;
@ -104,6 +107,7 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
$scopes = [$scopes]; $scopes = [$scopes];
} }
$addressContext = array_merge($context, ['docgen:expects' => Address::class, 'groups' => 'docgen:read']);
$dateContext = array_merge($context, ['docgen:expects' => DateTime::class, 'groups' => 'docgen:read']); $dateContext = array_merge($context, ['docgen:expects' => DateTime::class, 'groups' => 'docgen:read']);
$userContext = array_merge($context, ['docgen:expects' => User::class, 'groups' => 'docgen:read']); $userContext = array_merge($context, ['docgen:expects' => User::class, 'groups' => 'docgen:read']);
$participationContext = array_merge($context, ['docgen:expects' => AccompanyingPeriodParticipation::class, 'groups' => 'docgen:read']); $participationContext = array_merge($context, ['docgen:expects' => AccompanyingPeriodParticipation::class, 'groups' => 'docgen:read']);
@ -147,6 +151,10 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
}, $scopes)), }, $scopes)),
'hasRequestor' => $period->getRequestor() !== null, 'hasRequestor' => $period->getRequestor() !== null,
'requestorKind' => $period->getRequestorKind(), 'requestorKind' => $period->getRequestorKind(),
'hasLocation' => $period->getLocation() !== null,
'hasLocationPerson' => $period->getPersonLocation() !== null,
'locationPerson' => $this->normalizer->normalize($period->getPersonLocation(), $format, array_merge($context, ['docgen:expects' => Person::class])),
'location' => $this->normalizer->normalize($period->getLocation(), $format, $addressContext),
]; ];
} }
@ -162,6 +170,8 @@ class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterf
'hasRequestorThirdParty' => false, 'hasRequestorThirdParty' => false,
'isClosed' => false, 'isClosed' => false,
'confidential' => false, 'confidential' => false,
'hasLocation' => false,
'hasLocationPerson' => false,
] ]
); );
} }

View File

@ -15,10 +15,11 @@ use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension; use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\PersonRepository;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
use LogicException; use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
@ -78,13 +79,24 @@ class PersonJsonNormalizer implements
$person = new Person(); $person = new Person();
} }
foreach (['firstName', 'lastName', 'phonenumber', 'mobilenumber', 'gender', $fields = [
'birthdate', 'deathdate', 'center', ] 'firstName',
as $item) { 'lastName',
if (!array_key_exists($item, $data)) { 'phonenumber',
continue; 'mobilenumber',
} 'gender',
'birthdate',
'deathdate',
'center',
'altNames',
];
$fields = array_filter(
$fields,
static fn (string $field): bool => array_key_exists($field, $data)
);
foreach ($fields as $item) {
switch ($item) { switch ($item) {
case 'firstName': case 'firstName':
$person->setFirstName($data[$item]); $person->setFirstName($data[$item]);
@ -131,8 +143,23 @@ class PersonJsonNormalizer implements
break; break;
default: case 'altNames':
throw new LogicException("item not defined: {$item}"); foreach ($data[$item] as $altName) {
$oldAltName = $person
->getAltNames()
->filter(static fn (PersonAltName $n): bool => $n->getKey() === $altName['key'])->first();
if (false === $oldAltName) {
$newAltName = new PersonAltName();
$newAltName->setKey($altName['key']);
$newAltName->setLabel($altName['label']);
$person->addAltName($newAltName);
} else {
$oldAltName->setLabel($altName['label']);
}
}
break;
} }
} }
@ -175,14 +202,22 @@ class PersonJsonNormalizer implements
return $data instanceof Person && 'json' === $format; return $data instanceof Person && 'json' === $format;
} }
protected function normalizeAltNames($altNames): array /**
* @param Collection<array-key, PersonAltName> $altNames
*
* @return array<array-key, array<string, string>>
*/
protected function normalizeAltNames(Collection $altNames): array
{ {
$r = []; return $altNames
->map(
foreach ($altNames as $n) { static function (PersonAltName $personAltName): array {
$r[] = ['key' => $n->getKey(), 'label' => $n->getLabel()]; return [
'key' => $personAltName->getKey(),
'label' => $personAltName->getLabel(),
];
} }
)
return $r; ->toArray();
} }
} }

View File

@ -11,7 +11,6 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Tests\AccompanyingPeriod; namespace Chill\PersonBundle\Tests\AccompanyingPeriod;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
@ -45,13 +44,13 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
public function dataGenerateRandomAccompanyingCourse() public function dataGenerateRandomAccompanyingCourse()
{ {
// Disabling this dataprovider to avoid having errors while running the test.
return yield from [];
$maxGenerated = 3; $maxGenerated = 3;
$maxResults = $maxGenerated * 8; $maxResults = $maxGenerated * 8;
self::bootKernel(); self::bootKernel();
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); $em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
$center = $em->getRepository(Center::class)
->findOneBy(['name' => 'Center A']);
$qb = $em->createQueryBuilder(); $qb = $em->createQueryBuilder();
$personIds = $qb $personIds = $qb
@ -80,8 +79,7 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
while ($nbGenerated < $maxGenerated) { while ($nbGenerated < $maxGenerated) {
$id = array_pop($personIds)['id']; $id = array_pop($personIds)['id'];
$person = $em->getRepository(Person::class) $person = $em->getRepository(Person::class)->find($id);
->find($id);
$periods = $person->getAccompanyingPeriods(); $periods = $person->getAccompanyingPeriods();
yield [array_pop($personIds)['id'], $periods[array_rand($periods)]->getId()]; yield [array_pop($personIds)['id'], $periods[array_rand($periods)]->getId()];
@ -95,6 +93,10 @@ final class AccompanyingPeriodConfidentialTest extends WebTestCase
*/ */
public function testRemoveUserWhenConfidential(int $periodId) public function testRemoveUserWhenConfidential(int $periodId)
{ {
$this->markTestIncomplete(
'Marked as incomplete because of a problem in the dataprovider, at line 81.'
);
$period = self::$container->get(AccompanyingPeriodRepository::class) $period = self::$container->get(AccompanyingPeriodRepository::class)
->find($periodId); ->find($periodId);
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager'); $em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');

View File

@ -111,7 +111,7 @@ final class PersonAddressControllerTest extends WebTestCase
$crawler = $this->client->followRedirect(); $crawler = $this->client->followRedirect();
$this->assertRegexp( $this->assertMatchesRegularExpression(
'|/fr/person/[0-9]{1,}/address/list|', '|/fr/person/[0-9]{1,}/address/list|',
$this->client->getHistory()->current()->getUri(), $this->client->getHistory()->current()->getUri(),
'assert that the current page is on |/fr/person/[0-9]{1,}/address/list|' 'assert that the current page is on |/fr/person/[0-9]{1,}/address/list|'
@ -169,7 +169,7 @@ final class PersonAddressControllerTest extends WebTestCase
$crawler = $this->client->followRedirect(); $crawler = $this->client->followRedirect();
$this->assertRegexp( $this->assertMatchesRegularExpression(
'|/fr/person/[0-9]{1,}/address/list|', '|/fr/person/[0-9]{1,}/address/list|',
$this->client->getHistory()->current()->getUri(), $this->client->getHistory()->current()->getUri(),
'assert that the current page is on |/fr/person/[0-9]{1,}/address/list|' 'assert that the current page is on |/fr/person/[0-9]{1,}/address/list|'

View File

@ -206,7 +206,7 @@ final class PersonControllerCreateTest extends WebTestCase
$client->followRedirect(); $client->followRedirect();
// visualize regexp here : http://jex.im/regulex/#!embed=false&flags=&re=%2Ffr%2Fperson%2F[1-9][0-9]*%2Fgeneral%2Fedit%24 // visualize regexp here : http://jex.im/regulex/#!embed=false&flags=&re=%2Ffr%2Fperson%2F[1-9][0-9]*%2Fgeneral%2Fedit%24
$this->assertRegExp( $this->assertMatchesRegularExpression(
'|/fr/person/[1-9][0-9]*/general/edit$|', '|/fr/person/[1-9][0-9]*/general/edit$|',
$client->getHistory()->current()->getUri(), $client->getHistory()->current()->getUri(),
'a valid form redirect to url /{_locale}/person/{personId}/general/edit' 'a valid form redirect to url /{_locale}/person/{personId}/general/edit'
@ -259,7 +259,7 @@ final class PersonControllerCreateTest extends WebTestCase
'a valid form redirect to url /{_locale}/person/{personId}/general/edit' 'a valid form redirect to url /{_locale}/person/{personId}/general/edit'
); );
$client->followRedirect(); $client->followRedirect();
$this->assertRegExp( $this->assertMatchesRegularExpression(
'|/fr/person/[1-9][0-9]*/general/edit$|', '|/fr/person/[1-9][0-9]*/general/edit$|',
$client->getHistory()->current()->getUri(), $client->getHistory()->current()->getUri(),
'a valid form redirect to url /{_locale}/person/{personId}/general/edit' 'a valid form redirect to url /{_locale}/person/{personId}/general/edit'

View File

@ -26,19 +26,19 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawlerSpecial = $this->generateCrawlerForSearch('@person manço'); $crawlerSpecial = $this->generateCrawlerForSearch('@person manço');
$this->assertRegExp('/MANÇO/', $crawlerSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/MANÇO/', $crawlerSpecial->filter('.list-with-period')->text());
$crawlerNoSpecial = $this->generateCrawlerForSearch('@person manco'); $crawlerNoSpecial = $this->generateCrawlerForSearch('@person manco');
$this->assertRegExp('/MANÇO/', $crawlerNoSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/MANÇO/', $crawlerNoSpecial->filter('.list-with-period')->text());
$crawlerSpecial = $this->generateCrawlerForSearch('@person Étienne'); $crawlerSpecial = $this->generateCrawlerForSearch('@person Étienne');
$this->assertRegExp('/Étienne/', $crawlerSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Étienne/', $crawlerSpecial->filter('.list-with-period')->text());
$crawlerNoSpecial = $this->generateCrawlerForSearch('@person etienne'); $crawlerNoSpecial = $this->generateCrawlerForSearch('@person etienne');
$this->assertRegExp('/Étienne/', $crawlerNoSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Étienne/', $crawlerNoSpecial->filter('.list-with-period')->text());
} }
public function testExpected() public function testExpected()
@ -49,7 +49,7 @@ final class PersonSearchTest extends WebTestCase
'q' => '@person Depardieu', 'q' => '@person Depardieu',
]); ]);
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testExpectedNamed() public function testExpectedNamed()
@ -60,79 +60,79 @@ final class PersonSearchTest extends WebTestCase
'q' => '@person Depardieu', 'name' => 'person_regular', 'q' => '@person Depardieu', 'name' => 'person_regular',
]); ]);
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testLastNameAccentued() public function testLastNameAccentued()
{ {
$crawlerSpecial = $this->generateCrawlerForSearch('@person lastname:manço'); $crawlerSpecial = $this->generateCrawlerForSearch('@person lastname:manço');
$this->assertRegExp('/MANÇO/', $crawlerSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/MANÇO/', $crawlerSpecial->filter('.list-with-period')->text());
$crawlerNoSpecial = $this->generateCrawlerForSearch('@person lastname:manco'); $crawlerNoSpecial = $this->generateCrawlerForSearch('@person lastname:manco');
$this->assertRegExp('/MANÇO/', $crawlerNoSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/MANÇO/', $crawlerNoSpecial->filter('.list-with-period')->text());
} }
public function testSearchBirthdate() public function testSearchBirthdate()
{ {
$crawler = $this->generateCrawlerForSearch('@person birthdate:1948-12-27'); $crawler = $this->generateCrawlerForSearch('@person birthdate:1948-12-27');
$this->assertRegExp('/Gérard/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Gérard/', $crawler->filter('.list-with-period')->text());
$this->assertRegExp('/Bart/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Bart/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByFirstName() public function testSearchByFirstName()
{ {
$crawler = $this->generateCrawlerForSearch('@person firstname:Jean'); $crawler = $this->generateCrawlerForSearch('@person firstname:Jean');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByFirstNameAccented() public function testSearchByFirstNameAccented()
{ {
$crawlerSpecial = $this->generateCrawlerForSearch('@person firstname:Gérard'); $crawlerSpecial = $this->generateCrawlerForSearch('@person firstname:Gérard');
$this->assertRegExp('/Gérard/', $crawlerSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Gérard/', $crawlerSpecial->filter('.list-with-period')->text());
$crawlerNoSpecial = $this->generateCrawlerForSearch('@person firstname:Gerard'); $crawlerNoSpecial = $this->generateCrawlerForSearch('@person firstname:Gerard');
$this->assertRegExp('/Gérard/', $crawlerNoSpecial->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Gérard/', $crawlerNoSpecial->filter('.list-with-period')->text());
} }
public function testSearchByFirstNameLower() public function testSearchByFirstNameLower()
{ {
$crawler = $this->generateCrawlerForSearch('@person firstname:Gérard'); $crawler = $this->generateCrawlerForSearch('@person firstname:Gérard');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByFirstNameLower2() public function testSearchByFirstNameLower2()
{ {
$crawler = $this->generateCrawlerForSearch('@person firstname:jean'); $crawler = $this->generateCrawlerForSearch('@person firstname:jean');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByFirstNamePartim() public function testSearchByFirstNamePartim()
{ {
$crawler = $this->generateCrawlerForSearch('@person firstname:Ger'); $crawler = $this->generateCrawlerForSearch('@person firstname:Ger');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByFirstNamePartim2() public function testSearchByFirstNamePartim2()
{ {
$crawler = $this->generateCrawlerForSearch('@person firstname:ean'); $crawler = $this->generateCrawlerForSearch('@person firstname:ean');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchByLastName() public function testSearchByLastName()
{ {
$crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu'); $crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu');
$this->assertRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchCombineBirthdateAndLastName() public function testSearchCombineBirthdateAndLastName()
@ -140,8 +140,8 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawler = $this->generateCrawlerForSearch('@person birthdate:1948-12-27 lastname:(Van Snick)'); $crawler = $this->generateCrawlerForSearch('@person birthdate:1948-12-27 lastname:(Van Snick)');
$this->assertRegExp('/Bart/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Bart/', $crawler->filter('.list-with-period')->text());
$this->assertNotRegExp('/DEPARDIEU/', $crawler->filter('.list-with-period')->text()); $this->assertDoesNotMatchRegularExpression('/DEPARDIEU/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchCombineGenderAndLastName() public function testSearchCombineGenderAndLastName()
@ -149,8 +149,8 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawler = $this->generateCrawlerForSearch('@person gender:woman lastname:(Depardieu)'); $crawler = $this->generateCrawlerForSearch('@person gender:woman lastname:(Depardieu)');
$this->assertRegExp('/Charline/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Charline/', $crawler->filter('.list-with-period')->text());
$this->assertNotRegExp('/Gérard/', $crawler->filter('.list-with-period')->text()); $this->assertDoesNotMatchRegularExpression('/Gérard/', $crawler->filter('.list-with-period')->text());
} }
public function testSearchCombineLastnameAndFirstName() public function testSearchCombineLastnameAndFirstName()
@ -158,9 +158,9 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu firstname:Jean'); $crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu firstname:Jean');
$this->assertRegExp('/Depardieu/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Depardieu/', $crawler->filter('.list-with-period')->text());
//if this is a AND clause, Jean Depardieu should not appears //if this is a AND clause, Jean Depardieu should not appears
$this->assertNotRegExp( $this->assertDoesNotMatchRegularExpression(
'/Gérard/', '/Gérard/',
$crawler->filter('.list-with-period')->text(), $crawler->filter('.list-with-period')->text(),
'assert clause firstname and nationality are AND' 'assert clause firstname and nationality are AND'
@ -172,9 +172,9 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu nationality:RU'); $crawler = $this->generateCrawlerForSearch('@person lastname:Depardieu nationality:RU');
$this->assertRegExp('/Gérard/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Gérard/', $crawler->filter('.list-with-period')->text());
//if this is a AND clause, Jean Depardieu should not appears //if this is a AND clause, Jean Depardieu should not appears
$this->assertNotRegExp( $this->assertDoesNotMatchRegularExpression(
'/Jean/', '/Jean/',
$crawler->filter('.list-with-period')->text(), $crawler->filter('.list-with-period')->text(),
'assert clause firstname and nationality are AND' 'assert clause firstname and nationality are AND'
@ -186,9 +186,9 @@ final class PersonSearchTest extends WebTestCase
$this->markTestSkipped('skipped until adapted to new fixtures'); $this->markTestSkipped('skipped until adapted to new fixtures');
$crawler = $this->generateCrawlerForSearch('@person cha dep'); $crawler = $this->generateCrawlerForSearch('@person cha dep');
$this->assertRegExp('/Charline/', $crawler->filter('.list-with-period')->text()); $this->assertMatchesRegularExpression('/Charline/', $crawler->filter('.list-with-period')->text());
$this->assertNotRegExp('/Gérard/', $crawler->filter('.list-with-period')->text()); $this->assertDoesNotMatchRegularExpression('/Gérard/', $crawler->filter('.list-with-period')->text());
$this->assertNotRegExp('/Jean/', $crawler->filter('.list-with-period')->text()); $this->assertDoesNotMatchRegularExpression('/Jean/', $crawler->filter('.list-with-period')->text());
} }
/** /**
@ -199,12 +199,12 @@ final class PersonSearchTest extends WebTestCase
$crawlerCanSee = $this->generateCrawlerForSearch('Gérard', 'center a_social'); $crawlerCanSee = $this->generateCrawlerForSearch('Gérard', 'center a_social');
$crawlerCannotSee = $this->generateCrawlerForSearch('Gérard', 'center b_social'); $crawlerCannotSee = $this->generateCrawlerForSearch('Gérard', 'center b_social');
$this->assertRegExp( $this->assertMatchesRegularExpression(
'/DEPARDIEU/', '/DEPARDIEU/',
$crawlerCanSee->text(), $crawlerCanSee->text(),
'center a_social may see "Depardieu" in center a' 'center a_social may see "Depardieu" in center a'
); );
$this->assertNotRegExp( $this->assertDoesNotMatchRegularExpression(
'/DEPARDIEU/', '/DEPARDIEU/',
$crawlerCannotSee->text(), $crawlerCannotSee->text(),
'center b_social may not see "Depardieu" in center b' 'center b_social may not see "Depardieu" in center b'

View File

@ -1820,3 +1820,12 @@ paths:
description: "Unauthorized" description: "Unauthorized"
200: 200:
description: "OK" description: "OK"
/1.0/person/config/alt_names.json:
get:
tags:
- person
summary: Return a list of possible altNames that are defined in the config
responses:
200:
description: "OK"

View File

@ -44,6 +44,7 @@ services:
Chill\PersonBundle\Controller\PersonApiController: Chill\PersonBundle\Controller\PersonApiController:
arguments: arguments:
$authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper' $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
$configPersonAltNameHelper: '@Chill\PersonBundle\Config\ConfigPersonAltNamesHelper'
tags: ['controller.service_arguments'] tags: ['controller.service_arguments']
Chill\PersonBundle\Controller\AccompanyingCourseWorkApiController: Chill\PersonBundle\Controller\AccompanyingCourseWorkApiController:

View File

@ -215,6 +215,7 @@ Some peoples does not belong to any household currently. Add them to an househol
Add to household now: Ajouter à un ménage Add to household now: Ajouter à un ménage
Any resource for this accompanying course: Aucun interlocuteur privilégié pour ce parcours Any resource for this accompanying course: Aucun interlocuteur privilégié pour ce parcours
course.draft: Brouillon course.draft: Brouillon
Origin: Origine de la demande
# pickAPersonType # pickAPersonType
Pick a person: Choisir une personne Pick a person: Choisir une personne

View File

@ -212,7 +212,7 @@ final class ReportControllerTest extends WebTestCase
$linkSee = $crawler->filter('.bt-view')->links(); $linkSee = $crawler->filter('.bt-view')->links();
$this->assertGreaterThan(0, count($linkSee)); $this->assertGreaterThan(0, count($linkSee));
$this->assertRegExp(sprintf( $this->assertMatchesRegularExpression(sprintf(
'|/fr/person/%s/report/[0-9]*/view$|', '|/fr/person/%s/report/[0-9]*/view$|',
self::$person->getId(), self::$person->getId(),
$reportId $reportId
@ -220,7 +220,7 @@ final class ReportControllerTest extends WebTestCase
$linkUpdate = $crawler->filter('.bt-update')->links(); $linkUpdate = $crawler->filter('.bt-update')->links();
$this->assertGreaterThan(0, count($linkUpdate)); $this->assertGreaterThan(0, count($linkUpdate));
$this->assertRegExp(sprintf( $this->assertMatchesRegularExpression(sprintf(
'|/fr/person/%s/report/[0-9]*/edit$|', '|/fr/person/%s/report/[0-9]*/edit$|',
self::$person->getId(), self::$person->getId(),
$reportId $reportId
@ -372,7 +372,7 @@ final class ReportControllerTest extends WebTestCase
); );
$client->followRedirect(); $client->followRedirect();
$this->assertRegExp( $this->assertMatchesRegularExpression(
'|/fr/person/' . self::$person->getId() . '/report/[0-9]*/view$|', '|/fr/person/' . self::$person->getId() . '/report/[0-9]*/view$|',
$client->getHistory()->current()->getUri(), $client->getHistory()->current()->getUri(),
"The next page is a redirection to the new report's view page" "The next page is a redirection to the new report's view page"

View File

@ -43,7 +43,7 @@ final class ReportSearchTest extends WebTestCase
]); ]);
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->assertRegExp('/Situation de logement/i', $crawler->text()); $this->assertMatchesRegularExpression('/Situation de logement/i', $crawler->text());
} }
public function testSearchDoubleDate() public function testSearchDoubleDate()
@ -77,7 +77,7 @@ final class ReportSearchTest extends WebTestCase
]); ]);
$this->assertTrue($client->getResponse()->isSuccessful()); $this->assertTrue($client->getResponse()->isSuccessful());
$this->assertRegExp('/Situation de logement/i', $crawler->text()); $this->assertMatchesRegularExpression('/Situation de logement/i', $crawler->text());
} }
/** /**