add basic fields csperson to list person export

This commit is contained in:
Julie Lenaerts 2024-05-14 11:01:45 +02:00
parent 4ed9d3d8e2
commit 97846a5877
3 changed files with 185 additions and 630 deletions

View File

@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Export;
use Chill\JobBundle\Entity\CSPerson;
use Chill\PersonBundle\Export\Helper\CustomizeListPersonHelperInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Class ListCSPerson.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class AddCSPersonToPersonListHelper implements CustomizeListPersonHelperInterface
{
public function __construct(private readonly TranslatorInterface $translator)
{
}
private const CSPERSON_FIELDS = [
/* CSPerson::RESSOURCES,
CSPerson::MOBILITE_MOYEN_TRANSPORT,
CSPerson::TYPE_CONTRAT,
CSPerson::PERMIS_CONDUIRE,
CSPerson::ACCOMPAGNEMENTS,
CSPerson::SITUATION_PROFESSIONNELLE,
CSPerson::NEET_ELIGIBILITY,*/
'findernieremploidate',
/* 'prescripteur__name',
'prescripteur__email',
'prescripteur__phone',*/
'poleemploiid',
'cafid',
'cafinscriptiondate',
'contratiejdate',
'cerinscriptiondate',
'ppaeinscriptiondate',
'ppaesignataire',
'cersignataire',
'neetcommissiondate',
'fsemademarchecode',
];
public function alterKeys(array $existing): array
{
return [...$existing, ...self::CSPERSON_FIELDS];
}
public function alterSelect(QueryBuilder $qb, \DateTimeImmutable $computedDate): void
{
$qb
->leftJoin(CSPerson::class, 'cs_person', Query\Expr\Join::WITH, 'cs_person.person = person');
foreach (self::CSPERSON_FIELDS as $f) {
switch ($f) {
case 'findernieremploidate':
$qb->addSelect('cs_person.dateFinDernierEmploi AS findernieremploidate');
break;
case 'poleemploiid':
$qb->addSelect('cs_person.poleEmploiId AS poleemploiid');
break;
case 'cafid':
$qb->addSelect('cs_person.cafId AS cafid');
break;
case 'cafinscriptiondate':
$qb->addSelect('cs_person.cafInscriptionDate AS cafinscriptiondate');
break;
case 'contratiejdate':
$qb->addSelect('cs_person.dateContratIEJ AS contratiejdate');
break;
case 'cerinscriptiondate':
$qb->addSelect('cs_person.cERInscriptionDate AS cerinscriptiondate');
break;
case 'ppaeinscriptiondate':
$qb->addSelect('cs_person.pPAEInscriptionDate AS ppaeinscriptiondate');
break;
case 'ppaesignataire':
$qb->addSelect('cs_person.pPAESignataire AS ppaesignataire');
break;
case 'cersignataire':
$qb->addSelect('cs_person.cERSignataire AS cersignataire');
break;
case 'neetcommissiondate':
$qb->addSelect('cs_person.nEETCommissionDate AS neetcommissiondate');
break;
case 'fsemademarchecode':
$qb->addSelect('cs_person.fSEMaDemarcheCode AS fsemademarchecode');
break;
default:
$qb->addSelect(sprintf('person.%s as %s', $f, $f));
}
}
}
public function getLabels(string $key, array $values, array $data): ?callable
{
// dump('im here');
switch ($key) {
case 'cerinscriptiondate':
case 'ppaeinscriptiondate':
case 'neetcommissiondate':
case 'findernieremploidate':
case 'cafinscriptiondate':
case 'contratiejdate':
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 occur.
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;
};
default:
/* if (!\in_array($key, $this->getAllKeys(), 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;
};
}
}
}

View File

@ -1,628 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Export;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\PersonBundle\Export\Export\ListPerson;
use Chill\JobBundle\Entity\CSPerson;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Class ListCSPerson.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class ListCSPerson extends ListPerson implements ListInterface, ExportElementValidatedInterface
{
public const CSPERSON = [
'ressource__label' => CSPerson::RESSOURCES,
'moyen_transport__label' => CSPerson::MOBILITE_MOYEN_TRANSPORT,
'type_contrat__label' => CSPerson::TYPE_CONTRAT,
'permis_conduire__label' => CSPerson::PERMIS_CONDUIRE,
'accompagnement__label' => CSPerson::ACCOMPAGNEMENTS,
'situation_professionnelle__label' => CSPerson::SITUATION_PROFESSIONNELLE,
'neet_eligibility__label' => CSPerson::NEET_ELIGIBILITY,
];
protected array $personIds = [];
protected EntityManagerInterface $em;
protected TranslatableStringHelper $translatableStringHelper;
protected CustomFieldProvider $customFieldProvider;
public function __construct(
protected TranslatorInterface $translator,
EntityManagerInterface $em,
ListPersonHelper $listPersonHelper,
TranslatableStringHelper $translatableStringHelper,
CustomFieldProvider $customFieldProvider,
ParameterBagInterface $parameterBag
) {
parent::__construct($customFieldProvider, $listPersonHelper, $em, $translatableStringHelper, $parameterBag);
}
/**
* Rebuild fields array, combining parent ListPerson and ListCSPerson,
* adding query type as value.
*
* @return array
*/
protected function allFields()
{
$fields = [
'id' => 'integer',
'firstName' => 'string',
'lastName' => 'string',
'gender' => 'string',
'birthdate' => 'date',
'placeOfBirth' => 'string',
'countryOfBirth' => 'json',
'nationality' => 'json',
'email' => 'string',
'phonenumber' => 'string',
'mobilenumber' => 'string',
'contactInfo' => 'string',
'memo' => 'string',
'address_valid_from' => 'date',
'address_street_address_1' => 'string',
'address_street_address_2' => 'string',
'address_postcode_code' => 'string',
'address_postcode_label' => 'string',
'address_country_name' => 'string',
'address_country_code' => 'string',
'address_isnoaddress' => 'boolean',
];
$CSfields = [
'recentopeningdate' => 'date',
'recentclosingdate' => 'date',
'closingmotive' => 'json',
'situation_familiale' => 'json',
'enfantacharge' => 'integer',
'accompagnement__label' => 'json',
'findernieremploidate' => 'date',
'prescripteur__name' => 'string',
'prescripteur__email' => 'string',
'prescripteur__phone' => 'string',
'poleemploiid' => 'string',
'cafid' => 'string',
'cafinscriptiondate' => 'date',
'contratiejdate' => 'date',
'cerinscriptiondate' => 'date',
'ppaeinscriptiondate' => 'date',
'ppaesignataire' => 'string',
'cersignataire' => 'string',
'neet_eligibility__label' => 'string',
'neetcommissiondate' => 'date',
'fsemademarchecode' => 'string',
'permis_conduire__label' => 'json',
'situation_professionnelle__label' => 'string',
'datefindernieremploi' => 'date',
'type_contrat__label' => 'json',
'ressource__label' => 'json',
'moyen_transport__label' => 'json',
];
return array_merge($fields, $CSfields);
}
/**
* Return array FIELDS keys only.
*
* @return array
*/
private function getFields()
{
return array_keys($this->allFields());
}
/**
* give the list of keys the current export added to the queryBuilder in
* self::initiateQuery.
*
* Example: if your query builder will contains `SELECT count(id) AS count_id ...`,
* this function will return `array('count_id')`.
*
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*
* @return array
*/
public function getQueryKeys($data)
{
$csperson = self::CSPERSON;
$fields = [];
foreach ($data['fields'] as $key) {
switch ($key) {
case 'ressource__label':
case 'moyen_transport__label':
foreach ($csperson[$key] as $item) {
$this->translationCompatKey($item, $key);
$fields[] = $item;
}
break;
case 'prescripteur__name':
case 'prescripteur__email':
case 'prescripteur__phone':
$key = str_replace('__', '.', (string) $key);
// no break
case 'situation_professionnelle__label':
case 'neet_eligibility__label':
case 'accompagnement__label':
case 'permis_conduire__label':
case 'type_contrat__label':
default:
$fields[] = $key;
}
}
return $fields;
}
/**
* Some fields values are arrays that have to be splitted in columns.
* This function split theses fields.
*
* @param array $rows
*
* @return array|\Closure
*/
private function splitArrayToColumns($rows)
{
$csperson = self::CSPERSON;
$results = [];
foreach ($rows as $row) {
$res = [];
foreach ($row as $key => $value) {
switch ($key) {
case 'ressource__label':
case 'moyen_transport__label':
foreach ($csperson[$key] as $item) {
$this->translationCompatKey($item, $key);
if ((null === $value) || (0 === count($value))) {
$res[$item] = '';
} else {
foreach ($value as $v) {
$this->translationCompatKey($v, $key);
if ($item === $v) {
$res[$item] = 'x';
break;
}
$res[$item] = '';
}
}
}
break;
case 'situation_professionnelle__label':
$f = false;
if ('en_activite' === $value) {
$f = true;
}
$res[$key] = $value;
break;
case 'type_contrat__label':
if ('' !== $value) {
$res[$key] = ($f) ? $value : null;
} else {
$res[$key] = null;
}
break;
case 'prescripteur__name':
case 'prescripteur__email':
case 'prescripteur__phone':
$key = str_replace('__', '.', (string) $key);
// no break
case 'neet_eligibility__label':
case 'accompagnement__label':
case 'permis_conduire__label':
default:
$res[$key] = $value;
}
}
$results[] = $res;
}
return $results;
}
/**
* Make item compatible with YAML messages ids
* for fields that are splitted in columns (with field key to replace).
*
* AVANT
* key: projet_prof__volume_horaire__label
* item: temps_plein
* APRES
* item: projet_prof.volume_horaire.temps_plein
*
* @param string $item (&) passed by reference
* @param string $key
*/
private function translationCompatKey(&$item, $key)
{
$key = str_replace('label', $item, $key);
$key = str_replace('__', '.', $key);
$item = $key;
}
public function buildForm(FormBuilderInterface $builder)
{
parent::buildForm($builder); // ajouter un '?' dans la query
$choices = array_combine($this->getFields(), $this->getFields());
$builder->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
'choices' => $choices,
'data' => $choices,
// les checkbox cochés !!
// 'choices_as_values' => true,
'label' => 'Fields to include in export',
'choice_label' => fn ($key) => str_replace('__', '.', (string) $key),
'choice_attr' => function ($val) {
if (str_starts_with($val, 'address_')) {
return ['data-display-target' => 'address_date'];
}
return [];
},
'constraints' => [new Callback(['callback' => function ($selected, ExecutionContextInterface $context) {
if (0 === count($selected)) {
$context->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
}])],
]);
}
/**
* @return \Doctrine\ORM\NativeQuery|\Doctrine\ORM\QueryBuilder
*
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(fn ($el) => $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');
}
return $this->entityManager->createQueryBuilder()
->from('ChillPersonBundle:Person', 'person')
->join('person.center', 'center')
->andWhere('center IN (:authorized_centers)')
->setParameter('authorized_centers', $centers)
;
}
/**
* @param \Doctrine\ORM\NativeQuery|\Doctrine\ORM\QueryBuilder $qb
* @param mixed[] $data
*/
public function getResult($qb, $data)
{
$qb->select('person.id');
$ids = $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
$this->personIds = array_map(fn ($e) => $e['id'], $ids);
$personIdsParameters = '?'.\str_repeat(', ?', count($this->personIds) - 1);
$query = \str_replace('%person_ids%', $personIdsParameters, self::QUERY);
$rsm = new Query\ResultSetMapping();
foreach ($this->allFields() as $name => $type) {
if (null !== $data['fields'][$name]) {
$rsm->addScalarResult(strtolower($name), $name, $type);
}
}
$nq = $this->entityManager->createNativeQuery($query, $rsm);
$idx = 0;
for ($i = 1; $i <= 8; ++$i) {
++$idx;
$nq->setParameter($idx, $data['address_date'], 'date');
}
for ($i = 1; $i <= count($this->personIds); ++$i) {
++$idx;
$nq->setParameter($idx, $this->personIds[$i - 1]);
}
return $this->splitArrayToColumns(
$nq->getResult()
);
}
/**
* @param string $key The column key, as added in the query
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
* @param mixed $data The data from the export's form (as defined in `buildForm`
*/
public function getLabels($key, array $values, $data)
{
$csperson = self::CSPERSON;
return match ($key) {
'countryOfBirth', 'situation_familiale', 'closingmotive', 'nationality' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
return $value['fr'];
},
'accompagnement__label', 'permis_conduire__label', 'type_contrat__label' => function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans(
str_replace('__', '.', $key)
);
}
if ('' === $value) {
return '';
}
$arr = [];
foreach ($value as $v) {
$this->translationCompatKey($v, $key);
$arr[] = $this->translator->trans($v);
}
return implode(', ', $arr);
},
'situation_professionnelle__label', 'neet_eligibility__label' => function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans(
str_replace('__', '.', $key)
);
}
if ('' === $value) {
return '';
}
$this->translationCompatKey($value, $key);
return $this->translator->trans($value);
},
'birthdate', 'address_valid_from', 'recentopeningdate', 'recentclosingdate', 'findernieremploidate', 'cafinscriptiondate', 'contratiejdate', 'cerinscriptiondate', 'ppaeinscriptiondate', 'neetcommissiondate', 'datefindernieremploi' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if ('' === $value) {
return '';
}
return $value->format('d-m-Y');
},
'gender' => function ($value) {
if ('_header' === $value) {
return 'gender';
}
return $this->translator->trans($value);
},
'address_country_name' => function ($value) use ($key) {
if ('_header' === $value) {
return \strtolower($key);
}
if (null === $value) {
return '';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
},
'address_isnoaddress' => parent::getLabels($key, $values, $data),
default => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if ('' === $value) {
return '';
}
return $value;
},
};
}
/**
* Native Query SQL.
*/
public const QUERY = <<<'SQL'
WITH accompagning AS (
SELECT *
FROM (
SELECT
p.id,
ac.openingdate,
rank() OVER (
PARTITION BY p.id
ORDER BY ac.openingdate DESC
) openingrank,
ac.closingdate,
rank() OVER (
PARTITION BY p.id
ORDER BY ac.closingdate DESC
) closingrank,
( CASE
WHEN ac.closingdate IS NULL
THEN NULL
ELSE cm.name
END ) as closingmotive
FROM public.chill_person_person AS p
LEFT OUTER JOIN public.chill_person_accompanying_period AS ac
ON p.id = ac.person_id
LEFT OUTER JOIN public.chill_person_closingmotive AS cm
ON ac.closingmotive_id = cm.id
) AS sq
WHERE sq.openingrank = 1
OR sq.closingrank = 1
)
SELECT
-- identifiant
p.id as id,
-- **
p.firstname as firstName,
-- **
p.lastname as lastName,
-- genre
p.gender as gender,
-- date de naissance
p.birthdate as birthdate,
-- **
p.place_of_birth as placeOfBirth,
-- **
cnb.name as countryOfBirth,
-- nationalité
cnn.name as nationality,
-- Courriel
p.email as email,
-- numéro de téléphone
p.phonenumber as phonenumber,
-- numéro de téléphone portable
p.mobilenumber as mobilenumber,
-- **
p.contactInfo as contactInfo,
-- memo
p.memo as memo,
-- Date de début de validité de l'adresse
get_last_address_validfrom(p.id, ?::date) as address_valid_from,
-- Adresse SDF
get_last_address_isnoaddress(p.id, ?::date) as address_isnoaddress,
-- Adresse ligne 1
get_last_address_streetaddress1(p.id, ?::date) as address_street_address_1,
-- Adresse ligne 2
get_last_address_streetaddress2(p.id, ?::date) as address_street_address_2,
-- Code postal
get_last_address_postcode_code(p.id, ?::date) as address_postcode_code,
-- Commune
get_last_address_postcode_label(p.id, ?::date) as address_postcode_label,
-- Code pays
get_last_address_country_code(p.id, ?::date) as address_country_code,
-- Pays
get_last_address_country_name(p.id, ?::date) as address_country_name,
-- date douverture du dossier la plus récente
ac.openingdate as recentopeningdate,
-- date de fermeture du dossier la plus récente
ac.closingdate as recentclosingdate,
-- motif de cloture
ac.closingmotive as closingmotive,
-- Situation familiale
ms.name as situation_familiale,
-- Enfants à charge
cs.enfantacharge as enfantacharge,
-- Date de fin du dernier emploi
cs.datefindernieremploi as findernieremploidate,
-- Accompagnement
cs.accompagnement as accompagnement__label,
-- Prescripteur
tpp.name as prescripteur__name,
-- Email prescripteur
tpp.email as prescripteur__email,
-- Téléphone prescripteur
tpp.telephone as prescripteur__phone,
-- Identifiant pôle emploi
cs.poleemploiid as poleemploiid,
-- Numéro allocataire CAF
cs.cafid as cafid,
-- Date de linscription CAF
cs.cafinscriptiondate as cafinscriptiondate,
-- Date de lavenant du contrat
cs.datecontratiej as contratiejdate,
-- Date de linscription CER
cs.cerinscriptiondate as cerinscriptiondate,
-- Date de linscription PPAE
cs.ppaeinscriptiondate as ppaeinscriptiondate,
-- Signataire PPAE
cs.ppaesignataire as ppaesignataire,
-- Signataire CER
cs.cersignataire as cersignataire,
-- Éligibilité NEET
cs.neeteligibilite as neet_eligibility__label,
-- Date de commission NEET
cs.neetcommissiondate as neetcommissiondate,
-- Code démarche FSE
cs.fsemademarchecode as fsemademarchecode,
-- Permis de conduire
cs.permisconduire as permis_conduire__label,
-- Situation professionnelle
cs.situationprofessionnelle as situation_professionnelle__label,
-- Type de contrat
cs.typecontrat as type_contrat__label,
-- Salaire(s), ARE, ASS, RSA, AAH, Autre
cs.ressources as ressource__label,
-- Transport en commun, Scooter, Vélo, Voiture, Autre
cs.mobilitemoyentransport as moyen_transport__label
FROM public.chill_person_person AS p
LEFT JOIN chill_job.cs_person AS cs
ON p.id = cs.person_id
LEFT JOIN chill_3party.third_party AS tpp
ON cs.prescripteur_id = tpp.id
LEFT JOIN public.chill_person_marital_status AS ms
ON p.maritalstatus_id = ms.id
LEFT JOIN public.country AS cnb
ON p.countryofbirth_id = cnb.id
LEFT JOIN public.country AS cnn
ON p.nationality_id = cnn.id
LEFT JOIN accompagning AS ac
ON p.id = ac.id
WHERE
p.id IN (%person_ids%)
ORDER BY p.id ASC
SQL;
public function validateForm(mixed $data, ExecutionContextInterface $context)
{
// TODO: Implement validateForm() method.
}
}

View File

@ -3,9 +3,9 @@ services:
autowire: true
autoconfigure: true
Chill\JobBundle\Export\ListCSPerson:
Chill\JobBundle\Export\AddCSPersonToPersonListHelper:
tags:
- { name: chill.export, alias: list_CSPerson }
- { name: chill_person.list_person_customizer }
Chill\JobBundle\Export\ListCV:
tags: