Merge branch 'master' into issue279_accompanying_period_validation

This commit is contained in:
2021-12-07 17:55:00 +01:00
92 changed files with 3270 additions and 1063 deletions

View File

@@ -0,0 +1,78 @@
<?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\PersonBundle\Controller;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use function count;
use function in_array;
class AccompanyingPeriodWorkEvaluationApiController
{
private DocGeneratorTemplateRepository $docGeneratorTemplateRepository;
private PaginatorFactory $paginatorFactory;
private SerializerInterface $serializer;
public function __construct(
DocGeneratorTemplateRepository $docGeneratorTemplateRepository,
SerializerInterface $serializer,
PaginatorFactory $paginatorFactory
) {
$this->docGeneratorTemplateRepository = $docGeneratorTemplateRepository;
$this->serializer = $serializer;
$this->paginatorFactory = $paginatorFactory;
}
/**
* @Route("/api/1.0/person/docgen/template/by-evaluation/{id}.{_format}",
* requirements={"format": "json"})
*/
public function listTemplateByEvaluation(Evaluation $evaluation, string $_format): JsonResponse
{
if ('json' !== $_format) {
throw new BadRequestHttpException('format not supported');
}
$evaluations =
array_filter(
$this->docGeneratorTemplateRepository
->findByEntity(AccompanyingPeriodWorkEvaluation::class),
static function (DocGeneratorTemplate $t) use ($evaluation) {
$ids = $t->getOptions()['evaluations'] ?? [];
return in_array($evaluation->getId(), $ids, true);
}
);
$paginator = $this->paginatorFactory->create(count($evaluations));
$paginator->setItemsPerPage(count($evaluations));
return new JsonResponse($this->serializer->serialize(
new Collection($evaluations, $paginator),
'json',
[
AbstractNormalizer::GROUPS => ['read'],
]
), JsonResponse::HTTP_OK, [], true);
}
}

View File

@@ -120,11 +120,11 @@ class AccompanyingPeriod implements
* @var DateTime
*
* @ORM\Column(type="date", nullable=true)
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CLOSED})
* @Assert\GreaterThan(propertyPath="openingDate", groups={AccompanyingPeriod::STEP_CLOSED})
*/
private $closingDate;
private ?DateTime $closingDate = null;
/**
* @var AccompanyingPeriod\ClosingMotive
@@ -135,11 +135,9 @@ class AccompanyingPeriod implements
* @Groups({"read", "write"})
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CLOSED})
*/
private $closingMotive;
private ?ClosingMotive $closingMotive = null;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Comment",
* mappedBy="accompanyingPeriod",
* cascade={"persist", "remove"},
@@ -147,33 +145,32 @@ class AccompanyingPeriod implements
* )
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_DRAFT})
*/
private $comments;
private Collection $comments;
/**
* @var bool
* @ORM\Column(type="boolean", options={"default": false})
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
*/
private $confidential = false;
private bool $confidential = false;
/**
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
* @Groups({"docgen:read"})
*/
private DateTimeInterface $createdAt;
private ?DateTimeInterface $createdAt = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $createdBy;
private ?User $createdBy = null;
/**
* @var bool
* @ORM\Column(type="boolean", options={"default": false})
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
*/
private $emergency = false;
private bool $emergency = false;
/**
* @var int
@@ -181,9 +178,9 @@ class AccompanyingPeriod implements
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(
@@ -205,9 +202,9 @@ class AccompanyingPeriod implements
* @var DateTime
*
* @ORM\Column(type="date")
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
*/
private $openingDate;
private ?DateTime $openingDate = null;
/**
* @ORM\ManyToOne(targetEntity=Origin::class)
@@ -215,18 +212,16 @@ class AccompanyingPeriod implements
* @Groups({"read", "write"})
* @Assert\NotBlank(groups={AccompanyingPeriod::STEP_CONFIRMED})
*/
private $origin;
private ?Origin $origin = null;
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
* mappedBy="accompanyingPeriod", orphanRemoval=true,
* cascade={"persist", "refresh", "remove", "merge", "detach"})
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
* @ParticipationOverlap(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED})
*/
private $participations;
private Collection $participations;
/**
* @ORM\ManyToOne(
@@ -237,48 +232,42 @@ class AccompanyingPeriod implements
private ?Person $personLocation = null;
/**
* @var string
*
* @ORM\Column(type="text")
* @Groups({"read", "write"})
*/
private $remark = '';
private string $remark = '';
/**
* @var bool
* @ORM\Column(type="boolean", options={"default": false})
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
*/
private $requestorAnonymous = false;
private bool $requestorAnonymous = false;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodRequested")
* @ORM\JoinColumn(nullable=true)
*/
private $requestorPerson;
private ?Person $requestorPerson = null;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @ORM\JoinColumn(nullable=true)
*/
private $requestorThirdParty;
private ?ThirdParty $requestorThirdParty = null;
/**
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Resource",
* mappedBy="accompanyingPeriod",
* cascade={"persist", "remove"},
* orphanRemoval=true
* )
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
* @ResourceDuplicateCheck(groups={AccompanyingPeriod::STEP_DRAFT, AccompanyingPeriod::STEP_CONFIRMED, "Default", "default"})
*/
private $resources;
private Collection $resources;
/**
* @var Collection
* @ORM\ManyToMany(
* targetEntity=Scope::class,
* cascade={}
@@ -288,10 +277,10 @@ class AccompanyingPeriod implements
* joinColumns={@ORM\JoinColumn(name="accompanying_period_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")}
* )
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
* @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}, minMessage="A course must be associated to at least one scope")
*/
private $scopes;
private Collection $scopes;
/**
* @ORM\ManyToMany(
@@ -300,36 +289,35 @@ class AccompanyingPeriod implements
* @ORM\JoinTable(
* name="chill_person_accompanying_period_social_issues"
* )
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
* @Assert\Count(min=1, groups={AccompanyingPeriod::STEP_CONFIRMED}, minMessage="A course must contains at least one social issue")
*/
private Collection $socialIssues;
/**
* @var string
* @ORM\Column(type="string", length=32, nullable=true)
* @Groups({"read"})
*/
private $step = self::STEP_DRAFT;
private string $step = self::STEP_DRAFT;
/**
* @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
*/
private DateTimeInterface $updatedAt;
private ?DateTimeInterface $updatedAt = null;
/**
* @ORM\ManyToOne(
* targetEntity=User::class
* )
*/
private User $updatedBy;
private ?User $updatedBy = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
* @Groups({"read", "write"})
* @Groups({"read", "write", "docgen:read"})
*/
private $user;
private ?User $user = null;
/**
* @ORM\OneToMany(
@@ -355,6 +343,7 @@ class AccompanyingPeriod implements
$this->socialIssues = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->works = new ArrayCollection();
$this->resources = new ArrayCollection();
}
/**
@@ -580,11 +569,19 @@ class AccompanyingPeriod implements
});
}
public function getCreatedAt(): ?DateTime
{
return $this->createdAt;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
/**
* @Groups({"docgen:read"})
*/
public function getCurrentParticipations(): Collection
{
return $this->getOpenParticipations();
@@ -608,7 +605,7 @@ class AccompanyingPeriod implements
*
* @return int
*/
public function getId()
public function getId(): ?int
{
return $this->id;
}
@@ -634,7 +631,7 @@ class AccompanyingPeriod implements
public function getLocation(?DateTimeImmutable $at = null): ?Address
{
if ($this->getPersonLocation() instanceof Person) {
return $this->getPersonLocation()->getCurrentHouseholdAddress($at);
return $this->getPersonLocation()->getCurrentPersonAddress();
}
return $this->getAddressLocation();
@@ -663,7 +660,7 @@ class AccompanyingPeriod implements
*
* @return DateTime
*/
public function getOpeningDate()
public function getOpeningDate(): ?DateTime
{
return $this->openingDate;
}

View File

@@ -53,7 +53,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* cascade={"remove", "persist"},
* orphanRemoval=true
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*
* @internal /!\ the serialization for write evaluations is handled in `AccompanyingPeriodWorkDenormalizer`
*/
@@ -61,24 +61,26 @@ use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Column(type="datetime_immutable")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?DateTimeImmutable $createdAt = null;
/**
* @ORM\Column(type="boolean")
* @Serializer\Groups({"read", "docgen:read"})
*/
private bool $createdAutomatically = false;
/**
* @ORM\Column(type="text")
* @Serializer\Groups({"read", "docgen:read"})
*/
private string $createdAutomaticallyReason = '';
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?User $createdBy = null;
@@ -86,7 +88,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"accompanying_period_work:create"})
* @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\GreaterThan(propertyPath="startDate",
* message="accompanying_course_work.The endDate should be greater than the start date"
* )
@@ -100,14 +102,14 @@ use Symfony\Component\Validator\Constraints as Assert;
* cascade={"persist"},
* orphanRemoval=true
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:edit"})
*/
private Collection $goals;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:edit"})
*
* In schema : traitant
@@ -118,20 +120,20 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="text")
* @Serializer\Groups({"read", "accompanying_period_work:edit"})
* @Serializer\Groups({"read", "accompanying_period_work:edit", "docgen:read"})
*/
private string $note = '';
/**
* @ORM\ManyToMany(targetEntity=Person::class)
* @ORM\JoinTable(name="chill_person_accompanying_period_work_person")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"accompanying_period_work:create"})
*/
@@ -140,14 +142,14 @@ use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks")
* @ORM\JoinTable(name="chill_person_accompanying_period_work_result")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:edit"})
*/
private Collection $results;
/**
* @ORM\ManyToOne(targetEntity=SocialAction::class)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:create"})
*/
private ?SocialAction $socialAction = null;
@@ -156,30 +158,30 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\Column(type="date_immutable")
* @Serializer\Groups({"accompanying_period_work:create"})
* @Serializer\Groups({"accompanying_period_work:edit"})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private DateTimeImmutable $startDate;
private ?DateTimeImmutable $startDate = null;
/**
* @ORM\ManyToMany(targetEntity=ThirdParty::class)
* @ORM\JoinTable(name="chill_person_accompanying_period_work_third_party")
*
* In schema : intervenants
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work:edit"})
*/
private Collection $thirdParties;
/**
* @ORM\Column(type="datetime_immutable")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?DateTimeImmutable $updatedAt = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?User $updatedBy = null;

View File

@@ -44,7 +44,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="text", nullable=false, options={"default": ""})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
@@ -52,7 +52,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?DateTimeImmutable $createdAt = null;
@@ -60,7 +60,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* @ORM\ManyToOne(
* targetEntity=User::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?User $createdBy = null;
@@ -76,7 +76,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
@@ -86,7 +86,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* @ORM\ManyToOne(
* targetEntity=Evaluation::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?Evaluation $evaluation = null;
@@ -95,7 +95,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?int $id = null;
@@ -116,14 +116,14 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
private ?DateTimeImmutable $maxDate = null;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\Groups({"write"})
* @Serializer\Groups({"accompanying_period_work_evaluation:create"})
*/
@@ -131,7 +131,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?DateTimeImmutable $updatedAt = null;
@@ -139,7 +139,7 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
* @ORM\ManyToOne(
* targetEntity=User::class
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?User $updatedBy = null;
@@ -239,6 +239,18 @@ class AccompanyingPeriodWorkEvaluation implements TrackCreationInterface, TrackU
return $this->updatedBy;
}
/**
* @Serializer\Groups({"docgen:read"})
*/
public function getWarningDate(): ?DateTimeImmutable
{
if (null === $this->getEndDate() || null === $this->getWarningInterval()) {
return null;
}
return $this->getEndDate()->sub($this->getWarningInterval());
}
public function getWarningInterval(): ?DateInterval
{
return $this->warningInterval;

View File

@@ -33,7 +33,7 @@ class Origin
* @ORM\Column(type="integer")
* @Groups({"read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")
@@ -45,7 +45,7 @@ class Origin
* @ORM\Column(type="date_immutable", nullable=true)
* @Groups({"read"})
*/
private $noActiveAfter;
private ?DateTimeImmutable $noActiveAfter = null;
public function getId(): ?int
{
@@ -62,7 +62,7 @@ class Origin
return $this->noActiveAfter;
}
public function setLabel(string $label): self
public function setLabel(array $label): self
{
$this->label = $label;

View File

@@ -41,11 +41,10 @@ class Resource
* )
* @ORM\JoinColumn(nullable=false)
*/
private $accompanyingPeriod;
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\ManyToOne(targetEntity=Comment::class)
* @ORM\JoinColumn(nullable=true)
*/
private $comment;
@@ -53,21 +52,23 @@ class Resource
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class)
* @ORM\JoinColumn(nullable=true)
* @Groups({"docgen:read"})
*/
private $person;
private ?Person $person = null;
/**
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
* @ORM\JoinColumn(nullable=true)
* @Groups({"docgen:read"})
*/
private $thirdParty;
private ?ThirdParty $thirdParty = null;
public function getAccompanyingPeriod(): ?AccompanyingPeriod
{

View File

@@ -11,7 +11,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Entity;
use DateTimeImmutable;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
@@ -32,38 +32,38 @@ class AccompanyingPeriodParticipation
* @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations", cascade={"persist"})
* @ORM\JoinColumn(name="accompanyingperiod_id", referencedColumnName="id", nullable=false)
*/
private $accompanyingPeriod;
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\Column(type="date", nullable=true)
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $endDate;
private ?DateTime $endDate = null;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodParticipations")
* @ORM\JoinColumn(name="person_id", referencedColumnName="id", nullable=false)
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $person;
private ?Person $person = null;
/**
* @ORM\Column(type="date", nullable=false)
* @Groups({"read"})
* @Groups({"read", "docgen:read"})
*/
private $startDate;
private ?DateTime $startDate = null;
public function __construct(AccompanyingPeriod $accompanyingPeriod, Person $person)
{
$this->startDate = new DateTimeImmutable('now');
$this->startDate = new DateTime('now');
$this->accompanyingPeriod = $accompanyingPeriod;
$this->person = $person;
}
@@ -73,10 +73,6 @@ class AccompanyingPeriodParticipation
return $this->accompanyingPeriod;
}
/*
* public function setStartDate(\DateTimeInterface $startDate): self { $this->startDate = $startDate; return $this; }
*/
public function getEndDate(): ?DateTimeInterface
{
return $this->endDate;

View File

@@ -59,7 +59,7 @@ class Household
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?int $id = null;
@@ -68,17 +68,19 @@ class Household
* targetEntity=HouseholdMember::class,
* mappedBy="household"
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private Collection $members;
/**
* @ORM\Column(type="boolean", name="waiting_for_birth", options={"default": false})
* @Serializer\Groups({"docgen:read"})
*/
private bool $waitingForBirth = false;
/**
* @ORM\Column(type="date_immutable", name="waiting_for_birth_date", nullable=true, options={"default": null})
* @Serializer\Groups({"docgen:read"})
*/
private ?DateTimeImmutable $waitingForBirthDate = null;
@@ -134,7 +136,7 @@ class Household
}
/**
* @Serializer\Groups({ "read" })
* @Serializer\Groups({"read", "docgen:read"})
* @Serializer\SerializedName("current_address")
*/
public function getCurrentAddress(?DateTime $at = null): ?Address
@@ -154,6 +156,9 @@ class Household
return null;
}
/**
* @Serializer\Groups({"docgen:read"})
*/
public function getCurrentMembers(?DateTimeImmutable $now = null): Collection
{
return $this->getMembers()->matching($this->buildCriteriaCurrentMembers($now));

View File

@@ -28,13 +28,13 @@ class HouseholdMember
{
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?string $comment = null;
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\GreaterThan(
* propertyPath="startDate",
* message="household_membership.The end date must be after start date",
@@ -45,7 +45,7 @@ class HouseholdMember
/**
* @ORM\Column(type="boolean", options={"default": false})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private bool $holder = false;
@@ -63,7 +63,7 @@ class HouseholdMember
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $id;
@@ -72,7 +72,7 @@ class HouseholdMember
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Person"
* )
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\Valid(groups={"household_memberships"})
* @Assert\NotNull(groups={"household_memberships"})
*/
@@ -80,7 +80,7 @@ class HouseholdMember
/**
* @ORM\ManyToOne(targetEntity=Position::class)
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\NotNull(groups={"household_memberships_created"})
*/
private ?Position $position = null;
@@ -92,7 +92,7 @@ class HouseholdMember
/**
* @ORM\Column(type="date_immutable", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
* @Assert\NotNull(groups={"household_memberships"})
*/
private ?DateTimeImmutable $startDate = null;

View File

@@ -33,25 +33,25 @@ class Position
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({ "read" })
* @Serializer\Groups({"read", "docgen:read"})
*/
private ?int $id;
/**
* @ORM\Column(type="json")
* @Serializer\Groups({ "read" })
* @Serializer\Groups({"read", "docgen:read"})
*/
private array $label = [];
/**
* @ORM\Column(type="float")
* @Serializer\Groups({ "read" })
* @Serializer\Groups({"read"})
*/
private float $ordering = 0.00;
/**
* @ORM\Column(type="boolean")
* @Serializer\Groups({ "read" })
* @Serializer\Groups({"read"})
*/
private bool $shareHouseHold = true;

View File

@@ -28,21 +28,21 @@ class Evaluation
* @ORM\Column(type="dateinterval", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
*/
private $delay;
private ?DateInterval $delay = null;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $id;
private ?int $id = null;
/**
* @ORM\Column(type="dateinterval", nullable=true, options={"default": null})
* @Serializer\Groups({"read"})
*/
private $notificationDelay;
private ?DateInterval $notificationDelay = null;
/**
* @ORM\ManyToOne(
@@ -50,13 +50,13 @@ class Evaluation
* inversedBy="evaluations"
* )
*/
private $socialAction;
private ?SocialAction $socialAction = null;
/**
* @ORM\Column(type="json")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $title = [];
private array $title = [];
public function getDelay(): ?DateInterval
{

View File

@@ -38,7 +38,7 @@ class Goal
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $id;
@@ -55,7 +55,7 @@ class Goal
/**
* @ORM\Column(type="json")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $title = [];

View File

@@ -55,7 +55,7 @@ class Result
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $id;
@@ -66,7 +66,7 @@ class Result
/**
* @ORM\Column(type="json")
* @Serializer\Groups({"read"})
* @Serializer\Groups({"read", "docgen:read"})
*/
private $title = [];

View File

@@ -114,10 +114,10 @@ class PersonType extends AbstractType
$builder->get('placeOfBirth')->addModelTransformer(new CallbackTransformer(
static function ($string) {
return strtoupper($string);
return strtoupper((string) $string);
},
static function ($string) {
return strtoupper($string);
return strtoupper((string) $string);
}
));
}

View File

@@ -1,51 +1,6 @@
/// AccompanyingCourse Work list Page
div.accompanying_course_work-list {
div.timeline {
width: 100%;
ul {
display: flex;
align-items: center;
justify-content: center;
padding: 0;
list-style-type: none;
> li {
flex-grow: 1; flex-shrink: 1; flex-basis: auto;
div {
display: flex;
flex-direction: column;
align-items: center;
&.date {
margin-bottom: 1em;
}
&.label {
border-top: 3px solid $chill-green;
&:before {
content: '';
display: inline-block;
position: relative;
width: 15px;
height: 15px;
top: -9px;
background-color: $white;
border-radius: 12px;
border: 2px solid $chill-green;
}
&.no-label:before {
display: none;
}
}
}
}
}
}
div.objective_results {
width: 100%;
display: grid;
@@ -69,8 +24,10 @@ div.accompanying_course_work-list {
//&:nth-child(even) { background-color: $chill-llight-gray; }
&.without-objectives {}
&.with-objectives {}
}
div.objective_results,
div.evaluations {
h4.title_label {
display: block;
margin: 0.4em 0;

View File

@@ -39,69 +39,15 @@ span.fa-holder {
}
/*
* BADGE_TITLE
* Display Title like a badge (with background-colored label)
* DASHBOARDS
*/
h2.badge-title {
display: flex;
flex-direction: row;
width: 100%;
color: $dark;
span.title_label {
border-radius: 0.35rem 0 0 0.35rem;
color: $white;
font-size: 80%;
padding: 0.5em;
padding-right: 0;
h3 {
margin-bottom: 0.5rem;
}
//position: relative;
span {
display: none;
//position: absolute;
//top: 0;
//left: 0;
//transform: rotate(270deg);
//transform-origin: 0 0;
}
}
span.title_action {
flex-grow: 1;
margin: 0 0 0 auto;
border-radius: 0 0.35rem 0.35rem 0;
background-color: $chill-llight-gray;
padding: 0.2em 1em;
ul.small_in_title {
margin: 0;
//margin-top: 0.5em;
font-size: 70%;
padding-left: 1rem;
&.evaluations {
@include list_marker_triangle($orange);
}
}
ul.columns { // XS:1 SM:2 MD:1 LG:2 XL:2 XXL:2
@include media-breakpoint-only(sm) {
columns: 2; -webkit-columns: 2; -moz-columns: 2;
}
@include media-breakpoint-up(lg) {
columns: 2; -webkit-columns: 2; -moz-columns: 2;
}
}
}
}
/// Theses links apply on badge as parent tag.
/// Theses links apply on dashboards as parent tag.
/// They don't look like button, picto or simple text links
a.badge-link {
a.dashboard-link {
color: unset;
text-decoration: unset;
& > h2.badge-title {
& > div.dashboard {
&:hover {
//box-shadow: 0 0 7px 0 $chill-gray;
//opacity: 0.8;
@@ -114,21 +60,80 @@ a.badge-link {
}
}
/// badge_title in AccompanyingCourse Work list Page
div.dashboard {
font-weight: 700;
font-size: 1.5rem;
margin-bottom: 0.5rem;
line-height: 1.2;
span.like-h3 {
color: #334d5c;
}
}
div.dashboard,
h2.badge-title {
display: flex;
flex-direction: row;
width: 100%;
color: $dark;
span.title_label {
color: $white;
font-size: 80%;
padding: 0.5em;
padding-right: 0;
border-radius: 0.35rem 0 0 0.35rem;
h3 {
margin-bottom: 0.5rem;
}
}
span.title_action {
flex-grow: 1;
margin: 0 0 0 auto;
background-color: $chill-llight-gray;
padding: 0.2em 1em;
border-radius: 0 0.35rem 0.35rem 0;
ul.small_in_title {
font-size: 70%;
}
}
}
ul.small_in_title {
margin: 0;
//margin-top: 0.5em;
padding-left: 1rem;
&.evaluations {
@include list_marker_triangle($orange);
}
}
ul.columns { // XS:1 SM:2 MD:1 LG:2 XL:2 XXL:2
@include media-breakpoint-only(sm) {
columns: 2; -webkit-columns: 2; -moz-columns: 2;
}
@include media-breakpoint-up(lg) {
columns: 2; -webkit-columns: 2; -moz-columns: 2;
}
}
/// dashboard_like_badge in AccompanyingCourse Work list Page
div.accompanying_course_work-list {
div.dashboard,
h2.badge-title {
span.title_label {
// Calculate same color then border:groove
background-color: shade-color($social-action-color, 34%);
}
span.title_action {
@include badge_title($social-action-color);
@include dashboard_like_badge($social-action-color);
}
}
}
/// badge_title in Activities on resume page
/// dashboard_like_badge in Activities on resume page
div.activity-list {
div.dashboard,
h2.badge-title {
span.title_label {
// Calculate same color then border:groove
@@ -138,7 +143,7 @@ div.activity-list {
}
}
span.title_action {
@include badge_title($activity-color);
@include dashboard_like_badge($activity-color);
}
span.title_label {
div.duration {

View File

@@ -18,12 +18,6 @@ div.accompanyingcourse-list {
//&:nth-child(2) { flex-direction: row; }
//&:last-child { flex-direction: column; }
}
div.title h3 {
font-weight: 700;
font-size: 100%;
font-family: 'Open Sans';
}
div.list {}
}
/// Search Page (list_with_period.html.twig)

View File

@@ -27,11 +27,10 @@
}
///
/// Generic mixin for titles like badge
// define visual badge used in title area
/// Mixin for dashboards (with design like badge_social)
///
@mixin badge_title($color) {
@mixin dashboard_like_badge($color) {
@include chill_badge($color);
&:before {
margin: 0 0.3em 0 -1.05em;

View File

@@ -195,6 +195,18 @@
</ul>
</div>
<div>
<pick-template
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork"
:templates="this.templatesAvailablesForAction"
:entityId="work.id"
:beforeMove="beforeGenerateTemplate">
<template v-slot:title>
<h3>{{ $t('Generate doc') }}</h3>
</template>
</pick-template>
</div>
<div v-if="errors.length > 0" id="errors" class="alert alert-danger flashbag">
<p>{{ $t('fix_these_errors') }}</p>
<ul>
@@ -230,6 +242,7 @@ import AddEvaluation from './components/AddEvaluation.vue';
import PersonRenderBox from 'ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue';
import AddPersons from 'ChillPersonAssets/vuejs/_components/AddPersons.vue';
import AddressRenderBox from 'ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
const i18n = {
messages: {
@@ -276,6 +289,7 @@ export default {
AddPersons,
PersonRenderBox,
AddressRenderBox,
PickTemplate,
},
i18n,
data() {
@@ -318,6 +332,7 @@ export default {
'thirdParties',
'isPosting',
'errors',
'templatesAvailablesForAction',
]),
...mapGetters([
'hasResultsForAction',
@@ -386,7 +401,7 @@ export default {
this.$store.commit('removeGoal', g);
},
addEvaluation(e) {
this.$store.commit('addEvaluation', e);
this.$store.dispatch('addEvaluation', e);
},
toggleAddEvaluation() {
this.showAddEvaluation = !this.showAddEvaluation;
@@ -414,6 +429,10 @@ export default {
submit() {
this.$store.dispatch('submit');
},
beforeGenerateTemplate() {
console.log('before generate');
return Promise.resolve();
}
}
};

View File

@@ -61,21 +61,18 @@
</div>
</div>
<div class="row mb-3">
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
<div class="col-sm-8">
<div class="input-group">
<select class="form-select form-select-sm" v-model="template">
<option disabled value="">{{ $t('evaluation_choose_a_template') }}</option>
<template v-for="t in getTemplatesAvailaibleForEvaluation">
<option v-bind:value="t.id">{{ t.name.fr }}</option>
</template>
</select>
<button v-if="canGenerate" class="btn btn-update btn-sm change-icon" type="button" @click="generateDocument"><i class="fa fa-fw fa-cog"></i></button>
<button v-else class="btn btn-update btn-sm change-icon" type="button" disabled ><i class="fa fa-fw fa-cog"></i></button>
</div>
</div>
</div>
<div class="row mb-3">
<pick-template
entityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation"
:id="evaluation.id"
:templates="getTemplatesAvailables"
:beforeMove="submitBeforeGenerate"
>
<template v-slot:title>
<label class="col-sm-4 col-form-label">{{ $t('evaluation_generate_a_document') }}</label>
</template>
</pick-template>
</div>
</div>
</div>
</template>
@@ -85,6 +82,7 @@ import {dateToISO, ISOToDate, ISOToDatetime} from 'ChillMainAssets/chill/js/date
import CKEditor from '@ckeditor/ckeditor5-vue';
import ClassicEditor from 'ChillMainAssets/module/ckeditor5/index.js';
import { mapGetters, mapState } from 'vuex';
import PickTemplate from 'ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue';
const i18n = {
messages: {
@@ -111,6 +109,7 @@ export default {
props: ['evaluation'],
components: {
ckeditor: CKEditor.component,
PickTemplate,
},
i18n,
data() {
@@ -120,12 +119,12 @@ export default {
}
},
computed: {
...mapGetters([
'getTemplatesAvailaibleForEvaluation'
]),
...mapState([
'isPosting'
]),
getTemplatesAvailables() {
return this.$store.getters.getTemplatesAvailablesForEvaluation(this.evaluation.evaluation);
},
canGenerate() {
return !this.$store.state.isPosting && this.template !== null;
},
@@ -176,13 +175,14 @@ export default {
})
;
},
generateDocument() {
console.log('template picked', this.template);
this.$store.dispatch('generateDocument', { key: this.evaluation.key, templateId: this.template})
submitBeforeGenerate() {
const callback = (data) => {
let evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === this.evaluation.key).id;
return Promise.resolve({entityId: evaluationId});
};
return this.$store.dispatch('submit', callback).catch(e => { console.log(e); throw e; });
}
},
mounted() {
//this.listAllStatus();
}
}
</script>

View File

@@ -2,6 +2,8 @@ import { createStore } from 'vuex';
import { datetimeToISO, ISOToDatetime, intervalDaysToISO, intervalISOToDays } from 'ChillMainAssets/chill/js/date.js';
import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js';
import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js';
import { fetchResults, makeFetch } from 'ChillMainAssets/lib/api/apiMethods.js';
import { fetchTemplates } from 'ChillDocGeneratorAssets/api/pickTemplate.js';
const debug = process.env.NODE_ENV !== 'production';
const evalFQDN = encodeURIComponent("Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluation");
@@ -20,20 +22,10 @@ const store = createStore({
resultsPicked: window.accompanyingCourseWork.results,
resultsForAction: [],
resultsForGoal: [],
evaluationsPicked: window.accompanyingCourseWork.accompanyingPeriodWorkEvaluations.map((e, index) => {
var k = Object.assign(e, {
key: index,
editEvaluation: false,
startDate: e.startDate !== null ? ISOToDatetime(e.startDate.datetime) : null,
endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null,
maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
});
return k;
}),
evaluationsPicked: [],
evaluationsForAction: [],
templatesAvailableForEvaluation: [],
templatesAvailablesForAction: [],
templatesAvailablesForEvaluation: new Map([]),
personsPicked: window.accompanyingCourseWork.persons,
personsReachables: window.accompanyingCourseWork.accompanyingPeriod.participations.filter(p => p.endDate == null)
.map(p => p.person),
@@ -65,8 +57,8 @@ const store = createStore({
hasThirdParties(state) {
return state.thirdParties.length > 0;
},
getTemplatesAvailaibleForEvaluation(state) {
return state.templatesAvailableForEvaluation;
getTemplatesAvailablesForEvaluation: (state) => (evaluation) => {
return state.templatesAvailablesForEvaluation.get(evaluation.id) || [];
},
buildPayload(state) {
return {
@@ -125,6 +117,20 @@ const store = createStore({
}
},
mutations: {
setEvaluationsPicked(state, evaluations) {
state.evaluationsPicked = evaluations.map((e, index) => {
var k = Object.assign(e, {
key: index,
editEvaluation: false,
startDate: e.startDate !== null ? ISOToDatetime(e.startDate.datetime) : null,
endDate: e.endDate !== null ? ISOToDatetime(e.endDate.datetime) : null,
maxDate: e.maxDate !== null ? ISOToDatetime(e.maxDate.datetime) : null,
warningInterval: e.warningInterval !== null ? intervalISOToDays(e.warningInterval) : null,
});
return k;
});
},
setStartDate(state, date) {
state.startDate = date;
},
@@ -222,10 +228,11 @@ const store = createStore({
let evaluation = state.evaluationsPicked.find(e => e.key === key);
evaluation.editEvaluation = !evaluation.editEvaluation;
},
setTemplatesAvailableForEvaluation(state, templates) {
for (let i in templates) {
state.templatesAvailableForEvaluation.push(templates[i]);
}
setTemplatesForEvaluation(state, {templates, evaluation}) {
state.templatesAvailablesForEvaluation.set(evaluation.id, templates);
},
setTemplatesAvailablesForAction(state, templates) {
state.templatesAvailablesForAction = templates;
},
setPersonsPickedIds(state, ids) {
state.personsPicked = state.personsReachables
@@ -328,36 +335,19 @@ const store = createStore({
commit('setEvaluationsForAction', data.results);
});
},
getReachableTemplatesForEvaluation({commit}) {
const
url = `/fr/doc/gen/templates/for/${evalFQDN}`
;
window.fetch(url).then(r => {
if (r.ok) {
return r.json();
}
throw new Error("not possible to load templates for evaluations")
}).then(data => {
commit('setTemplatesAvailableForEvaluation', data.results);
}).catch(e => {
console.error(e);
})
addEvaluation({commit, dispatch}, evaluation) {
commit('addEvaluation', evaluation);
dispatch('fetchTemplatesAvailablesForEvaluation', evaluation);
},
generateDocument({ dispatch }, {key, templateId}) {
const callback = function(data) {
// get the evaluation id from the data
const
evaluationId = data.accompanyingPeriodWorkEvaluations.find(e => e.key === key).id,
returnPath = encodeURIComponent(window.location.pathname + window.location.search + window.location.hash),
url = `/fr/doc/gen/generate/from/${templateId}/for/${evalFQDN}/${evaluationId}?returnPath=${returnPath}`
;
//http://localhost:8001/fr/doc/gen/generate/from/12/for/Chill%5CPersonBundle%5CEntity%5CAccompanyingPeriod%5CAccompanyingPeriodWorkEvaluation/41
console.log('I will generate your doc at', url);
window.location.assign(url);
};
dispatch('submit', callback);
fetchTemplatesAvailablesForEvaluation({commit, state}, evaluation) {
if (!state.templatesAvailablesForEvaluation.has(evaluation.id)) {
// commit an empty array to avoid parallel fetching for same evaluation id
commit('setTemplatesForEvaluation', {templates: [], evaluation});
fetchResults(`/api/1.0/person/docgen/template/by-evaluation/${evaluation.id}.json`)
.then(templates => {
commit('setTemplatesForEvaluation', {templates, evaluation});
});
}
},
submit({ getters, state, commit }, callback) {
let
@@ -368,47 +358,36 @@ const store = createStore({
commit('setIsPosting', true);
window.fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}).then(response => {
if (response.ok || response.status === 422) {
return response.json().then(data => ({data, status: response.status}));
}
throw new Error(response.status);
}).then(({data, status}) => {
if (status === 422) {
for (let i in data.violations) {
errors.push(data.violations[i].title);
return makeFetch('PUT', url, payload)
.then(data => {
console.log('data received', data);
if (typeof(callback) !== 'undefined') {
return callback(data);
} else {
console.info('nothing to do here, bye bye');window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
}
commit('setErrors', errors);
}).catch(error => {
console.log('error on submit', error);
commit('setIsPosting', false);
} else if (typeof(callback) !== 'undefined') {
callback(data);
} else {
console.info('nothing to do here, bye bye');
window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`);
}
}).catch(e => {
commit('setErrors', [
'Erreur serveur ou réseau: veuillez ré-essayer. Code erreur: ' + e
]);
commit('setIsPosting', false);
});
},
initAsync({ dispatch }) {
dispatch('getReachablesResultsForAction');
dispatch('getReachablesGoalsForAction');
dispatch('getReachablesEvaluationsForAction');
dispatch('getReachableTemplatesForEvaluation');
commit('setErrors', error.violations);
});
},
}
});
store.dispatch('initAsync');
store.commit('setEvaluationsPicked', window.accompanyingCourseWork.accompanyingPeriodWorkEvaluations);
store.dispatch('getReachablesResultsForAction');
store.dispatch('getReachablesGoalsForAction');
store.dispatch('getReachablesEvaluationsForAction');
store.state.evaluationsPicked.forEach(evaluation => {
store.dispatch('fetchTemplatesAvailablesForEvaluation', evaluation.evaluation)
});
fetchTemplates('Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork')
.then(templates => {
store.commit('setTemplatesAvailablesForAction', templates);
}
)
export { store };

View File

@@ -103,7 +103,7 @@
</div>
<div class="social-actions my-4">
<h2 class="mb-3 d-none">{{ 'Last social actions'|trans }}</h2>
<h2 class="mb-3 visually-hidden">{{ 'Last social actions'|trans }}</h2>
{% include 'ChillPersonBundle:AccompanyingCourseWork:list_recent_by_accompanying_period.html.twig' with {'buttonText': false } %}
</div>
@@ -121,7 +121,7 @@
{% set accompanying_course_id = accompanyingCourse.id %}
{% endif %}
<h2 class="mb-3 d-none">{{ 'Last activities' |trans }}</h2>
<h2 class="mb-3 visually-hidden">{{ 'Last activities' |trans }}</h2>
{% include 'ChillActivityBundle:Activity:list_recent.html.twig' with { 'context': 'accompanyingCourse', 'no_action': true } %}
</div>
{% endblock %}

View File

@@ -8,7 +8,7 @@
<div class="accompanying_course_work-list">
<h2 class="badge-title">
<span class="title_label">{{ 'accompanying_course_work.action'|trans }}</span>
<span class="title_label"></span>
<span class="title_action">{{ work.socialAction|chill_entity_render_string }}</span>
</h2>

View File

@@ -7,7 +7,217 @@
<h1>{{ block('title') }}</h1>
{% include 'ChillPersonBundle:AccompanyingCourseWork:list_by_accompanying_period.html.twig' %}
{% if works|length == 0 %}
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}
<a class="btn btn-sm btn-create"
title="{{ 'accompanying_course_work.create'|trans }}"
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_new', { 'id': accompanyingCourse.id }) }}"
></a>
</p>
{% else %}
<div class="flex-table accompanying_course_work-list">
{% for w in works %}
<div class="item-bloc">
<div class="item-row">
<h2 class="badge-title">
<span class="title_label"></span>
<span class="title_action">{{ w.socialAction|chill_entity_render_string }}
<ul class="small_in_title columns mt-1">
<li>
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
<b>{{ w.startDate|format_date('short') }}</b>
</li>
{% if w.endDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
<b>{{ w.endDate|format_date('short') }}</b>
</li>
{% endif %}
</ul>
</span>
</h2>
</div>
<div class="item-row separator">
<div class="wrap-list">
{% if w.createdBy %}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Referrer'|trans }}</h3>
</div>
<div class="wl-col list">
<p class="wl-item">
{{ w.createdBy.usernameCanonical|chill_entity_render_string|capitalize }}
</p>
</div>
</div>
{% endif %}
{%- if w.persons -%}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Persons in accompanying course'|trans }}</h3>
</div>
<div class="wl-col list">
{% for p in w.persons %}
<span class="wl-item badge-person">
{{ p|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
</span>
{% endfor %}
</div>
</div>
{% endif %}
{%- if w.handlingThierParty -%}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Thirdparty handling'|trans }}</h3>
</div>
<div class="wl-col list">
<span class="wl-item badge-thirdparty">
{{ w.handlingThierParty|chill_entity_render_box({
'render': 'raw',
'addAltNames': false
}) }}
</span>
</div>
</div>
{% endif %}
{%- if w.socialAction.issue -%}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'Social issue'|trans }}</h3>
</div>
<div class="wl-col list">
<p class="wl-item social-issues">
{{ w.socialAction.issue|chill_entity_render_box }}
</p>
</div>
</div>
{% endif %}
{% if w.accompanyingPeriodWorkEvaluations|length > 0 %}
<div class="wl-row">
<div class="wl-col title">
<h3>{{ 'accompanying_course_work.evaluations'|trans }}</h3>
</div>
<div class="wl-col list">
<ul class="small_in_title evaluations mt-1">
{% for e in w.accompanyingPeriodWorkEvaluations %}
<li>
{{ e.evaluation.title|localize_translatable_string }}
<ul class="columns">
<li>
<span class="item-key">{{ 'accompanying_course_work.start_date'|trans ~ ' : ' }}</span>
<b>{{ e.startDate|format_date('short') }}</b>
</li>
{% if e.endDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.end_date'|trans ~ ' : ' }}</span>
<b>{{ e.endDate|format_date('short') }}</b>
</li>
{% endif %}
{% if e.maxDate %}
<li>
<span class="item-key">{{ 'accompanying_course_work.max_date'|trans ~ ' : ' }}</span>
<b>{{ e.maxDate|format_date('short') }}</b>
</li>
{% endif %}
{% if e.warningInterval and e.warningInterval.d > 0 %}
<li>
{% set days = (e.warningInterval.d + e.warningInterval.m * 30) %}
<span class="item-key">{{ 'accompanying_course_work.warning_interval'|trans ~ ' : ' }}</span>
{{ 'accompanying_course_work.%days% days before max_date'|trans({'%days%': days }) }}
</li>
{% endif %}
</ul>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>
</div>
<div class="item-row column">
{# SEULEMENT SI DÉTAILLÉ
{% if w.results|length > 0 %}
<div class="objective_results without-objectives">
<div class="objective">
<h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4>
<p class="chill-no-data-statement">{{ 'accompanying_course_work.results without objective'|trans }}</p>
</div>
<div class="results">
<h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4>
<ul class="result_list">
{% for r in w.results %}
<li>{{ r.title|localize_translatable_string }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if w.goals|length > 0 %}
{% for g in w.goals %}
<div class="objective_results with-objectives">
<div class="objective">
<h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4>
<ul class="goal_title"><li>{{ g.goal.title|localize_translatable_string }}</li></ul>
</div>
<div class="results">
<h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4>
{% if g.results|length == 0 %}
<p class="chill-no-data-statement">{{ 'accompanying_course_work.no_results'|trans }}</p>
{% else %}
<ul class="result_list">
{% for r in g.results %}
<li>{{ r.title|localize_translatable_string }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
#}
</div>
<div class="item-row separator">
<div class="updatedBy">
{{ 'Last updated by'|trans}} <b>{{ w.updatedBy|chill_entity_render_box }}</b>,<br>
{{ 'le ' ~ w.updatedAt|format_datetime('long', 'short') }}
</div>
<ul class="record_actions">
<li>
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
></a>
</li>
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
></a>
</li>
</ul>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<ul class="record_actions sticky-form-buttons">
<li>

View File

@@ -1,118 +0,0 @@
{% if works|length == 0 %}
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}
<a class="btn btn-sm btn-create"
href="" title="TODO"></a>{# TODO link #}
</p>
{% endif %}
<div class="flex-table accompanying_course_work-list">
{% for w in works %}
<div class="item-bloc">
<div class="item-row">
<h2 class="badge-title">
<span class="title_label">{{ 'accompanying_course_work.action'|trans }}</span>
<span class="title_action">{{ w.socialAction|chill_entity_render_string }}</span>
</h2>
</div>
<div class="item-row separator">
<div class="timeline">
<ul>
<li class="completed">
<div class="date">
<span>{{ w.startDate|format_date('long') }}</span>
</div>
<div class="label">
<span>{{ 'accompanying_course_work.start_date'|trans }}</span>
</div>
</li>
{% if w.endDate == null %}
<li>
<div class="label no-label"></div>
</li>
{% else %}
<li class="{%if date(w.endDate) < date('now') %}completed{% endif %}">
<div class="date">
<span>{{ w.endDate|format_date('long') }}</span>
</div>
<div class="label">
<span>{{ 'accompanying_course_work.end_date'|trans }}</span>
</div>
</li>
{% endif %}
</ul>
</div>
</div>
<div class="">
{% if w.results|length > 0 and w.goals|length > 0 %}
{% endif %}
{% if w.results|length > 0 %}
<div class="objective_results without-objectives">
<div class="objective">
<h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4>
<p class="chill-no-data-statement">{{ 'accompanying_course_work.results without objective'|trans }}</p>
</div>
<div class="results">
<h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4>
<ul class="result_list">
{% for r in w.results %}
<li>{{ r.title|localize_translatable_string }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if w.goals|length > 0 %}
{% for g in w.goals %}
<div class="objective_results with-objectives">
<div class="objective">
<h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4>
<ul class="goal_title"><li>{{ g.goal.title|localize_translatable_string }}</li></ul>
</div>
<div class="results">
<h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4>
{% if g.results|length == 0 %}
<p class="chill-no-data-statement">{{ 'accompanying_course_work.no_results'|trans }}</p>
{% else %}
<ul class="result_list">
{% for r in g.results %}
<li>{{ r.title|localize_translatable_string }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
<div class="item-row separator">
<div class="updatedBy">
{{ 'Last updated by'|trans}} <b>{{ w.updatedBy|chill_entity_render_box }}</b>,<br>
{{ 'le ' ~ w.updatedAt|format_datetime('long', 'short') }}
</div>
<ul class="record_actions">
<li>
<a class="btn btn-edit" title="{{ 'Edit'|trans }}"
href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
>{% if buttonText is not defined or buttonText == true %}{{ 'Edit'|trans }}{% endif %}</a>
</li>
<li>
<a class="btn btn-delete" title="{{ 'Delete'|trans }}"
href="{{ path('chill_person_accompanying_period_work_delete', { 'id': w.id } ) }}"
>{% if buttonText is not defined or buttonText == true %}{{ 'Delete'|trans }}{% endif %}</a>
</li>
</ul>
</div>
</div>
{% endfor %}
</div>

View File

@@ -2,14 +2,11 @@
{% for w in works | slice(0,5) %}
<a href="{{ chill_path_add_return_path('chill_person_accompanying_period_work_edit', { 'id': w.id }) }}"
class="badge-link" title="{{ 'crud.social_action.title_link'|trans }}">
class="dashboard-link" title="{{ 'crud.social_action.title_link'|trans }}">
<h2 class="badge-title">
<span class="title_label">
<span>{{ 'accompanying_course_work.action'|trans }}</span>
</span>
<span class="title_action">
{{ w.socialAction|chill_entity_render_string }}
<div class="dashboard">
<span class="title_label"></span>
<span class="title_action"><span class="like-h3">{{ w.socialAction|chill_entity_render_string }}</span>
<ul class="small_in_title columns mt-3">
<li>
@@ -75,7 +72,7 @@
</div>
</span>
</h2>
</div>
</a>{# {{ dump(w) }} #}
{% endfor %}

View File

@@ -0,0 +1,160 @@
<?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\PersonBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Security\Resolver\ScopeResolverDispatcher;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Templating\Entity\ClosingMotiveRender;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use DateTime;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function in_array;
use function is_array;
class AccompanyingPeriodDocGenNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
private const IGNORE_FIRST_PASS_KEY = 'acc_period_ignore_first_pass';
private const PERIOD_NULL = [
'id' => '',
'closingDate' => DateTime::class,
'confidential' => '',
'confidentialText' => '',
'createdAt' => DateTime::class,
'createdBy' => User::class,
'emergency' => '',
'emergencyText' => '',
'openingDate' => DateTime::class,
'originText' => '',
'requestorAnonymous' => false,
'socialIssues' => [],
'intensity' => '',
'step' => '',
'closingMotiveText' => '',
'socialIssuesText' => '',
'scopes' => [],
'scopesText' => '',
'ref' => User::class,
'participations' => [],
];
private ClosingMotiveRender $closingMotiveRender;
private ScopeResolverDispatcher $scopeResolverDispatcher;
private SocialIssueRender $socialIssueRender;
private TranslatableStringHelper $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator,
TranslatableStringHelper $translatableStringHelper,
SocialIssueRender $socialIssueRender,
ClosingMotiveRender $closingMotiveRender,
ScopeResolverDispatcher $scopeResolverDispatcher
) {
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->socialIssueRender = $socialIssueRender;
$this->closingMotiveRender = $closingMotiveRender;
$this->scopeResolverDispatcher = $scopeResolverDispatcher;
}
/**
* @param AccompanyingPeriod|null $period
*/
public function normalize($period, ?string $format = null, array $context = [])
{
if ($period instanceof AccompanyingPeriod) {
$ignored = $context[self::IGNORE_FIRST_PASS_KEY] ?? [];
$ignored[] = spl_object_hash($period);
$initial =
$this->normalizer->normalize($period, $format, array_merge(
$context,
[self::IGNORE_FIRST_PASS_KEY => $ignored, AbstractNormalizer::GROUPS => 'docgen:read']
));
// some transformation
$user = $initial['user'];
unset($initial['user']);
$scopes = $this->scopeResolverDispatcher->isConcerned($period) ? $this->scopeResolverDispatcher->resolveScope($period) : [];
if (!is_array($scopes)) {
$scopes = [$scopes];
}
return array_merge(
// get a first default data
$initial,
// and add data custom
[
'intensity' => $this->translator->trans($period->getIntensity()),
'step' => $this->translator->trans('accompanying_period.' . $period->getStep()),
'emergencyText' => $period->isEmergency() ? $this->translator->trans('accompanying_period.emergency') : '',
'confidentialText' => $period->isConfidential() ? $this->translator->trans('confidential') : '',
//'originText' => null !== $period->getOrigin() ? $this->translatableStringHelper->localize($period->getOrigin()->getLabel()) : '',
'closingMotiveText' => null !== $period->getClosingMotive() ?
$this->closingMotiveRender->renderString($period->getClosingMotive(), []) : '',
'ref' => $user,
'socialIssuesText' => implode(', ', array_map(function (SocialIssue $s) {
return $this->socialIssueRender->renderString($s, []);
}, $period->getSocialIssues()->toArray())),
'scopesText' => implode(', ', array_map(function (Scope $s) {
return $this->translatableStringHelper->localize($s->getName());
}, $scopes)),
'scopes' => $scopes,
]
);
} elseif (null === $period) {
return self::PERIOD_NULL;
}
throw new InvalidArgumentException('this neither an accompanying period or null');
}
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
{
if ('docgen' !== $format) {
return false;
}
if ($data instanceof AccompanyingPeriod) {
if (array_key_exists(self::IGNORE_FIRST_PASS_KEY, $context)
&& in_array(spl_object_hash($data), $context[self::IGNORE_FIRST_PASS_KEY], true)) {
return false;
}
return true;
}
if (null === $data && AccompanyingPeriod::class === ($context['docgen:expects'] ?? null)) {
return true;
}
return false;
}
}

View File

@@ -30,18 +30,49 @@ class SocialActionNormalizer implements NormalizerAwareInterface, NormalizerInte
public function normalize($socialAction, ?string $format = null, array $context = [])
{
return [
'id' => $socialAction->getId(),
'type' => 'social_work_social_action',
'text' => $this->render->renderString($socialAction, []),
'parent' => $this->normalizer->normalize($socialAction->getParent()),
'desactivationDate' => $this->normalizer->normalize($socialAction->getDesactivationDate()),
'title' => $socialAction->getTitle(),
];
switch ($format) {
case 'json':
return [
'id' => $socialAction->getId(),
'type' => 'social_work_social_action',
'text' => $this->render->renderString($socialAction, []),
'parent' => $this->normalizer->normalize($socialAction->getParent()),
'desactivationDate' => $this->normalizer->normalize($socialAction->getDesactivationDate()),
'title' => $socialAction->getTitle(),
];
case 'docgen':
if (null === $socialAction) {
return ['id' => 0, 'title' => '', 'text' => ''];
}
return [
'id' => $socialAction->getId(),
'text' => $this->render->renderString($socialAction, []),
'title' => $socialAction->getTitle(),
];
default:
throw new \Symfony\Component\Serializer\Exception\RuntimeException('format not supported');
}
}
public function supportsNormalization($data, ?string $format = null)
public function supportsNormalization($data, ?string $format = null, array $context = [])
{
return $data instanceof SocialAction;
if ($data instanceof SocialAction && 'json' === $format) {
return true;
}
if ('docgen' === $format) {
if ($data instanceof SocialAction) {
return true;
}
if (null === $data && SocialAction::class === ($context['docgen:expects'] ?? null)) {
return true;
}
}
return false;
}
}

View File

@@ -13,11 +13,11 @@ namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class SocialIssueNormalizer implements NormalizerAwareInterface, NormalizerInterface
class SocialIssueNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
@@ -31,18 +31,46 @@ class SocialIssueNormalizer implements NormalizerAwareInterface, NormalizerInter
public function normalize($socialIssue, ?string $format = null, array $context = [])
{
/** @var SocialIssue $socialIssue */
return [
'type' => 'social_issue',
'id' => $socialIssue->getId(),
'parent_id' => $socialIssue->hasParent() ? $socialIssue->getParent()->getId() : null,
'children_ids' => $socialIssue->getChildren()->map(static function (SocialIssue $si) { return $si->getId(); }),
'title' => $socialIssue->getTitle(),
'text' => $this->render->renderString($socialIssue, []),
];
switch ($format) {
case 'json':
return [
'type' => 'social_issue',
'id' => $socialIssue->getId(),
'parent_id' => $socialIssue->hasParent() ? $socialIssue->getParent()->getId() : null,
'children_ids' => $socialIssue->getChildren()->map(static function (SocialIssue $si) { return $si->getId(); }),
'title' => $socialIssue->getTitle(),
'text' => $this->render->renderString($socialIssue, []),
];
case 'docgen':
if (null === $socialIssue) {
return ['id' => 0, 'title' => '', 'text' => ''];
}
return [
'id' => $socialIssue->getId(),
'title' => $socialIssue->getTitle(),
'text' => $this->render->renderString($socialIssue, []),
];
}
}
public function supportsNormalization($data, ?string $format = null): bool
public function supportsNormalization($data, ?string $format = null, array $context = [])
{
return $data instanceof SocialIssue;
if ($data instanceof SocialIssue && 'json' === $format) {
return true;
}
if ('docgen' === $format) {
if ($data instanceof SocialIssue) {
return true;
}
if (null === $data && SocialIssue::class === ($context['docgen:expects'] ?? null)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,245 @@
<?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\PersonBundle\Service\DocGenerator;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Context\Exception\UnexpectedTypeException;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Repository\DocumentCategoryRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Templating\Entity\PersonRender;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
class AccompanyingPeriodContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
{
private DocumentCategoryRepository $documentCategoryRepository;
private EntityManagerInterface $em;
private NormalizerInterface $normalizer;
private PersonRender $personRender;
private TranslatableStringHelperInterface $translatableStringHelper;
private TranslatorInterface $translator;
public function __construct(
DocumentCategoryRepository $documentCategoryRepository,
NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper,
EntityManagerInterface $em,
PersonRender $personRender,
TranslatorInterface $translator
) {
$this->documentCategoryRepository = $documentCategoryRepository;
$this->normalizer = $normalizer;
$this->translatableStringHelper = $translatableStringHelper;
$this->em = $em;
$this->personRender = $personRender;
$this->translator = $translator;
}
public function adminFormReverseTransform(array $data): array
{
if (array_key_exists('category', $data)) {
$data['category'] = [
'idInsideBundle' => $data['category']->getIdInsideBundle(),
'bundleId' => $data['category']->getBundleId(),
];
}
return $data;
}
public function adminFormTransform(array $data): array
{
$data = [
'mainPerson' => $data['mainPerson'] ?? false,
'mainPersonLabel' => $data['mainPersonLabel'] ?? $this->translator->trans('docgen.Main person'),
'person1' => $data['person1'] ?? false,
'person1Label' => $data['person1Label'] ?? $this->translator->trans('docgen.person 1'),
'person2' => $data['person2'] ?? false,
'person2Label' => $data['person2Label'] ?? $this->translator->trans('docgen.person 2'),
];
if (array_key_exists('category', $data)) {
$data['category'] = array_key_exists('category', $data) ?
$this->documentCategoryRepository->find($data['category']) : null;
}
return $data;
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$builder
->add('mainPerson', CheckboxType::class, [
'required' => false,
'label' => 'docgen.Ask for main person',
])
->add('mainPersonLabel', TextType::class, [
'label' => 'main person label',
'required' => true,
])
->add('person1', CheckboxType::class, [
'required' => false,
'label' => 'docgen.Ask for person 1',
])
->add('person1Label', TextType::class, [
'label' => 'person 1 label',
'required' => true,
])
->add('person2', CheckboxType::class, [
'required' => false,
'label' => 'docgen.Ask for person 2',
])
->add('person2Label', TextType::class, [
'label' => 'person 2 label',
'required' => true,
])
->add('category', EntityType::class, [
'placeholder' => 'Choose a document category',
'class' => 'ChillDocStoreBundle:DocumentCategory',
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.documentClass = :docClass')
->setParameter('docClass', AccompanyingCourseDocument::class);
},
'choice_label' => function ($entity = null) {
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
},
]);
}
/**
* @param AccompanyingPeriod $entity
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$options = $template->getOptions();
$persons = $entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) { return $p->getPerson(); })
->toArray();
foreach (['mainPerson', 'person1', 'person2'] as $key) {
if ($options[$key] ?? false) {
$builder->add($key, EntityType::class, [
'class' => Person::class,
'choices' => $persons,
'choice_label' => function (Person $p) { return $this->personRender->renderString($p, []); },
'multiple' => false,
'expanded' => true,
'label' => $options[$key . 'Label'],
]);
}
}
}
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
if (!$entity instanceof AccompanyingPeriod) {
throw new UnexpectedTypeException($entity, AccompanyingPeriod::class);
}
$options = $template->getOptions();
$data = [];
$data['course'] = $this->normalizer->normalize($entity, 'docgen', ['docgen:expects' => AccompanyingPeriod::class]);
foreach (['mainPerson', 'person1', 'person2'] as $k) {
if ($options[$k]) {
$data[$k] = $this->normalizer->normalize($contextGenerationData[$k], 'docgen', ['docgen:expects' => Person::class]);
}
}
return $data;
}
public function getDescription(): string
{
return 'docgen.A basic context for accompanying period';
}
public function getEntityClass(): string
{
return AccompanyingPeriod::class;
}
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return [
'course' => $entity,
];
}
public static function getKey(): string
{
return self::class;
}
public function getName(): string
{
return 'Accompanying Period basic';
}
public function hasAdminForm(): bool
{
return true;
}
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
$options = $template->getOptions();
return $options['mainPerson'] || $options['person1'] || $options['person2'];
}
/**
* @param AccompanyingPeriod $entity
*/
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$doc = new AccompanyingCourseDocument();
$doc->setTitle($this->translatableStringHelper->localize($template->getName()))
->setDate(new DateTime())
->setDescription($this->translatableStringHelper->localize($template->getName()))
->setCourse($entity)
->setObject($storedObject);
if (array_key_exists('category', $template->getOptions()['category'])) {
$doc
->setCategory(
$this->documentCategoryRepository->find(
$template->getOptions()['category']
)
);
}
$this->em->persist($doc);
}
}

View File

@@ -0,0 +1,122 @@
<?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\PersonBundle\Service\DocGenerator;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class AccompanyingPeriodWorkContext implements
DocGeneratorContextInterface,
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
{
private NormalizerInterface $normalizer;
private AccompanyingPeriodContext $periodContext;
public function __construct(
AccompanyingPeriodContext $periodContext,
NormalizerInterface $normalizer
) {
$this->periodContext = $periodContext;
$this->normalizer = $normalizer;
}
public function adminFormReverseTransform(array $data): array
{
return $this->periodContext->adminFormReverseTransform($data);
}
public function adminFormTransform(array $data): array
{
return $this->periodContext->adminFormTransform($data);
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$this->periodContext->buildAdminForm($builder);
$builder->remove('category');
}
/**
* @param AccompanyingPeriodWork $entity
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$this->periodContext->buildPublicForm($builder, $template, $entity->getAccompanyingPeriod());
}
/**
* @param AccompanyingPeriodWork $entity
*/
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
$data = $this->periodContext->getData($template, $entity->getAccompanyingPeriod(), $contextGenerationData);
$data['work'] = $this->normalizer->normalize($entity, 'docgen', [
AbstractNormalizer::GROUPS => ['docgen:read'],
'docgen:expects' => AccompanyingPeriodWork::class,
]);
return $data;
}
public function getDescription(): string
{
return 'docgen.A context for accompanying period work';
}
public function getEntityClass(): string
{
return AccompanyingPeriodWork::class;
}
/**
* @param AccompanyingPeriodWork $entity
*/
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return $this->periodContext->getFormData($template, $entity->getAccompanyingPeriod());
}
public static function getKey(): string
{
return 'accompanying_period_work_regular';
}
public function getName(): string
{
return 'Accompanying period work';
}
public function hasAdminForm(): bool
{
return $this->periodContext->hasAdminForm();
}
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return $this->periodContext->hasPublicForm($template, $entity);
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
// TODO: Implement storeGenerated() method.
}
}

View File

@@ -0,0 +1,181 @@
<?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\PersonBundle\Service\DocGenerator;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithAdminFormInterface;
use Chill\DocGeneratorBundle\Context\DocGeneratorContextWithPublicFormInterface;
use Chill\DocGeneratorBundle\Entity\DocGeneratorTemplate;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class AccompanyingPeriodWorkEvaluationContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
{
private AccompanyingPeriodWorkContext $accompanyingPeriodWorkContext;
private EntityManagerInterface $em;
private EvaluationRepository $evaluationRepository;
private NormalizerInterface $normalizer;
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
AccompanyingPeriodWorkContext $accompanyingPeriodWorkContext,
EntityManagerInterface $em,
EvaluationRepository $evaluationRepository,
NormalizerInterface $normalizer,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->accompanyingPeriodWorkContext = $accompanyingPeriodWorkContext;
$this->em = $em;
$this->evaluationRepository = $evaluationRepository;
$this->normalizer = $normalizer;
$this->translatableStringHelper = $translatableStringHelper;
}
public function adminFormReverseTransform(array $data): array
{
return array_merge(
$this->accompanyingPeriodWorkContext->adminFormReverseTransform($data),
[
'evaluations' => array_map(
static function (Evaluation $e) { return $e->getId(); },
$data['evaluations']
),
]
);
}
public function adminFormTransform(array $data): array
{
return array_merge(
$this->accompanyingPeriodWorkContext->adminFormTransform($data),
[
'evaluations' => array_map(
function ($id) { return $this->evaluationRepository->find($id); },
$data['evaluations'] ?? []
),
]
);
}
public function buildAdminForm(FormBuilderInterface $builder): void
{
$this->accompanyingPeriodWorkContext->buildAdminForm($builder);
$builder->remove('category');
$builder->add('evaluations', EntityType::class, [
'class' => Evaluation::class,
'label' => 'Linked evaluations',
'choices' => $this->evaluationRepository->findAll(),
'choice_label' => function (Evaluation $e) {
return $this->translatableStringHelper->localize($e->getTitle());
},
'multiple' => true,
'expanded' => true,
]);
}
/**
* @param AccompanyingPeriodWorkEvaluation $entity
*/
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$this->accompanyingPeriodWorkContext->buildPublicForm($builder, $template, $entity->getAccompanyingPeriodWork());
}
/**
* @param AccompanyingPeriodWorkEvaluation $entity
*/
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
$data = $this->accompanyingPeriodWorkContext
->getData($template, $entity->getAccompanyingPeriodWork(), $contextGenerationData);
$data['evaluation'] = $this->normalizer->normalize(
$entity,
'docgen',
[
'docgen:expect' => AccompanyingPeriodWorkEvaluation::class,
AbstractNormalizer::GROUPS => ['docgen:read'],
]
);
return $data;
}
public function getDescription(): string
{
return 'docgen.A context for accompanying period work evaluation';
}
public function getEntityClass(): string
{
return AccompanyingPeriodWorkEvaluation::class;
}
/**
* @param AccompanyingPeriodWorkEvaluation $entity
*/
public function getFormData(DocGeneratorTemplate $template, $entity): array
{
return $this->accompanyingPeriodWorkContext->getFormData(
$template,
$entity->getAccompanyingPeriodWork()
);
}
public static function getKey(): string
{
return 'accompanying_period_work_evaluation_regular';
}
public function getName(): string
{
return 'Accompanying period work context';
}
public function hasAdminForm(): bool
{
return true;
}
/**
* @param AccompanyingPeriodWorkEvaluation $entity
*/
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return $this->accompanyingPeriodWorkContext
->hasPublicForm($template, $entity->getAccompanyingPeriodWork());
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$doc = new AccompanyingPeriodWorkEvaluationDocument();
$doc->setStoredObject($storedObject)
->setAccompanyingPeriodWorkEvaluation($entity)
->setTemplate($template);
$this->em->persist($doc);
}
}

View File

@@ -0,0 +1,137 @@
<?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\Entity\Scope;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodDocGenNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp(): void
{
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalize()
{
$period = new AccompanyingPeriod();
$period->setConfidential(true);
$period->setEmergency(true);
$period->setOrigin((new AccompanyingPeriod\Origin())->setLabel(['fr' => 'origin']));
$period->setClosingMotive((new AccompanyingPeriod\ClosingMotive())->setName(['closing']));
$period->addScope((new Scope())->setName(['fr' => 'scope1']));
$period->addScope((new Scope())->setName(['fr' => 'scope2']));
$period->addSocialIssue((new SocialIssue())->setTitle(['fr' => 'issue1']));
$period->addSocialIssue((new SocialIssue())->setTitle(['fr' => 'issue2']));
$data = $this->normalizer->normalize($period, 'docgen', ['docgen:expects' => AccompanyingPeriod::class]);
$expected = [
'id' => null,
'closingDate' => '@ignored',
'confidential' => true,
'confidentialText' => 'confidentiel',
'createdAt' => '@ignored',
'createdBy' => '@ignored',
'emergency' => true,
'emergencyText' => 'Urgent',
'openingDate' => '@ignored',
'originText' => 'origin',
'requestorAnonymous' => false,
'socialIssues' => '@ignored',
'intensity' => 'ponctuel',
'step' => 'Brouillon',
'closingMotiveText' => 'closing',
'socialIssuesText' => 'issue1, issue2',
'scopes' => '@ignored',
'scopesText' => 'scope1, scope2',
'ref' => '@ignored',
'participations' => '@ignored',
];
$this->assertIsArray($data);
$this->assertEqualsCanonicalizing(array_keys($expected), array_keys($data));
foreach ($expected as $key => $item) {
if ('@ignored' === $item) {
continue;
}
$this->assertEquals($item, $data[$key]);
}
}
public function testNormalizeNull()
{
$data = $this->normalizer->normalize(null, 'docgen', ['docgen:expects' => AccompanyingPeriod::class]);
$expected = [
'id' => '',
'closingDate' => '@ignored',
'confidential' => '',
'confidentialText' => '',
'createdAt' => '@ignored',
'createdBy' => '@ignored',
'emergency' => '',
'emergencyText' => '',
'openingDate' => '@ignored',
'originText' => '',
'requestorAnonymous' => '',
'socialIssues' => '@ignored',
'intensity' => '',
'step' => '',
'closingMotiveText' => '',
'socialIssuesText' => '',
'scopes' => '@ignored',
'scopesText' => '',
'ref' => '@ignored',
'participations' => '@ignored',
];
$this->assertIsArray($data);
$this->assertEqualsCanonicalizing(array_keys($expected), array_keys($data));
foreach ($expected as $key => $item) {
if ('@ignored' === $item) {
continue;
}
$this->assertEquals($item, $data[$key]);
}
}
public function testNormalizeParticipations()
{
$period = new AccompanyingPeriod();
$period->addPerson($person = new Person());
$person->setFirstName('test');
$data = $this->normalizer->normalize($period, 'docgen', ['docgen:expects' => AccompanyingPeriod::class]);
$this->assertIsArray($data);
$this->assertArrayHasKey('participations', $data);
$this->assertCount(1, $data['participations']);
$this->assertArrayHasKey('currentParticipations', $data);
$this->assertCount(1, $data['currentParticipations']);
}
}

View File

@@ -0,0 +1,100 @@
<?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\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use DateTimeImmutable;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* @internal
* @coversNothing
*/
final class AccompanyingPeriodWorkDocGenNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp()
{
parent::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalizationNull()
{
$actual = $this->normalizer->normalize(null, 'docgen', [
'docgen:expects' => AccompanyingPeriodWork::class,
AbstractNormalizer::GROUPS => ['docgen:read'],
]);
dump($actual);
$expected = [
'id' => '',
];
$this->assertIsArray($actual);
$this->assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
foreach ($expected as $key => $item) {
if ('@ignored' === $item) {
continue;
}
$this->assertEquals($item, $actual[$key]);
}
}
public function testNormlalize()
{
$work = new AccompanyingPeriodWork();
$work
->addPerson((new Person())->setFirstName('hello')->setLastName('name'))
->addGoal($g = new AccompanyingPeriodWorkGoal())
->addResult($r = new Result())
->setCreatedAt(new DateTimeImmutable())
->setUpdatedAt(new DateTimeImmutable())
->setCreatedBy($user = new User())
->setUpdatedBy($user);
$g->addResult($r)->setGoal($goal = new Goal());
$goal->addResult($r);
$actual = $this->normalizer->normalize($work, 'docgen', [
'docgen:expects' => AccompanyingPeriodWork::class,
AbstractNormalizer::GROUPS => ['docgen:read'],
]);
var_dump($actual);
$expected = [
'id' => 0,
];
$this->assertIsArray($actual);
$this->assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
foreach ($expected as $key => $item) {
if ('@ignored' === $item) {
continue;
}
$this->assertEquals($item, $actual[$key]);
}
}
}

View File

@@ -198,6 +198,7 @@ Resources: Interlocuteurs privilégiés
Any requestor to this accompanying course: Aucun demandeur pour ce parcours
Social actions: Actions d'accompagnement
Last social actions: Les dernières actions d'accompagnement
Social issue: Problématique sociale
Social issues: Problématiques sociales
Last events on accompanying course: Dernières actions de suivi
Edit & activate accompanying course: Modifier et valider
@@ -377,9 +378,14 @@ accompanying_period:
dates: Période
dates_from_%opening_date%: Ouvert depuis le %opening_date%
dates_from_%opening_date%_to_%closing_date%: Ouvert du %opening_date% au %closing_date%
DRAFT: Brouillon
CONFIRMED: Confirmé
CLOSED: Clotûré
emergency: Urgent
occasional: ponctuel
regular: régulier
Confidential: confidentiel
confidential: confidentiel
Draft: brouillon
Confirmed: en file active
Closed: Cloturé
@@ -429,10 +435,14 @@ accompanying_course_work:
create_date: Date de création
start_date: Date de début
end_date: Date de fin
max_date: Date d'échéance
warning_interval: Rappel
'%days% days before max_date': "%days% jour(s) avant l'échéance"
results without objective: Aucun objectif - motif - dispositif
no_results: Aucun résultat - orientation
results: Résultats - orientations
goal: Objectif - motif - dispositif
evaluations: Évaluations
Any work: Aucune action d'accompagnement
remove: Supprimer une action d'accompagnement
social_evaluation: Évaluation
@@ -443,3 +453,15 @@ Household addresses: Adresses de domicile
Insert an address: Insérer une adresse
see social issues: Voir les problématiques sociales
see persons associated: Voir les usagers concernés
docgen:
Main person: Personne principale
person 1: Première personne
person 2: Seconde personne
Ask for main person: Demander à l'utilisateur de préciser la personne principale
Ask for person 1: Demander à l'utilisateur de préciser la première personne
Ask for person 2: Demander à l'utilisateur de préciser la seconde personne
Accompanying period work: Actions
A basic context for accompanying period: Contexte pour les parcours
A context for accompanying period work: Contexte pour les actions d'accompagnement
A context for accompanying period work evaluation: Contexte pour les évaluations dans les actions d'accompagnement