Merge branch 'docgen/improve-context-courses' into 'master'

docgen: some fixes and improvements

See merge request Chill-Projet/chill-bundles!408
This commit is contained in:
Julien Fastré 2022-04-13 07:53:43 +00:00
commit e6169ddffa
8 changed files with 178 additions and 96 deletions

View File

@ -11,6 +11,9 @@ and this project adheres to
## Unreleased
<!-- write down unreleased development here -->
* [docgen] add more persons choices in docgen for course: amongst requestor (if person), resources of course (if person), and PersonResource (if person);
* [docgen] add a new context with a list of activities in course
* [docgen] add a comment in budget lines
## Test releases

View File

@ -15,7 +15,6 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityPresence;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Location;
use Chill\MainBundle\Entity\LocationType;
use Chill\MainBundle\Entity\Scope;
@ -84,7 +83,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
{
$rsm = new ResultSetMappingBuilder($this->em);
$sql = "
$sql = '
SELECT
a.id AS activity_id,
date,
@ -112,13 +111,13 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
LEFT JOIN LATERAL (SELECT jsonb_agg(user_id) userids, activity_id FROM activity_user AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS users ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(thirdparty_id) thirdpartyids, activity_id FROM activity_thirdparty AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS thirdparties ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(person_id) personids, activity_id FROM activity_person AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS persons ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialaction_id) socialactionids, activity_id FROM postgres.public.chill_activity_activity_chill_person_socialaction AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS actions ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialissue_id) socialissueids, activity_id FROM postgres.public.chill_activity_activity_chill_person_socialissue AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS issues ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialaction_id) socialactionids, activity_id FROM chill_activity_activity_chill_person_socialaction AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS actions ON TRUE
LEFT JOIN LATERAL (SELECT jsonb_agg(socialissue_id) socialissueids, activity_id FROM chill_activity_activity_chill_person_socialissue AS au WHERE a.id = au.activity_id GROUP BY activity_id) AS issues ON TRUE
WHERE accompanyingperiod_id = ?
ORDER BY a.date DESC, a.id DESC
LIMIT ?
";
';
$rsm
->addEntityResult(Activity::class, 'a')
@ -133,7 +132,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
->addFieldResult('location', 'phonenumber1', 'phonenumber1')
->addFieldResult('location', 'phonenumber2', 'phonenumber2')
->addFieldResult('location', 'email', 'email')
->addJoinedEntityResult(LocationType::class,'locationType', 'location', 'locationType' )
->addJoinedEntityResult(LocationType::class, 'locationType', 'location', 'locationType')
->addFieldResult('locationType', 'locationtype_id', 'id')
->addFieldResult('locationType', 'locationtype_title', 'title')
->addJoinedEntityResult(ActivityType::class, 'activityType', 'a', 'activityType')
@ -151,8 +150,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
->addScalarResult('socialactionids', 'socialActionIds', Types::JSON)
->addScalarResult('socialissueids', 'socialIssueIds', Types::JSON)
->addScalarResult('durationtimeminute', 'durationTimeMinute', Types::INTEGER)
->addScalarResult('traveltimeminute', 'travelTimeMinute', Types::INTEGER)
;
->addScalarResult('traveltimeminute', 'travelTimeMinute', Types::INTEGER);
$nq = $this->em->createNativeQuery($sql, $rsm);

View File

@ -26,10 +26,6 @@ interface ActivityACLAwareRepositoryInterface
*
* The aim of this method is to get a long list of activities and keep performance.
*
* @param AccompanyingPeriod $period
* @param string $role
* @param int|null $limit
* @param array|null $orderBy
* @return array an array of array, each item representing an activity
*/
public function findByAccompanyingPeriodSimplified(AccompanyingPeriod $period, ?int $limit = 1000): array;

View File

@ -1,5 +1,14 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Chill\ActivityBundle\Service\DocGenerator;
use Chill\ActivityBundle\Entity\ActivityPresence;
@ -23,6 +32,7 @@ use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Service\DocGenerator\AccompanyingPeriodContext;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use DateTime;
use libphonenumber\PhoneNumber;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
@ -38,18 +48,18 @@ class ListActivitiesByAccompanyingPeriodContext implements
private NormalizerInterface $normalizer;
private TranslatableStringHelperInterface $translatableStringHelper;
private PersonRepository $personRepository;
private SocialActionRepository $socialActionRepository;
private SocialIssueRepository $socialIssueRepository;
private UserRepository $userRepository;
private ThirdPartyRepository $thirdPartyRepository;
private TranslatableStringHelperInterface $translatableStringHelper;
private UserRepository $userRepository;
public function __construct(
AccompanyingPeriodContext $accompanyingPeriodContext,
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
@ -72,6 +82,26 @@ class ListActivitiesByAccompanyingPeriodContext implements
$this->userRepository = $userRepository;
}
public function adminFormReverseTransform(array $data): array
{
return $this->accompanyingPeriodContext->adminFormReverseTransform($data);
}
public function adminFormTransform(array $data): array
{
return $this->accompanyingPeriodContext->adminFormTransform($data);
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$this->accompanyingPeriodContext->buildAdminForm($builder);
}
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$this->accompanyingPeriodContext->buildPublicForm($builder, $template, $entity);
}
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
$data = $this->accompanyingPeriodContext->getData($template, $entity, $contextGenerationData);
@ -81,6 +111,46 @@ class ListActivitiesByAccompanyingPeriodContext implements
return $data;
}
public function getDescription(): string
{
return 'docgen.Accompanying period with a list of activities description';
}
public function getEntityClass(): string
{
return AccompanyingPeriod::class;
}
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return $this->accompanyingPeriodContext->getFormData($template, $entity);
}
public static function getKey(): string
{
return self::class;
}
public function getName(): string
{
return 'docgen.Accompanying period with a list of activities';
}
public function hasAdminForm(): bool
{
return $this->accompanyingPeriodContext->hasAdminForm();
}
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
}
private function getActivitiesSimplified(AccompanyingPeriod $period)
{
$activities =
@ -91,20 +161,21 @@ class ListActivitiesByAccompanyingPeriodContext implements
$activity = $row[0];
$activity['date'] = $this->normalizer->normalize($activity['date'], 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => \DateTime::class
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => DateTime::class,
]);
if (null === $activity['location']) {
$activity['location'] = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Location::class
$activity['location'] = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => Location::class,
]);
$activity['location']['type'] = 'location';
} else {
$activity['location']['isNull'] = false;
$activity['location']['type'] = 'location';
foreach (['1', '2'] as $key) {
$activity['location']['phonenumber'.$key] = $this->normalizer->normalize(
$activity['location']['phonenumber'.$key],
$activity['location']['phonenumber' . $key] = $this->normalizer->normalize(
$activity['location']['phonenumber' . $key],
'docgen',
[AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => PhoneNumber::class]
);
@ -127,7 +198,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
$activity['activityType']['type'] = 'activityType';
} else {
$activity['activityType'] = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityType::class
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityType::class,
]);
}
@ -139,7 +210,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
$activity['attendee']['type'] = 'activityPresence';
} else {
$activity['attendee'] = $this->normalizer->normalize(null, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityPresence::class
AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => ActivityPresence::class,
]);
}
@ -212,65 +283,4 @@ class ListActivitiesByAccompanyingPeriodContext implements
return $results;
}
public function getDescription(): string
{
return 'docgen.Accompanying period with a list of activities description';
}
public function getEntityClass(): string
{
return AccompanyingPeriod::class;
}
public static function getKey(): string
{
return self::class;
}
public function getName(): string
{
return 'docgen.Accompanying period with a list of activities';
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$this->accompanyingPeriodContext->storeGenerated($template, $storedObject, $entity, $contextGenerationData);
}
public function adminFormReverseTransform(array $data): array
{
return $this->accompanyingPeriodContext->adminFormReverseTransform($data);
}
public function adminFormTransform(array $data): array
{
return $this->accompanyingPeriodContext->adminFormTransform($data);
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$this->accompanyingPeriodContext->buildAdminForm($builder);
}
public function hasAdminForm(): bool
{
return $this->accompanyingPeriodContext->hasAdminForm();
}
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$this->accompanyingPeriodContext->buildPublicForm($builder, $template, $entity);
}
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return $this->accompanyingPeriodContext->getFormData($template, $entity);
}
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
}
}

View File

@ -25,13 +25,13 @@ use function count;
*/
class SummaryBudget implements SummaryBudgetInterface
{
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_CHARGE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_CHARGE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.charge WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_RESOURCE_BY_HOUSEHOLD = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE (person_id IN (_ids_) OR household_id = ?) AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private const QUERY_RESOURCE_BY_PERSON = 'select SUM(amount) AS sum, string_agg(comment, \'|\') AS comment, type FROM chill_budget.resource WHERE person_id = ? AND NOW() BETWEEN startdate AND COALESCE(enddate, \'infinity\'::timestamp) GROUP BY type';
private array $chargeLabels;
@ -110,7 +110,8 @@ class SummaryBudget implements SummaryBudgetInterface
$rsm = new ResultSetMapping();
$rsm
->addScalarResult('sum', 'sum')
->addScalarResult('type', 'type');
->addScalarResult('type', 'type')
->addScalarResult('comment', 'comment');
return $rsm;
}
@ -121,7 +122,7 @@ class SummaryBudget implements SummaryBudgetInterface
$labels = $this->chargeLabels;
return array_combine($keys, array_map(function ($i) use ($labels) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
}, $keys));
}
@ -131,7 +132,7 @@ class SummaryBudget implements SummaryBudgetInterface
$labels = $this->resourcesLabels;
return array_combine($keys, array_map(function ($i) use ($labels) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i])];
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($labels[$i]), 'comment' => ''];
}, $keys));
}
@ -158,6 +159,7 @@ class SummaryBudget implements SummaryBudgetInterface
$result[$row['type']] = [
'sum' => (float) $row['sum'],
'label' => $this->translatableStringHelper->localize($label[$row['type']]),
'comment' => (string) $row['comment'],
];
}

View File

@ -0,0 +1,54 @@
<?php
/**
* 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.
*/
declare(strict_types=1);
namespace Serializer\Normalizer;
use Chill\MainBundle\Serializer\Normalizer\PhonenumberNormalizer;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberUtil;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
/**
* @internal
* @coversNothing
*/
final class PhonenumberNormalizerTest extends TestCase
{
use ProphecyTrait;
public function dataProviderNormalizePhonenumber()
{
$phonenumberUtil = PhoneNumberUtil::getInstance();
yield [$phonenumberUtil->parse('+32486123465'), 'docgen', ['docgen:expects' => PhoneNumber::class], '0486 12 34 65'];
yield [null, 'docgen', ['docgen:expects' => PhoneNumber::class], ''];
}
/**
* @dataProvider dataProviderNormalizePhonenumber
*
* @param mixed $format
* @param mixed $context
* @param mixed $expected
*/
public function testNormalize(?Phonenumber $phonenumber, $format, $context, $expected)
{
$parameterBag = $this->prophesize(ParameterBagInterface::class);
$parameterBag->get(Argument::exact('chill_main'))->willReturn(['phone_helper' => ['default_carrier_code' => 'BE']]);
$normalizer = new PhonenumberNormalizer($parameterBag->reveal());
$this->assertEquals($expected, $normalizer->normalize($phonenumber, $format, $context));
}
}

View File

@ -23,6 +23,7 @@ use Chill\PersonBundle\Repository\Relationships\RelationshipRepository;
use Chill\PersonBundle\Templating\Entity\PersonRenderInterface;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use libphonenumber\PhoneNumber;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
@ -71,6 +72,7 @@ class PersonDocGenNormalizer implements
$dateContext = $context;
$dateContext['docgen:expects'] = DateTimeInterface::class;
$addressContext = array_merge($context, ['docgen:expects' => Address::class]);
$phonenumberContext = array_merge($context, ['docgen:expects' => PhoneNumber::class]);
$personResourceContext = array_merge($context, [
'docgen:expects' => Person\PersonResource::class,
// we simplify the list of attributes for the embedded persons
@ -113,9 +115,9 @@ class PersonDocGenNormalizer implements
'maritalStatus' => null !== ($ms = $person->getMaritalStatus()) ? $this->translatableStringHelper->localize($ms->getName()) : '',
'maritalStatusDate' => $this->normalizer->normalize($person->getMaritalStatusDate(), $format, $dateContext),
'email' => $person->getEmail(),
'firstPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber() ?? $person->getMobilenumber(), $format, $context),
'fixPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $context),
'mobilePhoneNumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $context),
'firstPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber() ?? $person->getMobilenumber(), $format, $phonenumberContext),
'fixPhoneNumber' => $this->normalizer->normalize($person->getPhonenumber(), $format, $phonenumberContext),
'mobilePhoneNumber' => $this->normalizer->normalize($person->getMobilenumber(), $format, $phonenumberContext),
'nationality' => null !== ($c = $person->getNationality()) ? $this->translatableStringHelper->localize($c->getName()) : '',
'placeOfBirth' => $person->getPlaceOfBirth(),
'memo' => $person->getMemo(),

View File

@ -152,8 +152,25 @@ class AccompanyingPeriodContext implements
$options = $template->getOptions();
$persons = $entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) {
return $p->getPerson();
})
->toArray();
});
foreach ($entity->getCurrentParticipations() as $p) {
foreach ($p->getPerson()->getResources() as $r) {
if (null !== $r->getPerson() && !$persons->contains($r->getPerson())) {
$persons->add($r->getPerson());
}
}
}
if (null !== $entity->getRequestorPerson() && !$persons->contains($entity->getRequestorPerson())) {
$persons->add($entity->getRequestorPerson());
}
foreach ($entity->getResources() as $r) {
if (null !== $r->getPerson() && !$persons->contains($r->getPerson())) {
$persons->add($r->getPerson());
}
}
foreach (['mainPerson', 'person1', 'person2'] as $key) {
if ($options[$key] ?? false) {