Fixed: [export][list person] use address from household for person list

This commit is contained in:
Julien Fastré 2022-10-25 10:23:02 +02:00
parent 781253a854
commit 2096e175d4
6 changed files with 515 additions and 237 deletions

View File

@ -5,69 +5,70 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt
These are alias conventions : These are alias conventions :
| Entity | Join | Attribute | Alias | | Entity | Join | Attribute | Alias |
|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:----------------------------------| |:----------------------------------------|:----------------------------------------|:-------------------------------------------|:---------------------------------------|
| AccompanyingPeriod::class | | | acp | | AccompanyingPeriod::class | | | acp |
| | AccompanyingPeriodWork::class | acp.works | acpw | | | AccompanyingPeriodWork::class | acp.works | acpw |
| | AccompanyingPeriodParticipation::class | acp.participations | acppart | | | AccompanyingPeriodParticipation::class | acp.participations | acppart |
| | Location::class | acp.administrativeLocation | acploc | | | Location::class | acp.administrativeLocation | acploc |
| | ClosingMotive::class | acp.closingMotive | acpmotive | | | ClosingMotive::class | acp.closingMotive | acpmotive |
| | UserJob::class | acp.job | acpjob | | | UserJob::class | acp.job | acpjob |
| | Origin::class | acp.origin | acporigin | | | Origin::class | acp.origin | acporigin |
| | Scope::class | acp.scopes | acpscope | | | Scope::class | acp.scopes | acpscope |
| | SocialIssue::class | acp.socialIssues | acpsocialissue | | | SocialIssue::class | acp.socialIssues | acpsocialissue |
| | User::class | acp.user | acpuser | | | User::class | acp.user | acpuser |
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | | | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
| AccompanyingPeriodWork::class | | | acpw | | AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | | | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | User::class | acpw.referrers | acpwuser | | | User::class | acpw.referrers | acpwuser |
| | SocialAction::class | acpw.socialAction | acpwsocialaction | | | SocialAction::class | acpw.socialAction | acpwsocialaction |
| | Goal::class | acpw.goals | goal | | | Goal::class | acpw.goals | goal |
| | Result::class | acpw.results | result | | | Result::class | acpw.results | result |
| AccompanyingPeriodParticipation::class | | | acppart | | AccompanyingPeriodParticipation::class | | | acppart |
| | Person::class | acppart.person | partperson | | | Person::class | acppart.person | partperson |
| AccompanyingPeriodWorkEvaluation::class | | | workeval | | AccompanyingPeriodWorkEvaluation::class | | | workeval |
| | Evaluation::class | workeval.evaluation | eval | | | Evaluation::class | workeval.evaluation | eval |
| Goal::class | | | goal | | Goal::class | | | goal |
| | Result::class | goal.results | goalresult | | | Result::class | goal.results | goalresult |
| Person::class | | | person | | Person::class | | | person |
| | Center::class | person.center | center | | | Center::class | person.center | center |
| | HouseholdMember::class | partperson.householdParticipations | householdmember | | | HouseholdMember::class | partperson.householdParticipations | householdmember |
| | MaritalStatus::class | person.maritalStatus | personmarital | | | MaritalStatus::class | person.maritalStatus | personmarital |
| | VendeePerson::class | | vp | | | VendeePerson::class | | vp |
| | VendeePersonMineur::class | | vpm | | | VendeePersonMineur::class | | vpm |
| ResidentialAddress::class | | | resaddr | | | CurrentPersonAddress::class | person.currentPersonAddress | currentPersonAddress (on a given date) |
| | ThirdParty::class | resaddr.hostThirdParty | tparty | | ResidentialAddress::class | | | resaddr |
| ThirdParty::class | | | tparty | | | ThirdParty::class | resaddr.hostThirdParty | tparty |
| | ThirdPartyCategory::class | tparty.categories | tpartycat | | ThirdParty::class | | | tparty |
| HouseholdMember::class | | | householdmember | | | ThirdPartyCategory::class | tparty.categories | tpartycat |
| | Household::class | householdmember.household | household | | HouseholdMember::class | | | householdmember |
| | Person::class | householdmember.person | memberperson | | | Household::class | householdmember.household | household |
| | | memberperson.center | membercenter | | | Person::class | householdmember.person | memberperson |
| Household::class | | | household | | | | memberperson.center | membercenter |
| | HouseholdComposition::class | household.compositions | composition | | Household::class | | | household |
| Activity::class | | | activity | | | HouseholdComposition::class | household.compositions | composition |
| | Person::class | activity.person | actperson | | Activity::class | | | activity |
| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | | | Person::class | activity.person | actperson |
| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | | | AccompanyingPeriod::class | activity.accompanyingPeriod | acp |
| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | | | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity |
| | ActivityType::class | activity.activityType | acttype | | | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity |
| | Location::class | activity.location | actloc | | | ActivityType::class | activity.activityType | acttype |
| | SocialAction::class | activity.socialActions | actsocialaction | | | Location::class | activity.location | actloc |
| | SocialIssue::class | activity.socialIssues | actsocialssue | | | SocialAction::class | activity.socialActions | actsocialaction |
| | ThirdParty::class | activity.thirdParties | acttparty | | | SocialIssue::class | activity.socialIssues | actsocialssue |
| | User::class | activity.user | actuser | | | ThirdParty::class | activity.thirdParties | acttparty |
| | User::class | activity.users | actusers | | | User::class | activity.user | actuser |
| | ActivityReason::class | activity.reasons | actreasons | | | User::class | activity.users | actusers |
| | Center::class | actperson.center | actcenter | | | ActivityReason::class | activity.reasons | actreasons |
| | Person::class | activity.createdBy | actcreator | | | Center::class | actperson.center | actcenter |
| ActivityReason::class | | | actreasons | | | Person::class | activity.createdBy | actcreator |
| | ActivityReasonCategory::class | actreason.category | actreasoncat | | ActivityReason::class | | | actreasons |
| Calendar::class | | | cal | | | ActivityReasonCategory::class | actreason.category | actreasoncat |
| | CancelReason::class | cal.cancelReason | calcancel | | Calendar::class | | | cal |
| | Location::class | cal.location | calloc | | | CancelReason::class | cal.cancelReason | calcancel |
| | User::class | cal.user | caluser | | | Location::class | cal.location | calloc |
| VendeePerson::class | | | vp | | | User::class | cal.user | caluser |
| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | | VendeePerson::class | | | vp |
| | StatutLogement::class | vp.statutLogement | vplog | | | SituationProfessionelle::class | vp.situationProfessionelle | vpprof |
| | TempsDeTravail::class | vp.tempsDeTravail | vptt | | | StatutLogement::class | vp.statutLogement | vplog |
| | TempsDeTravail::class | vp.tempsDeTravail | vptt |

View File

@ -0,0 +1,247 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Export\Helper;
use Chill\MainBundle\Repository\AddressRepository;
use Chill\MainBundle\Templating\Entity\AddressRender;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use LogicException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use function strlen;
/**
* Helps to load addresses and format them in list
*/
class ExportAddressHelper
{
public const ATTRIBUTES = 0b01000000;
public const BUILDING = 0b00001000;
public const COUNTRY = 0b00000001;
public const GEOM = 0b00100000;
public const POSTAL_CODE = 0b00000010;
public const STREET = 0b00000100;
public const STRING = 0b00010000;
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,
];
private const COLUMN_MAPPING = [
'country' => ['country'],
'postal_code' => ['postcode_code', 'postcode_name'],
'street' => ['street', 'streetNumber'],
'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor'],
'string' => ['_as_string'],
'attributes' => ['isNoAddress', 'confidential', 'id'],
'geom' => ['_lat', '_lon'],
];
private AddressRender $addressRender;
private AddressRepository $addressRepository;
private PropertyAccessor $propertyAccess;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
AddressRepository $addressRepository,
TranslatableStringHelperInterface $translatableStringHelper,
AddressRender $addressRender
) {
$this->addressRepository = $addressRepository;
$this->propertyAccess = PropertyAccess::createPropertyAccessor();
$this->translatableStringHelper = $translatableStringHelper;
$this->addressRender = $addressRender;
}
/**
* @return array|string[]
*/
public function getKeys(int $params, string $prefix = ''): array
{
$prefixes = [];
foreach (self::ALL as $key => $bitmask) {
if (($params & $bitmask) === $bitmask) {
$prefixes = array_merge(
$prefixes,
array_map(
static function ($item) use ($prefix) { return $prefix . $item; },
self::COLUMN_MAPPING[$key]
)
);
}
}
return $prefixes;
}
public function getLabel($key, array $values, $data, string $prefix = '', string $translationPrefix = 'export.address_helper.'): callable
{
$sanitizedKey = substr($key, strlen($prefix));
switch ($sanitizedKey) {
case 'id':
case 'street':
case 'streetNumber':
case 'buildingName':
case 'corridor':
case 'distribution':
case 'extra':
case 'flat':
case 'floor':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
return $this->propertyAccess->getValue($address, $sanitizedKey);
};
case '_lat':
case '_lon':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
$geom = $address->getPoint();
if (null === $geom) {
return '';
}
switch ($sanitizedKey) {
case '_lat':
return $geom->getLat();
case '_lon':
return $geom->getLon();
default:
throw new LogicException('only _lat or _lon accepted, given: ' . $sanitizedKey);
}
};
case 'isNoAddress':
case 'confidential':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
switch ($val = $this->propertyAccess->getValue($address, $sanitizedKey)) {
case null:
return '';
case true:
return 1;
case false:
return 0;
default:
throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $val);
}
};
case 'country':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
return $this->translatableStringHelper->localize($address->getPostcode()->getCountry()->getName());
};
case '_as_string':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
return $this->addressRender->renderString($address, []);
};
case 'postcode_code':
case 'postcode_name':
return function ($value) use ($sanitizedKey, $translationPrefix) {
if ('_header' === $value) {
return $translationPrefix . $sanitizedKey;
}
if (null === $value) {
return '';
}
$address = $this->addressRepository->find($value);
switch ($sanitizedKey) {
case 'postcode_code':
return $address->getPostcode()->getCode();
case 'postcode_name':
return $address->getPostcode()->getName();
default:
throw new LogicException('this key is not supported: ' . $sanitizedKey);
}
};
default:
throw new LogicException('this key is not supported: ' . $sanitizedKey);
}
}
}

View File

@ -3,6 +3,9 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\MainBundle\Export\Helper\:
resource: '../../Export/Helper'
chill.main.export_element_validator: chill.main.export_element_validator:
class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator
tags: tags:

View File

@ -79,17 +79,17 @@ Postal code: Code postal
Valid from: Valide à partir du Valid from: Valide à partir du
Choose a postal code: Choisir un code postal Choose a postal code: Choisir un code postal
address: address:
address_homeless: L'adresse est-elle celle d'un domicile fixe ? address_homeless: L'adresse est-elle celle d'un domicile fixe ?
real address: Adresse d'un domicile real address: Adresse d'un domicile
consider homeless: Cette adresse est incomplète consider homeless: Cette adresse est incomplète
address more: address more:
floor: ét floor: ét
corridor: coul corridor: coul
steps: esc steps: esc
flat: appart flat: appart
buildingName: résidence buildingName: résidence
extra: "" extra: ""
distribution: cedex distribution: cedex
Create a new address: Créer une nouvelle adresse Create a new address: Créer une nouvelle adresse
Create an address: Créer une adresse Create an address: Créer une adresse
Update address: Modifier l'adresse Update address: Modifier l'adresse
@ -125,7 +125,7 @@ Location and location type: Localisations et types de localisation
Back to the admin: Menu d'administration Back to the admin: Menu d'administration
"Administration interface": Interface d'administration "Administration interface": Interface d'administration
Welcome to the admin section !: > Welcome to the admin section !: >
Bienvenue dans l'interface d'administration ! Bienvenue dans l'interface d'administration !
#permissions #permissions
Permissions Menu: Gestion des droits Permissions Menu: Gestion des droits
@ -334,69 +334,69 @@ Impersonate: Incarner l'utilisateur
Impersonate mode: Mode fantôme Impersonate mode: Mode fantôme
crud: crud:
# general items # general items
new: new:
button_action_form: Créer button_action_form: Créer
link_edit: Modifier link_edit: Modifier
save_and_close: Créer & fermer save_and_close: Créer & fermer
save_and_show: Créer & voir save_and_show: Créer & voir
save_and_new: Créer & nouveau save_and_new: Créer & nouveau
success: Les données ont été créées success: Les données ont été créées
edit: edit:
button_action_form: Enregistrer button_action_form: Enregistrer
back_to_view: Voir back_to_view: Voir
save_and_close: Enregistrer & fermer save_and_close: Enregistrer & fermer
save_and_show: Enregistrer & voir save_and_show: Enregistrer & voir
success: Les données ont été modifiées success: Les données ont été modifiées
delete: delete:
success: Les données ont été supprimées success: Les données ont été supprimées
link_to_form: Supprimer link_to_form: Supprimer
default: default:
success: Les données ont été enregistrées success: Les données ont été enregistrées
view: view:
link_duplicate: Dupliquer link_duplicate: Dupliquer
admin_user: admin_user:
index: index:
title: Utilisateurs title: Utilisateurs
add_new: Créer add_new: Créer
title_edit: Modifier un utilisateur title_edit: Modifier un utilisateur
title_new: Créer un utilisateur title_new: Créer un utilisateur
admin_user_job: admin_user_job:
index: index:
title: Métiers title: Métiers
add_new: Créer add_new: Créer
title_new: Nouveau métier title_new: Nouveau métier
title_edit: Modifier un métier title_edit: Modifier un métier
main_location_type: main_location_type:
index: index:
title: Liste des types de localisations title: Liste des types de localisations
add_new: Ajouter un type de localisation add_new: Ajouter un type de localisation
title_new: Nouveau type de localisation title_new: Nouveau type de localisation
title_edit: Modifier un type de localisation title_edit: Modifier un type de localisation
main_location: main_location:
index: index:
title: Liste des localisations title: Liste des localisations
add_new: Ajouter une localisation add_new: Ajouter une localisation
title_new: Nouvelle localisation title_new: Nouvelle localisation
title_edit: Modifier une localisation title_edit: Modifier une localisation
main_language: main_language:
index: index:
title: Liste des langues title: Liste des langues
add_new: Ajouter une langue add_new: Ajouter une langue
title_new: Nouvelle langue title_new: Nouvelle langue
title_edit: Modifier une langue title_edit: Modifier une langue
main_country: main_country:
index: index:
title: Liste des pays title: Liste des pays
add_new: Ajouter un pays add_new: Ajouter un pays
title_new: Nouveau pays title_new: Nouveau pays
title_edit: Modifier un pays title_edit: Modifier un pays
main_civility: main_civility:
index: index:
title: Liste des civilités title: Liste des civilités
add_new: Ajouter une civilité add_new: Ajouter une civilité
title_new: Nouvelle civilité title_new: Nouvelle civilité
title_edit: Modifier une civilité title_edit: Modifier une civilité
No entities: Aucun élément No entities: Aucun élément
@ -515,3 +515,22 @@ notification:
Remove an email: Supprimer l'adresse email Remove an email: Supprimer l'adresse email
Email with access link: Adresse email ayant reçu un lien d'accès Email with access link: Adresse email ayant reçu un lien d'accès
export:
address_helper:
id: Identifiant de l'adresse
street: Voie
streetNumber: Numéro de voie
buildingName: Résidence
corridor: Couloir
distribution: Distribution
extra: Extra
flat: Appartement
floor: Étage
postcode_code: Code postal
postcode_name: Libellé du code postal
country: Pays
_as_string: Adresse formattée
confidential: Adresse confidentielle ?
isNoAddress: Adresse incomplète ?
_lat: Latitude
_lon: Longitude

View File

@ -1768,7 +1768,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
} }
/** /**
* @param type $spokenLanguages * @param Collection $spokenLanguages
*/ */
public function setSpokenLanguages($spokenLanguages): self public function setSpokenLanguages($spokenLanguages): self
{ {

View File

@ -17,28 +17,33 @@ use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\ExportAddressHelper;
use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\MainBundle\Repository\CountryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTime; use DateTime;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Exception; use Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function addcslashes; use function addcslashes;
use function array_key_exists; use function array_key_exists;
use function array_keys; use function array_keys;
use function array_merge; use function array_merge;
use function count; use function count;
use function in_array; use function in_array;
use function strlen;
use function strtolower; use function strtolower;
use function uniqid; use function uniqid;
@ -47,31 +52,40 @@ use function uniqid;
*/ */
class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface
{ {
protected CustomFieldProvider $customFieldProvider; public const FIELDS = [
'id',
protected EntityManagerInterface $entityManager; 'firstName',
'lastName',
protected array $fields = [ 'birthdate',
'id', 'firstName', 'lastName', 'birthdate',
'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber', 'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber',
'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality', 'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality',
'address_street_address_1', 'address_street_address_2', 'address',
'address_valid_from', 'address_postcode_label', 'address_postcode_code',
'address_country_name', 'address_country_code', 'address_isnoaddress',
]; ];
protected TranslatableStringHelper $translatableStringHelper; private ExportAddressHelper $addressHelper;
protected TranslatorInterface $translator; private CountryRepository $countryRepository;
private CustomFieldProvider $customFieldProvider;
private EntityManagerInterface $entityManager;
private $slugs = []; private $slugs = [];
private TranslatableStringHelper $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct( public function __construct(
CountryRepository $countryRepository,
ExportAddressHelper $addressHelper,
EntityManagerInterface $em, EntityManagerInterface $em,
TranslatorInterface $translator, TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper, TranslatableStringHelper $translatableStringHelper,
CustomFieldProvider $customFieldProvider CustomFieldProvider $customFieldProvider
) { ) {
$this->addressHelper = $addressHelper;
$this->countryRepository = $countryRepository;
$this->entityManager = $em; $this->entityManager = $em;
$this->translator = $translator; $this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper; $this->translatableStringHelper = $translatableStringHelper;
@ -80,7 +94,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
public function buildForm(FormBuilderInterface $builder) public function buildForm(FormBuilderInterface $builder)
{ {
$choices = array_combine($this->fields, $this->fields); $choices = array_combine(self::FIELDS, self::FIELDS);
foreach ($this->getCustomFields() as $cf) { foreach ($this->getCustomFields() as $cf) {
$choices[$this->translatableStringHelper->localize($cf->getName())] $choices[$this->translatableStringHelper->localize($cf->getName())]
@ -96,7 +110,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
'label' => 'Fields to include in export', 'label' => 'Fields to include in export',
'choice_attr' => static function (string $val): array { 'choice_attr' => static function (string $val): array {
// add a 'data-display-target' for address fields // add a 'data-display-target' for address fields
if (substr($val, 0, 8) === 'address_') { if (substr($val, 0, 7) === 'address') {
return ['data-display-target' => 'address_date']; return ['data-display-target' => 'address_date'];
} }
@ -111,17 +125,14 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
} }
}, },
])], ])],
'data' => array_values($choices),
]); ]);
// add a date field for addresses // add a date field for addresses
$builder->add('address_date', DateType::class, [ $builder->add('address_date', ChillDateType::class, [
'label' => 'Address valid at this date', 'label' => 'Address valid at this date',
'data' => new DateTime(), 'data' => new DateTimeImmutable(),
'attr' => ['class' => 'datepicker'], 'input' => 'datetime_immutable',
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
'required' => false,
'block_name' => 'list_export_form_address_date',
]); ]);
} }
@ -142,6 +153,10 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
public function getLabels($key, array $values, $data) public function getLabels($key, array $values, $data)
{ {
if (substr($key, 0, strlen('address')) === 'address') {
return $this->addressHelper->getLabel($key, $values, $data, 'address_');
}
switch ($key) { switch ($key) {
case 'birthdate': case 'birthdate':
// for birthdate, we have to transform the string into a date // for birthdate, we have to transform the string into a date
@ -151,10 +166,11 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
return 'birthdate'; return 'birthdate';
} }
if (empty($value)) { if (null === $value) {
return ''; return '';
} }
// warning: won't work with DateTimeImmutable as we reset time a few lines later
$date = DateTime::createFromFormat('Y-m-d', $value); $date = DateTime::createFromFormat('Y-m-d', $value);
// check that the creation could occurs. // check that the creation could occurs.
if (false === $date) { if (false === $date) {
@ -162,7 +178,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
. 'not be converted to %s', $value, DateTime::class)); . 'not be converted to %s', $value, DateTime::class));
} }
return $date->format('d-m-Y'); $date->setTime(0, 0, 0);
return $date;
}; };
case 'gender': case 'gender':
@ -177,29 +195,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
case 'countryOfBirth': case 'countryOfBirth':
case 'nationality': case 'nationality':
$countryRepository = $this->entityManager
->getRepository(\Chill\MainBundle\Entity\Country::class);
// load all countries in a single query
$countryRepository->findBy(['countryCode' => $values]);
return function ($value) use ($key, $countryRepository) {
if ('_header' === $value) {
return strtolower($key);
}
if (null === $value) {
return $this->translator->trans('no data');
}
$country = $countryRepository->find($value);
return $this->translatableStringHelper->localize(
$country->getName()
);
};
case 'address_country_name':
return function ($value) use ($key) { return function ($value) use ($key) {
if ('_header' === $value) { if ('_header' === $value) {
return strtolower($key); return strtolower($key);
@ -209,25 +204,16 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
return ''; return '';
} }
return $this->translatableStringHelper->localize(json_decode($value, true)); $country = $this->countryRepository->find($value);
};
case 'address_isnoaddress': return $this->translatableStringHelper->localize(
return static function (?string $value): string { $country->getName()
if ('_header' === $value) { );
return 'address.address_homeless';
}
if (null !== $value) {
return 'X';
}
return '';
}; };
default: default:
// for fields which are associated with person // for fields which are associated with person
if (in_array($key, $this->fields, true)) { if (in_array($key, self::FIELDS, true)) {
return static function ($value) use ($key) { return static function ($value) use ($key) {
if ('_header' === $value) { if ('_header' === $value) {
return strtolower($key); return strtolower($key);
@ -246,9 +232,13 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
$fields = []; $fields = [];
foreach ($data['fields'] as $key) { foreach ($data['fields'] as $key) {
if (in_array($key, $this->fields, true)) { if (substr($key, 0, strlen('address')) === 'address') {
$fields[] = $key; $fields = array_merge($fields, $this->addressHelper->getKeys(0b01111111, 'address_'));
continue;
} }
$fields[] = $key;
} }
// add the key from slugs and return // add the key from slugs and return
@ -270,6 +260,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
return Declarations::PERSON_TYPE; return Declarations::PERSON_TYPE;
} }
/**
* @param array{fields: string[], address_date: DateTimeImmutable} $data
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{ {
$centers = array_map(static function ($el) { $centers = array_map(static function ($el) {
@ -284,36 +277,57 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
$qb = $this->entityManager->createQueryBuilder(); $qb = $this->entityManager->createQueryBuilder();
foreach ($this->fields as $f) { $qb
if (in_array($f, $data['fields'], true)) { ->from(Person::class, 'person')
switch ($f) { ->andWhere(
case 'countryOfBirth': $qb->expr()->exists(
case 'nationality': 'SELECT 1 FROM ' . Person\PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
$qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); )
)
->setParameter('authorized_centers', $centers);
break; foreach (self::FIELDS as $f) {
if (!in_array($f, $data['fields'], true)) {
continue;
}
case 'address_street_address_1': switch ($f) {
case 'address_street_address_2': case 'countryOfBirth':
case 'address_valid_from': case 'nationality':
case 'address_postcode_label': $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f));
case 'address_postcode_code':
case 'address_country_name':
case 'address_country_code':
case 'address_isnoaddress':
$qb->addSelect(sprintf(
'GET_PERSON_ADDRESS_%s(person.id, :address_date) AS %s',
// get the part after address_
strtoupper(substr($f, 8)),
$f
));
$qb->setParameter('address_date', $data['address_date']);
break; break;
default: case 'address':
$qb->addSelect(sprintf('person.%s as %s', $f, $f)); foreach ($this->addressHelper->getKeys(0b01111111, 'address_') as $key) {
} $qb
->addSelect(sprintf('IDENTITY(currentPersonAddress.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')
)
)
)
)
->setParameter('address_date', $data['address_date']);
}
break;
default:
$qb->addSelect(sprintf('person.%s as %s', $f, $f));
} }
} }
@ -345,12 +359,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
} }
} }
$qb
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers);
return $qb; return $qb;
} }
@ -368,7 +376,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
{ {
// get the field starting with address_ // get the field starting with address_
$addressFields = array_filter( $addressFields = array_filter(
$this->fields, self::FIELDS,
static fn (string $el): bool => substr($el, 0, 8) === 'address_' static fn (string $el): bool => substr($el, 0, 8) === 'address_'
); );