merge master

This commit is contained in:
Julie Lenaerts 2023-10-26 09:36:14 +02:00
commit 2fc6e18d0f
2110 changed files with 38305 additions and 30857 deletions

6
.changes/header.tpl.md Normal file
View File

@ -0,0 +1,6 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).

677
.changes/v2.0.0.md Normal file
View File

@ -0,0 +1,677 @@
## 2.0.0
* this is a release to relaunch our proceess of release with semantic versioning
## Test releases
### 2.0.0-beta3
* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person
* [activity][export] Feature: improve label for aliases in "Filter by activity type"
* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository
* [person][export] Fixed: some inconsistency with date filter on accompanying courses
* [person][export] Fixed: use left join for related entities in accompanying course aggregators
* [workflow] Feature: allow user to copy and send manually the access link for the workflow
* [workflow] Feature: show the email addresses that received an access link for the workflow
### 2.0.0-beta2
* [workflow]: Fixed: the notification is sent when the user is added to the first step.
* [budget] Feature: allow to desactivate some charges and resources, adding an `active` key in the configuration
* [person] Feature: on Evaluation, allow to configure an URL from the admin
### 2022-06
* [workflow]: added pagination to workflow list page
* [homepage_widget]: null error on tasks widget fixed
* [person-thirdparty]: fix quick-add of names that consist of multiple parts (eg. De Vlieger) within onthefly modal person/thirdparty
* [search]: Order of birthdate fields changed in advanced search to avoid confusion.
* [workflow]: Constraint added to workflow (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/675)
* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625)
* [household]: Reposition and cut button for enfant hors menage have been deleted (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/620)
* [admin]: Add crud for composition type in admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/611)
* [social_action]: only show active objectives (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/625)
## Test releases
### 2022-05-30
* fix creating a new AccompanyingPeriodWorkEvaluationDocument when replacing the document (the workflow was lost)
### 2022-05-27
* [storedobject] add title field on StoredObject entity + use it in activity documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/604)
* [main] add a "read more..." on comment embeddable when overflown (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/604)
* [person] add closing motive to closed acc course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/603)
* [person] household filiation: fetch person info when unfolding person (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/586)
* [admin] repair edit of social action in the admin (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/601)
* [admin]: add select2 to Goal form type entity fields (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/702)
* [main] allow hide permissions group list menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577)
* [main] allow hide change user password menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577)
* [main] filter user jobs by active jobs (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577)
* [main] add civility to User (entity, migration and form type) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/577)
* [admin] refactorisation of the admin section: reorganisation of the menu, translations, form types, new entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/592)
* [admin] add admin section for languages and countries (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/596)
* [activity] activity admin: translations + remove label field for comment on admin activity type (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/587)
* [main] admin user_job: improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/588)
* [address] can add extra address info even if noAddress (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/576)
### 2022-05-06
* [person] add civility when creating a person (with the on-the-fly component or in the php form) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557)
* [person] add address when creating a person (with the on-the-fly component or in the php form) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557)
* [person] add household creation API point (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/557)
### 2021-04-29
* [person] prevent circular references in PersonDocGenNormalizer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/527)
* [person] add maritalStatusComment to PersonDocGenNormalizer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/582)
* Load relationships without gender in french fixtures
* Add command to remove old draft accompanying periods
* [parcours]: If users assings him/herself as referrer and job is not null. Update parcours job (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/578)
### 2021-04-28
* [address] fix bug when editing address: update location and addressreferenceId + better update of the map in edition (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/593)
* [main] avoid address reference search on undefined post code (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/561)
* [person] prevent duplicate relationship in filiation/household graph (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/560)
* [Documents] Validate storedObject and allow for null data (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/565)
* [parcours]: Comments can be unpinned + edit/delete for all users that are allowed to edit parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/566)
### 2021-04-26
* [Datepickers] datepickers fixed when using keyboard to enter date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/545)
* [social_action] Display 'agents traitants' in parcours resumé and social action list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/568)
* [Person_search] Closed parcours shown within an accordeon that can be opened/closed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/574)
### 2021-04-24
* [notification email on course designation] allow raw string in email content generation
* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
* [Documents] Change wording 'créer' to 'enregistrer' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/634)
* [Parcours]: The number of 'mes parcours' displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/572)
* [Hompage_widget]: Renaming of tabs and removal of social actions tab (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/570)
* [activity]: Ignore thirdparties when creating a social action via an activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/573)
* [parcours]: change wording of warning message and button when user is not associated to a household yet (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/590#note_918370943)
* [Accompanying period work evaluations] list documents associated to a work by creation date, and then by id, from the most recent to older
* [Course comment] add validationConstraint NotNull and NotBlank on comment content, to avoid sql error
* [Notifications] delay the sending of notificaiton to kernel.terminate
* [Notifications / Period user change] fix the sending of notification when user changes
* [Activity form] invert 'incoming' and 'receiving' in Activity form
* [Activity form] keep the same order for 'attendee' field in new and edit form
* [list with period] use "sameas" test operator to introduce requestor in list
* [notification email on course designation] allow raw string in email content generation
* [Accompanying period work] list evaluations associated to a work by startDate, and then by id, from the most recent to older
* [evaluation_document] changing date to datetime in order to display the time at which document was created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/569)
### 2021-04-13
* [person] household address: add a form for editing the validFrom date (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/541)
* [person] householdmemberseditor: fix composition type bug in select form (vuejs) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/543)
* [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
* [notifications] allow to send a notification to an email address. The address receive an access link
* [adresses] add constraints in database to avoid errors later: postcode not null, and validfrom <= validto
* [accompanying work editor] add a label on document title input
### 2021-04-07
* notification list: move action buttons outside of the toggle
* fix detecting of non-read notification
* filter users which are disabled in search user api
* order query for location and add pagination in list
* allow every person which has part for a workflow to see the workflow page
* able to see the workflow if the evaluation document has been deleted
* hardcode the list of supported mime types for edition with collabora
* list of accompanying course: allow to see the pinned comment in list_item
### 2021-04-06
* [main] notification toggle read: correct js syntax for compilation in production (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/548)
* [parcours] Display of interlocuteurs changed to flex-table in parcours edit page to prevent cut-off of information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/535)
* [activity] espace entre les boutons pour supprimer les documents
### continuous release in February and March
* Creation of PickCivilityType, and implementation in PersonType and ThirdpartyType
* [person] Accompanying course evaluation documents: disable the WOPI edit link if mimetype not supported and if no keyInfos
(https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/585)
* [activity] display error messages above the form in creating a new location (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/481)
* [activity] show required field in activity edit/new by an asterix in the vuejs fields (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/494)
* [ACL] fix allow to see the course, event if the scope'course does not contains the scope's user
* [search] enforce limit of results for fetching rsults by search api https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/576
* [activity] Fix delete button for document (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/554)
* [activity] Add return path the document generation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/553)
* [person] add person ressource to person docgen normaliser (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/517)
* [person] AccompanyingCourseWorkEdit: fix deleting evaluation documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/546)
* [person] AccompanyingCourseWorkEdit: download existing documents (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/512)
* [person] AccompanyingCourseWorkEdit: replace document by a new one (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/511)
* [person] AccompanyingPeriodWork: add referrers to work, add doctrine event listener to add logged user to referrers collection and display a referrers list in work list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/502)
* [person] AccompanyingPeriodWorkEvaluation: fix circular reference when serialising (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/495)
* [person] order accompanying period by opening date in search persons, person and household period lists (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/493)
* [parcours] autosave of the pinned comment for draft accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/477)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [main] filter user job in undispatch acc period to assign (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/472)
* [person] Add url in accompanying period work evaluations entity and form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/476)
* [person] Add document generation in admin and in person/{id}/document (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/464)
* [activity] do not override location if already exist (when validating new activity) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/470)
* [parcours] Toggle emergency/intensity only by referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
* [docstore] Add an API entrypoint for StoredObject (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add the possibility of uploading existing documents to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Add title to AccPeriodWorkEvaluationDocument (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/466)
* [person] Order social issues by the field "ordering" (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/388)
* [Person/Household list] when listing other simultaneous members of an household, exclude the members on person, not on members (avoid to show two membersship with the same person)
* [draft periods] add a delete button (if acl granted) on each draft period listed on draft period page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/463)
* [Person] Display suffixText in RenderPerson, PersonText.vue, RenderPersonBox.vue (was made for displaying "enfant confie") (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/441)
* [budget]: budget enabled for persons and households (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/469)
* [person] residential address: show residential address or info in PersonRenderBox, refactor Residential Address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/439)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [documents] Improve flex-table item-col placement when long buttons and long metadata
* [thirdparty] Fix display of multiple contact badges so they wrap onto next line (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/482)
* [confidential] Fix position of toggle button so it does not cover text nor fall outside of box (no issue)
* [parcours] Fix edit of both thirdparty and contact name (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/474)
* [template] do not list inactive templates (for doc generator)
* [household] bugfix if position of member is null, renderbox no longer throws an error (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/480)
* [parcours] location cannot be removed if linked to a user (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/478)
* [person] email added to twig personRenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/490)
* [activity] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
* [person] Add link to current household in person banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/484)
* [address] person badge in address history changed to open OnTheFly with all person info (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/489)
* [person] Change 'personne' with 'usager' and '&' with 'ET' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/499)
* [thirdparty] Add parameter condition to display centers or not (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/500)
* [phonenumber] Remove placeholder in phonenumber field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/496)
* [person_resource] separate create page created to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/504)
* [contact] add contact button color changed plus the pipe at the side removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/506)
* [thirdparty] For contacts show current civility/profession in edit form + fix saving of edited information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/491)
* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505)
* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486)
* [thirdparty] add firstname field to thirdparty 'child' or 'contact' types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/508)
* [household] create-edit household composition placed in separate page to avoid confusion (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/505)
* [blur] Improved positioning of toggle icon (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/486)
* [parcours] List of parcours for a specific user so they can be reassigned in case of absence (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/509)
* [thirdparty] Thirdparty view page, english text translated (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/534)
* [social_action] Translation changed in evaluation section (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/512)
* [filiation] Possible to add person (or create onthefly) to add to filiation graph + add relation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/519)
* [household] Within parcours listing page of household add create button (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/560)
* [person_resource] bugfix when adding thirdparty or freetext resource + prevent personOwner themselves to be added. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/526)
* [aside_activity] style correction + sticky-form create button (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/529)
* [budget] order within the menu adjusted (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/592)
* [onthefly] fix create person. Bug was noticed in filiation (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/591)
* [parcours] Create document buttons made sticky (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/532)
* [person] Trailing guillemet removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/530)
* [notification] Display of social action within workflow notification set to display block (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/537)
* [onthefly] trim trailing whitespace in email of person and thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/542)
* [action] Only youngest descendant is kept for social issues and actions (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/471)
## Test releases
### test release 2022-02-21
* [notifications] Word 'un' changed to number '1' for notifications in user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/483)
* [documents] 'gabarit' changed to 'modèle' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/405)
* [person_resources] Menu name and order changed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/460)
* workflow: fix sending notifications
* [thirdparty] Extend the thirdparty search to thirdparty children (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/448)
* [person]: AddPersons: allow creation of person or thirdparty only (no users) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [person]: AddPersons: allow creation of person or thirdparty depending on allowed types (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [person]: AddPersons: add suggestion of name when creating new person or thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/422)
* [main] Address: fix small bug: when modifying an address without street (isNoAddress), also check errors if street is an empty string as back-end change null value to empty string for street (and streetNumber)
* [main] Address: stronger client-side validation of addresses (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/449)
* [person] accompanying course: filter suggested entities by open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/415)
[activity] can click through the cross icon for removing person in concerned group (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
[activity] correct associated persons by considering only open participations (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/476)
* [person_resources]: Renderboxes used to display person/thirdparty info (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/465)
* [Household]: Add end date in HouseholdMember form for 'enfant hors menage' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/434)
* [homepage_widget]: If no sender then display as 'notification automatique' (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/435)
* [parcours]: Order social activities and only display most recent three in parcours resumé (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/481)
* [3party]: 3party: redirect to parent when contact (child) is opened in view page
* [parcours / addresses]: launch an event when a person change address (either through changing household or because the household is associated to a new address). If the person is localising a course, the course location go back to a temporarily address.
* [thirdparty]: address/phonenumber/email/fonction displayed in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/401)
* [thirdparty_contact]: in search results the 'qualité' is displayed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/465)
* [bug]: fix confidential toggle of address in thirdpartyrenderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/460)
### test release 2022-02-14
* AddPersons: remove ul-li html tags from AddPersons (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/419)
* [doc-generator] do not set required fields for mainPerson, person1, person2 (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement#456)
* [doc-generation] add age and obele in the mainPerson, person1 and person2 list + add obele in person renderString if addAge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/370)
* [person] accompanying course work: fix on-the-fly update of thirdParty
* fix normalisation of accompanying course requestor api (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/378)
* [person] add a returnPath when clicking on some Person or ThirdParty badge (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/427)
* [person] accompanying course work: fix on-the-fly update of thirdParty
* [on-the-fly] close modal only after validation
* [person] correct thirdparty PATCH url + add email and altnames in AddPerson and serializer (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/433)
* change order for accompanying course work list
* [parcours]: Mes parcours brouillon added to user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/440)
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
* [person]: style fix in parcours listing per person. (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/432)
* [parcours]: Only the referrer can toggle the intensity of the parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/442)
* [household]: display address of current household (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/415)
* ajoute un ordre dans les localisation (api)
* [pick entity]: fix translations in modal (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/419)
* [homepage_widget]: fix translation on emergency badge (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/440)
* [person]: create person and household added to button dropdown (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/454)
* display full address in address.text in normalization. Adapt AddressRenderBox
* [address]: Correction residential address 'depuis le' (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/459)
* [Documents]: List view adapted to display more information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/414)
* [Thirdparty_contact]: address blurred if confidential in view page (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/450)
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
### test release 2021-02-01
* renommer "dossier numéro" en "parcours numéro" dans les résultats de recherche
* renomme date de début en date d'ouverture dans le formulaire parcours
* [homepage widget] improve content tables, improve counter pluralization with style on number
* [notification lists] add comments counter information
* [workflows] fix popover header with previous transition
* [parcours]: validation + message for closing parcours adjusted.
* [household]: household composition double edit button replaced by a delete action (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/426)
[fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
[homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [person]: Comment on marital status is possible even if marital status is not defined (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/421)
* [parcours]: In the list of person results the requestor is not displayed if defined as anonymous (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/424)
* [bugfix]: modal closes and newly created person/thirdparty is selected when multiple persons/thirdparties are created through the modal (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/429)
* [person_resource]: Onthefly button added to view person/thirdparty and badge differentiation for a contact-thirdparty (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/428)
* [workflow][notification] improve how notifications and workflows are 'attached' to entities: contextual list, counter, buttons and vue modal
* [AddAddress] disable multiselect search, and rely only on most pertinent Cities and Street computed backend
* [fast_actions] improve fast-actions buttons override mechanism, fix https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/413
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
* [thirdparty] Add a contact to a thirdparty from within onTheFly (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/345)
* [homepage widget] add vue homepage_widget with asynchone loading, give a global view resume of the user concerned actions, notifications, etc.
### test release 2021-01-31
* [person] accompanying course: optimisation: do not fetch some resources for the banner (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/409)
* [person] accompanying course: close modal when edit participation (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [person] accompanying course: treat validation error when editing on-the-fly entities (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/420)
* [activity] show activity attendee (présence) in the activity list (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/412)
* [activity] admin: change validation rule for social action visible field (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/413)
* [parcours]: component added to change the opening date of a parcours (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/411)
* [search]: listing of parcours display changed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/410)
* [user]: page with accompanying periods to which is user is referent (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/408)
* [person] age added to renderstring + renderbox/ vue component created to display person text (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/389)
* [household member editor] allow to push to existing household
### test release 2021-01-28
* [person] improve filiations vis graph: disable physics, use chill colors for persons-households-course, increase label of relations, remove labels on household arrows and other improvements (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/286, https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/362)
* [activity] Order activity by date and by id (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/364)
* [main] increase length of 4 Address fields (change to TEXT, no size limits) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/277)
* [main] Add confidential option for address, in edit and view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/165)
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [person] Add residential address entity, form and list for each person
* [aside_activity]: dynamicUserPickerType used (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/399)
* dispatching list
### test release 2021-01-26
* [parcours] comments truncated if too long + link added (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/406)
* [person]: possibility to add person resources (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/382)
* [person ressources]: module added
### test release 2022-01-24
* [person] name suggestions within create person form when person is created departing from a search input (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/377)
* [notification: formulaire création] descend la box avec la description dans le bas du formulaire
* [notification for activity]: fix link to activity
* [notification] add "URGENT" before accompanying course with emergency = true
* [notification] add a "read more" button on system notification
* [notification] add `[Chill]` in the subject of each notification, automatically
* [notification] add a counter for notification in activity list and accompanying period list, and search results
* [parcours] bugfix if deathdate is not defined (eg. for a thirdparty) parcours is still displayed. Gave error before.
* [workflow] add breadcrumb to show steps
* [popover] add popover html popup mechanism (used by workflow breadcrumb)
* [templates] improve updatedBy macro in item metadatas
* [parcours]: bug fix when comment is pinned all other comments remain in the collection (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/385)
* [workflow]
* add My workflow section with my opened subscriptions
* apply workflow on documents, accompanyingCourseWork and Evaluations
* [wopi-link] a new vue component allow to open wopi link in a fullscreen chill-themed modal
### test release 2022-01-19
* vuejs: add dead information on all on-the-fly person render boxes, in vis graph and other templates (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/271)
* [thirdparty] fix bug in 3rd party view: types was replaced by thirdPartyTypes
* [main] location form type: fix unmapped address field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/246)
* [activity] fix wrong import of js assets for adding and viewing documents in activity (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/83 & https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/176)
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
* [forms] dynamic picker types for user/person/thirdparty types created (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/386)
### test release 2022-01-17
* [main] Add editableByUser field to locationType entity, adapt the admin template and add this condition in the location-type endpoint (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/297)
* [main] Add mainLocation field to User entity and add it in user form type
* rewrite page which allow to select activity
* [main] Add mainLocation field to User entity and add it in user form type
* [course list in person context] show full username/label for ref
* [accompanying period work] remove the possibility to generate document from an accompanying period work
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
## Test releases
* vuejs: add validation on required fields for AddPerson, Address and Location components
* vuejs: treat 422 validation errors in locations and AddPerson components
* [person]: space added between deathdate and age in twig renderbox (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/380)
### test release 2022-01-12
* fix thirdparty normalizer on telephone field: https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/322
### test release 2022-01-11
* vuejs: translate in French all multiselect widgets
* [address] define address lines according postal standards for France and Belgium (default) and change AddressRender, chill_entity_render_box and AddressRenderBox.vue
* [household] change translations (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [household] add address i18n in household component (champs-libres/departement-de-la-vendee/accent-suivi-developpement#158)
* [household] add on the fly i18n in household component
* [household] redirect to the household page when a household is created from a person (champs-libres/departement-de-la-vendee/accent-suivi-developpement#175)
* [household] household member editor: display alert if some members have already an household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#172)
* [household] household member editor: do not add in new members if the member is included in the members of household (champs-libres/departement-de-la-vendee/accent-suivi-developpement#123)
* [household] household member editor: remove markNoAddress button (champs-libres/departement-de-la-vendee/accent-suivi-developpement#109)
* [person]: ordering fields in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* [person]: Add email and alt names in add person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/61)
* [accompanyingCourse] Add a delete action and delete buttons to delete a accompanying course when step = DRAFT (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/64)
* [accompanyingCourse] Add a administrative location in the accompanying course, set the user current location as default, allow to select a location in a select field and do not allow to confirm the accompanying course if location is empty.
* [accompanyingCourse] Add the administrative location in the available variables for document generation
* AddAddress: optimize loading: wait for the user finish typing;
* UserPicker: fix bug with deprecated role
* docgen: add base context + tests
* docgen: add age for person
* [household menu] fix filiation order https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/265
* [AddAddress]: optimize loading: wait for the user finish typing;
* [UserPicker]: fix bug with deprecated role
* [docgen]: add base context + tests
* [docgen]: add age for person
* [task]: fix dropdown menu style + fix bug in singleTaskController (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/338)
* Household: fix bug when moving person on the same day (see https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/281)
* Household: show date validFrom and validTo when moving
* address reference: add index for refid
* [accompanyingCourse_work] fix styles conflicts + fix bug with remove goal (remove goals one at a time)
* [accompanyingCourse] improve masonry on resume page, add origin
* [notification] new notification interface, can be associated to AccompanyingCourse/Period, Activities.
* List notifications, show, and comment in User section
* Notify button and contextual notification box on associated objects pages
* [accompanyingCourse] add a comment for each resource associated. A modal allow to save comment. Comment is displayed in on-the-fly show modal of the accompanyingCourse context (edit page + resume page).
### test release 2021-12-14
* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262)
* [vendee/person] fix typo "situation professionelle" => "situation professionnelle"
* [main] add availableForUsers condition from locationType in the location API endpoint (champs-libres/departement-de-la-vendee/accent-suivi-developpement#248)
* [main] add the current location of the user as API point + add it in the activity location list (champs-libres/departement-de-la-vendee/accent-suivi-developpement#247)
* [activity] improve show/new/edit templates, fix SEE and SEE_DETAILS acl
* [badges] create specific badge for TMS, and make person/thirdparty badges clickable with on-the-fly modal in :
* concerned groups items (activity, calendar)
* accompanyingCourseWork lists
* accompanyingCourse lists
* [acompanyingCourse] add initial comment on Resume page
* [person] create button full width (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/330)
### test release 2021-12-11
* [main] add order field to civility
* [main] change address format in case the country is France, in Address render box and address normalizer
* [person] add validator for accompanying period with a test on social issues (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/76)
* [activity] fix visibility for location
* [origin] fix origin: use correctly the translatable strings
* /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');`
* [person] redirect bug fixed.
* [action] add an unrelated issue within action creation.
* [origin] fix origin: use correctly the translatable strings
* /!\ everyone must update the origin table. As there is only one row, execute `update chill_person_accompanying_period_origin set label = jsonb_build_object('fr', 'appel téléphonique');`
* [main] change order of civilities in civility fixtures (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] set min attr in the minimum of children field (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] add marital status date in person view (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] show number of children + allow set number of children to null (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] show acceptSMS option (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [person] add death information in person render box in twig and vue render boxes (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/191)
* [asideactivity] creation of aside activity category fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/262)
* [vendee/person] fix typo "situation professionelle" => "situation professionnelle"
* [accompanyingcourse_work] Changes in layout/behavior of edit form (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/321)
* [badge-entity] design coherency between pills badge-person and 3 kinds of badge-thirdparty
* [AddPersons] suggestions row are clickable, not only checkbox
### test release 2021-12-06
* [main] address: use search API end points for getting postal code and reference address (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316)
* [main] address: in edit mode, select the encoded values in multiselect for address reference and city (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/316)
* [person search] fix bug when using birthdate after and birthdate before
* [person search] increase pertinence when lastname begins with search pattern
* [activity/actions] Améliore la cohérence du design entre
* la page résumé d'un parcours (liste d'actions récentes et liste d'activités récentes)
* la page liste des actions
* la page liste des activités (contexte personne / contexte parcours)
* [household] field to edit wheter person is titulaire of household or not removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/322)
* [activity] create work if a work with same social action is not associated to the activity
* [visgraph] improve and fix bugs on vis-network relationship graph
* [bugfix] posting of birth- and deathdate through api fixed.
* [suggestions] improve suggestions lists
### Test release 2021-11-19 - bis
* [household] do not allow to create two addresses on the same date
* [activity] handle case when there is no social action associated to social issue
* [activity] layout for issues / actions
* [activity][bugfix] in edit mode, the form will now load the social action list
### Test release 2021-11-29
* [person] suggest entities (person | thirdparty) when creating/editing the accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/119)
* [activity] add custom validation on the Activity class, based on what is required from the ActivityType (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/188)
* [main] translate multiselect messages when selecting/creating address
* [main] set the coordinates of the city when creating a new address OR choosing "pas d'adresse complète"
* Use the user.label in accompanying course banner, instead of username;
* fix: show validation message when closing accompanying course;
* [thirdparty] link from modal to thirdparty detail page fixed (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/228)
* [assets] new asset to style suggestions lists (with add/remove item link) (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/258)
* [accompanyingCourseWorkEdit] improves hyphenation and line breaks for long badges
* [acompanyingCourse] improve Resume page
* complete all needed informations,
* actions and activities are clickables,
* better placement with js masonry blocks on top of content area,
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/101
* https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/295
* [activity/calendar] on show page, concerned groups of persons table adapt itself to isVisibles options
* [activity] remove the "plus" button in activity list
* [activity] check ACL on activity list in person context
* [list for accompanying course in person] filter list using ACL
* [validation] toasts are displayed for errors when modifying accompanying course (generalization required).
* [period] only the user can enable confidentiality
* add an endpoint for checking permissions. See https://gitlab.com/Chill-Projet/chill-bundles/-/merge_requests/232
* [activity] for a new activity: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
* [calendar] for a new rdv: suggest and create on-the-fly locations based on the accompanying course location + location of the suggested parties
* [period] Validation added when period is confidential and confirmed -> user cannot be null.
## Test releases
### Test release 2021-11-22
* [activity] delete admin_user_show in twig template because this route is not defined and should be defined
* [activity] suggest requestor, user and ressources for adding persons|user|3rdparty
* [calendar] suggest persons, professionals and invites for adding persons|3rdparty|user
* [activity] take into account the restrictions on person|thirdparties|users visibilities defined in ActivityType
* [main] Add currentLocation to the User entity + add a page for selecting this location + add in the user menu (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
* [activity] add user current location as default location for a new activity (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/133)
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
* remove "search by phone configuration option": search by phone is now executed by default
* remplacer le classement par ordre alphabétique par un classement par ordre de pertinence, qui tient compte:
* de la présence d'une string avec le nom de la ville;
* de la similarité;
* du fait que la recherche commence par une partie du mot recherché
* ajouter la recherche par numéro de téléphone directement dans la barre de recherche et dans le formulaire recherche avancée;
* ajouter la recherche par date de naissance directement dans la barre de recherche;
* ajouter la recherche par ville dans la recherche avancée
* ajouter un lien vers le ménage dans les résultats de recherche
* ajouter l'id du parcours dans les résultats de recherche
* ajouter le demandeur dans les résultats de recherche
* ajout d'un bouton "recherche avancée" sur la page d'accueil
* [person] create an accompanying course: add client-side validation if no origin (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/210)
* [person] fix bounds for computing current person address: the new address appears immediatly
* [docgen] create a normalizer and serializer for normalization on doc format
* [person normalization] the key center is now "centers" and is an array. Empty array if no center
* [accompanyingCourse] Ability to close accompanying course (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/296)
* [task] Select2 field in task form to allow search for a user (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/167)
* [list result] show all courses, except ones with period closed
* [accompanyingCourse] improve banner with small carousel to display slide social-issues or slide associated persons (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/69)
### Test release 2021-11-15
* [main] fix adding multiple AddresseDeRelais (combine PickAddressType with ChillCollection)
* [person]: do not suggest the current household of the person (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/51)
* [person]: display other phone numbers in view + add message in case no others phone numbers (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/184)
* unnecessary whitespace removed from person banner after person-id + double parentheses removed (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/290)
* [person]: delete accompanying period work, including related objects (cascade) (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/36)
* [address]: Display of incomplete address adjusted.
* [household]: improve relationship graph
* add form to create/edit/delete relationship link,
* improve graph refresh mechanism
* add feature to export canvas as image (png)
* [person suggest] In widget "add person", improve the pertinence of persons when one of the names starts with the pattern;
* [person] do not ask for center any more on person creation
* [3party] do not ask for center any more on 3party creation
## Test releases
### Test release 2021-11-08
* [person]: Display the name of a user when searching after a User (TMS)
* [person]: Add civility to the person
* [person]: Various improvements on the edit person form
* [person]: Set available_languages and available_countries as parameters for use in the edit person form
* [activity] Bugfix: documents can now be added to an activity.
* [tasks] improve tasks with filter order
* [tasks] refactor singleControllerTasks: limit the number of conditions from the context
* [validations] validation of accompanying period added: no duplicate participations or resources (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/60).
* [renderbox] If gender of person is not defined, no icon is displayed instead of neuter-icon (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/129).
* [confidential information] module added to blur confidential information (https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/248).
* refactor `AuthorizationHelper` and `UserACLAwareRepository` to fix constructor, and separate logic for parent role helper into `ParentRoleHelper`
* [main]: filter location and locationType in backend: exclude NULL names, only active and availableToUsers
* [activity]: perform client-side validation & show/hide fields in the "new location" modal
* [person]: normalize person with CenterResolverDispatcher and handle case where center is null or multiple in PersonRenderBox
* [docstore] voter for PersonDocument and AccompanyingCourseDocument on the 2.0 way (using VoterHelperFactory)
* [docstore] add authorization check inside controller and menu
* [activity]: fix inheritance for role `ACTIVITY FULL` and add missing acl in menu
* [person] show current address in search results
* [person] show alt names in search results
* [admin]: links to activity admin section added again.
* [household]: endDate field deleted from household edit form.
* [household]: View accompanying periods of current and old household members.
* [tasks]: different layout for task list / my tasks, and fix link to tasks in alert or in warning
* [admin]: links to activity admin section added again.
* [household]: household addresses ordered by ValidFrom date and by id to show the last created address on top.
* [socialWorkAction]: display of social issue and parent issues + banner context added.
* [DBAL dependencies] Upgrade to DBAL 3.1
### Test release 2021-10-27
* [person]: delete double actions buttons on search person page
* [person]: accompanying course work: remove creation date display the list of work + handle case when end date is null
* [main]: Add new pages with a menu for managing location and location type in the admin
* [main]: Add some fixtures for location type
* [calendar]: Pass the location when transforming a calendar item (rdv) into an activity
* [calendar]: Add a user menu for "my calendar"
### Test release 2021-10-18
* [3party]: french translation of contact and company
* [3party]: show parent in list
* [3party]: change color for badge "child"
* [3party]: fix address creation
* [household members editor] finalisation of editor
* [AccompanyingCourse banner]: replace translation referrer (https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/70)
* [Location]: add location system in activity and RV (calendar). User can choose in location list or create a new location.
* [household]: add relationship page with dynamic data visualisation graph
## Test releases
### Test release 2021-10-11
* Address: zoom on postal code geometry + fix origin of manually entered postal code
* in the Address vue component, order the postal code and street address by alphabetic and numeric order
* add 3 new fields to PostalCode and adapt postal code command and fixtures
* [Aside activity] Fixes for aside activity
* categories with child
* fast creation buttons
* add ordering for types
* [AccompanyingCourse Resume page] dashboard for AccompanyingCourseWork and for Activities;
* Improve badges behaviour with small screens;
* [ThirdParty]:
* third party list
* create a kind contact/institution when create a new thirdparty, and set contact embedded as kind=child;
* filter thirdparties in list
* [FilterOrder]: add development kit for generating filter and ordering in list
* [Capitalization of names] person names are capitalized on creation, on prePersist event
* [On-The-Fly] modale works for showing, editing and creating person or thirdparty ;
* [AccompanyingCourse Resume page] associated persons list, can see household when hover, and with show on-the-fly modale when clicking person ;
### test release 2021-10-04
* [Household editor][UI] Update how household suggestion and addresses are picked;
See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/80
* [AddAddress] Handle address suggestion;
* [CenterType][Create a person] when overriding the ACL rules, allow to show a PickCenterType
when no centers are reachable by the default ACL.
* [Household] Show comment event if no address are associated with the household;
* [Person results] Add requestor into search results:
* a badge "requestor" is shown into search results;
* periods where the person is only requestor (without participating) are also shown;
Issues:
* https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/13
* https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/199
* [Person form] "accept sms" not required:
https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/37
https://gitlab.com/champs-libres/departement-de-la-vendee/chill/-/issues/221
* [Household editor] suggest only temporarily addresses;
See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/82
* On-The-Fly modale works for showing, editing and creating person and thirdparty ;
* AccompanyingCourse Resume page: list associated persons by household, see household when hover, and show on-the-fly modale when clicking on person ;
* [AddAddress] Handle address suggestion;
* [AddAddress][Entity address]: add a link between address and address reference;
* [Household editor] suggest household by comparing the temporary addresses from courses;
See https://gitlab.com/champs-libres/departement-de-la-vendee/accent-suivi-developpement/-/issues/81
* On-The-Fly modale works for showing, editing and creating person and thirdparty
## Test released
<!--
Coming soon...
DO NOT ADD unreleased items here. Add them under "Unreleased" title
### Test release yyyy-mm-dd
-->
## Stable releases
No stable releases for v2+

17
.changes/v2.1.0.md Normal file
View File

@ -0,0 +1,17 @@
## v2.1.0 - 2023-06-12
### Feature
* [docgen] allow to pick a third party when generating a document in context Activity, AccompanyingPeriod
### Fixed
* ([#111](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/111)) List of "my accompanying periods": separate the active and closed periods in two different lists, and show the inactive_long and inactive_short periods
### Security
* ([#105](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/105)) Rights are checked for display of 'accompanying period' tab in household menu. Rights are also checked for creation of 'accompanying period' from within household context
### DX
* Add methods to RegroupmentRepository and fullfill Center / Regroupment Doctrine mapping

12
.changes/v2.2.0.md Normal file
View File

@ -0,0 +1,12 @@
## v2.2.0 - 2023-06-18
### Feature
* When navigating from a workflow regarding to an evaluation's document to an accompanying course, scroll directly to the document, and blink to highlight this document
* Add notification to accompanying period work and work's evaluation's documents
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[Export] Filter accompanying period by step at date: allow to pick multiple steps
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[export] add a filter on accompanying period: filter by step between two dates
### Fixed
* use the correct annotation for the association between PersonCurrentCenter and Person
* ([#58](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/58))Fix birthdate timezone in PersonRenderBox
* ([#55](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/55))Fix the notification counter
### DX
* DQL function OVERLAPSI: simplify expression in postgresql

3
.changes/v2.2.1.md Normal file
View File

@ -0,0 +1,3 @@
## v2.2.1 - 2023-06-19
### Fixed
* ([#114](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/114)) [notification on document evaluation] fix entityId and return path when adding a notification on a document in an evaluation

5
.changes/v2.2.2.md Normal file
View File

@ -0,0 +1,5 @@
## v2.2.2 - 2023-06-26
### Fixed
* [Accompanying period comments]: order comments from the most recent to the oldest, in the list
* Api: filter social action to keep only the currently activated
* ([#82](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/82)) Fix deletion and re-creation of filiation relationship

42
.changes/v2.3.0.md Normal file
View File

@ -0,0 +1,42 @@
## v2.3.0 - 2023-06-27
### Feature
* ([#110](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/110)) Edit saved exports options: the saved exports options (forms, filters, aggregators) are now editable.
* ([#103](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/103)) Get an unified list of document in person and accompanying period context
* [export] Set the default date of calculation of the accompanying period's list as "today"
* Force accompanying period user history to be unique for the same period and stardate/enddate [:warning: may encounter migration issue]
If some issue is encountered during migration, use this SQL to find the line which are in conflict, examine the problem and delete some of the concerning line
*
```sql
-- to see the line which are in conflict with another one
SELECT o.*
FROM chill_person_accompanying_period_user_history o
JOIN chill_person_accompanying_period_user_history c ON o.id < c.id AND o.accompanyingperiod_id = c.accompanyingperiod_id
WHERE tsrange(o.startdate, o.enddate, '[)') && tsrange(c.startdate, c.enddate, '[)')
ORDER BY accompanyingperiod_id;
-- to examine line in conflict for a given accompanyingperiod_id (given by the previous query)
SELECT * FROM chill_person_accompanying_period_user_history WHERE accompanyingperiod_id = IIIIDDDD order by startdate, enddate;
```
* Rename label of filter in French: "parcours actif" => "parcours ouvert", and "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
### Traduction francophone des principaux changements
* Les exports enregistrés sont éditables par l'utilisateur;
* L'onglet "Document" dans les parcours et les dossiers d'usager affiche désormais les documents ajoutés à différents endroits.
Pour les parcours, il s'agit de:
- documents ajoutés directement dans le parcours;
- documents des échanges;
- documents des rendez-vous;
- documents des évaluations;
- documents directement ajoutés dans le dossier des usagers concernés par le parcours;
Pour les usagers, il s'agit de:
- documents des échanges;
- documents des parcours;
- documents des rendez-vous;
- documents des actions, des échanges, des rendez-vous, des évaluations ajoutés dans les parcours.
* Dans la liste des parcours, la date de calcul des éléments associés est "aujourd'hui" par défaut.
* Dans les exports, renommage des libellés des filtres: "parcours actif" => "parcours ouvert", et "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"

36
.changes/v2.4.0.md Normal file
View File

@ -0,0 +1,36 @@
## v2.4.0 - 2023-07-07
### Feature
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] on "filter by user working" on accompanying period, add two dates to filters intervention within a period
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add an aggregator by user's job working on a course
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add an aggregator by user's scope working on a course
* [export] on aggregator "user working on a course"
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a center aggregator for Person
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a filter on "job working on a course"
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add a filter on "scope working on a course"
* ([#121](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/121)) Create a role "See Confidential Periods", separated from the "Reassign courses" role
* ([#124](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/124)) Sync user absence / presence through microsoft outlook / graph api.
### Fixed
* ([#116](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/116)) On the accompanying course page, open the action on view mode if the user does not have right to update them (i.e. if the accompanying period is closed)
* [export] Rename label for CurrentActionFilter (on accompanying period work) to make precision between "ouvert" and "sans date de fin"
* Force the db to have either a person_location or a address_location, and avoid to have both also internally in the entity
* [export] set rolling date on person age aggregator
* [export] fix list when a person locating a course is without address
* [export] remove unused condition on course about duration participation
* Command to subscribe on MS Graph users calendars: improve the loop to be more efficient
### DX
* Rolling Date: can receive a null parameter
### Traduction francophone des principaux changements
- sur le "filtre par intervenant", ajoute deux dates pour limiter la période d'intervention;
- ajout d'un regroupement par métier des intervenants sur un parcours;
- ajout d'un regroupement par service des intervenants sur un parcours;
- ajout d'un regroupement par utilisateur intervenant sur un parcours
- ajout d'un regroupement "par centre de l'usager";
- ajout d'un filtre "par métier intervenant sur un parcours";
- ajout d'un filtre "par service intervenant sur un parcours";
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
- synchronisation de l'absence des utilisateurs par microsoft graph api

39
.changes/v2.5.0.md Normal file
View File

@ -0,0 +1,39 @@
## v2.5.0 - 2023-07-14
### Feature
* Allow filtering on the basis of a user within general tasks lists
* ([#120](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/120)) Adding OrderFilter to the list of social actions.
* ([#125](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/125)) [export] Add a list for people with their associated course
* [export] Add ordering by person's lastname or course opening date in list which concerns accompanying course or peoples
* ([#128](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/128)) [Export] allow to group activities by localisation
* ([#129](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/129)) [export] Add a filter "filter course having an activity between two dates"
* ([#112](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/112)) [addresses] Add a cronjob to re-associate addresses with addresses reference every 6 hours
* Improve filtering layout
### Fixed
* reimplement the visualization of all calculator results
* ([#117](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/117)) Repair my unread notification list with actions and evaluations documents
* ([#126](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/126)) Correct bug in thirdparty API search query: simplify address joins clause for child and parent kind
### DX
* Documentation for database principles
* [cronjob] when a cronjob is executed, it may return an array of data that will be passed as argument on the next execution
### UX
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Better integration of address details button: look, position, title tag
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Show address detail button on person and household banners
* Improve residential address position on show onthefly modale
### Traduction francophone des principaux changements
* Ajout d'un filtre "par utilisateur" aux pages de tâche
* Filtre des actions d'accompagnement par date, type, intervenant
* export: liste des usagers concernés avec détail de leurs parcours
* export: ajout d'un regroupement des échanges par localisation
* export: ajout d'un filtre "parcours ayant reçu un échange entre deux dates"
* ajout d'une tâche cron pour associer les adresses à une adresse de référence
* correction: réparation de la liste des notifications sur la page d'accueil, dans le cas où une notification concerne une action ou un document dans une évaluation
* correction: réparation de la recherche des tiers ayant des codes postaux similaires entre les parents et enfants
* meilleure intégration du bouton "détail d'une adresse": améliration de la taille et de la position
* bouton permettant de visualiser les détails d'une adresse (modale avec carte) dans la bannière "Usager" et "Ménage"
* amélioration de la modale permettant de voir les détails d'un usager: les adresses de résidence sont dans la continuité des autres adresses, et non plus dans une colonne séparée
* améliore le design et l'expérience utilisateur des filtres

3
.changes/v2.5.1.md Normal file
View File

@ -0,0 +1,3 @@
## v2.5.1 - 2023-07-14
### Fixed
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match

3
.changes/v2.5.2.md Normal file
View File

@ -0,0 +1,3 @@
## v2.5.2 - 2023-07-15
### Fixed
* [Collate Address] when updating address point, do not use the point's address reference if the similarity is below the requirement for associating the address reference and the address (it uses the postcode's center instead)

3
.changes/v2.5.3.md Normal file
View File

@ -0,0 +1,3 @@
## v2.5.3 - 2023-07-20
### Fixed
* ([#132](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/132)) Rendez-vous documents created would appear in all documents lists of all persons with an accompanying period. Or statements are now added to the where clause to filter out documents that come from unrelated accompanying period/ or person rendez-vous.

21
.changes/v2.6.0.md Normal file
View File

@ -0,0 +1,21 @@
## v2.6.0 - 2023-09-14
### Feature
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Add locations in Aside Activity. By default, suggest user location, otherwise a select with all locations.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Adapt Aside Activity exports: display location, filter by location, group by location
* Use the CRUD controller for center entity + add the isActive property to be able to mask instances of Center that are no longer in use.
### Fixed
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) reinstate the fusion of duplicate persons
* Missing translation in Work Actions exports
* Reimplement the mission type filter on tasks, only for instances that have a config parameter indicating true for this.
* ([#135](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/135)) Corrects a typing error in 2 filters, which caused an
error when trying to reedit a saved export
* ([#136](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/136)) [household] when moving a person to a sharing position to a not-sharing position on the same household on the same date, remove the previous household membership on the same household. This fix duplicate member.
* Add missing translation for comment field placeholder in repositionning household editor.
* Do not send an email to creator twice when adding a comment to a notification
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) Fix gestion doublon functionality to work with chill bundles v2
### UX
* Uniformize badge-person in household banner (background, size)

3
.changes/v2.6.1.md Normal file
View File

@ -0,0 +1,3 @@
## v2.6.1 - 2023-09-14
### Fixed
* Filter out active centers in exports, which uses a different PickCenterType.

3
.changes/v2.6.2.md Normal file
View File

@ -0,0 +1,3 @@
## v2.6.2 - 2023-09-18
### Fixed
* Fix doctrine mapping of AbstractTaskPlaceEvent and SingleTaskPlaceEvent: id property moved.

4
.changes/v2.6.3.md Normal file
View File

@ -0,0 +1,4 @@
## v2.6.3 - 2023-09-19
### Fixed
* Remove id property from document
mappedsuperclass

6
.changes/v2.7.0.md Normal file
View File

@ -0,0 +1,6 @@
## v2.7.0 - 2023-09-27
### Feature
* ([#155](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/155)) The regulation list load accompanying periods by exact postal code (address associated with postal code), and not by the content of the postal code (postal code with same code's string)
### Fixed
* ([#142](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/142)) Fix the label of filter ActivityTypeFilter to a more obvious one
* ([#140](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/140)) [export] Fix association of filter "filter location by type" which did not appears on "list of activities"

19
.changes/v2.8.0.md Normal file
View File

@ -0,0 +1,19 @@
## v2.8.0 - 2023-10-05
### Feature
* ([#162](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/162)) Reassigning list: when reassigning courses to a new user, the job associated with the course become the one of the new user (if any)
* Reassining list: the length of the list is increased to 100 courses
### Fixed
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) Fix filter "accompanying course by social action" to avoid duplication in list
* ([#164](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/164)) View a third party: avoid errors when a contact has a civility
* ([#163](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/163)) Fix the filters and aggregators on exports "count peoples"
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) From the database, avoid the creation of location history for same period and at same dates
### Traduction francophone des principaux changements
- Fonctionnalité: Réassigner les parcours en lot: lorsque des parcours sont réassignés "en lot", les parcours sont maintenant associés au métier du nouveau référent;
- Correction: certaines causes qui créaient des doublons dans les listes ont été corrigées;
- Correction des associations entre l'export "nombre de personnes" et les filtres et regroupements associés

23
.changes/v2.9.0.md Normal file
View File

@ -0,0 +1,23 @@
## v2.9.0 - 2023-10-17
### Feature
* ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)) Add history to scopes and to jobs in administrator section. When user job or main scope of user is changed, automaticaly add a new row in history.
* ([#146](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/146)) Allow closing motives to be identified as 'canceling the accompanying period' + don't take canceled accompanying periods into account
* [export] add an aggregator for activities: group by job scope's creator aggregator
* DX: prepare the code for the upgrade to symfony 5.4
### Traductions francophones des principaux changements
- ajout de l'historique des services et métiers pour les utilisateurs. Les exports, filtres et regroupements sont adaptés pour tenir compte du métier et du service
de l'utilisateur au moment de l'échange, de sa désignation comme agent traitant de l'échange ou du moment du rendez-vous ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)))
- modification des motifs de cloture des parcours: ajout d'un chanmp "annule le parcours", qui permet d'indiquer que le motif "annule" le parcours. Les parcours annulés n'apparaissent
pas dans les statistiques
- ajouter d'un regroupement pour les échanges: grouper par métier et service du créateur de l'échange
### Possible BC break in configuration
This release remove the use of deprecated package [symfony/templating](https://symfony.com/components/Templating).
If you use this package in your own bundle (usually `src/` directory, or other dependencies), you should add this dependencies in your local composer.json (`composer require symfony/templating`).
But if you do not need this any more, you must ensure that the configuration key `framework.templating` is removed. This is usually located into `config/packages/framework.yaml`. [See here an example](https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic/commit/cc716beaecc239e6a189f3db62ea95f169a37505#diff-df607fe73ff82c569824a7392edf5e760e998efe)

3
.changes/v2.9.1.md Normal file
View File

@ -0,0 +1,3 @@
## v2.9.1 - 2023-10-17
### Fixed
* Fix the handling of activity form when editing or creating an activity in an accompanying period with multiple centers

3
.changes/v2.9.2.md Normal file
View File

@ -0,0 +1,3 @@
## v2.9.2 - 2023-10-17
### Fixed
* Fix possible null values in string's entities

39
.changie.yaml Normal file
View File

@ -0,0 +1,39 @@
changesDir: .changes
unreleasedDir: unreleased
headerPath: header.tpl.md
changelogPath: CHANGELOG.md
versionExt: md
versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
kindFormat: '### {{.Kind}}'
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
changeFormat: >-
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}} {{ if and (.Custom.Long) (not (eq .Custom.Long "")) }}
{{ .Custom.Long }}{{ end }}
custom:
- key: Issue
label: Issue number (on chill-bundles repository) (optional)
optional: true
type: int
minInt: 1
body:
# allow multiline messages
block: true
kinds:
- label: Feature
auto: minor
- label: Deprecated
auto: minor
- label: Fixed
auto: patch
- label: Security
auto: patch
- label: DX
auto: patch
- label: UX
auto: patch
newlines:
afterChangelogHeader: 1
beforeChangelogVersion: 1
endOfVersion: 1
envPrefix: CHANGIE_

View File

@ -3,3 +3,39 @@
# Run tests from root to adapt your own environment
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
ADMIN_PASSWORD=password
LOCALE=fr
REDIS_URL=redis
REDIS_PORT=6379
REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=2a30f6ba26521a2613821da35f28386e
TWILIO_SID=~
TWILIO_SECRET=~
DEFAULT_CARRIER_CODE=BE
ADD_ADDRESS_DEFAULT_COUNTRY=BE
ADD_ADDRESS_MAP_CENTER_X=50.8443
ADD_ADDRESS_MAP_CENTER_Y=4.3523
ADD_ADDRESS_MAP_CENTER_Z=15
SHORT_MESSAGE_DSN=null://null
MESSENGER_TRANSPORT_DSN=sync://
###< symfony/messenger ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
DATABASE_URL="postgresql://postgres:postgres@db:5432/test?serverVersion=14&charset=utf8"
###< doctrine/doctrine-bundle ###
ASYNC_UPLOAD_TEMP_URL_KEY=
ASYNC_UPLOAD_TEMP_URL_BASE_PATH=
ASYNC_UPLOAD_TEMP_URL_CONTAINER=

View File

@ -3,7 +3,7 @@
# Select what we should cache between builds
cache:
paths:
- tests/app/vendor/
- /vendor/
- .cache
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
@ -23,17 +23,16 @@ variables:
# configure database access
DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres?serverVersion=14&charset=utf8
# fetch the chill-app using git submodules
GIT_SUBMODULE_STRATEGY: recursive
# GIT_SUBMODULE_STRATEGY: recursive
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_URL: redis://redis:6379
# change vendor dir to make the app install into tests/apps
COMPOSER_VENDOR_DIR: tests/app/vendor
DEFAULT_CARRIER_CODE: BE
stages:
- Composer install
- Tests
- Deploy
build:
stage: Composer install
@ -49,7 +48,7 @@ build:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
- vendor/
code_style:
stage: Tests
@ -63,7 +62,7 @@ code_style:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
- vendor/
phpstan_tests:
stage: Tests
@ -77,13 +76,14 @@ phpstan_tests:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
- vendor/
rector_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
script:
- bin/rector --dry-run
- tests/console cache:clear
- bin/rector process --dry-run
cache:
paths:
- .cache/
@ -91,7 +91,7 @@ rector_tests:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
- vendor/
# psalm_tests:
# stage: Tests
@ -108,16 +108,25 @@ rector_tests:
unit_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
# until we fix testes
allow_failure: true
script:
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- php -d memory_limit=4G bin/phpunit --colors=never
- php tests/console doctrine:migrations:migrate -n --env=test
- php tests/console chill:db:sync-views --env=test
- php -d memory_limit=2G tests/console cache:clear --env=test
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
- vendor/
release:
stage: Deploy
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
script:
- echo "running release_job"
release:
tag_name: '$CI_COMMIT_TAG'
description: "./.changes/v$CI_COMMIT_TAG.md"

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "_exts/sphinx-php"]
path = _exts/sphinx-php
url = https://github.com/fabpot/sphinx-php.git
[submodule "tests/app"]
path = tests/app
url = https://gitlab.com/Chill-projet/chill-app.git

View File

@ -13,6 +13,7 @@ $finder = PhpCsFixer\Finder::create();
$finder
->in(__DIR__.'/src')
->in(__DIR__.'/utils')
->append([__FILE__])
->exclude(['docs/', 'tests/app'])
->notPath('tests/app')
@ -90,7 +91,7 @@ $rules = array_merge(
[
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => false,
'@Symfony' => false,
'@Symfony' => true,
'@Symfony:risky' => false,
'ordered_class_elements' => [
'order' => [
@ -110,7 +111,7 @@ $rules = array_merge(
'method_private',
],
'sort_algorithm' => 'alpha',
]
],
],
$rules,
$riskyRules,

View File

@ -1,16 +1,286 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
* [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for stable releases;
* date versioning for test releases
## Unreleased
## v2.9.2 - 2023-10-17
### Fixed
* Fix possible null values in string's entities
## v2.9.1 - 2023-10-17
### Fixed
* Fix the handling of activity form when editing or creating an activity in an accompanying period with multiple centers
## v2.9.0 - 2023-10-17
### Feature
* ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)) Add history to scopes and to jobs in administrator section. When user job or main scope of user is changed, automaticaly add a new row in history.
* ([#146](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/146)) Allow closing motives to be identified as 'canceling the accompanying period' + don't take canceled accompanying periods into account
* [export] add an aggregator for activities: group by job scope's creator aggregator
* DX: prepare the code for the upgrade to symfony 5.4
### Traductions francophones des principaux changements
- ajout de l'historique des services et métiers pour les utilisateurs. Les exports, filtres et regroupements sont adaptés pour tenir compte du métier et du service
de l'utilisateur au moment de l'échange, de sa désignation comme agent traitant de l'échange ou du moment du rendez-vous ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)))
- modification des motifs de cloture des parcours: ajout d'un chanmp "annule le parcours", qui permet d'indiquer que le motif "annule" le parcours. Les parcours annulés n'apparaissent
pas dans les statistiques
- ajouter d'un regroupement pour les échanges: grouper par métier et service du créateur de l'échange
### Possible BC break in configuration
This release remove the use of deprecated package [symfony/templating](https://symfony.com/components/Templating).
If you use this package in your own bundle (usually `src/` directory, or other dependencies), you should add this dependencies in your local composer.json (`composer require symfony/templating`).
But if you do not need this any more, you must ensure that the configuration key `framework.templating` is removed. This is usually located into `config/packages/framework.yaml`. [See here an example](https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic/commit/cc716beaecc239e6a189f3db62ea95f169a37505#diff-df607fe73ff82c569824a7392edf5e760e998efe)
## v2.8.0 - 2023-10-05
### Feature
* ([#162](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/162)) Reassigning list: when reassigning courses to a new user, the job associated with the course become the one of the new user (if any)
* Reassining list: the length of the list is increased to 100 courses
### Fixed
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) Fix filter "accompanying course by social action" to avoid duplication in list
* ([#164](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/164)) View a third party: avoid errors when a contact has a civility
* ([#163](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/163)) Fix the filters and aggregators on exports "count peoples"
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) From the database, avoid the creation of location history for same period and at same dates
### Traduction francophone des principaux changements
- Fonctionnalité: Réassigner les parcours en lot: lorsque des parcours sont réassignés "en lot", les parcours sont maintenant associés au métier du nouveau référent;
- Correction: certaines causes qui créaient des doublons dans les listes ont été corrigées;
- Correction des associations entre l'export "nombre de personnes" et les filtres et regroupements associés
## v2.7.0 - 2023-09-27
### Feature
* ([#155](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/155)) The regulation list load accompanying periods by exact postal code (address associated with postal code), and not by the content of the postal code (postal code with same code's string)
### Fixed
* ([#142](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/142)) Fix the label of filter ActivityTypeFilter to a more obvious one
* ([#140](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/140)) [export] Fix association of filter "filter location by type" which did not appears on "list of activities"
## v2.6.3 - 2023-09-19
### Fixed
* Remove id property from document
mappedsuperclass
## v2.6.2 - 2023-09-18
### Fixed
* Fix doctrine mapping of AbstractTaskPlaceEvent and SingleTaskPlaceEvent: id property moved.
## v2.6.1 - 2023-09-14
### Fixed
* Filter out active centers in exports, which uses a different PickCenterType.
## v2.6.0 - 2023-09-14
### Feature
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Add locations in Aside Activity. By default, suggest user location, otherwise a select with all locations.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Adapt Aside Activity exports: display location, filter by location, group by location
* Use the CRUD controller for center entity + add the isActive property to be able to mask instances of Center that are no longer in use.
### Fixed
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) reinstate the fusion of duplicate persons
* Missing translation in Work Actions exports
* Reimplement the mission type filter on tasks, only for instances that have a config parameter indicating true for this.
* ([#135](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/135)) Corrects a typing error in 2 filters, which caused an
error when trying to reedit a saved export
* ([#136](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/136)) [household] when moving a person to a sharing position to a not-sharing position on the same household on the same date, remove the previous household membership on the same household. This fix duplicate member.
* Add missing translation for comment field placeholder in repositionning household editor.
* Do not send an email to creator twice when adding a comment to a notification
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) Fix gestion doublon functionality to work with chill bundles v2
### UX
* Uniformize badge-person in household banner (background, size)
## v2.5.3 - 2023-07-20
### Fixed
* ([#132](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/132)) Rendez-vous documents created would appear in all documents lists of all persons with an accompanying period. Or statements are now added to the where clause to filter out documents that come from unrelated accompanying period/ or person rendez-vous.
## v2.5.2 - 2023-07-15
### Fixed
* [Collate Address] when updating address point, do not use the point's address reference if the similarity is below the requirement for associating the address reference and the address (it uses the postcode's center instead)
## v2.5.1 - 2023-07-14
### Fixed
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match
## v2.5.0 - 2023-07-14
### Feature
* Allow filtering on the basis of a user within general tasks lists
* ([#120](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/120)) Adding OrderFilter to the list of social actions.
* ([#125](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/125)) [export] Add a list for people with their associated course
* [export] Add ordering by person's lastname or course opening date in list which concerns accompanying course or peoples
* ([#128](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/128)) [Export] allow to group activities by localisation
* ([#129](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/129)) [export] Add a filter "filter course having an activity between two dates"
* ([#112](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/112)) [addresses] Add a cronjob to re-associate addresses with addresses reference every 6 hours
* Improve filtering layout
### Fixed
* reimplement the visualization of all calculator results
* ([#117](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/117)) Repair my unread notification list with actions and evaluations documents
* ([#126](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/126)) Correct bug in thirdparty API search query: simplify address joins clause for child and parent kind
### DX
* Documentation for database principles
* [cronjob] when a cronjob is executed, it may return an array of data that will be passed as argument on the next execution
### UX
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Better integration of address details button: look, position, title tag
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Show address detail button on person and household banners
* Improve residential address position on show onthefly modale
### Traduction francophone des principaux changements
* Ajout d'un filtre "par utilisateur" aux pages de tâche
* Filtre des actions d'accompagnement par date, type, intervenant
* export: liste des usagers concernés avec détail de leurs parcours
* export: ajout d'un regroupement des échanges par localisation
* export: ajout d'un filtre "parcours ayant reçu un échange entre deux dates"
* ajout d'une tâche cron pour associer les adresses à une adresse de référence
* correction: réparation de la liste des notifications sur la page d'accueil, dans le cas où une notification concerne une action ou un document dans une évaluation
* correction: réparation de la recherche des tiers ayant des codes postaux similaires entre les parents et enfants
* meilleure intégration du bouton "détail d'une adresse": améliration de la taille et de la position
* bouton permettant de visualiser les détails d'une adresse (modale avec carte) dans la bannière "Usager" et "Ménage"
* amélioration de la modale permettant de voir les détails d'un usager: les adresses de résidence sont dans la continuité des autres adresses, et non plus dans une colonne séparée
* améliore le design et l'expérience utilisateur des filtres
## v2.4.0 - 2023-07-07
### Feature
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] on "filter by user working" on accompanying period, add two dates to filters intervention within a period
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add an aggregator by user's job working on a course
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add an aggregator by user's scope working on a course
* [export] on aggregator "user working on a course"
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a center aggregator for Person
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] add a filter on "job working on a course"
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113)) [export] Add a filter on "scope working on a course"
* ([#121](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/121)) Create a role "See Confidential Periods", separated from the "Reassign courses" role
* ([#124](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/124)) Sync user absence / presence through microsoft outlook / graph api.
### Fixed
* ([#116](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/116)) On the accompanying course page, open the action on view mode if the user does not have right to update them (i.e. if the accompanying period is closed)
* [export] Rename label for CurrentActionFilter (on accompanying period work) to make precision between "ouvert" and "sans date de fin"
* Force the db to have either a person_location or a address_location, and avoid to have both also internally in the entity
* [export] set rolling date on person age aggregator
* [export] fix list when a person locating a course is without address
* [export] remove unused condition on course about duration participation
* Command to subscribe on MS Graph users calendars: improve the loop to be more efficient
### DX
* Rolling Date: can receive a null parameter
### Traduction francophone des principaux changements
- sur le "filtre par intervenant", ajoute deux dates pour limiter la période d'intervention;
- ajout d'un regroupement par métier des intervenants sur un parcours;
- ajout d'un regroupement par service des intervenants sur un parcours;
- ajout d'un regroupement par utilisateur intervenant sur un parcours
- ajout d'un regroupement "par centre de l'usager";
- ajout d'un filtre "par métier intervenant sur un parcours";
- ajout d'un filtre "par service intervenant sur un parcours";
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
- synchronisation de l'absence des utilisateurs par microsoft graph api
## v2.3.0 - 2023-06-27
### Feature
* ([#110](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/110)) Edit saved exports options: the saved exports options (forms, filters, aggregators) are now editable.
* ([#103](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/103)) Get an unified list of document in person and accompanying period context
* [export] Set the default date of calculation of the accompanying period's list as "today"
* Force accompanying period user history to be unique for the same period and stardate/enddate [:warning: may encounter migration issue]
If some issue is encountered during migration, use this SQL to find the line which are in conflict, examine the problem and delete some of the concerning line
*
```sql
-- to see the line which are in conflict with another one
SELECT o.*
FROM chill_person_accompanying_period_user_history o
JOIN chill_person_accompanying_period_user_history c ON o.id < c.id AND o.accompanyingperiod_id = c.accompanyingperiod_id
WHERE tsrange(o.startdate, o.enddate, '[)') && tsrange(c.startdate, c.enddate, '[)')
ORDER BY accompanyingperiod_id;
-- to examine line in conflict for a given accompanyingperiod_id (given by the previous query)
SELECT * FROM chill_person_accompanying_period_user_history WHERE accompanyingperiod_id = IIIIDDDD order by startdate, enddate;
```
* Rename label of filter in French: "parcours actif" => "parcours ouvert", and "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
### Traduction francophone des principaux changements
* Les exports enregistrés sont éditables par l'utilisateur;
* L'onglet "Document" dans les parcours et les dossiers d'usager affiche désormais les documents ajoutés à différents endroits.
Pour les parcours, il s'agit de:
- documents ajoutés directement dans le parcours;
- documents des échanges;
- documents des rendez-vous;
- documents des évaluations;
- documents directement ajoutés dans le dossier des usagers concernés par le parcours;
Pour les usagers, il s'agit de:
- documents des échanges;
- documents des parcours;
- documents des rendez-vous;
- documents des actions, des échanges, des rendez-vous, des évaluations ajoutés dans les parcours.
* Dans la liste des parcours, la date de calcul des éléments associés est "aujourd'hui" par défaut.
* Dans les exports, renommage des libellés des filtres: "parcours actif" => "parcours ouvert", et "filtrer les parcours ouverts" => "Filtrer les parcours dont la date d'ouverture"
## v2.2.2 - 2023-06-26
### Fixed
* [Accompanying period comments]: order comments from the most recent to the oldest, in the list
* Api: filter social action to keep only the currently activated
* ([#82](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/82)) Fix deletion and re-creation of filiation relationship
## v2.2.1 - 2023-06-19
### Fixed
* ([#114](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/114)) [notification on document evaluation] fix entityId and return path when adding a notification on a document in an evaluation
## v2.2.0 - 2023-06-18
### Feature
* When navigating from a workflow regarding to an evaluation's document to an accompanying course, scroll directly to the document, and blink to highlight this document
* Add notification to accompanying period work and work's evaluation's documents
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[Export] Filter accompanying period by step at date: allow to pick multiple steps
* ([#113](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/113))[export] add a filter on accompanying period: filter by step between two dates
### Fixed
* use the correct annotation for the association between PersonCurrentCenter and Person
* ([#58](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/58))Fix birthdate timezone in PersonRenderBox
* ([#55](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/55))Fix the notification counter
### DX
* DQL function OVERLAPSI: simplify expression in postgresql
## v2.1.0 - 2023-06-12
### Feature
* [docgen] allow to pick a third party when generating a document in context Activity, AccompanyingPeriod
### Fixed
* ([#111](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/111)) List of "my accompanying periods": separate the active and closed periods in two different lists, and show the inactive_long and inactive_short periods
### Security
* ([#105](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/105)) Rights are checked for display of 'accompanying period' tab in household menu. Rights are also checked for creation of 'accompanying period' from within household context
### DX
* Add methods to RegroupmentRepository and fullfill Center / Regroupment Doctrine mapping
## 2.0.0
* this is a release to relaunch our proceess of release with semantic versioning
## Test releases
### 2.0.0-beta3
<!-- write down unreleased development here -->
* [person][export] Fixed: rename the alias for `accompanying_period` to `acp` in filter associated with person
* [activity][export] Feature: improve label for aliases in "Filter by activity type"
* [activity][export] DX/Feature: use of an `ActivityTypeRepositoryInterface` instead of the old-style EntityRepository
@ -18,9 +288,6 @@ and this project adheres to
* [person][export] Fixed: use left join for related entities in accompanying course aggregators
* [workflow] Feature: allow user to copy and send manually the access link for the workflow
* [workflow] Feature: show the email addresses that received an access link for the workflow
## Test releases
### 2.0.0-beta2
* [workflow]: Fixed: the notification is sent when the user is added to the first step.

80
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,80 @@
# Contributing
Chill is an open source, community-driven project.
If you'd like to contribute, please read the following.
## What can you do ?
Chill is an open-source project driven by a community of developers, users and social workers. If you don't feel ready to contribute code or patches, reviewing issues and pull requests (PRs) can be a great start to get involved and give back.
If you don't have your own instance or don't want to use it, you can try to reproduce bugs using the instance https://demo.chill.social
## Core team
The core team is the group of developers that determine the direction and evolution of the Chill project. Their votes rule if the features and patches proposed by the community are approved or rejected.
All the Chill Core members are long-time contributors with solid technical expertise and they have demonstrated a strong commitment to drive the project forward.
The core team:
- elects his own members;
- merge pull requests;
### members
Project leader: [julienfastre](https://gitlab.com/julienfastre)
Core members:
- [tchama](https://gitlab.com/tchama)
- [LenaertsJ](https://gitlab.com/LenaertsJ)
- [nobohan](https://gitlab.com/nobohan)
### Becoming a project member
About once a year, the core team discusses the opportunity to invite new members. To become a core team member, you must:
- take part on the development for at least 6 month: propose multiple merge requests and participate to the peer review process;
- through this participation, demonstrate your technical skills and your knowledge of the software and any of their dependencies;
### Core Membership Revocation
A Chill Core membership can be revoked for any of the following reasons:
- Refusal to follow the rules and policies stated in this document;
- Lack of activity for the past six months;
- Willful negligence or intent to harm the Chill project;
The decision is taken by the majority of project members.
## Code development rules
### Merge requests
Every merge request must contains:
- one more entries suitable for generating a changelog. This is done using the [changie utility](https://changie.dev);
- a comprehensible description of the changes;
- if applicable, automated tests should be adapted or created;
- the code style must pass the project's rules, and non phpstan errors must be raised nor rector refactoring suggestion.
The pipelines must pass.
In case of emergency, some rules may be temporarily ignored.
### Merge Request Voting Policy
- -1 votes must always be justified by technical and objective reasons;
- +1 (technically: approbation on the merge request) votes do not require justification, unless there is at least one -1 vote;
- Core members can change their votes as many times as they desire during the course of a merge request discussion;
- Core members are not allowed to vote on their own merge requests.
### Merge Request Merging Process
All code must be committed to the repository through merge requests, except for minor changes which can be committed directly to the repository.
### Release Policy
The Core members are also the release manager for every Chill version.

View File

@ -8,7 +8,7 @@
"social worker"
],
"require": {
"php": "^7.4|^8.2",
"php": "^8.2",
"ext-json": "*",
"ext-openssl": "*",
"ext-redis": "*",
@ -34,6 +34,7 @@
"sensio/framework-extra-bundle": "^5.5",
"spomky-labs/base64url": "^2.0",
"symfony/browser-kit": "^4.4",
"symfony/clock": "^6.2",
"symfony/css-selector": "^4.4",
"symfony/expression-language": "^4.4",
"symfony/form": "^4.4",
@ -47,7 +48,6 @@
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4",
"symfony/serializer": "^5.3",
"symfony/templating": "^4.4",
"symfony/translation": "^4.4",
"symfony/twig-bundle": "^4.4",
"symfony/validator": "^4.4",
@ -66,6 +66,7 @@
"fakerphp/faker": "^1.13",
"jangregor/phpstan-prophecy": "^1.0",
"nelmio/alice": "^3.8",
"nikic/php-parser": "^4.15",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
@ -74,7 +75,7 @@
"phpunit/phpunit": ">= 7.5",
"psalm/plugin-phpunit": "^0.18.4",
"psalm/plugin-symfony": "^4.0.2",
"rector/rector": "^0.15.23",
"rector/rector": "^0.17.7",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^4.4",
"symfony/maker-bundle": "^1.20",
@ -96,20 +97,21 @@
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
"Chill\\TaskBundle\\": "src/Bundle/ChillTaskBundle",
"Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle",
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src"
"Chill\\WopiBundle\\": "src/Bundle/ChillWopiBundle/src",
"Chill\\Utils\\Rector\\": "utils/rector/src"
}
},
"autoload-dev": {
"psr-4": {
"App\\": "tests/app/src/",
"App\\": "tests/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests"
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests"
}
},
"config": {
@ -119,16 +121,15 @@
"ocramius/package-versions": true,
"phpro/grumphp": true,
"phpstan/extension-installer": true,
"roave/you-are-using-it-wrong": true
"roave/you-are-using-it-wrong": true,
"symfony/runtime": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true,
"vendor-dir": "tests/app/vendor"
"sort-packages": true
},
"scripts": {
"auto-scripts": {
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"cache:clear": "symfony-cmd"
}
}

View File

@ -62,7 +62,6 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
{
$builder->add('date_from', DateType::class, [
'label' => 'Born after this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
@ -70,12 +69,15 @@ class BirthdateFilter implements ExportElementValidatedInterface, FilterInterfac
$builder->add('date_to', DateType::class, [
'label' => 'Born before this date',
'data' => new DateTime(),
'attr' => ['class' => 'datepicker'],
'widget' => 'single_text',
'format' => 'dd-MM-yyyy',
]);
}
public function getFormDefaultData(): array
{
return ['date_from' => new DateTime(), 'date_to' => new DateTime()];
}
// here, we create a simple string which will describe the action of
// the filter in the Response

View File

@ -36,6 +36,10 @@ class CountPerson implements ExportInterface
{
// this export does not add any form
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
{
@ -50,18 +54,9 @@ class CountPerson implements ExportInterface
public function getLabels($key, array $values, $data)
{
// the Closure which will be executed by the formatter.
return function ($value) {
switch ($value) {
case '_header':
// we have to process specifically the '_header' string,
// which will be used by the formatter to show a column title
return $this->getTitle();
default:
// for all value, we do not process them and return them
// immediatly
return $value;
}
return fn($value) => match ($value) {
'_header' => $this->getTitle(),
default => $value,
};
}
@ -90,9 +85,7 @@ class CountPerson implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
// we gather all center the user choose.
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();

View File

@ -0,0 +1,36 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _faq:
Frequently asked questions
####################
Continuous integration
***********
Pipeline fails, but php-cs-fixer doesn't alert me when running it locally ?
========================================
It is possible that you run php-cs-fixer on your local instance of chill and no fixes are made.
Everything seems fine, so you push. However once the pipeline is run in gitlab, you're notified that it failed due to php
cs errors.
In this case it's likely that you have to update your version of php-cs-fixer.
php-cs-fixer is installed when building the docker image: https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic/src/branch/main/Dockerfile#L50
Consequently, to update php-cs-fixer we have to update the image by building it again.
For this the following commands can be used,
.. code-block:: php
docker compose build --pull php
# replace existing containers
docker compose up -d --force-recreate php

View File

@ -0,0 +1,84 @@
.. database-principles:
Principes de la base de données
###############################
Cette page donne une compréhension globale de la base de donnée de Chill, et explique quelques détails d'implémentations qui permettent d'accélérer les traitements à partir de la base de donnée, ou de l'exploiter plus aisément.
Cette page est rédigée en français.
.. note::
La stabilité du schéma de la base de donnée n'est pas garantie.
Toutefois, ce dernier évolue relativement peu. Il est rare que des tables ou des colonnes soient supprimées ou renommées. Mais il n'est pas garanti que cela puisse arriver.
Généralités
===========
Une liste commentée de toutes les tables :download:`est disponible au format CSV <./database/table_list.csv`.
Schéma et conventions de nommage
--------------------------------
Au début de l'histoire de Chill, les schémas postgresql n'étaient pas exploités. Les données étaient stockées dans le schéma :code:`public`.
Par la suite, des nouveaux bundles sont apparus, et les tables ont été classées dans des schémas dédiés.
A l'heure actuelle:
- pour les anciens bundle, ceux qui ont déjà des tables dans le schéma public, les nouvelles tables sont ajoutées à ce schéma. Elles sont préfixées par :code:`chill_<nom du bundle>_`;
- pour les bundles plus récents, les tables sont créées dans le schéma dédié
Données avec de l'historicité
-----------------------------
Certaines données sont historisées:
- les référents d'un parcours;
- les statuts d'un parcours;
- la liaison entre les centres et les usagers;
- etc.
Dans ces cas-là, Chill crée généralement deux colonnes, qui sont habituellement nommées :code:`startDate` et :code:`endDate`. Lorsque la colonne :code:`endDate` est à :code:`NULL`, cela signifie que la période n'est pas "fermée". La colonne :code:`startDate` n'est pas nullable.
Dans certains cas, la donnée actuelle (référent d'un parcours, par exemple) est également répétée au niveau de la table en elle-même. Par exemple, la table des parcours :code:`chill_person_accompanying_period` comporte une colonne :code:`step` (le statut du parcours) et :code:`user_id` (id du référent) en plus de l'historique. Bien que redondant, cela simplifie les traitements.
Relations particulières
=======================
Usagers, ménages, adresses
--------------------------
Les usagers ont une adresse au travers des ménages: dans l'interface, l'adresse est inscrite dans le dossier du ménage, et elle est "donnée" aux usagers membres du ménage, **et** qui partagent l'adresse de ce ménage. En effet, il est possible que des usagers "appartiennent" à un ménage sans y être domicilié: c'est le cas, par exemple, des enfants en garde alternée.
L'historique de l'appartenance des usagers au ménage est conservée, de même que l'historique des adresses pour un même ménage.
Les tables en jeu sont les suivantes:
- la table :code:`chill_person_person` liste les usagers;
- la table :code:`chill_person_household_members` liste les appartenances au ménage: il s'agit de la jointure entre les usagers et les ménages:
- les colonnes :code:`startDate` et :code:`endDate` indiquent la date de début et la date de fin de l'appartenance;
- la colonne :code:`shareHousehold` indique si l'utilisateur partage l'adresse du ménage (si oui, sa valeur est :code:`TRUE`)
- la table :code:`chill_person_household` liste les ménages
- la table :code:`chill_person_household_to_addresses` associe les ménages aux adresses;
- la table :code:`chill_main_address` contient les adresses, en indiquant la date de début de validité (:code:`validFrom`) et la fin de validité (:code:`validTo`).
Pour simplifier la résolution des adresses et des usagers, deux vues ont été mises en œuvre:
- la vue :code:`view_chill_person_household_address` reprend, pour chaque usager, l'historique des appartenances au ménage découpée par l'historique des adresses d'un ménage.
Autrement dit, une ligne est créée à chaque fois qu'un usager change de ménage, ou qu'un ménage change d'adresse. Il est donc possible de retrouver l'historique complet des adresses pour un usager donné via cette table.
- la vue :code:`view_chill_person_current_address` reprend l'adresse actuelle des usagers.
Adresses et unités géographiques
--------------------------------
Chill propose des statistiques sur la localisation des adresses par rapport à des zones géographiques (:code:`chill_main_geographical_unit`).
Comme la résolution géographique des adresses est coûteuse en CPU et en temps de traitement, une vue matérialisée a été créée: :code:`view_chill_main_address_geographical_unit`. Elle est rafraichie quotidiennement dans la base de donnée de production.
Liste des tables et commentaires
================================
Une liste commentée de toutes les tables :download:`est disponible au format CSV <./database/table_list.csv`.

View File

@ -0,0 +1,155 @@
order,table_schema,table_name,commentaire
1,chill_3party,party_category,Catégorie de tiers
2,chill_3party,party_center,Association entre les tiers et les centres (déprécié)
3,chill_3party,party_profession,Profession du tiers (déprécié)
4,chill_3party,third_party,Tiers
5,chill_3party,thirdparty_category,association tiers - catégories
6,chill_asideactivity,asideactivity,Activités annexes
7,chill_asideactivity,asideactivitycategory,Catégories d'activités annexes
8,chill_budget,charge,Charges du budget
9,chill_budget,charge_type,Types de charges
10,chill_budget,resource,Ressources du budget
11,chill_budget,resource_type,Types de ressources
12,chill_calendar,calendar,Rendez-vous
13,chill_calendar,calendar_doc,Document du rendez-vous
14,chill_calendar,calendar_range,Plage de disponibilité
15,chill_calendar,calendar_to_persons,association rendez-vous - usagers
16,chill_calendar,calendar_to_thirdparties,association rendez-vous - tiers
17,chill_calendar,cancel_reason,Motifs d'annulations
18,chill_calendar,invite,Invitation aux rendez-vous
19,chill_doc,accompanyingcourse_document,Documents associés aux parcours
20,chill_doc,document_category,Catégories de documents
21,chill_doc,person_document,Documents associés à l'usagers
22,chill_doc,stored_object,Documents
23,chill_task,recurring_task,Tâches récurrentes (non utilisé)
24,chill_task,single_task,Tâches
25,chill_task,single_task_place_event,Historique des transitions des tâches
26,chill_vendee,adressederelais,
27,chill_vendee,center_polygon
28,chill_vendee,entourage,
29,chill_vendee,geographical_unit
30,chill_vendee,geographical_unit_association
31,chill_vendee,mobilite
32,chill_vendee,niveauetude
33,chill_vendee,security_profile
34,chill_vendee,security_profile_action
35,chill_vendee,security_profile_jobs
36,chill_vendee,situationprofessionelle
37,chill_vendee,statutlogement
38,chill_vendee,tempsdetravail
39,chill_vendee,titredesejour
40,chill_vendee,vendee_person
41,chill_vendee,vendee_person_mineur
42,chill_vendee,vendeeperson_entourage
43,chill_vendee,vendeepersonmineur_adressederelais
44,public,accompanying_periods_scopes,Services associés aux parcours
45,public,activity,Échanges
46,public,activity_activityreason,s
47,public,activity_person,
48,public,activity_storedobject,
49,public,activity_thirdparty,
50,public,activity_user,
51,public,activityreason,Sujets d'échange
52,public,activityreasoncategory,Catégories de sujets
53,public,activitytpresence,Présence aux échanges
54,public,activitytype,Types d'échanges
55,public,activitytypecategory,Catégories de types d'échanges
56,public,centers,"Centres (territoires, agences, etc.)"
57,public,chill_activity_activity_chill_person_socialaction,
58,public,chill_activity_activity_chill_person_socialissue
59,public,chill_docgen_template,Gabarits de documents
60,public,chill_main_address,Adresses
61,public,chill_main_address_legacy,Anciennes adresses (dépréciés)
62,public,chill_main_address_reference,Adresses de référence
63,public,chill_main_civility,Civilités
64,public,chill_main_cronjob_execution,Dernière exécution des tâche cron
65,public,chill_main_geographical_unit,Unités géographiques
66,public,chill_main_geographical_unit_layer,Couches d'unités géographiques
67,public,chill_main_location,Localisations
68,public,chill_main_location_type,Types de localisations
69,public,chill_main_notification,Notifications
70,public,chill_main_notification_addresses_unread
71,public,chill_main_notification_addresses_user
72,public,chill_main_notification_comment,
73,public,chill_main_postal_code,Code postaux
74,public,chill_main_saved_export,Exports enregistrés
75,public,chill_main_user_job,Métiers
76,public,chill_main_workflow_entity,Workflows
77,public,chill_main_workflow_entity_comment
78,public,chill_main_workflow_entity_step,Etapes du workflow
79,public,chill_main_workflow_entity_step_cc_user,
80,public,chill_main_workflow_entity_step_user
81,public,chill_main_workflow_entity_step_user_by_accesskey,
82,public,chill_main_workflow_entity_subscriber_to_final,
83,public,chill_main_workflow_entity_subscriber_to_step
84,public,chill_person_accompanying_period,Parcours d'accompagnement
85,public,chill_person_accompanying_period_closingmotive,Motifs de cloture des parcours
86,public,chill_person_accompanying_period_comment,Commentaires des parcours
87,public,chill_person_accompanying_period_location_history,Historique de la localisatio ndes parcours
88,public,chill_person_accompanying_period_origin,Origine des parcours
89,public,chill_person_accompanying_period_participation,Appartenance des usagers au parcours
90,public,chill_person_accompanying_period_resource,Personnes ressources d'un parcours
91,public,chill_person_accompanying_period_social_issues,
92,public,chill_person_accompanying_period_step_history
93,public,chill_person_accompanying_period_user_history
94,public,chill_person_accompanying_period_work,Actions d'accompagnements
95,public,chill_person_accompanying_period_work_evaluation,Évaluations (dans les actions d'accompagnements)
96,public,chill_person_accompanying_period_work_evaluation_document,Documents des évaluations
97,public,chill_person_accompanying_period_work_goal,Objectifs d'une actions
98,public,chill_person_accompanying_period_work_goal_result,Objectifs et résultats d'une action
99,public,chill_person_accompanying_period_work_person,Usagers associés à une actions
100,public,chill_person_accompanying_period_work_referrer,Référents d'une actions
101,public,chill_person_accompanying_period_work_result,Résultats d'une action
102,public,chill_person_accompanying_period_work_third_party,Tiers traitants d'une action
103,public,chill_person_alt_name,"Noms supplémentaires d'un usager (nom marital, etc.)"
104,public,chill_person_household,Ménages
105,public,chill_person_household_composition,
106,public,chill_person_household_composition_type,Types de composition de ménage
107,public,chill_person_household_members,Membres du ménages
108,public,chill_person_household_position,Positions dans le ménage
109,public,chill_person_household_to_addresses,Association adresses - ménages
110,public,chill_person_marital_status,Etats civils
111,public,chill_person_not_duplicate,
112,public,chill_person_person,Usagers
113,public,chill_person_person_center_history,Historique des centres d'un usagers
114,public,chill_person_persons_to_addresses,Déprécié
115,public,chill_person_phone,Numéros d etéléphone supplémentaires d'un usager
116,public,chill_person_relations,Types de relations de filiation
117,public,chill_person_relationships,Relations de filiations
118,public,chill_person_residential_address,Adresses de résidences
119,public,chill_person_resource,Personnes ressources (pour les personnes)
120,public,chill_person_resource_kind,Type de personnes ressources
121,public,chill_person_social_action,Liste des actions d'accompagnement
122,public,chill_person_social_action_goal,Objectifs associés à une action
123,public,chill_person_social_action_result,Résultats associés à une action
124,public,chill_person_social_issue,Problématiques sociales
125,public,chill_person_social_work_evaluation,Evaluations disponibles
126,public,chill_person_social_work_evaluation_action,Associations entre les évaluations et les actions
127,public,chill_person_social_work_goal,Objectifs disponibles pour une actions
128,public,chill_person_social_work_goal_result,Objectifs et résultats disponible pour une action
129,public,chill_person_social_work_result,Résultats disponibles pour une action
130,public,country,Pays
131,public,custom_field_long_choice_options,
132,public,customfield
133,public,customfieldsdefaultgroup
134,public,customfieldsgroup
135,public,geography_columns,Table liée à postgis
136,public,geometry_columns,Table liée à postgis
137,public,group_centers,
138,public,language,Langues
139,public,messenger_messages,Table système
140,public,migration_versions,Table système
141,public,permission_groups
142,public,permissionsgroup_rolescope
143,public,persons_spoken_languages
144,public,regroupment,Regroupement de centres
145,public,regroupment_center,
146,public,role_scopes,
147,public,scopes,Services
148,public,spatial_ref_sys,Table système (postgis)
149,public,user_groupcenter,
150,public,users,Utilisateurs
151,public,view_chill_person_accompanying_period_info,
152,public,view_chill_person_current_address
153,public,view_chill_person_household_address
154,public,view_chill_person_person_center_history_current
Can't render this file because it has a wrong number of fields in line 28.

View File

@ -0,0 +1,203 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _entity-info:
Stats about event on entity in php world
########################################
It is necessary to be able to gather information about events for some entities:
- when the event has been done;
- who did it;
- ...
Those "infos" are not linked with right management, like describe in :ref:`timelines`.
“infos” for some stats and info about an entity
-----------------------------------------------
Building an info means:
- create an Entity, and map this entity to a SQL view (not a regular table);
- use the framework to build this entity dynamically.
A framework api is built to be able to build multiple “infos” entities
through “union” views:
- use a command ``bin/console chill:db:sync-views`` to synchronize view (create view if it does not exists, or update
views when new SQL parts are added in the UNION query. Internally, this command call a new ``ViewEntityInfoManager``,
which iterate over available views to build the SQL;
- one can create a new “view entity info” by implementing a
``ViewEntityInfoProviderInterface``
- this implementation of the interface is free to create another
interface for building each part of the UNION query. This interface
is created for AccompanyingPeriodInfo:
``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``
So, converting new “events” into rows for ``AccompanyingPeriodInfo`` is
just implementing this interface!
Implementation for AccompanyingPeriod (``AccompanyingPeriod/AccompanyingPeriodInfo``)
-------------------------------------------------------------------------------------
A class is created for computing some statistical info for an
AccompanyingPeriod: ``AccompanyingPeriod/AccompanyingPeriodInfo``. This
contains information about “something happens”, who did it and when.
Having those info in table answer some questions like:
- when is the last and the first action (AccompanyingPeriodWork,
Activity, AccompanyingPeriodWorkEvaluation, …) on the period;
- who is “acting” on the period, and when is the last “action” for each
user.
The AccompanyingPeriod info is mapped to a SQL view, not a table. The
sql view is built dynamically (see below), and gather infos from
ActivityBundle, PersonBundle, CalendarBundle, … It is possible to create
custom bundle and add info on this view.
.. code:: php
/**
*
* @ORM\Entity()
* @ORM\Table(name="view_chill_person_accompanying_period_info") <==== THIS IS A VIEW, NOT A TABLE
*/
class AccompanyingPeriodInfo
{
// ...
}
Why do we need this ?
~~~~~~~~~~~~~~~~~~~~~
For multiple jobs in PHP world:
- moving the accompanying period to another steps when inactive,
automatically;
- listing all the users which are intervening on the action on a new
“Liste des intervenants” page;
- filtering on exports
Later, we will launch automatic anonymise for accompanying period and
all related entities through this information.
How is built the SQL views which is mapped to “info” entities ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The AccompanyingPeriodInfo entity is mapped by a SQL view (not a regular
table).
The sql view is built dynamically, it is a SQL view like this, for now (April 2023):
.. code:: sql
create view view_chill_person_accompanying_period_info
(accompanyingperiod_id, relatedentity, relatedentityid, user_id, infodate, discriminator, metadata) as
SELECT w.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
w.id AS relatedentityid,
cpapwr.user_id,
w.enddate AS infodate,
'accompanying_period_work_end'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work w
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
WHERE w.enddate IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
e.updatedby_id AS user_id,
e.updatedat AS infodate,
'accompanying_period_work_evaluation_updated_at'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
WHERE e.updatedat IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.maxdate AS infodate,
'accompanying_period_work_evaluation_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
WHERE e.maxdate IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.startdate AS infodate,
'accompanying_period_work_evaluation_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument'::text AS relatedentity,
doc.id AS relatedentityid,
doc.updatedby_id AS user_id,
doc.updatedat AS infodate,
'accompanying_period_work_evaluation_document_updated_at'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation_document doc
JOIN chill_person_accompanying_period_work_evaluation e ON doc.accompanyingperiodworkevaluation_id = e.id
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
WHERE doc.updatedat IS NOT NULL
UNION
SELECT cpapw.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
e.id AS relatedentityid,
cpapwr.user_id,
e.maxdate AS infodate,
'accompanying_period_work_evaluation_max'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work_evaluation e
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
WHERE e.maxdate IS NOT NULL
UNION
SELECT w.accompanyingperiod_id,
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
w.id AS relatedentityid,
cpapwr.user_id,
w.startdate AS infodate,
'accompanying_period_work_start'::text AS discriminator,
'{}'::jsonb AS metadata
FROM chill_person_accompanying_period_work w
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
UNION
SELECT activity.accompanyingperiod_id,
'Chill\ActivityBundle\Entity\Activity'::text AS relatedentity,
activity.id AS relatedentityid,
au.user_id,
activity.date AS infodate,
'activity_date'::text AS discriminator,
'{}'::jsonb AS metadata
FROM activity
LEFT JOIN activity_user au ON activity.id = au.activity_id
WHERE activity.accompanyingperiod_id IS NOT NULL;
As you can see, the view gather multiple SELECT queries and bind them
with UNION.
Each SELECT query is built dynamically, through a class implementing an
interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``, `like
here <https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php>`__
To add new `SELECT` query in different `UNION` parts in the sql view, create a
service and implements this interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``.

View File

@ -9,7 +9,7 @@
Development
###########
As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the framework's documentation should answer most of your questions. We are explaining here some tips to work with Chill, and things we provide to encounter our needs.
As Chill relies on the `symfony <http://symfony.com>`_ framework, reading the framework's documentation should answer most of your questions. We are explaining here some tips to work with Chill, and help with things we've encountered.
.. toctree::
:maxdepth: 2
@ -35,6 +35,9 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
manual/index.rst
Assets <assets.rst>
Cron Jobs <cronjob.rst>
Info about entities <entity-info.rst>
Info about database (in French) <database-principles.rst>
Developer FAQ <FAQ.rst>
Layout and UI
**************

View File

@ -13,7 +13,7 @@ namespace Chill\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class example extends Controller
class example extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
public function yourAction()
{

View File

@ -6,6 +6,8 @@
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _timelines:
Timelines
*********

View File

@ -16,7 +16,7 @@ use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Role\Role;
class ConsultationController extends Controller
class ConsultationController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
/**
* @param int $id personId
@ -43,7 +43,7 @@ class ConsultationController extends Controller
$circles = $authorizationHelper->getReachableCircles(
$this->getUser(),
new Role(ConsultationVoter::SEE),
ConsultationVoter::SEE,
$person->getCenter()
);

View File

@ -23,25 +23,18 @@ class ChillMainConfiguration implements ConfigurationInterface
{
use AddWidgetConfigurationTrait;
/**
* @var ContainerBuilder
*/
private $containerBuilder;
public function __construct(
array $widgetFactories,
ContainerBuilder $containerBuilder
private readonly ContainerBuilder $containerBuilder
) {
// we register here widget factories (see below)
$this->setWidgetFactories($widgetFactories);
// we will need the container builder later...
$this->containerBuilder = $containerBuilder;
}
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('chill_main');
$treeBuilder = new TreeBuilder('chill_main');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()

View File

@ -87,7 +87,7 @@ class ChillPersonAddAPersonWidget implements WidgetInterface
// show only the person from the authorized centers
$and = $qb->expr()->andX();
$centers = $this->authorizationHelper
->getReachableCenters($this->getUser(), new Role(PersonVoter::SEE));
->getReachableCenters($this->getUser(), PersonVoter::SEE);
$and->add($qb->expr()->in('person.center', ':centers'));
$qb->setParameter('centers', $centers);

View File

@ -151,6 +151,7 @@ This script will :
# mount into to container
./docker-php.sh
bin/console chill:db:sync-views
# and load fixtures
bin/console doctrine:migrations:migrate
@ -161,7 +162,7 @@ Chill will be available at ``http://localhost:8001.`` Currently, there isn't any
# mount into to container
./docker-php.sh
# and load fixtures
# and load fixtures (do not this for production)
bin/console doctrine:fixtures:load --purge-with-truncate
There are several users available:
@ -204,8 +205,10 @@ How to create the database schema (= run migrations) ?
# if a container is running
./docker-php.sh
bin/console doctrine:migrations:migrate
bin/console chill:db:sync-views
# if not
docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate
docker-compose run --user $(id -u) php bin/console chill:db:sync-views
How to read the email sent by the program ?
@ -236,6 +239,23 @@ How to open a terminal in the project
# if not
docker-compose run --user $(id -u) php /bin/bash
How to run cron-jobs ?
======================
Some command must be executed in :ref:`cron jobs <cronjob>`. To execute them:
.. code-block:: bash
# if a container is running
./docker-php.sh
bin/console chill:cron-job:execute
# some of them are executed only during the night. So, we have to force the execution during the day:
bin/console chill:cron-job:execute 'name-of-the-cron'
# if not
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute
# some of them are executed only during the night. So, we have to force the execution during the day:
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute 'name-of-the-cron'
How to run composer ?
=====================

View File

@ -38,6 +38,14 @@ This should be adapted to your needs:
* Think about how you will backup your database. Some adminsys find easier to store database outside of docker, which might be easier to administrate or replicate.
Run migrations on each update
=============================
Every time you start a new version, you should apply update the sql schema:
- running ``bin/console doctrine:migration:migrate`` to run sql migration;
- synchonizing sql views to the last state: ``bin/console chill:db:sync-views``
Cron jobs
=========

View File

@ -1,63 +0,0 @@
Entity,Join,Attribute,Alias
AccompanyingPeriod::class,,,acp
,AccompanyingPeriodWork::class,acp.works,acpw
,AccompanyingPeriodParticipation::class,acp.participations,acppart
,Location::class,acp.administrativeLocation,acploc
,ClosingMotive::class,acp.closingMotive,acpmotive
,UserJob::class,acp.job,acpjob
,Origin::class,acp.origin,acporigin
,Scope::class,acp.scopes,acpscope
,SocialIssue::class,acp.socialIssues,acpsocialissue
,User::class,acp.user,acpuser
AccompanyingPeriodWork::class,,,acpw
,AccompanyingPeriodWorkEvaluation::class,acpw.accompanyingPeriodWorkEvaluations,workeval
,User::class,acpw.referrers,acpwuser
,SocialAction::class,acpw.socialAction,acpwsocialaction
,Goal::class,acpw.goals,goal
,Result::class,acpw.results,result
AccompanyingPeriodParticipation::class,,,acppart
,Person::class,acppart.person,partperson
AccompanyingPeriodWorkEvaluation::class,,,workeval
,Evaluation::class,workeval.evaluation,eval
Goal::class,,,goal
,Result::class,goal.results,goalresult
Person::class,,,person
,Center::class,person.center,center
,HouseholdMember::class,partperson.householdParticipations,householdmember
,MaritalStatus::class,person.maritalStatus,personmarital
,VendeePerson::class,,vp
,VendeePersonMineur::class,,vpm
ResidentialAddress::class,,,resaddr
,ThirdParty::class,resaddr.hostThirdParty,tparty
ThirdParty::class,,,tparty
,ThirdPartyCategory::class,tparty.categories,tpartycat
HouseholdMember::class,,,householdmember
,Household::class,householdmember.household,household
,Person::class,householdmember.person,memberperson
,,memberperson.center,membercenter
Household::class,,,household
,HouseholdComposition::class,household.compositions,composition
Activity::class,,,activity
,Person::class,activity.person,actperson
,AccompanyingPeriod::class,activity.accompanyingPeriod,acp
,Person::class,activity_person_having_activity.person,person_person_having_activity
,ActivityReason::class,activity_person_having_activity.reasons,reasons_person_having_activity
,ActivityType::class,activity.activityType,acttype
,Location::class,activity.location,actloc
,SocialAction::class,activity.socialActions,actsocialaction
,SocialIssue::class,activity.socialIssues,actsocialssue
,ThirdParty::class,activity.thirdParties,acttparty
,User::class,activity.user,actuser
,User::class,activity.users,actusers
,ActivityReason::class,activity.reasons,actreasons
,Center::class,actperson.center,actcenter
ActivityReason::class,,,actreasons
,ActivityReasonCategory::class,actreason.category,actreasoncat
Calendar::class,,,cal
,CancelReason::class,cal.cancelReason,calcancel
,Location::class,cal.location,calloc
,User::class,cal.user,caluser
VendeePerson::class,,,vp
,SituationProfessionelle::class,vp.situationProfessionelle,vpprof
,StatutLogement::class,vp.statutLogement,vplog
,TempsDeTravail::class,vp.tempsDeTravail,vptt
1 Entity Join Attribute Alias
2 AccompanyingPeriod::class acp
3 AccompanyingPeriodWork::class acp.works acpw
4 AccompanyingPeriodParticipation::class acp.participations acppart
5 Location::class acp.administrativeLocation acploc
6 ClosingMotive::class acp.closingMotive acpmotive
7 UserJob::class acp.job acpjob
8 Origin::class acp.origin acporigin
9 Scope::class acp.scopes acpscope
10 SocialIssue::class acp.socialIssues acpsocialissue
11 User::class acp.user acpuser
12 AccompanyingPeriodWork::class acpw
13 AccompanyingPeriodWorkEvaluation::class acpw.accompanyingPeriodWorkEvaluations workeval
14 User::class acpw.referrers acpwuser
15 SocialAction::class acpw.socialAction acpwsocialaction
16 Goal::class acpw.goals goal
17 Result::class acpw.results result
18 AccompanyingPeriodParticipation::class acppart
19 Person::class acppart.person partperson
20 AccompanyingPeriodWorkEvaluation::class workeval
21 Evaluation::class workeval.evaluation eval
22 Goal::class goal
23 Result::class goal.results goalresult
24 Person::class person
25 Center::class person.center center
26 HouseholdMember::class partperson.householdParticipations householdmember
27 MaritalStatus::class person.maritalStatus personmarital
28 VendeePerson::class vp
29 VendeePersonMineur::class vpm
30 ResidentialAddress::class resaddr
31 ThirdParty::class resaddr.hostThirdParty tparty
32 ThirdParty::class tparty
33 ThirdPartyCategory::class tparty.categories tpartycat
34 HouseholdMember::class householdmember
35 Household::class householdmember.household household
36 Person::class householdmember.person memberperson
37 memberperson.center membercenter
38 Household::class household
39 HouseholdComposition::class household.compositions composition
40 Activity::class activity
41 Person::class activity.person actperson
42 AccompanyingPeriod::class activity.accompanyingPeriod acp
43 Person::class activity_person_having_activity.person person_person_having_activity
44 ActivityReason::class activity_person_having_activity.reasons reasons_person_having_activity
45 ActivityType::class activity.activityType acttype
46 Location::class activity.location actloc
47 SocialAction::class activity.socialActions actsocialaction
48 SocialIssue::class activity.socialIssues actsocialssue
49 ThirdParty::class activity.thirdParties acttparty
50 User::class activity.user actuser
51 User::class activity.users actusers
52 ActivityReason::class activity.reasons actreasons
53 Center::class actperson.center actcenter
54 ActivityReason::class actreasons
55 ActivityReasonCategory::class actreason.category actreasoncat
56 Calendar::class cal
57 CancelReason::class cal.cancelReason calcancel
58 Location::class cal.location calloc
59 User::class cal.user caluser
60 VendeePerson::class vp
61 SituationProfessionelle::class vp.situationProfessionelle vpprof
62 StatutLogement::class vp.statutLogement vplog
63 TempsDeTravail::class vp.tempsDeTravail vptt

View File

@ -18,9 +18,9 @@ These are alias conventions :
| | SocialIssue::class | acp.socialIssues | acpsocialissue |
| | User::class | acp.user | acpuser |
| | AccompanyingPeriopStepHistory::class | acp.stepHistories | acpstephistories |
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
| AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | User::class | acpw.referrers | acpwuser |
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
| | Goal::class | acpw.goals | goal |
| | Result::class | acpw.results | result |
@ -28,6 +28,8 @@ These are alias conventions :
| | Person::class | acppart.person | partperson |
| AccompanyingPeriodWorkEvaluation::class | | | workeval |
| | Evaluation::class | workeval.evaluation | eval |
| AccompanyingPeriodInfo::class | | | acpinfo |
| | User::class | acpinfo.user | acpinfo_user |
| Goal::class | | | goal |
| | Result::class | goal.results | goalresult |
| Person::class | | | person |

View File

@ -2,6 +2,7 @@ parameters:
level: 5
paths:
- src/
- utils/
tmpDir: .cache/
reportUnmatchedIgnoredErrors: false
excludePaths:

29
phpunit.rector.xml Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd"
bootstrap="tests/app/vendor/autoload.php"
cacheResultFile=".cache/phpunit/test-results-rector"
executionOrder="depends,defects"
forceCoversAnnotation="true"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
convertDeprecationsToExceptions="true"
failOnRisky="true"
failOnWarning="true"
verbose="true"
colors="true"
>
<testsuites>
<testsuite name="default">
<directory>utils/rector/tests</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory=".cache/phpunit/code-coverage-rector"
processUncoveredFiles="true">
<include>
<directory suffix=".php">utils/rector/src</directory>
</include>
</coverage>
</phpunit>

View File

@ -2,26 +2,51 @@
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="tests/app/vendor/phpunit/phpunit/phpunit.xsd"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/app/tests/bootstrap.php"
bootstrap="tests/bootstrap.php"
>
<php>
<ini name="error_reporting" value="-1" />
<server name="APP_ENV" value="test" force="true" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[direct]=0&amp;max[indirect]=999999" />
<server name="SHELL_VERBOSITY" value="-1" />
<env name="KERNEL_CLASS" value="\App\Kernel" />
</php>
<testsuites>
<!--
<testsuite name="ActivityBundle">
<directory suffix="Test.php">src/Bundle/ChillActivityBundle/Tests/</directory>
</testsuite>
-->
<testsuite name="AsideActivityBundle">
<directory suffix="Test.php">src/Bundle/ChillAsideActivityBundle/src/Tests/</directory>
</testsuite>
<testsuite name="BudgetBundle">
<directory suffix="Test.php">src/Bundle/ChillBudgetBundle/Tests/</directory>
</testsuite>
<testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
</testsuite>
<!-- Missing CustomFieldBundle -->
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
</testsuite>
<testsuite name="DocStoreBundle">
<directory suffix="Test.php">src/Bundle/ChillDocStoreBundle/Tests/</directory>
</testsuite>
<!--
<testsuite name="EventBundle">
<directory suffix="Test.php">src/Bundle/ChillEventBundle/tests/</directory>
</testsuite>
-->
<testsuite name="MainBundle">
<directory suffix="Test.php">src/Bundle/ChillMainBundle/Tests/</directory>
</testsuite>
<testsuite name="PersonBundle">
<directory suffix="Test.php">src/Bundle/ChillPersonBundle/Tests/</directory>
<!-- test for export will be runned later -->
<exclude>src/Bundle/ChillPersonBundle/Tests/Export/*</exclude>
<!-- we are rewriting accompanying periods... Work in progress -->
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/AccompanyingPeriodControllerTest.php</exclude>
<!-- we are rewriting address, Work in progress -->
@ -31,14 +56,18 @@
<!-- temporarily removed, the time to find a fix -->
<exclude>src/Bundle/ChillPersonBundle/Tests/Controller/PersonDuplicateControllerViewTest.php</exclude>
</testsuite>
<testsuite name="AsideActivityBundle">
<directory suffix="Test.php">src/Bundle/ChillAsideActivityBundle/src/Tests/</directory>
<!--
<testsuite name="ReportBundle">
<directory suffix="Test.php">src/Bundle/ChillReportBundle/Tests/</directory>
</testsuite>
<testsuite name="CalendarBundle">
<directory suffix="Test.php">src/Bundle/ChillCalendarBundle/Tests/</directory>
-->
<!--
<testsuite name="TaskBundle">
<directory suffix="Test.php">src/Bundle/ChillTaskBundle/Tests</directory>
</testsuite>
<testsuite name="DocGeneratorBundle">
<directory suffix="Test.php">src/Bundle/ChillDocGeneratorBundle/tests/</directory>
-->
<testsuite name="ThirdPartyBundle">
<directory suffix="Test.php">src/Bundle/ChillThirdPartyBundle/Tests</directory>
</testsuite>
<testsuite name="WopiBundle">
<directory suffix="Test.php">src/Bundle/ChillWopiBundle/tests/</directory>

View File

@ -2,6 +2,13 @@
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
@ -12,14 +19,37 @@ return static function (RectorConfig $rectorConfig): void {
__DIR__ . '/src',
]);
$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
$rectorConfig->cacheDirectory(__DIR__.'/.cache/rector');
$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/testsApp_KernelDevDebugContainer.xml');
$rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php');
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
$rectorConfig->disableParallel();
//define sets of rules
// $rectorConfig->sets([
// LevelSetList::UP_TO_PHP_74
// ]);
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
\Rector\Symfony\Set\SymfonyLevelSetList::UP_TO_SYMFONY_44,
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
\Rector\PHPUnit\Set\PHPUnitLevelSetList::UP_TO_PHPUNIT_90,
]);
// some routes are added twice if it remains activated
// $rectorConfig->rule(\Rector\Symfony\Configs\Rector\ClassMethod\AddRouteAnnotationRector::class);
// chill rules
//$rectorConfig->rule(\Chill\Utils\Rector\Rector\ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector::class);
// skip some path...
$rectorConfig->skip([
// we need to discuss this: are we going to have FALSE in tests instead of an error ?
\Rector\Php71\Rector\FuncCall\CountOnNullRector::class,
// we must adapt service definition
\Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector::class,
\Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector::class,
]);
};

View File

@ -13,6 +13,4 @@ namespace Chill\ActivityBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillActivityBundle extends Bundle
{
}
class ChillActivityBundle extends Bundle {}

View File

@ -20,20 +20,22 @@ use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\UserJob;
use Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Repository\LocationRepository;
use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
@ -43,80 +45,36 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
final class ActivityController extends AbstractController
{
private AccompanyingPeriodRepository $accompanyingPeriodRepository;
private ActivityACLAwareRepositoryInterface $activityACLAwareRepository;
private ActivityRepository $activityRepository;
private ActivityTypeCategoryRepository $activityTypeCategoryRepository;
private ActivityTypeRepositoryInterface $activityTypeRepository;
private CenterResolverManagerInterface $centerResolver;
private EntityManagerInterface $entityManager;
private EventDispatcherInterface $eventDispatcher;
private LocationRepository $locationRepository;
private LoggerInterface $logger;
private PersonRepository $personRepository;
private SerializerInterface $serializer;
private ThirdPartyRepository $thirdPartyRepository;
private TranslatorInterface $translator;
private UserRepositoryInterface $userRepository;
public function __construct(
ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
ActivityTypeRepositoryInterface $activityTypeRepository,
ActivityTypeCategoryRepository $activityTypeCategoryRepository,
PersonRepository $personRepository,
ThirdPartyRepository $thirdPartyRepository,
LocationRepository $locationRepository,
ActivityRepository $activityRepository,
AccompanyingPeriodRepository $accompanyingPeriodRepository,
EntityManagerInterface $entityManager,
EventDispatcherInterface $eventDispatcher,
LoggerInterface $logger,
SerializerInterface $serializer,
UserRepositoryInterface $userRepository,
CenterResolverManagerInterface $centerResolver,
TranslatorInterface $translator
) {
$this->activityACLAwareRepository = $activityACLAwareRepository;
$this->activityTypeRepository = $activityTypeRepository;
$this->activityTypeCategoryRepository = $activityTypeCategoryRepository;
$this->personRepository = $personRepository;
$this->thirdPartyRepository = $thirdPartyRepository;
$this->locationRepository = $locationRepository;
$this->activityRepository = $activityRepository;
$this->accompanyingPeriodRepository = $accompanyingPeriodRepository;
$this->entityManager = $entityManager;
$this->eventDispatcher = $eventDispatcher;
$this->logger = $logger;
$this->serializer = $serializer;
$this->userRepository = $userRepository;
$this->centerResolver = $centerResolver;
$this->translator = $translator;
}
private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository,
private readonly ActivityTypeRepositoryInterface $activityTypeRepository,
private readonly ActivityTypeCategoryRepository $activityTypeCategoryRepository,
private readonly PersonRepository $personRepository,
private readonly ThirdPartyRepository $thirdPartyRepository,
private readonly LocationRepository $locationRepository,
private readonly ActivityRepository $activityRepository,
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
private readonly EntityManagerInterface $entityManager,
private readonly EventDispatcherInterface $eventDispatcher,
private readonly LoggerInterface $logger,
private readonly SerializerInterface $serializer,
private readonly UserRepositoryInterface $userRepository,
private readonly CenterResolverManagerInterface $centerResolver,
private readonly TranslatorInterface $translator,
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly PaginatorFactory $paginatorFactory,
) {}
/**
* Deletes a Activity entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/delete", name="chill_activity_activity_delete", methods={"GET", "POST", "DELETE"})
*/
public function deleteAction(Request $request, $id)
public function deleteAction(Request $request, mixed $id)
{
$view = null;
@ -129,10 +87,10 @@ final class ActivityController extends AbstractController
}
if ($activity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:confirm_deleteAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/confirm_deleteAccompanyingCourse.html.twig';
$accompanyingPeriod = $activity->getAccompanyingPeriod();
} else {
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
$view = '@ChillActivity/Activity/confirm_deletePerson.html.twig';
}
// TODO
@ -140,7 +98,7 @@ final class ActivityController extends AbstractController
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if ($request->getMethod() === Request::METHOD_DELETE) {
if (Request::METHOD_DELETE === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
@ -173,10 +131,6 @@ final class ActivityController extends AbstractController
}
}
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
@ -187,6 +141,8 @@ final class ActivityController extends AbstractController
/**
* Displays a form to edit an existing Activity entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/edit", name="chill_activity_activity_edit", methods={"GET", "POST", "PUT"})
*/
public function editAction(int $id, Request $request): Response
{
@ -202,10 +158,10 @@ final class ActivityController extends AbstractController
$person = $entity->getPerson();
if ($entity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:editAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/editAccompanyingCourse.html.twig';
$accompanyingPeriod = $entity->getAccompanyingPeriod();
} else {
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
$view = '@ChillActivity/Activity/editPerson.html.twig';
}
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity);
@ -266,10 +222,6 @@ final class ActivityController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
@ -284,34 +236,64 @@ final class ActivityController extends AbstractController
/**
* Lists all Activity entities.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/", name="chill_activity_activity_list")
*/
public function listAction(Request $request): Response
{
$view = null;
$activities = [];
// TODO: add pagination
[$person, $accompanyingPeriod] = $this->getEntity($request);
$filter = $this->buildFilterOrder($person ?? $accompanyingPeriod);
$filterArgs = [
'my_activities' => $filter->getSingleCheckboxData('my_activities'),
'types' => $filter->hasEntityChoice('activity_types') ? $filter->getEntityChoiceData('activity_types') : [],
'jobs' => $filter->hasEntityChoice('jobs') ? $filter->getEntityChoiceData('jobs') : [],
'before' => $filter->getDateRangeData('activity_date')['to'],
'after' => $filter->getDateRangeData('activity_date')['from'],
];
if ($person instanceof Person) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $person);
$count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE, $filterArgs);
$paginator = $this->paginatorFactory->create($count);
$activities = $this->activityACLAwareRepository
->findByPerson($person, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
->findByPerson(
$person,
ActivityVoter::SEE,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
['date' => 'DESC', 'id' => 'DESC'],
$filterArgs
);
$event = new PrivacyEvent($person, [
'element_class' => Activity::class,
'action' => 'list',
]);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$this->eventDispatcher->dispatch($event, PrivacyEvent::PERSON_PRIVACY_EVENT);
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
$view = '@ChillActivity/Activity/listPerson.html.twig';
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
$count = $this->activityACLAwareRepository->countByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, $filterArgs);
$paginator = $this->paginatorFactory->create($count);
$activities = $this->activityACLAwareRepository
->findByAccompanyingPeriod($accompanyingPeriod, ActivityVoter::SEE, 0, null, ['date' => 'DESC', 'id' => 'DESC']);
->findByAccompanyingPeriod(
$accompanyingPeriod,
ActivityVoter::SEE,
$paginator->getCurrentPageFirstItemNumber(),
$paginator->getItemsPerPage(),
['date' => 'DESC', 'id' => 'DESC'],
$filterArgs
);
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/listAccompanyingCourse.html.twig';
} else {
throw new \LogicException('Unsupported');
}
return $this->render(
@ -320,10 +302,49 @@ final class ActivityController extends AbstractController
'activities' => $activities,
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
'filter' => $filter,
'paginator' => $paginator,
]
);
}
private function buildFilterOrder(AccompanyingPeriod|Person $associated): FilterOrderHelper
{
$filterBuilder = $this->filterOrderHelperFactory->create(self::class);
$types = $this->activityACLAwareRepository->findActivityTypeByAssociated($associated);
$jobs = $this->activityACLAwareRepository->findUserJobByAssociated($associated);
$filterBuilder
->addDateRange('activity_date', 'activity.date')
->addSingleCheckbox('my_activities', 'activity_filter.My activities');
if (1 < count($types)) {
$filterBuilder
->addEntityChoice('activity_types', 'activity_filter.Types', \Chill\ActivityBundle\Entity\ActivityType::class, $types, [
'choice_label' => function (\Chill\ActivityBundle\Entity\ActivityType $activityType) {
$text = match ($activityType->hasCategory()) {
true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()).' > ',
false => '',
};
return $text.$this->translatableStringHelper->localize($activityType->getName());
},
]);
}
if (1 < count($jobs)) {
$filterBuilder
->addEntityChoice('jobs', 'activity_filter.Jobs', UserJob::class, $jobs, [
'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel()),
]);
}
return $filterBuilder->build();
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/new", name="chill_activity_activity_new", methods={"POST", "GET"})
*/
public function newAction(Request $request): Response
{
$view = null;
@ -331,16 +352,16 @@ final class ActivityController extends AbstractController
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:newAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/newAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:newPerson.html.twig';
$view = '@ChillActivity/Activity/newPerson.html.twig';
}
$activityType_id = $request->get('activityType_id', 0);
$activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new InvalidArgumentException('Activity type must be active');
throw new \InvalidArgumentException('Activity type must be active');
}
$activityData = null;
@ -375,45 +396,45 @@ final class ActivityController extends AbstractController
}
$entity->setActivityType($activityType);
$entity->setDate(new DateTime('now'));
$entity->setDate(new \DateTime('now'));
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (array_key_exists('durationTime', $activityData) && $activityType->getDurationTimeVisible() > 0) {
if (\array_key_exists('durationTime', $activityData) && $activityType->getDurationTimeVisible() > 0) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
$duration = DateTime::createFromFormat('H:i', $hours . ':' . $minutes);
$duration = \DateTime::createFromFormat('H:i', $hours.':'.$minutes);
if ($duration) {
$entity->setDurationTime($duration);
}
}
if (array_key_exists('date', $activityData)) {
$date = DateTime::createFromFormat('Y-m-d', $activityData['date']);
if (\array_key_exists('date', $activityData)) {
$date = \DateTime::createFromFormat('Y-m-d', $activityData['date']);
if ($date) {
$entity->setDate($date);
}
}
if (array_key_exists('personsId', $activityData) && $activityType->getPersonsVisible() > 0) {
if (\array_key_exists('personsId', $activityData) && $activityType->getPersonsVisible() > 0) {
foreach ($activityData['personsId'] as $personId) {
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (array_key_exists('professionalsId', $activityData) && $activityType->getThirdPartiesVisible() > 0) {
if (\array_key_exists('professionalsId', $activityData) && $activityType->getThirdPartiesVisible() > 0) {
foreach ($activityData['professionalsId'] as $professionalsId) {
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (array_key_exists('usersId', $activityData) && $activityType->getUsersVisible() > 0) {
if (\array_key_exists('usersId', $activityData) && $activityType->getUsersVisible() > 0) {
foreach ($activityData['usersId'] as $userId) {
$user = $this->userRepository->find($userId);
@ -423,16 +444,16 @@ final class ActivityController extends AbstractController
}
}
if (array_key_exists('location', $activityData) && $activityType->getLocationVisible() > 0) {
if (\array_key_exists('location', $activityData) && $activityType->getLocationVisible() > 0) {
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
if (array_key_exists('comment', $activityData) && $activityType->getCommentVisible() > 0) {
if (\array_key_exists('comment', $activityData) && $activityType->getCommentVisible() > 0) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());
$comment->setDate(new DateTime('now'));
$comment->setDate(new \DateTime('now'));
$entity->setComment($comment);
}
}
@ -504,6 +525,9 @@ final class ActivityController extends AbstractController
]);
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/select-type", name="chill_activity_activity_select_type")
*/
public function selectTypeAction(Request $request): Response
{
$view = null;
@ -511,9 +535,9 @@ final class ActivityController extends AbstractController
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:selectTypeAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/selectTypeAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:selectTypePerson.html.twig';
$view = '@ChillActivity/Activity/selectTypePerson.html.twig';
}
$data = [];
@ -548,6 +572,9 @@ final class ActivityController extends AbstractController
]);
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/show", name="chill_activity_activity_show")
*/
public function showAction(Request $request, int $id): Response
{
$entity = $this->activityRepository->find($id);
@ -560,11 +587,11 @@ final class ActivityController extends AbstractController
$person = $entity->getPerson();
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = 'ChillActivityBundle:Activity:showAccompanyingCourse.html.twig';
$view = '@ChillActivity/Activity/showAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
$view = '@ChillActivity/Activity/showPerson.html.twig';
} else {
throw new RuntimeException('the activity should be linked with a period or person');
throw new \RuntimeException('the activity should be linked with a period or person');
}
if (null !== $accompanyingPeriod) {
@ -587,10 +614,6 @@ final class ActivityController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,

View File

@ -24,6 +24,8 @@ class ActivityReasonCategoryController extends AbstractController
{
/**
* Creates a new ActivityReasonCategory entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/create", name="chill_activity_activityreasoncategory_create", methods={"POST"})
*/
public function createAction(Request $request)
{
@ -31,15 +33,15 @@ class ActivityReasonCategoryController extends AbstractController
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_show', ['id' => $entity->getId()]));
return $this->redirectToRoute('chill_activity_activityreasoncategory_show', ['id' => $entity->getId()]);
}
return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
@ -48,9 +50,9 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Displays a form to edit an existing ActivityReasonCategory entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/{id}/edit", name="chill_activity_activityreasoncategory_edit")
*/
public function editAction($id)
public function editAction(mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -62,7 +64,7 @@ class ActivityReasonCategoryController extends AbstractController
$editForm = $this->createEditForm($entity);
return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
@ -70,6 +72,8 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Lists all ActivityReasonCategory entities.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/", name="chill_activity_activityreasoncategory")
*/
public function indexAction()
{
@ -77,20 +81,22 @@ class ActivityReasonCategoryController extends AbstractController
$entities = $em->getRepository(\Chill\ActivityBundle\Entity\ActivityReasonCategory::class)->findAll();
return $this->render('ChillActivityBundle:ActivityReasonCategory:index.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/index.html.twig', [
'entities' => $entities,
]);
}
/**
* Displays a form to create a new ActivityReasonCategory entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/new", name="chill_activity_activityreasoncategory_new")
*/
public function newAction()
{
$entity = new ActivityReasonCategory();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReasonCategory:new.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
@ -99,9 +105,9 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Finds and displays a ActivityReasonCategory entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/{id}/show", name="chill_activity_activityreasoncategory_show")
*/
public function showAction($id)
public function showAction(mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -111,7 +117,7 @@ class ActivityReasonCategoryController extends AbstractController
throw $this->createNotFoundException('Unable to find ActivityReasonCategory entity.');
}
return $this->render('ChillActivityBundle:ActivityReasonCategory:show.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/show.html.twig', [
'entity' => $entity,
]);
}
@ -119,9 +125,9 @@ class ActivityReasonCategoryController extends AbstractController
/**
* Edits an existing ActivityReasonCategory entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreasoncategory/{id}/update", name="chill_activity_activityreasoncategory_update", methods={"POST", "PUT"})
*/
public function updateAction(Request $request, $id)
public function updateAction(Request $request, mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -134,13 +140,13 @@ class ActivityReasonCategoryController extends AbstractController
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreasoncategory_edit', ['id' => $id]));
return $this->redirectToRoute('chill_activity_activityreasoncategory_edit', ['id' => $id]);
}
return $this->render('ChillActivityBundle:ActivityReasonCategory:edit.html.twig', [
return $this->render('@ChillActivity/ActivityReasonCategory/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);

View File

@ -24,15 +24,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class ActivityReasonController extends AbstractController
{
private ActivityReasonRepository $activityReasonRepository;
public function __construct(ActivityReasonRepository $activityReasonRepository)
{
$this->activityReasonRepository = $activityReasonRepository;
}
public function __construct(private readonly ActivityReasonRepository $activityReasonRepository) {}
/**
* Creates a new ActivityReason entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/create", name="chill_activity_activityreason_create", methods={"POST"})
*/
public function createAction(Request $request)
{
@ -40,15 +37,15 @@ class ActivityReasonController extends AbstractController
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreason', ['id' => $entity->getId()]));
return $this->redirectToRoute('chill_activity_activityreason', ['id' => $entity->getId()]);
}
return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', [
return $this->render('@ChillActivity/ActivityReason/new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
@ -57,9 +54,9 @@ class ActivityReasonController extends AbstractController
/**
* Displays a form to edit an existing ActivityReason entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/{id}/edit", name="chill_activity_activityreason_edit")
*/
public function editAction($id)
public function editAction(mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -71,7 +68,7 @@ class ActivityReasonController extends AbstractController
$editForm = $this->createEditForm($entity);
return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', [
return $this->render('@ChillActivity/ActivityReason/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
@ -79,6 +76,8 @@ class ActivityReasonController extends AbstractController
/**
* Lists all ActivityReason entities.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/", name="chill_activity_activityreason")
*/
public function indexAction()
{
@ -86,20 +85,22 @@ class ActivityReasonController extends AbstractController
$entities = $this->activityReasonRepository->findAll();
return $this->render('ChillActivityBundle:ActivityReason:index.html.twig', [
return $this->render('@ChillActivity/ActivityReason/index.html.twig', [
'entities' => $entities,
]);
}
/**
* Displays a form to create a new ActivityReason entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/new", name="chill_activity_activityreason_new")
*/
public function newAction()
{
$entity = new ActivityReason();
$form = $this->createCreateForm($entity);
return $this->render('ChillActivityBundle:ActivityReason:new.html.twig', [
return $this->render('@ChillActivity/ActivityReason/new.html.twig', [
'entity' => $entity,
'form' => $form->createView(),
]);
@ -108,9 +109,9 @@ class ActivityReasonController extends AbstractController
/**
* Finds and displays a ActivityReason entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/{id}/show", name="chill_activity_activityreason_show")
*/
public function showAction($id)
public function showAction(mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -120,7 +121,7 @@ class ActivityReasonController extends AbstractController
throw $this->createNotFoundException('Unable to find ActivityReason entity.');
}
return $this->render('ChillActivityBundle:ActivityReason:show.html.twig', [
return $this->render('@ChillActivity/ActivityReason/show.html.twig', [
'entity' => $entity,
]);
}
@ -128,9 +129,9 @@ class ActivityReasonController extends AbstractController
/**
* Edits an existing ActivityReason entity.
*
* @param mixed $id
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activityreason/{id}/update", name="chill_activity_activityreason_update", methods={"POST", "PUT"})
*/
public function updateAction(Request $request, $id)
public function updateAction(Request $request, mixed $id)
{
$em = $this->getDoctrine()->getManager();
@ -143,13 +144,13 @@ class ActivityReasonController extends AbstractController
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('chill_activity_activityreason', ['id' => $id]));
return $this->redirectToRoute('chill_activity_activityreason', ['id' => $id]);
}
return $this->render('ChillActivityBundle:ActivityReason:edit.html.twig', [
return $this->render('@ChillActivity/ActivityReason/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);

View File

@ -24,7 +24,7 @@ class AdminActivityPresenceController extends CRUDController
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
/** @var \Doctrine\ORM\QueryBuilder $query */
/* @var \Doctrine\ORM\QueryBuilder $query */
return $query->orderBy('e.id', 'ASC');
}
}

View File

@ -24,7 +24,7 @@ class AdminActivityTypeCategoryController extends CRUDController
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
/** @var \Doctrine\ORM\QueryBuilder $query */
/* @var \Doctrine\ORM\QueryBuilder $query */
return $query->orderBy('e.ordering', 'ASC');
}
}

View File

@ -24,7 +24,7 @@ class AdminActivityTypeController extends CRUDController
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
/** @var \Doctrine\ORM\QueryBuilder $query */
/* @var \Doctrine\ORM\QueryBuilder $query */
return $query->orderBy('e.ordering', 'ASC')
->addOrderBy('e.id', 'ASC');
}

View File

@ -18,11 +18,18 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
*/
class AdminController extends AbstractController
{
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activity", name="chill_activity_admin_index")
*/
public function indexActivityAction()
{
return $this->render('ChillActivityBundle:Admin:layout_activity.html.twig');
return $this->render('@ChillActivity/Admin/layout_activity.html.twig');
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activity_redirect_to_main", name="chill_admin_aside_activity_redirect_to_admin_index", options={null})
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/admin/activity_redirect_to_main", name="chill_admin_activity_redirect_to_admin_index")
*/
public function redirectToAdminIndexAction()
{
return $this->redirectToRoute('chill_main_admin_central');

View File

@ -25,17 +25,11 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
private EntityManagerInterface $em;
private readonly \Faker\Generator $faker;
/**
* @var \Faker\Generator
*/
private $faker;
public function __construct(EntityManagerInterface $em)
public function __construct(private readonly EntityManagerInterface $em)
{
$this->faker = FakerFactory::create('fr_FR');
$this->em = $em;
}
public function getOrder()
@ -50,7 +44,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
->findAll();
foreach ($persons as $person) {
$activityNbr = mt_rand(0, 3);
$activityNbr = random_int(0, 3);
for ($i = 0; $i < $activityNbr; ++$i) {
$activity = $this->newRandomActivity($person);
@ -75,7 +69,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
// ->setAttendee($this->faker->boolean())
for ($i = 0; mt_rand(0, 4) > $i; ++$i) {
for ($i = 0; random_int(0, 4) > $i; ++$i) {
$reason = $this->getRandomActivityReason();
if (null !== $reason) {

View File

@ -54,7 +54,7 @@ class LoadActivityReason extends AbstractFixture implements OrderedFixtureInterf
foreach ($reasons as $r) {
echo 'Creating activity reason : '.$r['name']['en']."\n";
$activityReason = (new ActivityReason())
->setName(($r['name']))
->setName($r['name'])
->setActive(true)
->setCategory($this->getReference($r['category']));
$manager->persist($activityReason);

View File

@ -36,7 +36,7 @@ class LoadActivityReasonCategory extends AbstractFixture implements OrderedFixtu
foreach ($categs as $c) {
echo 'Creating activity reason category : '.$c['name']['en']."\n";
$activityReasonCategory = (new ActivityReasonCategory())
->setName(($c['name']))
->setName($c['name'])
->setActive(true);
$manager->persist($activityReasonCategory);
$this->addReference(

View File

@ -56,7 +56,7 @@ class LoadActivityType extends Fixture implements OrderedFixtureInterface
foreach ($types as $t) {
echo 'Creating activity type : '.$t['name']['fr'].' (cat:'.$t['category']." \n";
$activityType = (new ActivityType())
->setName(($t['name']))
->setName($t['name'])
->setCategory($this->getReference('activity_type_cat_'.$t['category']))
->setSocialIssuesVisible(1)
->setSocialActionsVisible(1);

View File

@ -45,7 +45,7 @@ class LoadActivityTypeCategory extends Fixture implements OrderedFixtureInterfac
echo 'Creating activity type category : '.$cat['ref']."\n";
$newCat = (new ActivityTypeCategory())
->setName(($cat['name']));
->setName($cat['name']);
$manager->persist($newCat);
$reference = 'activity_type_cat_'.$cat['ref'];

View File

@ -20,8 +20,6 @@ use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use function in_array;
/**
* Add a role CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE for all groups except administrative,
* and a role CHILL_ACTIVITY_SEE for administrative.
@ -43,7 +41,7 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
// create permission group
switch ($permissionsGroup->getName()) {
case 'social':
if ($scope->getName()['en'] === 'administrative') {
if ('administrative' === $scope->getName()['en']) {
break 2; // we do not want any power on administrative
}
@ -51,7 +49,7 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
case 'administrative':
case 'direction':
if (in_array($scope->getName()['en'], ['administrative', 'social'], true)) {
if (\in_array($scope->getName()['en'], ['administrative', 'social'], true)) {
break 2; // we do not want any power on social or administrative
}

View File

@ -13,7 +13,6 @@ namespace Chill\ActivityBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use function is_int;
/**
@ -26,7 +25,7 @@ class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_activity');
$rootNode = $treeBuilder->getRootNode('chill_activity');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()
@ -59,9 +58,7 @@ class Configuration implements ConfigurationInterface
->info('The number of seconds of this duration. Must be an integer.')
->cannotBeEmpty()
->validate()
->ifTrue(static function ($data) {
return !is_int($data);
})->thenInvalid('The value %s is not a valid integer')
->ifTrue(static fn ($data) => !\is_int($data))->thenInvalid('The value %s is not a valid integer')
->end()
->end()
->scalarNode('label')

View File

@ -36,7 +36,6 @@ use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
@ -46,11 +45,15 @@ use Symfony\Component\Validator\Constraints as Assert;
* Class Activity.
*
* @ORM\Entity(repositoryClass="Chill\ActivityBundle\Repository\ActivityRepository")
*
* @ORM\Table(name="activity")
*
* @ORM\HasLifecycleCallbacks
*
* @DiscriminatorMap(typeProperty="type", mapping={
* "activity": Activity::class
* })
*
* @ActivityValidator\ActivityValidity
*
* TODO see if necessary
@ -65,69 +68,84 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
use TrackUpdateTrait;
public const SENTRECEIVED_RECEIVED = 'received';
final public const SENTRECEIVED_RECEIVED = 'received';
public const SENTRECEIVED_SENT = 'sent';
final public const SENTRECEIVED_SENT = 'sent';
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod")
*
* @Groups({"read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityType")
*
* @Groups({"read", "docgen:read"})
*
* @SerializedName("activityType")
*
* @ORM\JoinColumn(name="type_id")
*/
private ActivityType $activityType;
/**
* @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\ActivityPresence")
*
* @Groups({"docgen:read"})
*/
private ?ActivityPresence $attendee = null;
/**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\CommentEmbeddable", columnPrefix="comment_")
*
* @Groups({"docgen:read"})
*/
private CommentEmbeddable $comment;
/**
* @ORM\Column(type="datetime")
*
* @Groups({"docgen:read"})
*/
private DateTime $date;
private \DateTime $date;
/**
* @ORM\ManyToMany(targetEntity="Chill\DocStoreBundle\Entity\StoredObject", cascade={"persist"})
*
* @Assert\Valid(traverse=true)
*
* @var Collection<StoredObject>
*/
private Collection $documents;
/**
* @ORM\Column(type="time", nullable=true)
*/
private ?DateTime $durationTime = null;
private ?\DateTime $durationTime = null;
/**
* @ORM\Column(type="boolean", options={"default": false})
*
* @Groups({"docgen:read"})
*/
private bool $emergency = false;
/**
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*
* @Groups({"read", "docgen:read"})
*/
private ?int $id = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location")
*
* @groups({"read", "docgen:read"})
*/
private ?Location $location = null;
@ -139,9 +157,12 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person")
*
* @Groups({"read", "docgen:read"})
*
* @var Collection<Person>
*/
private ?Collection $persons = null;
private Collection $persons;
/**
* @ORM\Embedded(class="Chill\MainBundle\Entity\Embeddable\PrivateCommentEmbeddable", columnPrefix="privateComment_")
@ -150,58 +171,78 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
/**
* @ORM\ManyToMany(targetEntity="Chill\ActivityBundle\Entity\ActivityReason")
*
* @Groups({"docgen:read"})
*
* @var Collection<ActivityReason>
*/
private Collection $reasons;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Scope")
*
* @Groups({"docgen:read"})
*/
private ?Scope $scope = null;
/**
* @ORM\Column(type="string", options={"default": ""})
*
* @Groups({"docgen:read"})
*/
private string $sentReceived = '';
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialAction")
*
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialaction")
*
* @Groups({"read", "docgen:read"})
*
* @var Collection<SocialAction>
*/
private Collection $socialActions;
/**
* @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\SocialWork\SocialIssue")
*
* @ORM\JoinTable(name="chill_activity_activity_chill_person_socialissue")
*
* @Groups({"read", "docgen:read"})
*
* @var Collection<SocialIssue>
*/
private Collection $socialIssues;
/**
* @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
*
* @Groups({"read", "docgen:read"})
*
* @var Collection<ThirdParty>
*/
private ?Collection $thirdParties = null;
private Collection $thirdParties;
/**
* @ORM\Column(type="time", nullable=true)
*/
private ?DateTime $travelTime = null;
private ?\DateTime $travelTime = null;
/**
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
*
* @Groups({"docgen:read"})
*/
private ?User $user;
private ?User $user = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
*
* @Groups({"read", "docgen:read"})
*
* @var Collection<User>
*/
private ?Collection $users = null;
private Collection $users;
public function __construct()
{
@ -268,7 +309,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
$this->socialIssues[] = $socialIssue;
}
if ($this->getAccompanyingPeriod() !== null) {
if (null !== $this->getAccompanyingPeriod()) {
$this->getAccompanyingPeriod()->addSocialIssue($socialIssue);
}
@ -334,7 +375,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->comment;
}
public function getDate(): DateTime
public function getDate(): \DateTime
{
return $this->date;
}
@ -356,7 +397,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return (int) round(($this->durationTime->getTimestamp() + $this->durationTime->getOffset()) / 60.0, 0);
}
public function getDurationTime(): ?DateTime
public function getDurationTime(): ?\DateTime
{
return $this->durationTime;
}
@ -410,7 +451,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
// TODO better semantic with: return $this->persons->filter(...);
foreach ($this->persons as $person) {
if ($this->accompanyingPeriod->getOpenParticipationContainsPerson($person) === null) {
if (null === $this->accompanyingPeriod->getOpenParticipationContainsPerson($person)) {
$personsNotAssociated[] = $person;
}
}
@ -469,7 +510,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->thirdParties;
}
public function getTravelTime(): ?DateTime
public function getTravelTime(): ?\DateTime
{
return $this->travelTime;
}
@ -580,7 +621,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this;
}
public function setDate(DateTime $date): self
public function setDate(\DateTime $date): self
{
$this->date = $date;
@ -594,7 +635,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this;
}
public function setDurationTime(?DateTime $durationTime): self
public function setDurationTime(?\DateTime $durationTime): self
{
$this->durationTime = $durationTime;
@ -664,7 +705,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this;
}
public function setTravelTime(DateTime $travelTime): self
public function setTravelTime(\DateTime $travelTime): self
{
$this->travelTime = $travelTime;

View File

@ -18,7 +18,9 @@ use Symfony\Component\Serializer\Annotation as Serializer;
* Class ActivityPresence.
*
* @ORM\Entity
*
* @ORM\Table(name="activitytpresence")
*
* @ORM\HasLifecycleCallbacks
*/
class ActivityPresence
@ -30,15 +32,20 @@ class ActivityPresence
/**
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*
* @Serializer\Groups({"docgen:read"})
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")
*
* @Serializer\Groups({"docgen:read"})
*
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $name = [];

View File

@ -17,39 +17,38 @@ use Doctrine\ORM\Mapping as ORM;
* Class ActivityReason.
*
* @ORM\Entity
*
* @ORM\Table(name="activityreason")
*
* @ORM\HasLifecycleCallbacks
*/
class ActivityReason
{
/**
* @var bool
* @ORM\Column(type="boolean")
*/
private $active = true;
private bool $active = true;
/**
* @var ActivityReasonCategory
* @ORM\ManyToOne(
* targetEntity="Chill\ActivityBundle\Entity\ActivityReasonCategory",
* inversedBy="reasons")
*/
private $category;
private ?ActivityReasonCategory $category = null;
/**
* @var int
*
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @var array
* @ORM\Column(type="json")
*/
private $name;
private array $name;
/**
* Get active.
@ -81,27 +80,9 @@ class ActivityReason
/**
* Get name.
*
* @param mixed|null $locale
*
* @return array | string
*/
public function getName($locale = null)
public function getName(): array
{
if ($locale) {
if (isset($this->name[$locale])) {
return $this->name[$locale];
}
foreach ($this->name as $name) {
if (!empty($name)) {
return $name;
}
}
return '';
}
return $this->name;
}

View File

@ -12,34 +12,37 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* Class ActivityReasonCategory.
*
* @ORM\Entity
*
* @ORM\Table(name="activityreasoncategory")
*
* @ORM\HasLifecycleCallbacks
*/
class ActivityReasonCategory
class ActivityReasonCategory implements \Stringable
{
/**
* @var bool
* @ORM\Column(type="boolean")
*/
private $active = true;
private bool $active = true;
/**
* @var int
*
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private ?int $id = null;
/**
* @var string
*
* @ORM\Column(type="json")
*/
private $name;
@ -47,12 +50,13 @@ class ActivityReasonCategory
/**
* Array of ActivityReason.
*
* @var ArrayCollection
* @var Collection<ActivityReason>
*
* @ORM\OneToMany(
* targetEntity="Chill\ActivityBundle\Entity\ActivityReason",
* mappedBy="category")
*/
private $reasons;
private Collection $reasons;
/**
* ActivityReasonCategory constructor.
@ -62,10 +66,7 @@ class ActivityReasonCategory
$this->reasons = new ArrayCollection();
}
/**
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'ActivityReasonCategory('.$this->getName('x').')';
}
@ -121,11 +122,9 @@ class ActivityReasonCategory
* as unactive, all the reason have this entity as category is also
* set as unactive.
*
* @param bool $active
*
* @return ActivityReasonCategory
*/
public function setActive($active)
public function setActive(bool $active)
{
if ($this->active !== $active && !$active) {
foreach ($this->reasons as $reason) {

View File

@ -12,7 +12,6 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
@ -22,31 +21,36 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
* Class ActivityType.
*
* @ORM\Entity
*
* @ORM\Table(name="activitytype")
*
* @ORM\HasLifecycleCallbacks
*/
class ActivityType
{
public const FIELD_INVISIBLE = 0;
final public const FIELD_INVISIBLE = 0;
public const FIELD_OPTIONAL = 1;
final public const FIELD_OPTIONAL = 1;
public const FIELD_REQUIRED = 2;
final public const FIELD_REQUIRED = 2;
/**
* @deprecated not in use
*
* @ORM\Column(type="string", nullable=false, options={"default": ""})
*/
private string $accompanyingPeriodLabel = '';
/**
* @deprecated not in use
*
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
*/
private int $accompanyingPeriodVisible = self::FIELD_INVISIBLE;
/**
* @ORM\Column(type="boolean")
*
* @Groups({"read"})
*/
private bool $active = true;
@ -118,11 +122,14 @@ class ActivityType
/**
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*
* @Groups({"docgen:read"})
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="string", nullable=false, options={"default": ""})
@ -136,7 +143,9 @@ class ActivityType
/**
* @ORM\Column(type="json")
*
* @Groups({"read", "docgen:read"})
*
* @Serializer\Context({"is-translatable": true}, groups={"docgen:read"})
*/
private array $name = [];
@ -158,6 +167,7 @@ class ActivityType
/**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
*
* @Groups({"read"})
*/
private int $personsVisible = self::FIELD_OPTIONAL;
@ -238,6 +248,7 @@ class ActivityType
/**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
*
* @Groups({"read"})
*/
private int $thirdPartiesVisible = self::FIELD_INVISIBLE;
@ -264,6 +275,7 @@ class ActivityType
/**
* @ORM\Column(type="smallint", nullable=false, options={"default": 1})
*
* @Groups({"read"})
*/
private int $usersVisible = self::FIELD_OPTIONAL;
@ -275,10 +287,8 @@ class ActivityType
/**
* @Assert\Callback
*
* @param mixed $payload
*/
public function checkSocialActionsVisibility(ExecutionContextInterface $context, $payload)
public function checkSocialActionsVisibility(ExecutionContextInterface $context, mixed $payload)
{
if ($this->socialIssuesVisible !== $this->socialActionsVisible) {
if (!(2 === $this->socialIssuesVisible && 1 === $this->socialActionsVisible)) {
@ -377,10 +387,10 @@ class ActivityType
$property = $field.'Label';
if (!property_exists($this, $property)) {
throw new InvalidArgumentException('Field "' . $field . '" not found');
throw new \InvalidArgumentException('Field "'.$field.'" not found');
}
/** @phpstan-ignore-next-line */
/* @phpstan-ignore-next-line */
return $this->{$property};
}
@ -536,10 +546,10 @@ class ActivityType
$property = $field.'Visible';
if (!property_exists($this, $property)) {
throw new InvalidArgumentException('Field "' . $field . '" not found');
throw new \InvalidArgumentException('Field "'.$field.'" not found');
}
/** @phpstan-ignore-next-line */
/* @phpstan-ignore-next-line */
return self::FIELD_REQUIRED === $this->{$property};
}
@ -548,10 +558,10 @@ class ActivityType
$property = $field.'Visible';
if (!property_exists($this, $property)) {
throw new InvalidArgumentException('Field "' . $field . '" not found');
throw new \InvalidArgumentException('Field "'.$field.'" not found');
}
/** @phpstan-ignore-next-line */
/* @phpstan-ignore-next-line */
return self::FIELD_INVISIBLE !== $this->{$property};
}

View File

@ -15,7 +15,9 @@ use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*
* @ORM\Table(name="activitytypecategory")
*
* @ORM\HasLifecycleCallbacks
*/
class ActivityTypeCategory
@ -27,7 +29,9 @@ class ActivityTypeCategory
/**
* @ORM\Id
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\GeneratedValue(strategy="AUTO")
*/
private ?int $id = null;

View File

@ -15,22 +15,11 @@ use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use function in_array;
class ActivityEntityListener
{
private EntityManagerInterface $em;
private AccompanyingPeriodWorkRepository $workRepository;
public function __construct(EntityManagerInterface $em, AccompanyingPeriodWorkRepository $workRepository)
{
$this->em = $em;
$this->workRepository = $workRepository;
}
public function __construct(private readonly EntityManagerInterface $em, private readonly AccompanyingPeriodWorkRepository $workRepository) {}
public function persistActionToCourse(Activity $activity)
{
@ -39,11 +28,11 @@ class ActivityEntityListener
$accompanyingCourseWorks = $this->workRepository->findByAccompanyingPeriod($period);
$periodActions = [];
$now = new DateTimeImmutable();
$now = new \DateTimeImmutable();
foreach ($accompanyingCourseWorks as $key => $work) {
// take only the actions which are still opened
if ($work->getEndDate() === null || $work->getEndDate() > ($activity->getDate() ?? $now)) {
if (null === $work->getEndDate() || $work->getEndDate() > ($activity->getDate() ?? $now)) {
$periodActions[$key] = spl_object_hash($work->getSocialAction());
}
}
@ -52,14 +41,14 @@ class ActivityEntityListener
$associatedThirdparties = $activity->getThirdParties();
foreach ($activity->getSocialActions() as $action) {
if (in_array(spl_object_hash($action), $periodActions, true)) {
if (\in_array(spl_object_hash($action), $periodActions, true)) {
continue;
}
$newAction = new AccompanyingPeriodWork();
$newAction->setSocialAction($action);
$period->addWork($newAction);
$date = DateTimeImmutable::createFromMutable($activity->getDate());
$date = \DateTimeImmutable::createFromMutable($activity->getDate());
$newAction->setStartDate($date);
foreach ($associatedPersons as $person) {

View File

@ -41,6 +41,11 @@ class ByActivityNumberAggregator implements AggregatorInterface
// No form needed
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return static function ($value) {

View File

@ -17,21 +17,10 @@ use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class BySocialActionAggregator implements AggregatorInterface
{
private SocialActionRender $actionRender;
private SocialActionRepository $actionRepository;
public function __construct(
SocialActionRender $actionRender,
SocialActionRepository $actionRepository
) {
$this->actionRender = $actionRender;
$this->actionRepository = $actionRepository;
}
public function __construct(private readonly SocialActionRender $actionRender, private readonly SocialActionRepository $actionRepository) {}
public function addRole(): ?string
{
@ -40,7 +29,7 @@ class BySocialActionAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actsocialaction', $qb->getAllAliases(), true)) {
if (!\in_array('actsocialaction', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialActions', 'actsocialaction');
}
@ -58,6 +47,11 @@ class BySocialActionAggregator implements AggregatorInterface
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value) {

View File

@ -17,21 +17,10 @@ use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class BySocialIssueAggregator implements AggregatorInterface
{
private SocialIssueRender $issueRender;
private SocialIssueRepository $issueRepository;
public function __construct(
SocialIssueRepository $issueRepository,
SocialIssueRender $issueRender
) {
$this->issueRepository = $issueRepository;
$this->issueRender = $issueRender;
}
public function __construct(private readonly SocialIssueRepository $issueRepository, private readonly SocialIssueRender $issueRender) {}
public function addRole(): ?string
{
@ -40,7 +29,7 @@ class BySocialIssueAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actsocialissue', $qb->getAllAliases(), true)) {
if (!\in_array('actsocialissue', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.socialIssues', 'actsocialissue');
}
@ -58,6 +47,11 @@ class BySocialIssueAggregator implements AggregatorInterface
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {

View File

@ -1,89 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorScopeAggregator implements AggregatorInterface
{
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actcreator', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.createdBy', 'actcreator');
}
$qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator');
$qb->addGroupBy('creatorscope_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Scope';
}
if (null === $value || '' === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return ['creatorscope_aggregator'];
}
public function getTitle(): string
{
return 'Group activity by creator scope';
}
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ActivityLocationAggregator implements AggregatorInterface
{
public const KEY = 'activity_location_aggregator';
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
}
$qb->addSelect(sprintf('actloc.name AS %s', self::KEY));
$qb->addGroupBy(self::KEY);
}
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
{
// no form required for this aggregator
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
{
return function ($value): string {
if ('_header' === $value) {
return 'export.aggregator.activity.by_location.Activity Location';
}
if (null === $value || '' === $value) {
return '';
}
return $value;
};
}
public function getQueryKeys($data): array
{
return [self::KEY];
}
public function getTitle()
{
return 'export.aggregator.activity.by_location.Title';
}
}

View File

@ -15,26 +15,14 @@ use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ActivityTypeAggregator implements AggregatorInterface
{
public const KEY = 'activity_type_aggregator';
final public const KEY = 'activity_type_aggregator';
protected ActivityTypeRepositoryInterface $activityTypeRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityTypeRepositoryInterface $activityTypeRepository,
TranslatableStringHelperInterface $translatableStringHelper
) {
$this->activityTypeRepository = $activityTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function __construct(protected ActivityTypeRepositoryInterface $activityTypeRepository, protected TranslatableStringHelperInterface $translatableStringHelper) {}
public function addRole(): ?string
{
@ -43,7 +31,7 @@ class ActivityTypeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acttype', $qb->getAllAliases(), true)) {
if (!\in_array('acttype', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.activityType', 'acttype');
}
@ -61,7 +49,12 @@ class ActivityTypeAggregator implements AggregatorInterface
// no form required for this aggregator
}
public function getLabels($key, array $values, $data): Closure
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
{
// for performance reason, we load data from db only once
$this->activityTypeRepository->findBy(['id' => $values]);

View File

@ -15,25 +15,14 @@ use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Templating\Entity\UserRender;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class ActivityUserAggregator implements AggregatorInterface
{
public const KEY = 'activity_user_id';
final public const KEY = 'activity_user_id';
private UserRender $userRender;
private UserRepository $userRepository;
public function __construct(
UserRepository $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function __construct(private readonly UserRepository $userRepository, private readonly UserRender $userRender) {}
public function addRole(): ?string
{
@ -59,7 +48,12 @@ class ActivityUserAggregator implements AggregatorInterface
// nothing to add
}
public function getLabels($key, $values, $data): Closure
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, $values, $data): \Closure
{
return function ($value) {
if ('_header' === $value) {

View File

@ -17,19 +17,10 @@ use Chill\MainBundle\Repository\UserRepositoryInterface;
use Chill\MainBundle\Templating\Entity\UserRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ActivityUsersAggregator implements AggregatorInterface
{
private UserRender $userRender;
private UserRepositoryInterface $userRepository;
public function __construct(UserRepositoryInterface $userRepository, UserRender $userRender)
{
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function __construct(private readonly UserRepositoryInterface $userRepository, private readonly UserRender $userRender) {}
public function addRole(): ?string
{
@ -38,7 +29,7 @@ class ActivityUsersAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actusers', $qb->getAllAliases(), true)) {
if (!\in_array('actusers', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.users', 'actusers');
}
@ -57,6 +48,11 @@ class ActivityUsersAggregator implements AggregatorInterface
// nothing to add on the form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value) {

View File

@ -12,23 +12,22 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorInterface
class ActivityUsersJobAggregator implements AggregatorInterface
{
private TranslatableStringHelperInterface $translatableStringHelper;
private const PREFIX = 'act_agg_user_job';
private UserJobRepositoryInterface $userJobRepository;
public function __construct(UserJobRepositoryInterface $userJobRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->userJobRepository = $userJobRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function __construct(
private readonly UserJobRepositoryInterface $userJobRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string
{
@ -37,23 +36,40 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actusers', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.users', 'actusers');
}
$p = self::PREFIX;
$qb
->addSelect('IDENTITY(actusers.userJob) AS activity_users_job_aggregator')
->addGroupBy('activity_users_job_aggregator');
->leftJoin('activity.users', "{$p}_user")
->leftJoin(
UserJobHistory::class,
"{$p}_history",
Expr\Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// job_at based on activity.date
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'activity.date')
)
)
)
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
// nothing to add in the form
return [];
}
public function getLabels($key, array $values, $data)
@ -77,11 +93,11 @@ class ActivityUsersJobAggregator implements \Chill\MainBundle\Export\AggregatorI
public function getQueryKeys($data): array
{
return ['activity_users_job_aggregator'];
return [self::PREFIX.'_select'];
}
public function getTitle()
public function getTitle(): string
{
return 'Aggregate by users job';
return 'export.aggregator.activity.by_user_job.Aggregate by users job';
}
}

View File

@ -12,23 +12,22 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\AggregatorInterface
class ActivityUsersScopeAggregator implements AggregatorInterface
{
private ScopeRepositoryInterface $scopeRepository;
private const PREFIX = 'act_agg_user_scope';
private TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(ScopeRepositoryInterface $scopeRepository, TranslatableStringHelperInterface $translatableStringHelper)
{
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function __construct(
private readonly ScopeRepositoryInterface $scopeRepository,
private readonly TranslatableStringHelperInterface $translatableStringHelper
) {}
public function addRole(): ?string
{
@ -37,23 +36,40 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actusers', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.users', 'actusers');
}
$p = self::PREFIX;
$qb
->addSelect('IDENTITY(actusers.mainScope) AS activity_users_main_scope_aggregator')
->addGroupBy('activity_users_main_scope_aggregator');
->leftJoin('activity.users', "{$p}_user")
->leftJoin(
UserScopeHistory::class,
"{$p}_history",
Expr\Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// scope_at based on activity.date
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'activity.date')
)
)
)
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn()
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
// nothing to add in the form
return [];
}
public function getLabels($key, array $values, $data)
@ -77,11 +93,11 @@ class ActivityUsersScopeAggregator implements \Chill\MainBundle\Export\Aggregato
public function getQueryKeys($data): array
{
return ['activity_users_main_scope_aggregator'];
return [self::PREFIX.'_select'];
}
public function getTitle()
public function getTitle(): string
{
return 'Aggregate by users scope';
return 'export.aggregator.activity.by_user_scope.Aggregate by users scope';
}
}

View File

@ -9,7 +9,7 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
@ -20,17 +20,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class ByCreatorAggregator implements AggregatorInterface
{
private UserRender $userRender;
private UserRepositoryInterface $userRepository;
public function __construct(
UserRepositoryInterface $userRepository,
UserRender $userRender
) {
$this->userRepository = $userRepository;
$this->userRender = $userRender;
}
public function __construct(private readonly UserRepositoryInterface $userRepository, private readonly UserRender $userRender) {}
public function addRole(): ?string
{
@ -45,7 +35,7 @@ class ByCreatorAggregator implements AggregatorInterface
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@ -53,6 +43,11 @@ class ByCreatorAggregator implements AggregatorInterface
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {

View File

@ -9,7 +9,7 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
@ -17,21 +17,10 @@ use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class ByThirdpartyAggregator implements AggregatorInterface
{
private ThirdPartyRender $thirdPartyRender;
private ThirdPartyRepository $thirdPartyRepository;
public function __construct(
ThirdPartyRepository $thirdPartyRepository,
ThirdPartyRender $thirdPartyRender
) {
$this->thirdPartyRepository = $thirdPartyRepository;
$this->thirdPartyRender = $thirdPartyRender;
}
public function __construct(private readonly ThirdPartyRepository $thirdPartyRepository, private readonly ThirdPartyRender $thirdPartyRender) {}
public function addRole(): ?string
{
@ -40,7 +29,7 @@ class ByThirdpartyAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('acttparty', $qb->getAllAliases(), true)) {
if (!\in_array('acttparty', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.thirdParties', 'acttparty');
}
@ -50,7 +39,7 @@ class ByThirdpartyAggregator implements AggregatorInterface
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@ -58,6 +47,11 @@ class ByThirdpartyAggregator implements AggregatorInterface
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {

View File

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class CreatorScopeAggregator implements AggregatorInterface
{
private const PREFIX = 'acp_agg_creator_scope';
public function __construct(
private readonly ScopeRepository $scopeRepository,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
$qb
->leftJoin('activity.createdBy', "{$p}_user")
->leftJoin(
UserScopeHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// scope_at based on activity.date
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'activity.date')
)
)
)
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Scope';
}
if (null === $value || '' === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return [self::PREFIX.'_select'];
}
public function getTitle(): string
{
return 'export.aggregator.activity.by_creator_scope.Group activity by creator scope';
}
}

View File

@ -9,15 +9,13 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Doctrine\ORM\QueryBuilder;
use RuntimeException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class DateAggregator implements AggregatorInterface
{
@ -29,14 +27,6 @@ class DateAggregator implements AggregatorInterface
private const DEFAULT_CHOICE = 'year';
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator
) {
$this->translator = $translator;
}
public function addRole(): ?string
{
return null;
@ -64,7 +54,7 @@ class DateAggregator implements AggregatorInterface
break; // order DESC does not works !
default:
throw new RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
throw new \RuntimeException(sprintf("The frequency data '%s' is invalid.", $data['frequency']));
}
$qb->addSelect(sprintf("TO_CHAR(activity.date, '%s') AS date_aggregator", $fmt));
@ -74,7 +64,7 @@ class DateAggregator implements AggregatorInterface
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@ -84,10 +74,14 @@ class DateAggregator implements AggregatorInterface
'multiple' => false,
'expanded' => true,
'empty_data' => self::DEFAULT_CHOICE,
'data' => self::DEFAULT_CHOICE,
]);
}
public function getFormDefaultData(): array
{
return ['frequency' => self::DEFAULT_CHOICE];
}
public function getLabels($key, array $values, $data)
{
return static function ($value) use ($data): string {
@ -99,17 +93,9 @@ class DateAggregator implements AggregatorInterface
return '';
}
switch ($data['frequency']) {
case 'month':
case 'week':
//return $this->translator->trans('for week') .' '. $value ;
case 'year':
//return $this->translator->trans('in year') .' '. $value ;
default:
return $value;
}
return match ($data['frequency']) {
default => $value,
};
};
}

View File

@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserJobHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
class JobScopeAggregator implements AggregatorInterface
{
private const PREFIX = 'acp_agg_creator_job';
public function __construct(
private readonly ScopeRepository $scopeRepository,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
$qb
->leftJoin('activity.createdBy', "{$p}_user")
->leftJoin(
UserJobHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// job_at based on activity.date
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'activity.date')
)
)
)
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
->addGroupBy("{$p}_select");
}
public function applyOn(): string
{
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {
if ('_header' === $value) {
return 'Scope';
}
if (null === $value || '' === $value) {
return '';
}
$s = $this->scopeRepository->find($value);
return $this->translatableStringHelper->localize(
$s->getName()
);
};
}
public function getQueryKeys($data): array
{
return [self::PREFIX.'_select'];
}
public function getTitle(): string
{
return 'export.aggregator.activity.by_creator_job.Group activity by creator job';
}
}

View File

@ -9,7 +9,7 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
@ -17,21 +17,10 @@ use Chill\MainBundle\Repository\LocationTypeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class LocationTypeAggregator implements AggregatorInterface
{
private LocationTypeRepository $locationTypeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
LocationTypeRepository $locationTypeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->locationTypeRepository = $locationTypeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function __construct(private readonly LocationTypeRepository $locationTypeRepository, private readonly TranslatableStringHelper $translatableStringHelper) {}
public function addRole(): ?string
{
@ -40,7 +29,7 @@ class LocationTypeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!in_array('actloc', $qb->getAllAliases(), true)) {
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
}
@ -50,7 +39,7 @@ class LocationTypeAggregator implements AggregatorInterface
public function applyOn(): string
{
return Declarations::ACTIVITY_ACP;
return Declarations::ACTIVITY;
}
public function buildForm(FormBuilderInterface $builder)
@ -58,6 +47,11 @@ class LocationTypeAggregator implements AggregatorInterface
// no form
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
return function ($value): string {

View File

@ -17,34 +17,15 @@ use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use RuntimeException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use function count;
use function in_array;
class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
protected ActivityReasonCategoryRepository $activityReasonCategoryRepository;
protected ActivityReasonRepository $activityReasonRepository;
protected TranslatableStringHelperInterface $translatableStringHelper;
public function __construct(
ActivityReasonCategoryRepository $activityReasonCategoryRepository,
ActivityReasonRepository $activityReasonRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->activityReasonCategoryRepository = $activityReasonCategoryRepository;
$this->activityReasonRepository = $activityReasonRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function __construct(protected ActivityReasonCategoryRepository $activityReasonCategoryRepository, protected ActivityReasonRepository $activityReasonRepository, protected TranslatableStringHelper $translatableStringHelper) {}
public function addRole(): ?string
{
@ -61,20 +42,20 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
$elem = 'actreasoncat.id';
$alias = 'activity_categories_id';
} else {
throw new RuntimeException('The data provided are not recognized.');
throw new \RuntimeException('The data provided are not recognized.');
}
$qb->addSelect($elem.' as '.$alias);
// make a jointure only if needed
if (!in_array('actreasons', $qb->getAllAliases(), true)) {
if (!\in_array('actreasons', $qb->getAllAliases(), true)) {
$qb->innerJoin('activity.reasons', 'actreasons');
}
// join category if necessary
if ('activity_categories_id' === $alias) {
// add join only if needed
if (!in_array('actreasoncat', $qb->getAllAliases(), true)) {
if (!\in_array('actreasoncat', $qb->getAllAliases(), true)) {
$qb->join('actreasons.category', 'actreasoncat');
}
}
@ -82,7 +63,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
// add the "group by" part
$groupBy = $qb->getDQLPart('groupBy');
if (count($groupBy) > 0) {
if (\count($groupBy) > 0) {
$qb->addGroupBy($alias);
} else {
$qb->groupBy($alias);
@ -111,23 +92,18 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
);
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data)
{
// for performance reason, we load data from db only once
switch ($data['level']) {
case 'reasons':
$this->activityReasonRepository->findBy(['id' => $values]);
break;
case 'categories':
$this->activityReasonCategoryRepository->findBy(['id' => $values]);
break;
default:
throw new RuntimeException(sprintf("The level data '%s' is invalid.", $data['level']));
}
match ($data['level']) {
'reasons' => $this->activityReasonRepository->findBy(['id' => $values]),
'categories' => $this->activityReasonCategoryRepository->findBy(['id' => $values]),
default => throw new \RuntimeException(sprintf("The level data '%s' is invalid.", $data['level'])),
};
return function ($value) use ($data) {
if ('_header' === $value) {
@ -167,7 +143,7 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
return ['activity_categories_id'];
}
throw new RuntimeException('The data provided are not recognised.');
throw new \RuntimeException('The data provided are not recognised.');
}
public function getTitle()

View File

@ -14,18 +14,12 @@ namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Export\AggregatorInterface;
use Doctrine\ORM\QueryBuilder;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class SentReceivedAggregator implements AggregatorInterface
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function __construct(private readonly TranslatorInterface $translator) {}
public function addRole(): ?string
{
@ -48,6 +42,11 @@ class SentReceivedAggregator implements AggregatorInterface
// No form needed
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): callable
{
return function (?string $value): string {
@ -67,7 +66,7 @@ class SentReceivedAggregator implements AggregatorInterface
return $this->translator->trans('export.aggregator.activity.by_sent_received.is received');
default:
throw new LogicException(sprintf('The value %s is not valid', $value));
throw new \LogicException(sprintf('The value %s is not valid', $value));
}
};
}

View File

@ -16,9 +16,9 @@ namespace Chill\ActivityBundle\Export;
*/
abstract class Declarations
{
public const ACTIVITY = 'activity';
final public const ACTIVITY = 'activity';
public const ACTIVITY_ACP = 'activity_linked_to_acp';
final public const ACTIVITY_ACP = 'activity_linked_to_acp';
public const ACTIVITY_PERSON = 'activity_linked_to_person';
final public const ACTIVITY_PERSON = 'activity_linked_to_person';
}

View File

@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@ -23,7 +24,6 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityDuration implements ExportInterface, GroupedExportInterface
@ -31,13 +31,16 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
@ -58,7 +61,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period duration' : $value;
@ -86,9 +89,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository->createQueryBuilder('activity');
@ -108,6 +109,8 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
)
->setParameter('authorized_centers', $centers);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@ -23,7 +24,6 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface
@ -31,7 +31,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
) {
$this->repository = $em->getRepository(Activity::class);
}
@ -41,6 +41,11 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
@ -59,7 +64,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_visit_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period visit duration' : $value;
@ -87,9 +92,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository->createQueryBuilder('activity');
@ -109,6 +112,8 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
)
->setParameter('authorized_centers', $centers);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@ -23,7 +24,6 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
@ -31,13 +31,16 @@ class CountActivity implements ExportInterface, GroupedExportInterface
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
) {
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
@ -58,7 +61,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to an accompanying period' : $value;
@ -86,9 +89,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')
@ -105,6 +106,8 @@ class CountActivity implements ExportInterface, GroupedExportInterface
)
->setParameter('authorized_centers', $centers);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
$qb->select('COUNT(DISTINCT activity.id) as export_count_activity');
return $qb;

View File

@ -12,9 +12,11 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Export\Export\ListActivityHelper;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
use Chill\MainBundle\Export\ListInterface;
@ -24,27 +26,18 @@ use Symfony\Component\Form\FormBuilderInterface;
class ListActivity implements ListInterface, GroupedExportInterface
{
private EntityManagerInterface $entityManager;
private ListActivityHelper $helper;
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper;
public function __construct(
ListActivityHelper $helper,
EntityManagerInterface $entityManager,
TranslatableStringExportLabelHelper $translatableStringExportLabelHelper
) {
$this->helper = $helper;
$this->entityManager = $entityManager;
$this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper;
}
public function __construct(private readonly ListActivityHelper $helper, private readonly EntityManagerInterface $entityManager, private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper) {}
public function buildForm(FormBuilderInterface $builder)
{
$this->helper->buildForm($builder);
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
{
return $this->helper->getAllowedFormattersTypes();
@ -62,22 +55,17 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
switch ($key) {
case 'acpId':
return static function ($value) {
return match ($key) {
'acpId' => static function ($value) {
if ('_header' === $value) {
return ListActivityHelper::MSG_KEY.'accompanying course id';
}
return $value ?? '';
},
'scopesNames' => $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY.'course circles'),
default => $this->helper->getLabels($key, $values, $data),
};
case 'scopesNames':
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles');
default:
return $this->helper->getLabels($key, $values, $data);
}
}
public function getQueryKeys($data)
@ -109,9 +97,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();
@ -145,6 +131,8 @@ class ListActivity implements ListInterface, GroupedExportInterface
->addSelect('(SELECT AGGREGATE(acpScope.name) FROM '.Scope::class.' acpScope WHERE acpScope MEMBER OF acp.scopes) AS scopesNames')
->addGroupBy('scopesNames');
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}
@ -158,6 +146,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return array_merge(
$this->helper->supportsModifiers(),
[
Declarations::ACTIVITY,
\Chill\PersonBundle\Export\Declarations::ACP_TYPE,
]
);

View File

@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@ -23,7 +24,6 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityDuration implements ExportInterface, GroupedExportInterface
@ -31,7 +31,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
) {
$this->repository = $em->getRepository(Activity::class);
}
@ -41,6 +41,11 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
@ -59,7 +64,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period duration' : $value;
@ -87,9 +92,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')
@ -109,6 +112,8 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
)
->setParameter('authorized_centers', $centers);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@ -14,6 +14,7 @@ namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
@ -23,7 +24,6 @@ use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface
@ -31,7 +31,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
protected EntityRepository $repository;
public function __construct(
EntityManagerInterface $em
EntityManagerInterface $em,
) {
$this->repository = $em->getRepository(Activity::class);
}
@ -41,6 +41,11 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes(): array
{
return [FormatterInterface::TYPE_TABULAR];
@ -59,7 +64,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_visit_duration' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period visit duration' : $value;
@ -87,9 +92,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')
@ -109,6 +112,8 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
)
->setParameter('authorized_centers', $centers);
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}

View File

@ -19,21 +19,17 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
{
protected ActivityRepository $activityRepository;
public function __construct(protected ActivityRepository $activityRepository) {}
public function __construct(
ActivityRepository $activityRepository
) {
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder)
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
@ -54,7 +50,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
throw new \LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to a person' : $value;

View File

@ -20,7 +20,6 @@ use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use DateTime;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
@ -30,14 +29,8 @@ use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function count;
use function in_array;
class ListActivity implements ListInterface, GroupedExportInterface
{
protected EntityManagerInterface $entityManager;
protected array $fields = [
'id',
'date',
@ -52,23 +45,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
'person_id',
];
protected TranslatableStringHelperInterface $translatableStringHelper;
protected TranslatorInterface $translator;
private ActivityRepository $activityRepository;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
ActivityRepository $activityRepository
) {
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
}
public function __construct(protected EntityManagerInterface $entityManager, protected TranslatorInterface $translator, protected TranslatableStringHelperInterface $translatableStringHelper, private readonly ActivityRepository $activityRepository) {}
public function buildForm(FormBuilderInterface $builder)
{
@ -79,7 +56,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
'label' => 'Fields to include in export',
'constraints' => [new Callback([
'callback' => static function ($selected, ExecutionContextInterface $context) {
if (count($selected) === 0) {
if (0 === \count($selected)) {
$context->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
@ -89,6 +66,11 @@ class ListActivity implements ListInterface, GroupedExportInterface
]);
}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
@ -113,7 +95,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'date';
}
$date = DateTime::createFromFormat('Y-m-d H:i:s', $value);
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y');
};
@ -137,13 +119,11 @@ class ListActivity implements ListInterface, GroupedExportInterface
$activity = $activityRepository->find($value);
return implode(', ', array_map(function (ActivityReason $r) {
return '"' .
return implode(', ', array_map(fn (ActivityReason $r) => '"'.
$this->translatableStringHelper->localize($r->getCategory()->getName())
.' > '.
$this->translatableStringHelper->localize($r->getName())
. '"';
}, $activity->getReasons()->toArray()));
.'"', $activity->getReasons()->toArray()));
};
case 'circle_name':
@ -152,7 +132,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'circle';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR));
};
case 'type_name':
@ -161,7 +141,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'activity type';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR));
};
default:
@ -197,12 +177,10 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
// throw an error if any fields are present
if (!array_key_exists('fields', $data)) {
if (!\array_key_exists('fields', $data)) {
throw new InvalidArgumentException('Any fields have been checked.');
}
@ -226,7 +204,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
->setParameter('centers', $centers);
foreach ($this->fields as $f) {
if (in_array($f, $data['fields'], true)) {
if (\in_array($f, $data['fields'], true)) {
switch ($f) {
case 'id':
$qb->addSelect('activity.id AS id');
@ -299,7 +277,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
PersonDeclarations::PERSON_TYPE,
];
}
}

View File

@ -20,7 +20,6 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
/**
@ -30,28 +29,24 @@ use Symfony\Component\Form\FormBuilderInterface;
*/
class StatActivityDuration implements ExportInterface, GroupedExportInterface
{
public const SUM = 'sum';
/**
* The action for this report.
*/
protected string $action;
private ActivityRepository $activityRepository;
final public const SUM = 'sum';
/**
* @param string $action the stat to perform
*/
public function __construct(
ActivityRepository $activityRepository,
string $action = 'sum'
) {
$this->action = $action;
$this->activityRepository = $activityRepository;
}
private readonly ActivityRepository $activityRepository,
/**
* The action for this report.
*/
protected string $action = 'sum'
) {}
public function buildForm(FormBuilderInterface $builder)
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
@ -65,7 +60,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
return 'Sum activities linked to a person duration by various parameters.';
}
throw new LogicException('this action is not supported: ' . $this->action);
throw new \LogicException('this action is not supported: '.$this->action);
}
public function getGroup(): string
@ -76,7 +71,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_stat_activity' !== $key) {
throw new LogicException(sprintf('The key %s is not used by this export', $key));
throw new \LogicException(sprintf('The key %s is not used by this export', $key));
}
$header = self::SUM === $this->action ? 'Sum activities linked to a person duration' : false;
@ -100,7 +95,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
return 'Sum activity linked to a person duration';
}
throw new LogicException('This action is not supported: ' . $this->action);
throw new \LogicException('This action is not supported: '.$this->action);
}
public function getType(): string
@ -153,7 +148,7 @@ class StatActivityDuration implements ExportInterface, GroupedExportInterface
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
//PersonDeclarations::PERSON_TYPE,
PersonDeclarations::PERSON_TYPE,
];
}
}

Some files were not shown because too many files have changed in this diff Show More