From f434cc5c0251a3d2e2f919653d6b12c008d77049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 12:47:32 +0200 Subject: [PATCH] Fixed: [list person] fix list person and add new fields --- phpstan-critical.neon | 10 - phpstan-types.neon | 5 - .../Export/Helper/ExportAddressHelper.php | 32 +- .../Repository/CivilityRepository.php | 43 ++- .../CivilityRepositoryInterface.php | 34 ++ .../Repository/LanguageRepository.php | 7 +- .../LanguageRepositoryInterface.php | 37 ++ .../translations/messages.fr.yml | 4 + .../Household/PersonHouseholdAddress.php | 2 +- .../Export/Export/ListPerson.php | 330 +++++++++++++++--- .../Repository/MaritalStatusRepository.php | 3 +- .../MaritalStatusRepositoryInterface.php | 27 ++ .../translations/messages.fr.yml | 15 + 13 files changed, 457 insertions(+), 92 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php create mode 100644 src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php create mode 100644 src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php diff --git a/phpstan-critical.neon b/phpstan-critical.neon index 1dc516834..bfbb2dc7c 100644 --- a/phpstan-critical.neon +++ b/phpstan-critical.neon @@ -5,11 +5,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php - - - message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\Household\\\\PersonHouseholdAddress\\:\\:\\$relation\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Entity/Household/PersonHouseholdAddress.php - - message: "#^Access to an undefined property Chill\\\\PersonBundle\\\\Entity\\\\AccompanyingPeriod\\:\\:\\$work\\.$#" count: 1 @@ -30,11 +25,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Serializer/Normalizer/MembersEditorNormalizer.php - - - message: "#^Undefined variable\\: \\$choiceSlug$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - message: "#^Undefined variable\\: \\$choiceSlug$#" count: 1 diff --git a/phpstan-types.neon b/phpstan-types.neon index a0493ce0b..bf2ece912 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -340,11 +340,6 @@ parameters: count: 1 path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 2 - path: src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 09aaa7a1d..6c6fcb76e 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -20,32 +20,32 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use function strlen; /** - * Helps to load addresses and format them in list + * Helps to load addresses and format them in list. */ class ExportAddressHelper { - public const ATTRIBUTES = 0b01000000; + public const F_AS_STRING = 0b00010000; - public const BUILDING = 0b00001000; + public const F_ATTRIBUTES = 0b01000000; - public const COUNTRY = 0b00000001; + public const F_BUILDING = 0b00001000; - public const GEOM = 0b00100000; + public const F_COUNTRY = 0b00000001; - public const POSTAL_CODE = 0b00000010; + public const F_GEOM = 0b00100000; - public const STREET = 0b00000100; + public const F_POSTAL_CODE = 0b00000010; - public const STRING = 0b00010000; + public const F_STREET = 0b00000100; private const ALL = [ - 'country' => self::COUNTRY, - 'postal_code' => self::POSTAL_CODE, - 'street' => self::STREET, - 'building' => self::BUILDING, - 'string' => self::STRING, - 'geom' => self::GEOM, - 'attributes' => self::ATTRIBUTES, + 'country' => self::F_COUNTRY, + 'postal_code' => self::F_POSTAL_CODE, + 'street' => self::F_STREET, + 'building' => self::F_BUILDING, + 'string' => self::F_AS_STRING, + 'geom' => self::F_GEOM, + 'attributes' => self::F_ATTRIBUTES, ]; private const COLUMN_MAPPING = [ @@ -78,6 +78,8 @@ class ExportAddressHelper } /** + * @param self::F_* $params + * * @return array|string[] */ public function getKeys(int $params, string $prefix = ''): array diff --git a/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php b/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php index 385488839..70b4a2bc4 100644 --- a/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/CivilityRepository.php @@ -12,19 +12,40 @@ declare(strict_types=1); namespace Chill\MainBundle\Repository; use Chill\MainBundle\Entity\Civility; -use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; -use Doctrine\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; -/** - * @method Civility|null find($id, $lockMode = null, $lockVersion = null) - * @method Civility|null findOneBy(array $criteria, array $orderBy = null) - * @method Civility[] findAll() - * @method Civility[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -class CivilityRepository extends ServiceEntityRepository +class CivilityRepository implements CivilityRepositoryInterface { - public function __construct(ManagerRegistry $registry) + private EntityRepository $repository; + + public function __construct(EntityManagerInterface $entityManager) { - parent::__construct($registry, Civility::class); + $this->repository = $entityManager->getRepository($this->getClassName()); + } + + public function find($id): ?Civility + { + return $this->repository->find($id); + } + + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?Civility + { + return $this->repository->findOneBy($criteria); + } + + public function getClassName(): string + { + return Civility::class; } } diff --git a/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php new file mode 100644 index 000000000..5d687ac7e --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/CivilityRepositoryInterface.php @@ -0,0 +1,34 @@ +repository = $entityManager->getRepository(Language::class); + $this->repository = $entityManager->getRepository($this->getClassName()); } public function find($id, $lockMode = null, $lockVersion = null): ?Language @@ -54,7 +53,7 @@ final class LanguageRepository implements ObjectRepository return $this->repository->findOneBy($criteria, $orderBy); } - public function getClassName() + public function getClassName(): string { return Language::class; } diff --git a/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php new file mode 100644 index 000000000..397b264e4 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Repository/LanguageRepositoryInterface.php @@ -0,0 +1,37 @@ +relation; + return $this->household; } public function getPerson(): ?Person diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index e799a53e4..631e3b69a 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -20,15 +20,20 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Repository\CivilityRepositoryInterface; use Chill\MainBundle\Repository\CountryRepository; +use Chill\MainBundle\Repository\LanguageRepositoryInterface; +use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; +use Chill\PersonBundle\Repository\MaritalStatusRepositoryInterface; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTime; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -44,7 +49,6 @@ use function array_merge; use function count; use function in_array; use function strlen; -use function strtolower; use function uniqid; /** @@ -54,42 +58,89 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { public const FIELDS = [ 'id', + 'civility', 'firstName', 'lastName', 'birthdate', - 'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber', - 'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality', - 'address', + 'center', + 'deathdate', + 'placeOfBirth', + 'gender', + 'genderComment', + 'maritalStatus', + 'maritalStatusComment', + 'maritalStatusDate', + 'memo', + 'email', + 'phonenumber', + 'mobilenumber', + 'numberOfChildren', + 'contactInfo', + 'countryOfBirth', + 'nationality', + // add full addresses + 'address_fields', + // add a list of spoken languages + 'spokenLanguages', + // add household id + 'household_id', + // add created at, created by, updated at, and updated by + 'lifecycleUpdate', ]; + private const HELPER_ATTRIBUTES = + ExportAddressHelper::F_ATTRIBUTES | + ExportAddressHelper::F_BUILDING | + ExportAddressHelper::F_COUNTRY | + ExportAddressHelper::F_GEOM | + ExportAddressHelper::F_POSTAL_CODE | + ExportAddressHelper::F_STREET | + ExportAddressHelper::F_AS_STRING; + private ExportAddressHelper $addressHelper; + private CivilityRepositoryInterface $civilityRepository; + private CountryRepository $countryRepository; private CustomFieldProvider $customFieldProvider; private EntityManagerInterface $entityManager; + private LanguageRepositoryInterface $languageRepository; + + private MaritalStatusRepositoryInterface $maritalStatusRepository; + private $slugs = []; private TranslatableStringHelper $translatableStringHelper; private TranslatorInterface $translator; + private UserRepositoryInterface $userRepository; + public function __construct( - CountryRepository $countryRepository, ExportAddressHelper $addressHelper, + CivilityRepositoryInterface $civilityRepository, + CountryRepository $countryRepository, + CustomFieldProvider $customFieldProvider, EntityManagerInterface $em, - TranslatorInterface $translator, + LanguageRepositoryInterface $languageRepository, + MaritalStatusRepositoryInterface $maritalStatusRepository, TranslatableStringHelper $translatableStringHelper, - CustomFieldProvider $customFieldProvider + TranslatorInterface $translator, + UserRepositoryInterface $userRepository ) { $this->addressHelper = $addressHelper; + $this->civilityRepository = $civilityRepository; $this->countryRepository = $countryRepository; $this->entityManager = $em; + $this->languageRepository = $languageRepository; + $this->maritalStatusRepository = $maritalStatusRepository; $this->translator = $translator; $this->translatableStringHelper = $translatableStringHelper; $this->customFieldProvider = $customFieldProvider; + $this->userRepository = $userRepository; } public function buildForm(FormBuilderInterface $builder) @@ -110,7 +161,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou 'label' => 'Fields to include in export', 'choice_attr' => static function (string $val): array { // add a 'data-display-target' for address fields - if (substr($val, 0, 7) === 'address') { + if (substr($val, 0, 7) === 'address' || 'center' === $val || 'household' === $val) { return ['data-display-target' => 'address_date']; } @@ -130,7 +181,8 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // add a date field for addresses $builder->add('address_date', ChillDateType::class, [ - 'label' => 'Address valid at this date', + 'label' => 'Data valid at this date', + 'help' => 'Data regarding center, addresses, and so on will be computed at this date', 'data' => new DateTimeImmutable(), 'input' => 'datetime_immutable', ]); @@ -153,17 +205,21 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { - if (substr($key, 0, strlen('address')) === 'address') { - return $this->addressHelper->getLabel($key, $values, $data, 'address_'); + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); } switch ($key) { case 'birthdate': + case 'deathdate': + case 'maritalStatusDate': + case 'createdAt': + case 'updatedAt': // for birthdate, we have to transform the string into a date // to format the date correctly. - return static function ($value) { + return function ($value) use ($key) { if ('_header' === $value) { - return 'birthdate'; + return $this->translator->trans($key); } if (null === $value) { @@ -172,32 +228,120 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // warning: won't work with DateTimeImmutable as we reset time a few lines later $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + // check that the creation could occurs. if (false === $date) { throw new Exception(sprintf('The value %s could ' . 'not be converted to %s', $value, DateTime::class)); } - $date->setTime(0, 0, 0); + if (!$hasTime) { + $date->setTime(0, 0, 0); + } return $date; }; + case 'createdBy': + case 'updatedBy': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + return $this->userRepository->find($value)->getLabel(); + }; + + case 'civility': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $civility = $this->civilityRepository->find($value); + + if (null === $civility) { + return ''; + } + + return $this->translatableStringHelper->localize($civility->getName()); + }; + case 'gender': // for gender, we have to translate men/women statement - return function ($value) { + return function ($value) use ($key) { if ('_header' === $value) { - return 'gender'; + return $this->translator->trans($key); } return $this->translator->trans($value); }; + case 'maritalStatus': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $maritalStatus = $this->maritalStatusRepository->find($value); + + return $this->translatableStringHelper->localize($maritalStatus->getName()); + }; + + case 'spokenLanguages': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $ids = json_decode($value); + + return + implode( + '|', + array_map(function ($id) { + if (null === $id) { + return ''; + } + + $lang = $this->languageRepository->find($id); + + if (null === $lang) { + return null; + } + + return $this->translatableStringHelper->localize($lang->getName()); + }, $ids) + ); + }; + case 'countryOfBirth': case 'nationality': return function ($value) use ($key) { if ('_header' === $value) { - return strtolower($key); + return $this->translator->trans($key); } if (null === $value) { @@ -214,9 +358,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou default: // for fields which are associated with person if (in_array($key, self::FIELDS, true)) { - return static function ($value) use ($key) { + return function ($value) use ($key) { if ('_header' === $value) { - return strtolower($key); + return $this->translator->trans($key); } return $value; @@ -231,9 +375,19 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { $fields = []; - foreach ($data['fields'] as $key) { - if (substr($key, 0, strlen('address')) === 'address') { - $fields = array_merge($fields, $this->addressHelper->getKeys(0b01111111, 'address_')); + foreach (self::FIELDS as $key) { + if (!in_array($key, $data['fields'], true)) { + continue; + } + + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + $fields = array_merge($fields, $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields')); + + continue; + } + + if ('lifecycleUpdate' === $key) { + $fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']); continue; } @@ -286,8 +440,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou ) ->setParameter('authorized_centers', $centers); + $fields = $data['fields']; + foreach (self::FIELDS as $f) { - if (!in_array($f, $data['fields'], true)) { + if (!in_array($f, $fields, true)) { continue; } @@ -298,31 +454,90 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou break; - case 'address': - foreach ($this->addressHelper->getKeys(0b01111111, 'address_') as $key) { + case 'address_fields': + foreach ($this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') as $key) { $qb - ->addSelect(sprintf('IDENTITY(currentPersonAddress.address) AS %s', $key)); + ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); } - if (!(in_array('currentPersonAddress', $qb->getAllAliases(), true))) { - $qb - ->leftJoin('person.currentPersonAddress', 'currentPersonAddress') - ->andWhere( - $qb->expr()->orX( - // no address at this time - $qb->expr()->isNull('currentPersonAddress'), - // there is one address... - $qb->expr()->andX( - $qb->expr()->lte('currentPersonAddress.validFrom', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('currentPersonAddress.validTo'), - $qb->expr()->gt('currentPersonAddress.validTo', ':address_date') - ) + $this->addCurrentAddressAt($qb, $data['address_date']); + + break; + + case 'spokenLanguages': + $qb + ->leftJoin('person.spokenLanguages', 'spokenLanguage') + ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') + ->addGroupBy('person'); + + if (in_array('center', $fields, true)) { + $qb->addGroupBy('center'); + } + + if (in_array('address_fields', $fields, true)) { + $qb->addGroupBy('address_fieldsid'); + } + + if (in_array('household_id', $fields, true)) { + $qb->addGroupBy('household_id'); + } + + break; + + case 'household_id': + $qb + ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); + + $this->addCurrentAddressAt($qb, $data['address_date']); + + break; + + case 'center': + $qb + ->addSelect('IDENTITY(centerHistory.center) AS center') + ->leftJoin('person.centerHistory', 'centerHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory'), + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gte('centerHistory.endDate', ':address_date') ) ) ) - ->setParameter('address_date', $data['address_date']); - } + ) + ->setParameter('address_date', $data['address_date']); + + break; + + case 'lifecycleUpdate': + $qb + ->addSelect('person.createdAt AS createdAt') + ->addSelect('IDENTITY(person.createdBy) AS createdBy') + ->addSelect('person.updatedAt AS updatedAt') + ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); + + break; + + case 'genderComment': + $qb->addSelect('person.genderComment.comment AS genderComment'); + + break; + + case 'maritalStatus': + $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); + + break; + + case 'maritalStatusComment': + $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); + + break; + + case 'civility': + $qb->addSelect('IDENTITY(person.civility) AS civility'); break; @@ -332,6 +547,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } foreach ($this->getCustomFields() as $cf) { + if (!in_array($cf->getSlug(), $fields, true)) { + continue; + } + $cfType = $this->customFieldProvider->getCustomFieldByType($cf->getType()); if ($cfType instanceof CustomFieldChoice && $cfType->isMultiple($cf)) { @@ -383,7 +602,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou // check if there is one field starting with address in data if (count(array_intersect($data['fields'], $addressFields)) > 0) { // if a field address is checked, the date must not be empty - if (empty($data['address_date'])) { + if (!$data['address_date'] instanceof DateTimeImmutable) { $context ->buildViolation('You must set this date if an address is checked') ->atPath('address_date') @@ -392,6 +611,29 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } } + private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void + { + if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { + $qb + ->leftJoin('person.householdAddresses', 'personHouseholdAddress') + ->andWhere( + $qb->expr()->orX( + // no address at this time + $qb->expr()->isNull('personHouseholdAddress'), + // there is one address... + $qb->expr()->andX( + $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('personHouseholdAddress.validTo'), + $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $date); + } + } + private function DQLToSlug($cleanedSlug) { return $this->slugs[$cleanedSlug]['slug']; @@ -464,7 +706,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou . ' | ' . $label; } - if ('_other' === $slugChoice && $cfType->isChecked($cf, $choiceSlug, $decoded)) { + if ('_other' === $slugChoice && $cfType->isChecked($cf, $slugChoice, $decoded)) { return $cfType->extractOtherValue($cf, $decoded); } diff --git a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php index 1df23fa31..8697f9346 100644 --- a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepository.php @@ -14,9 +14,8 @@ namespace Chill\PersonBundle\Repository; use Chill\PersonBundle\Entity\MaritalStatus; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; -use Doctrine\Persistence\ObjectRepository; -class MaritalStatusRepository implements ObjectRepository +class MaritalStatusRepository implements MaritalStatusRepositoryInterface { private EntityRepository $repository; diff --git a/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php new file mode 100644 index 000000000..1f51060e9 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Repository/MaritalStatusRepositoryInterface.php @@ -0,0 +1,27 @@ +