From 4af261a36631058588ed1f43687143165c15d6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Oct 2022 09:25:30 +0200 Subject: [PATCH 01/12] Fixed: correctly show datetime in spreadsheet list --- .../Formatter/SpreadsheetListFormatter.php | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php index ab9c2e893..0e5e339ea 100644 --- a/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php +++ b/src/Bundle/ChillMainBundle/Export/Formatter/SpreadsheetListFormatter.php @@ -20,11 +20,12 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; +use RuntimeException; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\HttpFoundation\Response; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function array_key_exists; use function array_keys; use function array_map; @@ -80,7 +81,7 @@ class SpreadsheetListFormatter implements FormatterInterface * * @uses appendAggregatorForm * - * @param type $exportAlias + * @param string $exportAlias */ public function buildForm( FormBuilderInterface $builder, @@ -144,8 +145,6 @@ class SpreadsheetListFormatter implements FormatterInterface $i = 1; foreach ($result as $row) { - $line = []; - if (true === $this->formatterData['numerotation']) { $worksheet->setCellValue('A' . ($i + 1), (string) $i); } @@ -155,13 +154,22 @@ class SpreadsheetListFormatter implements FormatterInterface foreach ($row as $key => $value) { $row = $a . ($i + 1); - if ($value instanceof DateTimeInterface) { - $worksheet->setCellValue($row, Date::PHPToExcel($value)); - $worksheet->getStyle($row) - ->getNumberFormat() - ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY); + $formattedValue = $this->getLabel($key, $value); + + if ($formattedValue instanceof DateTimeInterface) { + $worksheet->setCellValue($row, Date::PHPToExcel($formattedValue)); + + if ($formattedValue->format('His') === '000000') { + $worksheet->getStyle($row) + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY); + } else { + $worksheet->getStyle($row) + ->getNumberFormat() + ->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME); + } } else { - $worksheet->setCellValue($row, $this->getLabel($key, $value)); + $worksheet->setCellValue($row, $formattedValue); } ++$a; } @@ -259,6 +267,10 @@ class SpreadsheetListFormatter implements FormatterInterface foreach ($keys as $key) { // get an array with all values for this key if possible $values = array_map(static function ($v) use ($key) { + if (!array_key_exists($key, $v)) { + throw new RuntimeException(sprintf('This key does not exists: %s. Available keys are %s', $key, implode(', ', array_keys($v)))); + } + return $v[$key]; }, $this->result); // store the label in the labelsCache property From 333c305eef548fe0fc4270f1098f9c1fc8a9a977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Oct 2022 10:23:02 +0200 Subject: [PATCH 02/12] Fixed: [export][list person] use address from household for person list --- exports_alias_conventions.md | 133 +++++----- .../Export/Helper/ExportAddressHelper.php | 247 ++++++++++++++++++ .../config/services/export.yaml | 3 + .../translations/messages.fr.yml | 167 ++++++------ .../ChillPersonBundle/Entity/Person.php | 2 +- .../Export/Export/ListPerson.php | 200 +++++++------- 6 files changed, 515 insertions(+), 237 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php diff --git a/exports_alias_conventions.md b/exports_alias_conventions.md index 62fc745d8..fd7844691 100644 --- a/exports_alias_conventions.md +++ b/exports_alias_conventions.md @@ -5,69 +5,70 @@ Add condition with distinct alias on each export join clauses (Indicators + Filt These are alias conventions : -| Entity | Join | Attribute | Alias | -|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:----------------------------------| -| AccompanyingPeriod::class | | | acp | -| | AccompanyingPeriodWork::class | acp.works | acpw | -| | AccompanyingPeriodParticipation::class | acp.participations | acppart | -| | Location::class | acp.administrativeLocation | acploc | -| | ClosingMotive::class | acp.closingMotive | acpmotive | -| | UserJob::class | acp.job | acpjob | -| | Origin::class | acp.origin | acporigin | -| | Scope::class | acp.scopes | acpscope | -| | SocialIssue::class | acp.socialIssues | acpsocialissue | -| | User::class | acp.user | acpuser | -| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | -| AccompanyingPeriodWork::class | | | acpw | -| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | -| | User::class | acpw.referrers | acpwuser | -| | SocialAction::class | acpw.socialAction | acpwsocialaction | -| | Goal::class | acpw.goals | goal | -| | Result::class | acpw.results | result | -| AccompanyingPeriodParticipation::class | | | acppart | -| | Person::class | acppart.person | partperson | -| AccompanyingPeriodWorkEvaluation::class | | | workeval | -| | Evaluation::class | workeval.evaluation | eval | -| Goal::class | | | goal | -| | Result::class | goal.results | goalresult | -| Person::class | | | person | -| | Center::class | person.center | center | -| | HouseholdMember::class | partperson.householdParticipations | householdmember | -| | MaritalStatus::class | person.maritalStatus | personmarital | -| | VendeePerson::class | | vp | -| | VendeePersonMineur::class | | vpm | -| ResidentialAddress::class | | | resaddr | -| | ThirdParty::class | resaddr.hostThirdParty | tparty | -| ThirdParty::class | | | tparty | -| | ThirdPartyCategory::class | tparty.categories | tpartycat | -| HouseholdMember::class | | | householdmember | -| | Household::class | householdmember.household | household | -| | Person::class | householdmember.person | memberperson | -| | | memberperson.center | membercenter | -| Household::class | | | household | -| | HouseholdComposition::class | household.compositions | composition | -| Activity::class | | | activity | -| | Person::class | activity.person | actperson | -| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | -| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | -| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | -| | ActivityType::class | activity.activityType | acttype | -| | Location::class | activity.location | actloc | -| | SocialAction::class | activity.socialActions | actsocialaction | -| | SocialIssue::class | activity.socialIssues | actsocialssue | -| | ThirdParty::class | activity.thirdParties | acttparty | -| | User::class | activity.user | actuser | -| | User::class | activity.users | actusers | -| | ActivityReason::class | activity.reasons | actreasons | -| | Center::class | actperson.center | actcenter | -| | Person::class | activity.createdBy | actcreator | -| ActivityReason::class | | | actreasons | -| | ActivityReasonCategory::class | actreason.category | actreasoncat | -| Calendar::class | | | cal | -| | CancelReason::class | cal.cancelReason | calcancel | -| | Location::class | cal.location | calloc | -| | User::class | cal.user | caluser | -| VendeePerson::class | | | vp | -| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | -| | StatutLogement::class | vp.statutLogement | vplog | -| | TempsDeTravail::class | vp.tempsDeTravail | vptt | +| Entity | Join | Attribute | Alias | +|:----------------------------------------|:----------------------------------------|:-------------------------------------------|:---------------------------------------| +| AccompanyingPeriod::class | | | acp | +| | AccompanyingPeriodWork::class | acp.works | acpw | +| | AccompanyingPeriodParticipation::class | acp.participations | acppart | +| | Location::class | acp.administrativeLocation | acploc | +| | ClosingMotive::class | acp.closingMotive | acpmotive | +| | UserJob::class | acp.job | acpjob | +| | Origin::class | acp.origin | acporigin | +| | Scope::class | acp.scopes | acpscope | +| | SocialIssue::class | acp.socialIssues | acpsocialissue | +| | User::class | acp.user | acpuser | +| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories | +| AccompanyingPeriodWork::class | | | acpw | +| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval | +| | User::class | acpw.referrers | acpwuser | +| | SocialAction::class | acpw.socialAction | acpwsocialaction | +| | Goal::class | acpw.goals | goal | +| | Result::class | acpw.results | result | +| AccompanyingPeriodParticipation::class | | | acppart | +| | Person::class | acppart.person | partperson | +| AccompanyingPeriodWorkEvaluation::class | | | workeval | +| | Evaluation::class | workeval.evaluation | eval | +| Goal::class | | | goal | +| | Result::class | goal.results | goalresult | +| Person::class | | | person | +| | Center::class | person.center | center | +| | HouseholdMember::class | partperson.householdParticipations | householdmember | +| | MaritalStatus::class | person.maritalStatus | personmarital | +| | VendeePerson::class | | vp | +| | VendeePersonMineur::class | | vpm | +| | CurrentPersonAddress::class | person.currentPersonAddress | currentPersonAddress (on a given date) | +| ResidentialAddress::class | | | resaddr | +| | ThirdParty::class | resaddr.hostThirdParty | tparty | +| ThirdParty::class | | | tparty | +| | ThirdPartyCategory::class | tparty.categories | tpartycat | +| HouseholdMember::class | | | householdmember | +| | Household::class | householdmember.household | household | +| | Person::class | householdmember.person | memberperson | +| | | memberperson.center | membercenter | +| Household::class | | | household | +| | HouseholdComposition::class | household.compositions | composition | +| Activity::class | | | activity | +| | Person::class | activity.person | actperson | +| | AccompanyingPeriod::class | activity.accompanyingPeriod | acp | +| | Person::class | activity\_person\_having\_activity.person | person\_person\_having\_activity | +| | ActivityReason::class | activity\_person\_having\_activity.reasons | reasons\_person\_having\_activity | +| | ActivityType::class | activity.activityType | acttype | +| | Location::class | activity.location | actloc | +| | SocialAction::class | activity.socialActions | actsocialaction | +| | SocialIssue::class | activity.socialIssues | actsocialssue | +| | ThirdParty::class | activity.thirdParties | acttparty | +| | User::class | activity.user | actuser | +| | User::class | activity.users | actusers | +| | ActivityReason::class | activity.reasons | actreasons | +| | Center::class | actperson.center | actcenter | +| | Person::class | activity.createdBy | actcreator | +| ActivityReason::class | | | actreasons | +| | ActivityReasonCategory::class | actreason.category | actreasoncat | +| Calendar::class | | | cal | +| | CancelReason::class | cal.cancelReason | calcancel | +| | Location::class | cal.location | calloc | +| | User::class | cal.user | caluser | +| VendeePerson::class | | | vp | +| | SituationProfessionelle::class | vp.situationProfessionelle | vpprof | +| | StatutLogement::class | vp.statutLogement | vplog | +| | TempsDeTravail::class | vp.tempsDeTravail | vptt | diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php new file mode 100644 index 000000000..09aaa7a1d --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -0,0 +1,247 @@ + 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); + } + } +} diff --git a/src/Bundle/ChillMainBundle/config/services/export.yaml b/src/Bundle/ChillMainBundle/config/services/export.yaml index d330f30f5..ea7328839 100644 --- a/src/Bundle/ChillMainBundle/config/services/export.yaml +++ b/src/Bundle/ChillMainBundle/config/services/export.yaml @@ -3,6 +3,9 @@ services: autowire: true autoconfigure: true + Chill\MainBundle\Export\Helper\: + resource: '../../Export/Helper' + chill.main.export_element_validator: class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator tags: diff --git a/src/Bundle/ChillMainBundle/translations/messages.fr.yml b/src/Bundle/ChillMainBundle/translations/messages.fr.yml index b4ce910c4..01fcf0b8c 100644 --- a/src/Bundle/ChillMainBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillMainBundle/translations/messages.fr.yml @@ -79,17 +79,17 @@ Postal code: Code postal Valid from: Valide à partir du Choose a postal code: Choisir un code postal address: - address_homeless: L'adresse est-elle celle d'un domicile fixe ? - real address: Adresse d'un domicile - consider homeless: Cette adresse est incomplète + address_homeless: L'adresse est-elle celle d'un domicile fixe ? + real address: Adresse d'un domicile + consider homeless: Cette adresse est incomplète address more: - floor: ét - corridor: coul - steps: esc - flat: appart - buildingName: résidence - extra: "" - distribution: cedex + floor: ét + corridor: coul + steps: esc + flat: appart + buildingName: résidence + extra: "" + distribution: cedex Create a new address: Créer une nouvelle adresse Create an address: Créer une 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 "Administration interface": Interface d'administration Welcome to the admin section !: > - Bienvenue dans l'interface d'administration ! + Bienvenue dans l'interface d'administration ! #permissions Permissions Menu: Gestion des droits @@ -334,69 +334,69 @@ Impersonate: Incarner l'utilisateur Impersonate mode: Mode fantôme crud: - # general items - new: - button_action_form: Créer - link_edit: Modifier - save_and_close: Créer & fermer - save_and_show: Créer & voir - save_and_new: Créer & nouveau - success: Les données ont été créées - edit: - button_action_form: Enregistrer - back_to_view: Voir - save_and_close: Enregistrer & fermer - save_and_show: Enregistrer & voir - success: Les données ont été modifiées - delete: - success: Les données ont été supprimées - link_to_form: Supprimer - default: - success: Les données ont été enregistrées - view: - link_duplicate: Dupliquer - admin_user: - index: - title: Utilisateurs - add_new: Créer - title_edit: Modifier un utilisateur - title_new: Créer un utilisateur - admin_user_job: - index: - title: Métiers - add_new: Créer - title_new: Nouveau métier - title_edit: Modifier un métier - main_location_type: - index: - title: Liste des types de localisations - add_new: Ajouter un type de localisation - title_new: Nouveau type de localisation - title_edit: Modifier un type de localisation - main_location: - index: - title: Liste des localisations - add_new: Ajouter une localisation - title_new: Nouvelle localisation - title_edit: Modifier une localisation - main_language: - index: - title: Liste des langues - add_new: Ajouter une langue - title_new: Nouvelle langue - title_edit: Modifier une langue - main_country: - index: - title: Liste des pays - add_new: Ajouter un pays - title_new: Nouveau pays - title_edit: Modifier un pays - main_civility: - index: - title: Liste des civilités - add_new: Ajouter une civilité - title_new: Nouvelle civilité - title_edit: Modifier une civilité + # general items + new: + button_action_form: Créer + link_edit: Modifier + save_and_close: Créer & fermer + save_and_show: Créer & voir + save_and_new: Créer & nouveau + success: Les données ont été créées + edit: + button_action_form: Enregistrer + back_to_view: Voir + save_and_close: Enregistrer & fermer + save_and_show: Enregistrer & voir + success: Les données ont été modifiées + delete: + success: Les données ont été supprimées + link_to_form: Supprimer + default: + success: Les données ont été enregistrées + view: + link_duplicate: Dupliquer + admin_user: + index: + title: Utilisateurs + add_new: Créer + title_edit: Modifier un utilisateur + title_new: Créer un utilisateur + admin_user_job: + index: + title: Métiers + add_new: Créer + title_new: Nouveau métier + title_edit: Modifier un métier + main_location_type: + index: + title: Liste des types de localisations + add_new: Ajouter un type de localisation + title_new: Nouveau type de localisation + title_edit: Modifier un type de localisation + main_location: + index: + title: Liste des localisations + add_new: Ajouter une localisation + title_new: Nouvelle localisation + title_edit: Modifier une localisation + main_language: + index: + title: Liste des langues + add_new: Ajouter une langue + title_new: Nouvelle langue + title_edit: Modifier une langue + main_country: + index: + title: Liste des pays + add_new: Ajouter un pays + title_new: Nouveau pays + title_edit: Modifier un pays + main_civility: + index: + title: Liste des civilités + add_new: Ajouter une civilité + title_new: Nouvelle civilité + title_edit: Modifier une civilité No entities: Aucun élément @@ -515,3 +515,22 @@ notification: Remove an email: Supprimer l'adresse email 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 diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 0d6e3e493..48864120d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -1760,7 +1760,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI } /** - * @param type $spokenLanguages + * @param Collection $spokenLanguages */ public function setSpokenLanguages($spokenLanguages): self { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 5f5d95034..e799a53e4 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -17,28 +17,33 @@ use Chill\CustomFieldsBundle\Service\CustomFieldProvider; use Chill\MainBundle\Export\ExportElementValidatedInterface; use Chill\MainBundle\Export\FormatterInterface; 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\CountryRepository; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Security\Authorization\PersonVoter; use DateTime; +use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; use Exception; +use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Contracts\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use function addcslashes; use function array_key_exists; use function array_keys; use function array_merge; use function count; use function in_array; +use function strlen; use function strtolower; use function uniqid; @@ -47,31 +52,40 @@ use function uniqid; */ class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { - protected CustomFieldProvider $customFieldProvider; - - protected EntityManagerInterface $entityManager; - - protected array $fields = [ - 'id', 'firstName', 'lastName', 'birthdate', + public const FIELDS = [ + 'id', + 'firstName', + 'lastName', + 'birthdate', 'placeOfBirth', 'gender', 'memo', 'email', 'phonenumber', 'mobilenumber', 'contactInfo', 'countryOfBirth', 'nationality', - 'address_street_address_1', 'address_street_address_2', - 'address_valid_from', 'address_postcode_label', 'address_postcode_code', - 'address_country_name', 'address_country_code', 'address_isnoaddress', + 'address', ]; - protected TranslatableStringHelper $translatableStringHelper; + private ExportAddressHelper $addressHelper; - protected TranslatorInterface $translator; + private CountryRepository $countryRepository; + + private CustomFieldProvider $customFieldProvider; + + private EntityManagerInterface $entityManager; private $slugs = []; + private TranslatableStringHelper $translatableStringHelper; + + private TranslatorInterface $translator; + public function __construct( + CountryRepository $countryRepository, + ExportAddressHelper $addressHelper, EntityManagerInterface $em, TranslatorInterface $translator, TranslatableStringHelper $translatableStringHelper, CustomFieldProvider $customFieldProvider ) { + $this->addressHelper = $addressHelper; + $this->countryRepository = $countryRepository; $this->entityManager = $em; $this->translator = $translator; $this->translatableStringHelper = $translatableStringHelper; @@ -80,7 +94,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function buildForm(FormBuilderInterface $builder) { - $choices = array_combine($this->fields, $this->fields); + $choices = array_combine(self::FIELDS, self::FIELDS); foreach ($this->getCustomFields() as $cf) { $choices[$this->translatableStringHelper->localize($cf->getName())] @@ -96,7 +110,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, 8) === 'address_') { + if (substr($val, 0, 7) === 'address') { 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 - $builder->add('address_date', DateType::class, [ + $builder->add('address_date', ChillDateType::class, [ 'label' => 'Address valid at this date', - 'data' => new DateTime(), - 'attr' => ['class' => 'datepicker'], - 'widget' => 'single_text', - 'format' => 'dd-MM-yyyy', - 'required' => false, - 'block_name' => 'list_export_form_address_date', + 'data' => new DateTimeImmutable(), + 'input' => 'datetime_immutable', ]); } @@ -142,6 +153,10 @@ 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_'); + } + switch ($key) { case 'birthdate': // for birthdate, we have to transform the string into a date @@ -151,10 +166,11 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return 'birthdate'; } - if (empty($value)) { + if (null === $value) { return ''; } + // warning: won't work with DateTimeImmutable as we reset time a few lines later $date = DateTime::createFromFormat('Y-m-d', $value); // check that the creation could occurs. if (false === $date) { @@ -162,7 +178,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou . 'not be converted to %s', $value, DateTime::class)); } - return $date->format('d-m-Y'); + $date->setTime(0, 0, 0); + + return $date; }; case 'gender': @@ -177,29 +195,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou case 'countryOfBirth': 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) { if ('_header' === $value) { return strtolower($key); @@ -209,25 +204,16 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return ''; } - return $this->translatableStringHelper->localize(json_decode($value, true)); - }; + $country = $this->countryRepository->find($value); - case 'address_isnoaddress': - return static function (?string $value): string { - if ('_header' === $value) { - return 'address.address_homeless'; - } - - if (null !== $value) { - return 'X'; - } - - return ''; + return $this->translatableStringHelper->localize( + $country->getName() + ); }; default: // 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) { if ('_header' === $value) { return strtolower($key); @@ -246,9 +232,13 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $fields = []; foreach ($data['fields'] as $key) { - if (in_array($key, $this->fields, true)) { - $fields[] = $key; + if (substr($key, 0, strlen('address')) === 'address') { + $fields = array_merge($fields, $this->addressHelper->getKeys(0b01111111, 'address_')); + + continue; } + + $fields[] = $key; } // add the key from slugs and return @@ -270,6 +260,9 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou return Declarations::PERSON_TYPE; } + /** + * @param array{fields: string[], address_date: DateTimeImmutable} $data + */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { $centers = array_map(static function ($el) { @@ -284,36 +277,57 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $qb = $this->entityManager->createQueryBuilder(); - foreach ($this->fields as $f) { - if (in_array($f, $data['fields'], true)) { - switch ($f) { - case 'countryOfBirth': - case 'nationality': - $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); + $qb + ->from(Person::class, 'person') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . Person\PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + ) + ->setParameter('authorized_centers', $centers); - break; + foreach (self::FIELDS as $f) { + if (!in_array($f, $data['fields'], true)) { + continue; + } - case 'address_street_address_1': - case 'address_street_address_2': - case 'address_valid_from': - case 'address_postcode_label': - 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']); + switch ($f) { + case 'countryOfBirth': + case 'nationality': + $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); - break; + break; - default: - $qb->addSelect(sprintf('person.%s as %s', $f, $f)); - } + case 'address': + 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; } @@ -368,7 +376,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { // get the field starting with address_ $addressFields = array_filter( - $this->fields, + self::FIELDS, static fn (string $el): bool => substr($el, 0, 8) === 'address_' ); 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 03/12] 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 @@ + Date: Wed, 26 Oct 2022 13:30:16 +0200 Subject: [PATCH 04/12] DX: Refactor ListPerson to extract methods linked to label and select --- .../Export/Export/ListPerson.php | 383 +--------------- .../Export/Helper/ListPersonHelper.php | 428 ++++++++++++++++++ 2 files changed, 443 insertions(+), 368 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 631e3b69a..eb746e122 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -20,28 +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\Export\Helper\ListPersonHelper; 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; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Contracts\Translation\TranslatorInterface; use function addcslashes; use function array_key_exists; use function array_keys; @@ -56,96 +48,35 @@ use function uniqid; */ class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { - public const FIELDS = [ - 'id', - 'civility', - 'firstName', - 'lastName', - 'birthdate', - '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 ListPersonHelper $listPersonHelper; private $slugs = []; private TranslatableStringHelper $translatableStringHelper; - private TranslatorInterface $translator; - - private UserRepositoryInterface $userRepository; - public function __construct( ExportAddressHelper $addressHelper, - CivilityRepositoryInterface $civilityRepository, - CountryRepository $countryRepository, CustomFieldProvider $customFieldProvider, + ListPersonHelper $listPersonHelper, EntityManagerInterface $em, - LanguageRepositoryInterface $languageRepository, - MaritalStatusRepositoryInterface $maritalStatusRepository, - TranslatableStringHelper $translatableStringHelper, - TranslatorInterface $translator, - UserRepositoryInterface $userRepository + TranslatableStringHelper $translatableStringHelper ) { $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; + $this->listPersonHelper = $listPersonHelper; + $this->entityManager = $em; + $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder) { - $choices = array_combine(self::FIELDS, self::FIELDS); + $choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS); foreach ($this->getCustomFields() as $cf) { $choices[$this->translatableStringHelper->localize($cf->getName())] @@ -205,183 +136,24 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { - if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + if (in_array($key, $this->listPersonHelper->getAllPossibleFields(), true)) { + return $this->listPersonHelper->getLabels($key, $values, $data); } - 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 function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - // 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)); - } - - 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) use ($key) { - if ('_header' === $value) { - 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 $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $country = $this->countryRepository->find($value); - - return $this->translatableStringHelper->localize( - $country->getName() - ); - }; - - default: - // for fields which are associated with person - if (in_array($key, self::FIELDS, true)) { - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $value; - }; - } - - return $this->getLabelForCustomField($key, $values, $data); - } + return $this->getLabelForCustomField($key, $values, $data); } public function getQueryKeys($data) { $fields = []; - foreach (self::FIELDS as $key) { + foreach (ListPersonHelper::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')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); continue; } @@ -442,109 +214,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $fields = $data['fields']; - foreach (self::FIELDS as $f) { - if (!in_array($f, $fields, true)) { - continue; - } - - switch ($f) { - case 'countryOfBirth': - case 'nationality': - $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); - - break; - - case 'address_fields': - foreach ($this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - - $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']); - - 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; - - default: - $qb->addSelect(sprintf('person.%s as %s', $f, $f)); - } - } + $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); foreach ($this->getCustomFields() as $cf) { if (!in_array($cf->getSlug(), $fields, true)) { @@ -595,7 +265,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { // get the field starting with address_ $addressFields = array_filter( - self::FIELDS, + ListPersonHelper::FIELDS, static fn (string $el): bool => substr($el, 0, 8) === 'address_' ); @@ -611,29 +281,6 @@ 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']; diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php new file mode 100644 index 000000000..7a14e0446 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -0,0 +1,428 @@ +addressHelper = $addressHelper; + $this->civilityRepository = $civilityRepository; + $this->countryRepository = $countryRepository; + $this->languageRepository = $languageRepository; + $this->maritalStatusRepository = $maritalStatusRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->translator = $translator; + $this->userRepository = $userRepository; + } + + /** + * @param array|value-of[] $fields + */ + public function addSelect(QueryBuilder $qb, array $fields, DateTimeImmutable $computedDate): void + { + foreach (ListPersonHelper::FIELDS as $f) { + if (!in_array($f, $fields, true)) { + continue; + } + + switch ($f) { + case 'countryOfBirth': + case 'nationality': + $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); + + break; + + case 'address_fields': + foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { + $qb + ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); + } + + $this->addCurrentAddressAt($qb, $computedDate); + + 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, $computedDate); + + 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', $computedDate); + + 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; + + default: + $qb->addSelect(sprintf('person.%s as %s', $f, $f)); + } + } + } + + /** + * @return array|string[] + */ + public function getAllPossibleFields(): array + { + return array_merge( + self::FIELDS, + ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], + $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + ); + } + + public function getLabels($key, array $values, $data): callable + { + 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 function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + // 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)); + } + + 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) use ($key) { + if ('_header' === $value) { + 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 $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $country = $this->countryRepository->find($value); + + return $this->translatableStringHelper->localize( + $country->getName() + ); + }; + + default: + // for fields which are associated with person + if (in_array($key, ListPersonHelper::FIELDS, true)) { + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $value; + }; + } + } + } + + 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); + } + } +} From cf7d0c1bdb7176b163d84f7077016458a1198551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:57:10 +0200 Subject: [PATCH 05/12] Fixed: [export][acl][Social issue filter] fix type for retrieving data from form --- .../Filter/AccompanyingCourseFilters/SocialIssueFilter.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php index 141a1a2db..917e129bf 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/SocialIssueFilter.php @@ -12,7 +12,6 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Filter\AccompanyingCourseFilters; use Chill\MainBundle\Export\FilterInterface; -use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Form\Type\PickSocialIssueType; @@ -31,15 +30,11 @@ class SocialIssueFilter implements FilterInterface private SocialIssueRender $socialIssueRender; - private TranslatableStringHelper $translatableStringHelper; - public function __construct( TranslatorInterface $translator, - TranslatableStringHelper $translatableStringHelper, SocialIssueRender $socialIssueRender ) { $this->translator = $translator; - $this->translatableStringHelper = $translatableStringHelper; $this->socialIssueRender = $socialIssueRender; } @@ -59,7 +54,7 @@ class SocialIssueFilter implements FilterInterface $qb->andWhere($clause) ->setParameter( 'socialissues', - SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']) + SocialIssue::getDescendantsWithThisForIssues($data['accepted_socialissues']->toArray()) ); } From 4e55f1ede77cac731d77fcfc515733d37b95400f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:58:28 +0200 Subject: [PATCH 06/12] DX: ensure that the return type is correct in ListPersonHelper --- .../Export/Helper/ListPersonHelper.php | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 7a14e0446..69f797c3d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -25,6 +25,7 @@ use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; +use RuntimeException; use Symfony\Contracts\Translation\TranslatorInterface; use function in_array; use function strlen; @@ -390,16 +391,18 @@ class ListPersonHelper }; default: - // for fields which are associated with person - if (in_array($key, ListPersonHelper::FIELDS, true)) { - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $value; - }; + if (!in_array($key, self::getAllPossibleFields(), true)) { + throw new RuntimeException("this key is not supported by this helper: {$key}"); } + + // for fields which are associated with person + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $value; + }; } } From d75ec92417d0f82b12aae3e8a33f9244326638ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 14:59:06 +0200 Subject: [PATCH 07/12] Feature: [export] add a list of people having an accompanying period --- .../ListPersonWithAccompanyingPeriod.php | 218 ++++++++++++++++++ .../config/services/exports_person.yaml | 9 +- .../translations/messages.fr.yml | 5 +- 3 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php new file mode 100644 index 000000000..ce97ce662 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -0,0 +1,218 @@ +addressHelper = $addressHelper; + $this->listPersonHelper = $listPersonHelper; + $this->entityManager = $em; + } + + public function buildForm(FormBuilderInterface $builder) + { + $choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS); + + // Add a checkbox to select fields + $builder->add('fields', ChoiceType::class, [ + 'multiple' => true, + 'expanded' => true, + 'choices' => $choices, + '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' || 'center' === $val || 'household' === $val) { + return ['data-display-target' => 'address_date']; + } + + return []; + }, + 'constraints' => [new Callback([ + 'callback' => static function ($selected, ExecutionContextInterface $context) { + if (count($selected) === 0) { + $context->buildViolation('You must select at least one element') + ->atPath('fields') + ->addViolation(); + } + }, + ])], + 'data' => array_values($choices), + ]); + + // add a date field for addresses + $builder->add('address_date', ChillDateType::class, [ + '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', + ]); + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.person_with_acp.Create a list of people having an accompaying periods, according to various filters.'; + } + + public function getGroup(): string + { + return 'Exports of persons'; + } + + public function getLabels($key, array $values, $data) + { + return $this->listPersonHelper->getLabels($key, $values, $data); + } + + public function getQueryKeys($data) + { + $fields = []; + + foreach (ListPersonHelper::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(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + + continue; + } + + if ('lifecycleUpdate' === $key) { + $fields = array_merge($fields, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']); + + continue; + } + + $fields[] = $key; + } + + return $fields; + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.person_with_acp.List peoples having an accompanying period'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + /** + * @param array{fields: string[], address_date: DateTimeImmutable} $data + */ + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + // throw an error if any fields are present + if (!array_key_exists('fields', $data)) { + throw new \Doctrine\DBAL\Exception\InvalidArgumentException('any fields ' + . 'have been checked'); + } + + $qb = $this->entityManager->createQueryBuilder(); + + $qb->from(Person::class, 'person') + ->join('person.accompanyingPeriodParticipations', 'acppart') + ->join('acppart.accompanyingPeriod', 'acp') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' + ) + )->setParameter('authorized_centers', $centers); + + $fields = $data['fields']; + + $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [Declarations::PERSON_TYPE, Declarations::PERSON_IMPLIED_IN, Declarations::ACP_TYPE]; + } + + public function validateForm($data, ExecutionContextInterface $context) + { + // get the field starting with address_ + $addressFields = array_filter( + ListPersonHelper::FIELDS, + static fn (string $el): bool => substr($el, 0, 8) === 'address_' + ); + + // 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 (!$data['address_date'] instanceof DateTimeImmutable) { + $context + ->buildViolation('You must set this date if an address is checked') + ->atPath('address_date') + ->addViolation(); + } + } + } +} diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index ffffaf175..b9030ec62 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -15,13 +15,18 @@ services: tags: - { name: chill.export, alias: count_person_with_accompanying_course } - chill.person.export.list_person: - class: Chill\PersonBundle\Export\Export\ListPerson + Chill\PersonBundle\Export\Export\ListPerson: autowire: true autoconfigure: true tags: - { name: chill.export, alias: list_person } + Chill\PersonBundle\Export\Export\ListPersonWithAccompanyingPeriod: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: list_person_with_acp } + chill.person.export.list_person.duplicate: class: Chill\PersonBundle\Export\Export\ListPersonDuplicate arguments: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 0b71d9698..5f43608ce 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1019,8 +1019,9 @@ export: Computation date for referrer: Date à laquelle le référent était actif by_referrer: Computation date for referrer: Date à laquelle le référent était actif - lists: - + list: + person_with_acp.List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement + Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager social_action: and children: et dérivés From 6ffe99a2be1196ead6eec156a99bce5bda0059f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 28 Oct 2022 22:25:53 +0200 Subject: [PATCH 08/12] Feature: [export] Add a list of accompanying periods --- .../ChillMainExtension.php | 4 + .../ChillMainBundle/Doctrine/DQL/STX.php | 37 ++ .../ChillMainBundle/Doctrine/DQL/STY.php | 37 ++ .../Export/Helper/DateTimeHelper.php | 60 +++ .../Export/Helper/ExportAddressHelper.php | 174 +++++--- .../Export/Helper/UserHelper.php | 43 ++ .../Export/Export/CountAccompanyingCourse.php | 2 +- .../Export/Export/ListAccompanyingPeriod.php | 416 ++++++++++++++++++ .../Export/Export/ListPerson.php | 2 +- .../ListPersonWithAccompanyingPeriod.php | 4 +- .../Export/Helper/ListPersonHelper.php | 42 +- .../config/services/exports_person.yaml | 6 + .../translations/messages.fr.yml | 44 +- 13 files changed, 778 insertions(+), 93 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php create mode 100644 src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php create mode 100644 src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php create mode 100644 src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 17c6e0b64..7c63263ba 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -34,6 +34,8 @@ use Chill\MainBundle\Doctrine\DQL\Replace; use Chill\MainBundle\Doctrine\DQL\Similarity; use Chill\MainBundle\Doctrine\DQL\STContains; use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; +use Chill\MainBundle\Doctrine\DQL\STX; +use Chill\MainBundle\Doctrine\DQL\STY; use Chill\MainBundle\Doctrine\DQL\ToChar; use Chill\MainBundle\Doctrine\DQL\Unaccent; use Chill\MainBundle\Doctrine\ORM\Hydration\FlatHierarchyEntityHydrator; @@ -242,6 +244,8 @@ class ChillMainExtension extends Extension implements 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class, 'ST_CONTAINS' => STContains::class, 'JSONB_ARRAY_LENGTH' => JsonbArrayLength::class, + 'ST_X' => STX::class, + 'ST_Y' => STY::class, ], 'datetime_functions' => [ 'EXTRACT' => Extract::class, diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php new file mode 100644 index 000000000..d5d8d0a3f --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STX.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php new file mode 100644 index 000000000..e827da543 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/STY.php @@ -0,0 +1,37 @@ +field->dispatch($sqlWalker)); + } + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->field = $parser->ArithmeticExpression(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php new file mode 100644 index 000000000..86a2458b2 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/DateTimeHelper.php @@ -0,0 +1,60 @@ +translator = $translator; + } + + public function getLabel($header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $this->translator->trans($header); + } + + if (null === $value) { + return ''; + } + + // 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)); + } + + if (!$hasTime) { + $date->setTime(0, 0, 0); + } + + return $date; + }; + } +} diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 6c6fcb76e..41443e3d8 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -14,9 +14,11 @@ namespace Chill\MainBundle\Export\Helper; use Chill\MainBundle\Repository\AddressRepository; use Chill\MainBundle\Templating\Entity\AddressRender; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; +use Doctrine\ORM\QueryBuilder; use LogicException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; +use function in_array; use function strlen; /** @@ -24,6 +26,10 @@ use function strlen; */ class ExportAddressHelper { + public const F_ALL = + self::F_ATTRIBUTES | self::F_BUILDING | self::F_COUNTRY | + self::F_GEOM | self::F_POSTAL_CODE | self::F_STREET; + public const F_AS_STRING = 0b00010000; public const F_ATTRIBUTES = 0b01000000; @@ -77,6 +83,87 @@ class ExportAddressHelper $this->addressRender = $addressRender; } + public function addSelectClauses(int $params, QueryBuilder $queryBuilder, $entityName = 'address', $prefix = 'add') + { + foreach (self::ALL as $key => $bitmask) { + if (($params & $bitmask) === $bitmask) { + foreach (self::COLUMN_MAPPING[$key] as $field) { + switch ($field) { + case 'id': + case '_as_string': + $queryBuilder->addSelect(sprintf('%s.id AS %s%s', $entityName, $prefix, $field)); + + break; + + case 'street': + case 'streetNumber': + case 'building': + case 'floor': + case 'corridor': + case 'steps': + case 'buildingName': + case 'flat': + case 'distribution': + case 'extra': + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case 'country': + case 'postcode_name': + case 'postcode_code': + $postCodeAlias = sprintf('%spostcode_t', $prefix); + + if (!in_array($postCodeAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin($entityName . '.postcode', $postCodeAlias); + } + + if ('postcode_name' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'name', $prefix, $field)); + + break; + } + + if ('postcode_code' === $field) { + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $postCodeAlias, 'code', $prefix, $field)); + + break; + } + + $countryAlias = sprintf('%scountry_t', $prefix); + + if (!in_array($countryAlias, $queryBuilder->getAllAliases(), true)) { + $queryBuilder->leftJoin(sprintf('%s.country', $postCodeAlias), $countryAlias); + } + + $queryBuilder->addSelect(sprintf('%s.%s AS %s%s', $countryAlias, 'name', $prefix, $field)); + + break; + + case 'isNoAddress': + case 'confidential': + $queryBuilder->addSelect(sprintf('CASE WHEN %s.%s = \'TRUE\' THEN 1 ELSE 0 END AS %s%s', $entityName, $field, $prefix, $field)); + + break; + + case '_lat': + $queryBuilder->addSelect(sprintf('ST_Y(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + case '_lon': + $queryBuilder->addSelect(sprintf('ST_X(%s.point) AS %s%s', $entityName, $prefix, $field)); + + break; + + default: + throw new LogicException('This key is not supported: ' . $key); + } + } + } + } + } + /** * @param self::F_* $params * @@ -115,23 +202,11 @@ class ExportAddressHelper 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) { + case 'postcode_code': + case 'postcode_name': + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -140,28 +215,25 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - $geom = $address->getPoint(); + return $value; + }; - if (null === $geom) { + case 'country': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp' . $key; + } + + if (null === $value) { return ''; } - switch ($sanitizedKey) { - case '_lat': - return $geom->getLat(); - - case '_lon': - return $geom->getLon(); - - default: - throw new LogicException('only _lat or _lon accepted, given: ' . $sanitizedKey); - } + return $this->translatableStringHelper->localize(json_decode($value, true)); }; case 'isNoAddress': case 'confidential': - return function ($value) use ($sanitizedKey, $translationPrefix) { + return static function ($value) use ($sanitizedKey, $translationPrefix) { if ('_header' === $value) { return $translationPrefix . $sanitizedKey; } @@ -170,9 +242,7 @@ class ExportAddressHelper return ''; } - $address = $this->addressRepository->find($value); - - switch ($val = $this->propertyAccess->getValue($address, $sanitizedKey)) { + switch ($value) { case null: return ''; @@ -187,21 +257,6 @@ class ExportAddressHelper } }; - 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) { @@ -217,31 +272,6 @@ class ExportAddressHelper 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); } diff --git a/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php new file mode 100644 index 000000000..98c4c5579 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Export/Helper/UserHelper.php @@ -0,0 +1,43 @@ +userRender = $userRender; + $this->userRepository = $userRepository; + } + + public function getLabel($key, array $values, string $header): callable + { + return function ($value) use ($header) { + if ('_header' === $value) { + return $header; + } + + if (null === $value || null === $user = $this->userRepository->find($value)) { + return ''; + } + + return $this->userRender->renderString($user, []); + }; + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php index 8c8744dfd..d07ee3639 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/CountAccompanyingCourse.php @@ -38,7 +38,7 @@ class CountAccompanyingCourse implements ExportInterface, GroupedExportInterface public function buildForm(FormBuilderInterface $builder): void { - // TODO: Implement buildForm() method. + // Nothing to add here } public function getAllowedFormattersTypes(): array diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php new file mode 100644 index 000000000..28ad94bd3 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListAccompanyingPeriod.php @@ -0,0 +1,416 @@ +addressHelper = $addressHelper; + $this->dateTimeHelper = $dateTimeHelper; + $this->entityManager = $entityManager; + $this->personRender = $personRender; + $this->personRepository = $personRepository; + $this->socialIssueRender = $socialIssueRender; + $this->socialIssueRepository = $socialIssueRepository; + $this->thirdPartyRender = $thirdPartyRender; + $this->thirdPartyRepository = $thirdPartyRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->userHelper = $userHelper; + } + + public function buildForm(FormBuilderInterface $builder) + { + $builder + ->add('calc_date', ChillDateType::class, [ + 'input' => 'datetime_immutable', + 'label' => 'export.list.acp.Date of calculation for associated elements', + 'help' => 'export.list.acp.The associated referree, localisation, and other elements will be valid at this date', + 'required' => true, + ]); + } + + public function getAllowedFormattersTypes() + { + return [FormatterInterface::TYPE_LIST]; + } + + public function getDescription() + { + return 'export.list.acp.Generate a list of accompanying periods, filtered on different parameters.'; + } + + public function getGroup(): string + { + return 'Exports of accompanying courses'; + } + + public function getLabels($key, array $values, $data) + { + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + } + + switch ($key) { + case 'stepSince': + case 'openingDate': + case 'closingDate': + case 'referrerSince': + case 'createdAt': + case 'updatedAt': + return $this->dateTimeHelper->getLabel('export.list.acp.' . $key); + + case 'origin': + case 'closingMotive': + case 'job': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $this->translatableStringHelper->localize(json_decode($value, true)); + }; + + case 'locationPersonName': + case 'requestorPerson': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $person = $this->personRepository->find($value)) { + return ''; + } + + return $this->personRender->renderString($person, []); + }; + + case 'requestorThirdParty': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value || null === $thirdparty = $this->thirdPartyRepository->find($value)) { + return ''; + } + + return $this->thirdPartyRender->renderString($thirdparty, []); + }; + + case 'scopes': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->translatableStringHelper->localize($s), + json_decode($value, true) + ) + ); + }; + + case 'socialIssues': + return function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return implode( + '|', + array_map( + fn ($s) => $this->socialIssueRender->renderString($this->socialIssueRepository->find($s), []), + json_decode($value, true) + ) + ); + }; + + default: + return static function ($value) use ($key) { + if ('_header' === $value) { + return 'export.list.acp.' . $key; + } + + if (null === $value) { + return ''; + } + + return $value; + }; + } + } + + public function getQueryKeys($data) + { + return array_merge( + self::FIELDS, + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') + ); + } + + public function getResult($query, $data) + { + return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR); + } + + public function getTitle() + { + return 'export.list.acp.List of accompanying periods'; + } + + public function getType() + { + return Declarations::PERSON_TYPE; + } + + public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) + { + $centers = array_map(static function ($el) { + return $el['center']; + }, $acl); + + $qb = $this->entityManager->createQueryBuilder(); + + $qb + ->from(AccompanyingPeriod::class, 'acp') + ->andWhere('acp.step != :list_acp_step') + ->andWhere( + $qb->expr()->exists( + 'SELECT 1 FROM ' . AccompanyingPeriodParticipation::class . ' acl_count_part + JOIN ' . PersonCenterHistory::class . ' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person) + WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers) + ' + ) + ) + ->setParameter('list_acp_step', AccompanyingPeriod::STEP_DRAFT) + ->setParameter('authorized_centers', $centers); + + $this->addSelectClauses($qb, $data['calc_date']); + + return $qb; + } + + public function requiredRole(): string + { + return PersonVoter::LISTS; + } + + public function supportsModifiers() + { + return [ + Declarations::ACP_TYPE, + ]; + } + + private function addSelectClauses(QueryBuilder $qb, DateTimeImmutable $calcDate): void + { + // add the regular fields + foreach (['id', 'openingDate', 'closingDate', 'confidential', 'emergency', 'intensity', 'createdAt', 'updatedAt'] as $field) { + $qb->addSelect(sprintf('acp.%s AS %s', $field, $field)); + } + + // add the field which are simple association + foreach (['origin' => 'label', 'closingMotive' => 'name', 'job' => 'label', 'createdBy' => 'label', 'updatedBy' => 'label', 'administrativeLocation' => 'name'] as $entity => $field) { + $qb + ->leftJoin(sprintf('acp.%s', $entity), "{$entity}_t") + ->addSelect(sprintf('%s_t.%s AS %s', $entity, $field, $entity)); + } + + // step at date + $qb + ->addSelect('stepHistory.step AS step') + ->addSelect('stepHistory.startDate AS stepSince') + ->leftJoin('acp.stepHistories', 'stepHistory') + ->andWhere( + $qb->expr()->andX( + $qb->expr()->lte('stepHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('stepHistory.endDate'), $qb->expr()->gt('stepHistory.endDate', ':calcDate')) + ) + ); + + // referree at date + $qb + ->addSelect('referrer_t.label AS referrer') + ->addSelect('userHistory.startDate AS referrerSince') + ->leftJoin('acp.userHistories', 'userHistory') + ->leftJoin('userHistory.user', 'referrer_t') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('userHistory'), + $qb->expr()->andX( + $qb->expr()->lte('userHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('userHistory.endDate'), $qb->expr()->gt('userHistory.endDate', ':calcDate')) + ) + ) + ); + + // location of the acp + $qb + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 1 ELSE 0 END AS locationIsPerson') + ->addSelect('CASE WHEN locationHistory.personLocation IS NOT NULL THEN 0 ELSE 1 END AS locationIsTemp') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonName') + ->addSelect('IDENTITY(locationHistory.personLocation) AS locationPersonId') + ->leftJoin('acp.locationHistories', 'locationHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('locationHistory'), + $qb->expr()->andX( + $qb->expr()->lte('locationHistory.startDate', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('locationHistory.endDate'), $qb->expr()->gt('locationHistory.endDate', ':calcDate')) + ) + ) + ) + ->leftJoin(PersonHouseholdAddress::class, 'personAddress', Join::WITH, 'locationHistory.personLocation = personAddress.person') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('personAddress'), + $qb->expr()->andX( + $qb->expr()->lte('personAddress.validFrom', ':calcDate'), + $qb->expr()->orX($qb->expr()->isNull('personAddress.validTo'), $qb->expr()->gt('personAddress.validTo', ':calcDate')) + ) + ) + ) + ->leftJoin(Address::class, 'acp_address', Join::WITH, 'COALESCE(IDENTITY(locationHistory.addressLocation), IDENTITY(personAddress.address)) = acp_address.id'); + + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'acp_address', 'address_fields'); + + // requestor + $qb + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 1 ELSE 0 END AS isRequestorPerson') + ->addSelect('CASE WHEN acp.requestorPerson IS NULL THEN 0 ELSE 1 END AS isRequestorThirdParty') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPersonId') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdPartyId') + ->addSelect('IDENTITY(acp.requestorPerson) AS requestorPerson') + ->addSelect('IDENTITY(acp.requestorThirdParty) AS requestorThirdParty'); + + $qb + // scopes + ->addSelect('(SELECT AGGREGATE(scope.name) FROM ' . Scope::class . ' scope WHERE scope MEMBER OF acp.scopes) AS scopes') + // social issues + ->addSelect('(SELECT AGGREGATE(socialIssue.id) FROM ' . SocialIssue::class . ' socialIssue WHERE socialIssue MEMBER OF acp.socialIssues) AS socialIssues'); + + // add parameter + $qb->setParameter('calcDate', $calcDate); + } +} diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index eb746e122..d1a23a570 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -153,7 +153,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index ce97ce662..534eaecc6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -17,6 +17,7 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person\PersonCenterHistory; use Chill\PersonBundle\Export\Declarations; @@ -121,7 +122,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields')); continue; } @@ -173,6 +174,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac $qb->from(Person::class, 'person') ->join('person.accompanyingPeriodParticipations', 'acppart') ->join('acppart.accompanyingPeriod', 'acp') + ->andWhere($qb->expr()->neq('acp.step', "'" . AccompanyingPeriod::STEP_DRAFT . "'")) ->andWhere( $qb->expr()->exists( 'SELECT 1 FROM ' . PersonCenterHistory::class . ' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)' diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php index 69f797c3d..de8870674 100644 --- a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Export\Helper; use Chill\MainBundle\Export\Helper\ExportAddressHelper; +use Chill\MainBundle\Repository\CenterRepositoryInterface; use Chill\MainBundle\Repository\CivilityRepositoryInterface; use Chill\MainBundle\Repository\CountryRepository; use Chill\MainBundle\Repository\LanguageRepositoryInterface; @@ -72,16 +73,10 @@ class ListPersonHelper 'lifecycleUpdate', ]; - public 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 CenterRepositoryInterface $centerRepository; + private CivilityRepositoryInterface $civilityRepository; private CountryRepository $countryRepository; @@ -98,6 +93,7 @@ class ListPersonHelper public function __construct( ExportAddressHelper $addressHelper, + CenterRepositoryInterface $centerRepository, CivilityRepositoryInterface $civilityRepository, CountryRepository $countryRepository, LanguageRepositoryInterface $languageRepository, @@ -107,6 +103,7 @@ class ListPersonHelper UserRepositoryInterface $userRepository ) { $this->addressHelper = $addressHelper; + $this->centerRepository = $centerRepository; $this->civilityRepository = $civilityRepository; $this->countryRepository = $countryRepository; $this->languageRepository = $languageRepository; @@ -134,12 +131,9 @@ class ListPersonHelper break; case 'address_fields': - foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - $this->addCurrentAddressAt($qb, $computedDate); + $qb->leftJoin('personHouseholdAddress.address', 'personAddress'); + $this->addressHelper->addSelectClauses(ExportAddressHelper::F_ALL, $qb, 'personAddress', 'address_fields'); break; @@ -154,7 +148,10 @@ class ListPersonHelper } if (in_array('address_fields', $fields, true)) { - $qb->addGroupBy('address_fieldsid'); + $qb + ->addGroupBy('address_fieldsid') + ->addGroupBy('address_fieldscountry_t.id') + ->addGroupBy('address_fieldspostcode_t.id'); } if (in_array('household_id', $fields, true)) { @@ -234,7 +231,7 @@ class ListPersonHelper return array_merge( self::FIELDS, ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], - $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + $this->addressHelper->getKeys(ExportAddressHelper::F_ALL, 'address_fields') ); } @@ -245,6 +242,19 @@ class ListPersonHelper } switch ($key) { + case 'center': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value || null === $center = $this->centerRepository->find($value)) { + return ''; + } + + return $center->getName(); + }; + case 'birthdate': case 'deathdate': case 'maritalStatusDate': @@ -413,7 +423,7 @@ class ListPersonHelper ->leftJoin('person.householdAddresses', 'personHouseholdAddress') ->andWhere( $qb->expr()->orX( - // no address at this time + // no address at this time $qb->expr()->isNull('personHouseholdAddress'), // there is one address... $qb->expr()->andX( diff --git a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml index b9030ec62..b2b182c5b 100644 --- a/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml +++ b/src/Bundle/ChillPersonBundle/config/services/exports_person.yaml @@ -27,6 +27,12 @@ services: tags: - { name: chill.export, alias: list_person_with_acp } + Chill\PersonBundle\Export\Export\ListAccompanyingPeriod: + autowire: true + autoconfigure: true + tags: + - { name: chill.export, alias: list_acp } + chill.person.export.list_person.duplicate: class: Chill\PersonBundle\Export\Export\ListPersonDuplicate arguments: diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 5f43608ce..319231489 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -1020,8 +1020,48 @@ export: by_referrer: Computation date for referrer: Date à laquelle le référent était actif list: - person_with_acp.List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement - Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + person_with_acp: + List peoples having an accompanying period: Liste des personnes ayant un parcours d'accompagnement + Create a list of people having an accompaying periods, according to various filters.: Génère une liste des personnes ayant un parcours d'accompagnement, selon différents critères liés au parcours ou à l'usager + acp: + List of accompanying periods: Liste de périodes d'accompagnements + Generate a list of accompanying periods, filtered on different parameters.: Génère une liste des périodes d'accompagnement, filtrée sur différents paramètres. + Date of calculation for associated elements: Date de calcul des éléments associés + The associated referree, localisation, and other elements will be valid at this date: Les éléments associés, comme la localisation, le référent et d'autres éléments seront valides à cette date + id: Identifiant du parcours + openingDate: Date d'ouverture du parcours + closingDate: Date de fermeture du parcours + confidential: Confidentiel + emergency: Urgent + intensity: Intensité + createdAt: Créé le + updatedAt: Dernière mise à jour le + acpOrigin: Origine du parcours + acpClosingMotive: Motif de fermeture + acpJob: Métier du parcours + createdBy: Créé par + updatedBy: Dernière modification par + administrativeLocation: Location administrative + step: Etape + stepSince: Dernière modification de l'étape + referrer: Référent + referrerSince: Référent depuis le + locationIsPerson: Parcours localisé auprès d'un usager concerné + locationIsTemp: Parcours avec une localisation temporaire + acpLocationPersonName: Usager auprès duquel le parcours est localisé + locationPersonId: Identifiant de l'usager auprès duquel le parcours est localisé + acpaddress_fieldscountry: Pays de l'adresse + isRequestorPerson: Le demandeur est-il un usager ? + isRequestorThirdParty: Le demandeur est-il un tiers ? + requestorPersonId: Identifiant du demandeur personne + requestorThirdPartyId: Identifiant du tiers + acprequestorPerson: Nom du demandeur personne + acprequestorThirdPaty: Nom du demandeur tiers + scopes: Services + socialIssues: Problématiques sociales + + + social_action: and children: et dérivés From c331f9420174cae5e17204410e27e1d5d638dd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 17:53:25 +0100 Subject: [PATCH 09/12] fix cs --- .../ChillActivityBundle/Export/Filter/UsersJobFilter.php | 4 ++-- .../ByHouseholdCompositionAggregator.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php index dcdacd84a..b52ef441c 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/UsersJobFilter.php @@ -39,9 +39,9 @@ class UsersJobFilter implements FilterInterface $qb ->andWhere( $qb->expr()->exists( - 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act + 'SELECT 1 FROM ' . Activity::class . ' activity_users_job_filter_act JOIN activity_users_job_filter_act.users users WHERE users.userJob IN (:activity_users_job_filter_jobs) AND activity_users_job_filter_act = activity ' - ) + ) ) ->setParameter('activity_users_job_filter_jobs', $data['jobs']); } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php index e96c8a228..f299762e6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByHouseholdCompositionAggregator.php @@ -18,6 +18,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdComposition; use Chill\PersonBundle\Entity\Household\HouseholdMember; use Chill\PersonBundle\Export\Declarations; use Chill\PersonBundle\Repository\Household\HouseholdCompositionTypeRepositoryInterface; +use DateTimeImmutable; use Doctrine\ORM\Query\Expr\Join; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; @@ -96,7 +97,7 @@ class ByHouseholdCompositionAggregator implements AggregatorInterface $builder->add('date_calc', ChillDateType::class, [ 'label' => 'export.aggregator.course.by_household_composition.Calc date', 'input_format' => 'datetime_immutable', - 'data' => new \DateTimeImmutable('now'), + 'data' => new DateTimeImmutable('now'), ]); } From afa6dfd77c92fd1c6cf8f3a3aa6bbc72595e01d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 18:00:41 +0100 Subject: [PATCH 10/12] fix psalm errors --- .../ChillMainBundle/Export/Helper/ExportAddressHelper.php | 2 +- .../ChillMainBundle/Tests/Export/ExportManagerTest.php | 6 ++++-- src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php | 2 +- .../Export/Export/ListPersonWithAccompanyingPeriod.php | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php index 41443e3d8..268abc3ea 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -253,7 +253,7 @@ class ExportAddressHelper return 0; default: - throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $val); + throw new LogicException('this value is not supported for ' . $sanitizedKey . ': ' . $value); } }; diff --git a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php index b3f5eac85..701b3d14e 100644 --- a/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Export/ExportManagerTest.php @@ -680,10 +680,12 @@ final class ExportManagerTest extends KernelTestCase return new ExportManager( $logger ?? self::$container->get('logger'), - $em ?? self::$container->get('doctrine.orm.entity_manager'), $authorizationChecker ?? self::$container->get('security.authorization_checker'), $authorizationHelper ?? self::$container->get('chill.main.security.authorization.helper'), - $tokenStorage + $tokenStorage, + [], + [], + [] ); } } diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index d1a23a570..549fe1c4d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -187,7 +187,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } /** - * @param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index 534eaecc6..7ddc6c9d3 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -155,7 +155,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } /** - * @param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { From acc9523ff5edff313bb19bfb1f300ef9487e5ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 31 Oct 2022 18:22:01 +0100 Subject: [PATCH 11/12] DX: repairs code style, and psalm types --- phpstan-types.neon | 20 ------------------- .../Export/Helper/ExportAddressHelper.php | 7 +------ .../Export/Export/ListPerson.php | 2 +- .../ListPersonWithAccompanyingPeriod.php | 2 +- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/phpstan-types.neon b/phpstan-types.neon index bf2ece912..1aae06880 100644 --- a/phpstan-types.neon +++ b/phpstan-types.neon @@ -10,16 +10,6 @@ parameters: count: 1 path: src/Bundle/ChillActivityBundle/Entity/ActivityReasonCategory.php - - - message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getDescription\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - - - message: "#^Method Chill\\\\ActivityBundle\\\\Export\\\\Export\\\\StatActivityDuration\\:\\:getTitle\\(\\) should return string but return statement is missing\\.$#" - count: 1 - path: src/Bundle/ChillActivityBundle/Export/Export/StatActivityDuration.php - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 1 @@ -330,16 +320,6 @@ parameters: count: 6 path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/CountryOfBirthAggregator.php - - - - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" - count: 1 - path: src/Bundle/ChillPersonBundle/Export/Aggregator/NationalityAggregator.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 268abc3ea..2f9a270ff 100644 --- a/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php +++ b/src/Bundle/ChillMainBundle/Export/Helper/ExportAddressHelper.php @@ -58,7 +58,7 @@ class ExportAddressHelper 'country' => ['country'], 'postal_code' => ['postcode_code', 'postcode_name'], 'street' => ['street', 'streetNumber'], - 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor'], + 'building' => ['buildingName', 'corridor', 'distribution', 'extra', 'flat', 'floor', 'steps'], 'string' => ['_as_string'], 'attributes' => ['isNoAddress', 'confidential', 'id'], 'geom' => ['_lat', '_lon'], @@ -97,7 +97,6 @@ class ExportAddressHelper case 'street': case 'streetNumber': - case 'building': case 'floor': case 'corridor': case 'steps': @@ -238,10 +237,6 @@ class ExportAddressHelper return $translationPrefix . $sanitizedKey; } - if (null === $value) { - return ''; - } - switch ($value) { case null: return ''; diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 549fe1c4d..acaeb498c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -187,7 +187,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } /** - * param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data. */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php index 7ddc6c9d3..bab67fb39 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPersonWithAccompanyingPeriod.php @@ -155,7 +155,7 @@ class ListPersonWithAccompanyingPeriod implements ExportElementValidatedInterfac } /** - * param array{fields: string[], address_date: DateTimeImmutable} $data + * param array{fields: string[], address_date: DateTimeImmutable} $data. */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []) { From 8d4ec5d6756c049de96017e8f88f6af56bc0e9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 2 Nov 2022 11:12:55 +0100 Subject: [PATCH 12/12] fix code style --- .../ACPAggregators/ByActivityNumberAggregator.php | 4 ++-- .../Export/Aggregator/SentReceivedAggregator.php | 10 ++++++---- .../Export/Filter/ACPFilters/HasNoActivityFilter.php | 7 ++++--- .../Export/Aggregator/ByActivityTypeAggregator.php | 4 ++-- .../src/Export/Declarations.php | 2 +- .../src/Export/Export/CountAsideActivity.php | 6 +----- .../src/Export/Filter/ByActivityTypeFilter.php | 2 +- .../src/Export/Filter/ByDateFilter.php | 2 +- .../ByActionNumberAggregator.php | 4 ++-- .../CreatorJobAggregator.php | 4 ++-- .../EvaluationAggregators/ByEndDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByMaxDateAggregator.php | 4 ++-- .../EvaluationAggregators/ByStartDateAggregator.php | 4 ++-- .../HavingEndDateAggregator.php | 9 +++++---- .../CurrentActionAggregator.php | 10 ++++++---- .../AccompanyingCourseFilters/CreatorFilter.php | 2 +- .../AccompanyingCourseFilters/CreatorJobFilter.php | 2 +- .../AccompanyingCourseFilters/HasNoActionFilter.php | 3 ++- .../HasNoReferrerFilter.php | 12 ++++++------ .../HasTemporaryLocationFilter.php | 2 +- .../Filter/EvaluationFilters/ByEndDateFilter.php | 4 ++-- .../Filter/EvaluationFilters/ByStartDateFilter.php | 4 ++-- .../EvaluationFilters/CurrentEvaluationsFilter.php | 2 +- .../Filter/SocialWorkFilters/CurrentActionFilter.php | 2 +- 24 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php index 44a3fd8f0..de35f75cc 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/ACPAggregators/ByActivityNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActivityNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php index b5300834c..2a6dec7f7 100644 --- a/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php +++ b/src/Bundle/ChillActivityBundle/Export/Aggregator/SentReceivedAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -11,9 +11,10 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Export\Aggregator; -use Chill\MainBundle\Export\AggregatorInterface; use Chill\ActivityBundle\Export\Declarations; +use Chill\MainBundle\Export\AggregatorInterface; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class SentReceivedAggregator implements AggregatorInterface @@ -41,10 +42,11 @@ class SentReceivedAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case 'sent': return 'is sent'; @@ -53,7 +55,7 @@ class SentReceivedAggregator implements AggregatorInterface return 'is received'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php index 13aaf475a..508644a07 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/HasNoActivityFilter.php @@ -14,9 +14,10 @@ namespace Chill\ActivityBundle\Export\Filter\ACPFilters; use Chill\ActivityBundle\Entity\Activity; use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; +use Doctrine\ORM\Query\Expr; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use Doctrine\ORM\Query\Expr; +use function in_array; class HasNoActivityFilter implements FilterInterface { @@ -36,7 +37,7 @@ class HasNoActivityFilter implements FilterInterface //TODO check this: ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. Activity::class .' activity + SELECT 1 FROM ' . Activity::class . ' activity WHERE activity.accompanyingPeriod = acp ) '); @@ -61,4 +62,4 @@ class HasNoActivityFilter implements FilterInterface { return 'Filter acp which has no activity'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index bbbdb6284..2104db81e 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php index 0262516aa..925169a68 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Declarations.php @@ -17,4 +17,4 @@ namespace ChillAsideActivityBundle\Export; abstract class Declarations { public const ASIDE_ACTIVITY_TYPE = 'aside_activity'; -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index 7af39568e..01b6a5204 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -16,9 +16,9 @@ use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\FormatterInterface; use Chill\MainBundle\Export\GroupedExportInterface; use ChillAsideActivityBundle\Export\Declarations; -use Symfony\Component\Form\FormBuilderInterface; use Doctrine\ORM\Query; use LogicException; +use Symfony\Component\Form\FormBuilderInterface; class CountAsideActivity implements ExportInterface, GroupedExportInterface { @@ -105,7 +105,3 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface return []; } } - - - - diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 60ca914b2..46d84c064 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -52,4 +52,4 @@ class ByActivityTypeFilter implements FilterInterface { return 'Filter by aside activity type'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index dc6b89662..ad876571e 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -52,4 +52,4 @@ class ByDateFilter implements FilterInterface { return 'Filter by aside activity date'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php index 3c16aa161..0f51b6feb 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/ByActionNumberAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByActionNumberAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php index 1449091e7..c473bf7a1 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/AccompanyingCourseAggregators/CreatorJobAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class CreatorJobAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php index 9c90c8dd3..40b035a02 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php index ded9fb685..36ab0f50b 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByMaxDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByMaxDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php index 121171d8c..e64245d53 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/ByStartDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -41,7 +41,7 @@ class ByStartDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php index 8def8cf88..af801b74d 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/EvaluationAggregators/HavingEndDateAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\EvaluationAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class HavingEndDateAggregator implements AggregatorInterface @@ -45,10 +46,11 @@ class HavingEndDateAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'enddate is specified'; @@ -57,9 +59,8 @@ class HavingEndDateAggregator implements AggregatorInterface return 'enddate is not specified'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } - }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php index d9120d121..a77838167 100644 --- a/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php +++ b/src/Bundle/ChillPersonBundle/Export/Aggregator/SocialWorkAggregators/CurrentActionAggregator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -/** +/* * Chill is a software for social workers * * For the full copyright and license information, please view @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Export\Aggregator\SocialWorkAggregators; use Chill\MainBundle\Export\AggregatorInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; +use LogicException; use Symfony\Component\Form\FormBuilderInterface; class CurrentActionAggregator implements AggregatorInterface @@ -27,7 +28,7 @@ class CurrentActionAggregator implements AggregatorInterface { $qb ->addSelect(' - (CASE true WHEN acpw.startDate IS NULL ELSE false END) + (CASE true WHEN acpw.startDate IS NULL ELSE false END) AS acpw_current_action_aggregator ') ->addGroupBy('acpw_current_action_aggregator'); @@ -45,10 +46,11 @@ class CurrentActionAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return function ($value): string { + return static function ($value): string { if ('_header' === $value) { return ''; } + switch ($value) { case true: return 'Current action'; @@ -57,7 +59,7 @@ class CurrentActionAggregator implements AggregatorInterface return 'Not current action'; default: - throw new \LogicException(sprintf('The value %s is not valid', $value)); + throw new LogicException(sprintf('The value %s is not valid', $value)); } }; } diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php index 7dd78d0b4..c7e110f62 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorFilter.php @@ -52,4 +52,4 @@ class CreatorFilter implements FilterInterface { return 'Filter by creator'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php index 021d2227c..6f094fe97 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/CreatorJobFilter.php @@ -52,4 +52,4 @@ class CreatorJobFilter implements FilterInterface { return 'Filter by creator job'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php index a26628e95..771b8d063 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoActionFilter.php @@ -15,6 +15,7 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\PersonBundle\Export\Declarations; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use function in_array; class HasNoActionFilter implements FilterInterface { @@ -51,4 +52,4 @@ class HasNoActionFilter implements FilterInterface { return 'Filter by which has no action'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php index c37d423f8..b18bcd1de 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasNoReferrerFilter.php @@ -15,10 +15,10 @@ use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class HasNoReferrerFilter implements FilterInterface { @@ -32,12 +32,12 @@ class HasNoReferrerFilter implements FilterInterface $qb ->andWhere(' NOT EXISTS ( - SELECT 1 FROM '. UserHistory::class .' uh - WHERE uh.startDate < :date + SELECT 1 FROM ' . UserHistory::class . ' uh + WHERE uh.startDate < :date AND ( - uh.endDate IS NULL + uh.endDate IS NULL or uh.endDate > :date - ) + ) AND uh.accompanyingPeriod = acp ) ') @@ -69,4 +69,4 @@ class HasNoReferrerFilter implements FilterInterface { return 'Filter by which has no referrer'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php index 18a6c3918..85d8d93b6 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/AccompanyingCourseFilters/HasTemporaryLocationFilter.php @@ -52,4 +52,4 @@ class HasTemporaryLocationFilter implements FilterInterface { return 'Filter by temporary location'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php index d40170447..5c509d50c 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByEndDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByEndDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByEndDateFilter implements FilterInterface { return 'Filter by end date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php index afa3c212a..e21a1ea68 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/ByStartDateFilter.php @@ -14,10 +14,10 @@ namespace Chill\PersonBundle\Export\Filter\EvaluationFilters; use Chill\MainBundle\Export\FilterInterface; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\PersonBundle\Export\Declarations; +use DateTime; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; -use DateTime; class ByStartDateFilter implements FilterInterface { @@ -64,4 +64,4 @@ class ByStartDateFilter implements FilterInterface { return 'Filter by start date evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php index 293e551f1..50c95600e 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/EvaluationFilters/CurrentEvaluationsFilter.php @@ -47,4 +47,4 @@ class CurrentEvaluationsFilter implements FilterInterface { return 'Filter by current evaluations'; } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php index ecbe6086a..cbf958182 100644 --- a/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php +++ b/src/Bundle/ChillPersonBundle/Export/Filter/SocialWorkFilters/CurrentActionFilter.php @@ -47,4 +47,4 @@ class CurrentActionFilter implements FilterInterface { return 'Filter by current actions'; } -} \ No newline at end of file +}