mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-10 16:55:00 +00:00
Compare commits
109 Commits
replace_tr
...
226-upgrad
Author | SHA1 | Date | |
---|---|---|---|
b19a1ba53b
|
|||
3d4c439be4 | |||
4727a57825
|
|||
5f441eb5ac
|
|||
2e4e5ee79a
|
|||
1467c708f2 | |||
170bb9586d
|
|||
c704ffa379 | |||
947b7b90e2
|
|||
992f7761bb
|
|||
35170e1f7c
|
|||
7132dfa3f6 | |||
7e09e0ea54 | |||
ccf8cc4d6e | |||
63124f8f92
|
|||
75d80ebd98 | |||
0ea6f36297 | |||
975ea417b7 | |||
af8e02f76b | |||
cbaeb3d7e8 | |||
f609ddb315 | |||
d0bceb59dc
|
|||
2883e085ed
|
|||
b05ed86d1e
|
|||
c855d0badc | |||
be57c96a2f | |||
eb01c7c203
|
|||
53b4747697
|
|||
89e19502d3
|
|||
ff344dbb0c
|
|||
8719b4dedd | |||
d8fa743bc9
|
|||
eaa40d6725
|
|||
1f47f157ea
|
|||
5ab0d3f8da
|
|||
98fd5cae78 | |||
2bb29242e4 | |||
e6cab938c8
|
|||
f5f4d8fcdd
|
|||
e9df26c2f7
|
|||
af6bee2497
|
|||
af585bada3
|
|||
f9763b866d | |||
7f18a2fb7d | |||
3cdad6caff | |||
200ab836c7 | |||
3e39c0ced7 | |||
83c3621c26 | |||
b790e2fcf1 | |||
fde6000d0b | |||
3892d1e877 | |||
6944773868 | |||
d5bc9d10d5 | |||
3743b336a6 | |||
3332413fe0 | |||
86e659edd4
|
|||
2e60ba3137
|
|||
1663c6f7c7
|
|||
d01b6db5dc
|
|||
847c8238e8
|
|||
2cc5fa06ae
|
|||
0d741ab886
|
|||
9cb794fef2 | |||
fd03855ee6 | |||
ac9897d9e7
|
|||
88469dbe8e
|
|||
cbc83c0e63
|
|||
27012d842d
|
|||
4112e59af4
|
|||
1b0a30baf8 | |||
9673b369ef
|
|||
43966c4d5a
|
|||
c9f438319c
|
|||
58b45c6f1b
|
|||
f756c0d71e | |||
bb1602934c
|
|||
83fe3ec3fc
|
|||
abebb79e8b
|
|||
3a269ba953 | |||
a4a4aa119e | |||
139f5a1ff9 | |||
1e43772b17 | |||
c29b356c02 | |||
050a2955ca | |||
a1132cf82f | |||
db70ce4a61 | |||
4d20a46717
|
|||
dd75cb695c
|
|||
8cb9142800
|
|||
4847724f6f
|
|||
6280453523
|
|||
68f56671a4
|
|||
dc860d0c46
|
|||
6fd80f9f2e
|
|||
3ce78d0c16 | |||
f43bd2bc58 | |||
ec85f05e0b | |||
bfe0d9a137 | |||
cac092c45a | |||
39bb06b991 | |||
b2aa465b03 | |||
0fa8944c29 | |||
7494dbfc7c | |||
642958bde2 | |||
57bd6d1be4 | |||
bbcbbf078f | |||
ba778b1cf8 | |||
728fd642fa | |||
4150eb56b4 |
@@ -1,5 +0,0 @@
|
||||
kind: Fixed
|
||||
body: Replace old method of getting translator with injection of translatorInterface
|
||||
time: 2023-10-26T15:22:05.134223653+02:00
|
||||
custom:
|
||||
Issue: "175"
|
5
.changes/unreleased/Fixed-20231127-210138.yaml
Normal file
5
.changes/unreleased/Fixed-20231127-210138.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
kind: Fixed
|
||||
body: 'Export: fix list person with custom fields'
|
||||
time: 2023-11-27T21:01:38.260730706+01:00
|
||||
custom:
|
||||
Issue: ""
|
3
.changes/v2.10.2.md
Normal file
3
.changes/v2.10.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.10.2 - 2023-10-26
|
||||
### Fixed
|
||||
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Use injection of translator instead of ->get().
|
3
.changes/v2.10.3.md
Normal file
3
.changes/v2.10.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.10.3 - 2023-10-26
|
||||
### Fixed
|
||||
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Replace old method of getting translator with injection of translatorInterface
|
3
.changes/v2.10.4.md
Normal file
3
.changes/v2.10.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.10.4 - 2023-10-26
|
||||
### Fixed
|
||||
* Fix null value constraint errors when merging relationships in doubles
|
4
.changes/v2.10.5.md
Normal file
4
.changes/v2.10.5.md
Normal file
@@ -0,0 +1,4 @@
|
||||
## v2.10.5 - 2023-11-05
|
||||
### Fixed
|
||||
* ([#183](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/183)) Fix "problem during download" on some filters, which used a wrong data type
|
||||
* ([#184](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/184)) Fix filter "activity by date"
|
4
.changes/v2.10.6.md
Normal file
4
.changes/v2.10.6.md
Normal file
@@ -0,0 +1,4 @@
|
||||
## v2.10.6 - 2023-11-07
|
||||
### Fixed
|
||||
* ([#182](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/182)) Fix merging of double person files. Adjustement relationship sql statement
|
||||
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix aggregator by geographical unit on person: avoid inconsistencies
|
6
.changes/v2.11.0.md
Normal file
6
.changes/v2.11.0.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## v2.11.0 - 2023-11-07
|
||||
### Feature
|
||||
* ([#194](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/194)) Export: add a filter "filter activity by creator job"
|
||||
### Fixed
|
||||
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix "group accompanying period by geographical unit": take into account the accompanying periods when the period is not located within an unit
|
||||
* Fix "group activity by creator job" aggregator
|
26
.changes/v2.12.0.md
Normal file
26
.changes/v2.12.0.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## v2.12.0 - 2023-11-15
|
||||
### Feature
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add an aggregator "group activities by presence"
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add a filter "filter activity by activity presence"
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add an aggregator "group activities by person" (only for the activities saved in a person context)
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add a new aggregator "group peoples by postal code"
|
||||
* ([#200](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/200)) Export: split export about person on accompanying period work: one with the people associated with the work, another one with the people associated with the accompanying period
|
||||
* ([#204](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/204)) Add 3 new filters and 3 new aggregators for work action creator (with jobs and scopes)
|
||||
|
||||
* ([#202](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/202)) Create export for the average duration of social work actions
|
||||
* ([#206](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/206)) Export: add a export which count persons on accompanying period work
|
||||
* ([#206](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/206)) Export: add an export which count persons on activity
|
||||
* ([#203](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/203)) Export: add clauses on the social work start date and end date within the filter "Filter accompanying period by accompanying period work"
|
||||
### Fixed
|
||||
* Export: fix typo in filter "filter accompanying period work on end date"
|
||||
* ([#189](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/189)) Export: Fix failure in export linked to household
|
||||
* ([#205](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/205)) Fix loading of accompanying period work referrers
|
||||
### Traduction francophone des principaux changements
|
||||
* export: ajout d'un regroupement "grouper les échanges par présence de l'usager";
|
||||
* export: ajout d'un filtre "filtre les échanges par présence de l'usager";
|
||||
* export: ajout d'un regroupement "regrouper les échanges par personne" (seulement pour les échanges enregistrés dans le contexte de l'usager);
|
||||
* export: ajout d'un regroupement "grouper les usagers par codes postaux"
|
||||
* export: séparation des exports sur les actions: dans l'un, les filtres des usagers portent sur les usagers concernés par l'action, dans l'autre, les filtres portent sur les usagers concernés par le parcours de l'action;
|
||||
* export: ajout de 3 nouveaux filtres et regroupements sur le créateur de l'action, son métier et son service;
|
||||
* export: correction de l'export sur les ménages liés aux parcours;
|
||||
* correction du chargement des actions d'accompagnement
|
3
.changes/v2.12.1.md
Normal file
3
.changes/v2.12.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## v2.12.1 - 2023-11-16
|
||||
### Fixed
|
||||
* ([#208](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/208)) Export: fix loading of form for "filter action by type, goal and result"
|
9
.changes/v2.13.0.md
Normal file
9
.changes/v2.13.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## v2.13.0 - 2023-11-21
|
||||
### Feature
|
||||
* ([#173](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/173)) Allow user to add a phonenumber to their profile which will be included in automatically generated documents
|
||||
### Fixed
|
||||
* ([#211](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/211)) Export: fix loading of "Group activity by type"
|
||||
* ([#190](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/190)) Export: fix loading of "group activity by reasons"
|
||||
* ([#213](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/213)) Export: fix usage of some Collection returned instead of array in export filters
|
||||
* ([#215](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/215)) Use only the string 'both' for gender (with a database migration)
|
||||
* ([#212](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/212)) Clean the database to make working the "Group people by gender" aggregator
|
8
.changes/v2.14.0.md
Normal file
8
.changes/v2.14.0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
## v2.14.0 - 2023-11-24
|
||||
### Feature
|
||||
* ([#161](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/161)) Export: in filter "Filter accompanying period work (social action) by type, goal and result", order the items alphabetically or with the defined order
|
||||
### Fixed
|
||||
* ([#141](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/141)) Export: on filter "action by type goals, and results", restore the fields when editing a saved export
|
||||
* ([#219](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/219)) Export: fix the list of accompanying period work, when the "calc date" is null
|
||||
* ([#222](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/222)) Fix rendering of custom fields
|
||||
* Fix various errors in custom fields administration
|
@@ -11,6 +11,10 @@ cache:
|
||||
services:
|
||||
- name: postgis/postgis:14-3.3-alpine
|
||||
alias: db
|
||||
command:
|
||||
- postgres
|
||||
- "-c"
|
||||
- max_connections=1000
|
||||
- name: redis
|
||||
alias: redis
|
||||
|
||||
@@ -28,6 +32,8 @@ variables:
|
||||
REDIS_PORT: 6379
|
||||
REDIS_URL: redis://redis:6379
|
||||
DEFAULT_CARRIER_CODE: BE
|
||||
# force a timezone
|
||||
TZ: Europe/Brussels
|
||||
|
||||
stages:
|
||||
- Composer install
|
||||
|
75
CHANGELOG.md
75
CHANGELOG.md
@@ -6,6 +6,81 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||
|
||||
|
||||
## v2.14.0 - 2023-11-24
|
||||
### Feature
|
||||
* ([#161](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/161)) Export: in filter "Filter accompanying period work (social action) by type, goal and result", order the items alphabetically or with the defined order
|
||||
### Fixed
|
||||
* ([#141](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/141)) Export: on filter "action by type goals, and results", restore the fields when editing a saved export
|
||||
* ([#219](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/219)) Export: fix the list of accompanying period work, when the "calc date" is null
|
||||
* ([#222](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/222)) Fix rendering of custom fields
|
||||
* Fix various errors in custom fields administration
|
||||
|
||||
## v2.13.0 - 2023-11-21
|
||||
### Feature
|
||||
* ([#173](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/173)) Allow user to add a phonenumber to their profile which will be included in automatically generated documents
|
||||
### Fixed
|
||||
* ([#211](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/211)) Export: fix loading of "Group activity by type"
|
||||
* ([#190](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/190)) Export: fix loading of "group activity by reasons"
|
||||
* ([#213](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/213)) Export: fix usage of some Collection returned instead of array in export filters
|
||||
* ([#215](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/215)) Use only the string 'both' for gender (with a database migration)
|
||||
* ([#212](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/212)) Clean the database to make working the "Group people by gender" aggregator
|
||||
|
||||
## v2.12.1 - 2023-11-16
|
||||
### Fixed
|
||||
* ([#208](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/208)) Export: fix loading of form for "filter action by type, goal and result"
|
||||
|
||||
## v2.12.0 - 2023-11-15
|
||||
### Feature
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add an aggregator "group activities by presence"
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add a filter "filter activity by activity presence"
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add an aggregator "group activities by person" (only for the activities saved in a person context)
|
||||
* ([#199](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/199)) Export: add a new aggregator "group peoples by postal code"
|
||||
* ([#200](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/200)) Export: split export about person on accompanying period work: one with the people associated with the work, another one with the people associated with the accompanying period
|
||||
* ([#204](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/204)) Add 3 new filters and 3 new aggregators for work action creator (with jobs and scopes)
|
||||
|
||||
* ([#202](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/202)) Create export for the average duration of social work actions
|
||||
* ([#206](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/206)) Export: add a export which count persons on accompanying period work
|
||||
* ([#206](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/206)) Export: add an export which count persons on activity
|
||||
* ([#203](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/203)) Export: add clauses on the social work start date and end date within the filter "Filter accompanying period by accompanying period work"
|
||||
### Fixed
|
||||
* Export: fix typo in filter "filter accompanying period work on end date"
|
||||
* ([#189](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/189)) Export: Fix failure in export linked to household
|
||||
* ([#205](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/205)) Fix loading of accompanying period work referrers
|
||||
### Traduction francophone des principaux changements
|
||||
* export: ajout d'un regroupement "grouper les échanges par présence de l'usager";
|
||||
* export: ajout d'un filtre "filtre les échanges par présence de l'usager";
|
||||
* export: ajout d'un regroupement "regrouper les échanges par personne" (seulement pour les échanges enregistrés dans le contexte de l'usager);
|
||||
* export: ajout d'un regroupement "grouper les usagers par codes postaux"
|
||||
* export: séparation des exports sur les actions: dans l'un, les filtres des usagers portent sur les usagers concernés par l'action, dans l'autre, les filtres portent sur les usagers concernés par le parcours de l'action;
|
||||
* export: ajout de 3 nouveaux filtres et regroupements sur le créateur de l'action, son métier et son service;
|
||||
* export: correction de l'export sur les ménages liés aux parcours;
|
||||
* correction du chargement des actions d'accompagnement
|
||||
|
||||
## v2.11.0 - 2023-11-07
|
||||
### Feature
|
||||
* ([#194](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/194)) Export: add a filter "filter activity by creator job"
|
||||
### Fixed
|
||||
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix "group accompanying period by geographical unit": take into account the accompanying periods when the period is not located within an unit
|
||||
* Fix "group activity by creator job" aggregator
|
||||
|
||||
## v2.10.6 - 2023-11-07
|
||||
### Fixed
|
||||
* ([#182](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/182)) Fix merging of double person files. Adjustement relationship sql statement
|
||||
* ([#185](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/185)) Export: fix aggregator by geographical unit on person: avoid inconsistencies
|
||||
|
||||
## v2.10.5 - 2023-11-05
|
||||
### Fixed
|
||||
* ([#183](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/183)) Fix "problem during download" on some filters, which used a wrong data type
|
||||
* ([#184](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/184)) Fix filter "activity by date"
|
||||
|
||||
## v2.10.4 - 2023-10-26
|
||||
### Fixed
|
||||
* Fix null value constraint errors when merging relationships in doubles
|
||||
|
||||
## v2.10.3 - 2023-10-26
|
||||
### Fixed
|
||||
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Replace old method of getting translator with injection of translatorInterface
|
||||
|
||||
## v2.10.2 - 2023-10-26
|
||||
### Fixed
|
||||
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Use injection of translator instead of ->get().
|
||||
|
@@ -1,13 +1,15 @@
|
||||
|
||||
Configure Chill for calendar sync and SSO with Microsoft Graph (Outlook)
|
||||
========================================================================
|
||||
Configure Chill for calendar and absence synchronisation and SSO with Microsoft Graph (Outlook)
|
||||
===============================================================================================
|
||||
|
||||
Chill offers the possibility to:
|
||||
|
||||
* authenticate users using Microsoft Graph, with relatively small adaptations;
|
||||
* synchronize calendar in both ways (`see the user manual for a large description of the feature <https://gitea.champs-libres.be/Chill-project/manuals>`_).
|
||||
|
||||
Both can be configured separately (synchronising calendars without SSO, or SSO without calendar). When calendar sync is configured without SSL, the user's email address is the key to associate Chill's users with Microsoft's ones.
|
||||
Both can be configured separately (synchronising calendars without SSO, or SSO without calendar).
|
||||
|
||||
Please note that the user's email address is the key to associate Chill's users with Microsoft's ones.
|
||||
|
||||
Configure SSO
|
||||
-------------
|
||||
@@ -46,7 +48,7 @@ Do not forget to provider user's accesses to your app, using the "Utilisateurs e
|
||||
|
||||
You must know have gathered all the required variables for SSO:
|
||||
|
||||
.. code-block::
|
||||
.. code-block::
|
||||
|
||||
SAML_BASE_URL=https://test.chill.be # must be
|
||||
SAML_ENTITY_ID=https://test.chill.be # must match the one entered
|
||||
@@ -186,20 +188,27 @@ Configure chill app
|
||||
|
||||
|
||||
|
||||
Configure sync
|
||||
--------------
|
||||
Configure sync and calendar access
|
||||
----------------------------------
|
||||
|
||||
The sync processe might be configured in the same app, or into a different app.
|
||||
The purpose of this configuration is the following:
|
||||
|
||||
The synchronization processes use Oauth2.0 for authentication and authorization.
|
||||
- let user read their calendar and shared calendar within Chill (with the same permissions as the one configured in Outlook / Azure);
|
||||
- allow chill instance to write appointment ("Rendez-vous") into their calendar, and invite other users to their appointment;
|
||||
- allow chill instance to be notified if an appoint is added or removed by the user within another interface than Chill: if the appointment match another one created in the Chill interface, the date and time are updated in Chill;
|
||||
- allow chill instance to read the absence of the user and, if set, mark the user as absent in Chill;
|
||||
|
||||
The sync processe might be configured in the same app, or into a different app on the Azure side.
|
||||
|
||||
The synchronization processes use Oauth 2.0 / OpenID Connect for authentication and authorization.
|
||||
|
||||
.. note::
|
||||
|
||||
Two flows are in use:
|
||||
|
||||
* we authenticate "on behalf of a user", to allow users to see their own calendar or other user's calendar into the web interface.
|
||||
* we authenticate "on behalf of a user", to allow users to see their own calendar or other user's calendar into the web interface.
|
||||
|
||||
Typically, when the page is loaded, Chill first check that an authorization token exists. If not, the user is redirected to Microsoft Azure for authentification and a new token is grabbed (most of the times, this is transparent for users).
|
||||
Typically, when the page is loaded, Chill first check that an authorization token exists. If not, the user is redirected to Microsoft Azure for authentification and a new token is grabbed (most of the times, this is transparent for users).
|
||||
|
||||
* Chill also acts "as a machine", to synchronize calendars with a daemon background.
|
||||
|
||||
@@ -229,8 +238,9 @@ Some explanation:
|
||||
The sync daemon must have write access:
|
||||
|
||||
* the daemon must be allowed to read all users and their profile, to establish a link between them and the Chill's users: (:code:`Users.Read.All`);
|
||||
* it must also be allowed to read and write into the calendars (:code:`Calendars.ReadWrite.All`)
|
||||
* for sending invitation to other users, the permission (:code:`Mail.Send`) must be granted.
|
||||
* it must also be allowed to read and write into the calendars (:code:`Calendars.ReadWrite.All`);
|
||||
* for sending invitation to other users, the permission (:code:`Mail.Send`) must be granted;
|
||||
* and, for reading the absence status of the user and sync it with chill, it must be able to read the mailboxSettings (:code:`MailboxSettings.Read`).
|
||||
|
||||
At this step, you might choose to accept those permissions for all users, or let them do it by yourself.
|
||||
|
||||
@@ -301,7 +311,7 @@ The calendar synchronization is processed using symfony messenger. It seems to b
|
||||
|
||||
The association between chill's users and Microsoft's users is done by this cli command:
|
||||
|
||||
.. code-block::
|
||||
.. code-block::
|
||||
|
||||
bin/console chill:calendar:msgraph-user-map-subscribe
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 166 KiB |
@@ -15,7 +15,7 @@
|
||||
"@symfony/webpack-encore": "^4.1.0",
|
||||
"@tsconfig/node14": "^1.0.1",
|
||||
"bindings": "^1.5.0",
|
||||
"bootstrap": "^5.0.1",
|
||||
"bootstrap": "^5.3.0",
|
||||
"chokidar": "^3.5.1",
|
||||
"fork-awesome": "^1.1.7",
|
||||
"jquery": "^3.6.0",
|
||||
|
@@ -673,8 +673,8 @@ final class ActivityController extends AbstractController
|
||||
throw $this->createNotFoundException('Accompanying Period not found');
|
||||
}
|
||||
|
||||
// TODO Add permission
|
||||
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
// TODO Add permission
|
||||
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
|
||||
} else {
|
||||
throw $this->createNotFoundException('Person or Accompanying Period not found');
|
||||
}
|
||||
|
@@ -0,0 +1,72 @@
|
||||
<?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\ActivityBundle\Repository\ActivityPresenceRepositoryInterface;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class ActivityPresenceAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private ActivityPresenceRepositoryInterface $activityPresenceRepository, private TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return function (null|int|string $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.activity.by_activity_presence.header';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $presence = $this->activityPresenceRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize($presence->getName());
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return ['activity_presence_aggregator_attendee'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.activity.by_activity_presence.Group activity by presence';
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
{
|
||||
$qb->addSelect('IDENTITY(activity.attendee) AS activity_presence_aggregator_attendee');
|
||||
$qb->addGroupBy('activity_presence_aggregator_attendee');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
}
|
@@ -56,20 +56,15 @@ class ActivityTypeAggregator implements AggregatorInterface
|
||||
|
||||
public function getLabels($key, array $values, $data): \Closure
|
||||
{
|
||||
// for performance reason, we load data from db only once
|
||||
$this->activityTypeRepository->findBy(['id' => $values]);
|
||||
|
||||
return function ($value): string {
|
||||
return function (null|int|string $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Activity type';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
if (null === $value || '' === $value || null === $t = $this->activityTypeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$t = $this->activityTypeRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize($t->getName());
|
||||
};
|
||||
}
|
||||
|
@@ -14,18 +14,18 @@ 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\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class JobScopeAggregator implements AggregatorInterface
|
||||
class CreatorJobAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'acp_agg_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly ScopeRepository $scopeRepository,
|
||||
private readonly UserJobRepositoryInterface $userJobRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
@@ -76,17 +76,15 @@ class JobScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Scope';
|
||||
return 'Job';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
if (null === $value || '' === $value || null === $s = $this->userJobRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$s = $this->scopeRepository->find($value);
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
$s->getLabel()
|
||||
);
|
||||
};
|
||||
}
|
@@ -99,12 +99,6 @@ class ActivityReasonAggregator implements AggregatorInterface, ExportElementVali
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
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) {
|
||||
return 'reasons' === $data['level'] ? 'Group by reasons' : 'Group by categories of reason';
|
||||
|
@@ -0,0 +1,65 @@
|
||||
<?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\PersonAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class PersonAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private LabelPersonHelper $labelPersonHelper) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
// nothing to add here
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return $this->labelPersonHelper->getLabel($key, $values, 'export.aggregator.person.by_person.person');
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return ['activity_by_person_agg'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.aggregator.person.by_person.title';
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb
|
||||
->addSelect('IDENTITY(activity.person) AS activity_by_person_agg')
|
||||
->addGroupBy('activity_by_person_agg');
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY_PERSON;
|
||||
}
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
<?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\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;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountPersonsOnActivity implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
protected EntityRepository $repository;
|
||||
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->repository = $em->getRepository(Activity::class);
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.export.count_person_on_activity.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of activities linked to an accompanying period';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_count_activity' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'export.export.count_person_on_activity.header' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_count_activity'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.export.count_person_on_activity.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository
|
||||
->createQueryBuilder('activity')
|
||||
->join('activity.persons', 'person')
|
||||
->join('activity.accompanyingPeriod', 'acp');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.AccompanyingPeriodParticipation::class.' acl_count_part
|
||||
JOIN '.PersonCenterHistory::class.' acl_count_person_history WITH IDENTITY(acl_count_person_history.person) = IDENTITY(acl_count_part.person)
|
||||
WHERE acl_count_part.accompanyingPeriod = acp.id AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
$qb->select('COUNT(DISTINCT person.id) as export_count_activity');
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return ActivityStatsVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::ACTIVITY,
|
||||
Declarations::ACTIVITY_ACP,
|
||||
PersonDeclarations::ACP_TYPE,
|
||||
PersonDeclarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -13,16 +13,12 @@ namespace Chill\ActivityBundle\Export\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\Export\FilterType;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ActivityDateFilter implements FilterInterface
|
||||
@@ -74,46 +70,6 @@ class ActivityDateFilter implements FilterInterface
|
||||
->add('date_to', PickRollingDateType::class, [
|
||||
'label' => 'Activities before this date',
|
||||
]);
|
||||
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
|
||||
/** @var \Symfony\Component\Form\FormInterface $filterForm */
|
||||
$filterForm = $event->getForm()->getParent();
|
||||
$enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData();
|
||||
|
||||
if (true === $enabled) {
|
||||
// if the filter is enabled, add some validation
|
||||
$form = $event->getForm();
|
||||
$date_from = $form->get('date_from')->getData();
|
||||
$date_to = $form->get('date_to')->getData();
|
||||
|
||||
// check that fields are not empty
|
||||
if (null === $date_from) {
|
||||
$form->get('date_from')->addError(new FormError(
|
||||
$this->translator->trans('This field '
|
||||
.'should not be empty')
|
||||
));
|
||||
}
|
||||
|
||||
if (null === $date_to) {
|
||||
$form->get('date_to')->addError(new FormError(
|
||||
$this->translator->trans('This field '
|
||||
.'should not be empty')
|
||||
));
|
||||
}
|
||||
|
||||
// check that date_from is before date_to
|
||||
if (
|
||||
(null !== $date_from && null !== $date_to)
|
||||
&& $date_from >= $date_to
|
||||
) {
|
||||
$form->get('date_to')->addError(new FormError(
|
||||
$this->translator->trans('This date should be after '
|
||||
.'the date given in "Implied in an activity after '
|
||||
.'this date" field')
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
|
@@ -0,0 +1,82 @@
|
||||
<?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\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Entity\ActivityPresence;
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final readonly class ActivityPresenceFilter implements FilterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
private TranslatorInterface $translator
|
||||
) {}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.filter.activity.by_presence.Filter activity by activity presence';
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder->add('presences', EntityType::class, [
|
||||
'class' => ActivityPresence::class,
|
||||
'choice_label' => fn (ActivityPresence $presence) => $this->translatableStringHelper->localize($presence->getName())
|
||||
.($presence->isActive() ? '' : ' ('.$this->translator->trans('inactive').')'),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'label' => 'export.filter.activity.by_presence.presences',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string')
|
||||
{
|
||||
$presences = array_map(
|
||||
fn (ActivityPresence $presence) => $this->translatableStringHelper->localize($presence->getName()),
|
||||
$data['presences'] instanceof Collection ? $data['presences']->toArray() : $data['presences']
|
||||
);
|
||||
|
||||
return [
|
||||
'export.filter.activity.by_presence.Filtered by activity presence: only %presences%',
|
||||
['%presences%' => implode(', ', $presences)],
|
||||
];
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$qb
|
||||
->andWhere('activity.attendee IN (:activity_presence_filter_presences)')
|
||||
->setParameter('activity_presence_filter_presences', $data['presences']);
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
}
|
@@ -80,7 +80,7 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
|
||||
// collect all the reasons'name used in this filter in one array
|
||||
$reasonsNames = array_map(
|
||||
fn (ActivityType $t): string => $this->translatableStringHelper->localize($t->getName()),
|
||||
$this->activityTypeRepository->findBy(['id' => $data['types']->toArray()])
|
||||
$this->activityTypeRepository->findBy(['id' => $data['types'] instanceof \Doctrine\Common\Collections\Collection ? $data['types']->toArray() : $data['types']])
|
||||
);
|
||||
|
||||
return ['Filtered by activity type: only %list%', [
|
||||
|
@@ -0,0 +1,116 @@
|
||||
<?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\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
final readonly class CreatorJobFilter implements FilterInterface
|
||||
{
|
||||
private const PREFIX = 'acp_act_filter_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private TranslatableStringHelper $translatableStringHelper,
|
||||
private TranslatorInterface $translator,
|
||||
private UserJobRepositoryInterface $userJobRepository,
|
||||
) {}
|
||||
|
||||
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')
|
||||
)
|
||||
)
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->in("{$p}_history.job", ":{$p}_jobs")
|
||||
)
|
||||
->setParameter(
|
||||
"{$p}_jobs",
|
||||
$data['jobs'],
|
||||
);
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::ACTIVITY;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('jobs', EntityType::class, [
|
||||
'choices' => $this->userJobRepository->findAllOrderedByName(),
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => fn (UserJob $s) => $this->translatableStringHelper->localize(
|
||||
$s->getLabel()
|
||||
).($s->isActive() ? '' : '('.$this->translator->trans('inactive').')'),
|
||||
'label' => 'export.filter.activity.by_creator_job.job_form_label',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$jobs = array_map(
|
||||
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
$data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs']
|
||||
);
|
||||
|
||||
return ['export.filter.activity.by_creator_job.Filtered activity by user job: only %jobs%', [
|
||||
'%jobs%' => implode(', ', $jobs),
|
||||
]];
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [
|
||||
'jobs' => [],
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.filter.activity.by_creator_job.Filter activity by user job';
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ use Chill\ActivityBundle\Repository\ActivityReasonRepository;
|
||||
use Chill\MainBundle\Export\ExportElementValidatedInterface;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
@@ -79,7 +80,7 @@ class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInt
|
||||
// collect all the reasons'name used in this filter in one array
|
||||
$reasonsNames = array_map(
|
||||
fn (ActivityReason $r): string => '"'.$this->translatableStringHelper->localize($r->getName()).'"',
|
||||
$this->activityReasonRepository->findBy(['id' => $data['reasons']->toArray()])
|
||||
$this->activityReasonRepository->findBy(['id' => $data['reasons'] instanceof Collection ? $data['reasons']->toArray() : $data['reasons']])
|
||||
);
|
||||
|
||||
return [
|
||||
|
@@ -17,6 +17,7 @@ use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -80,7 +81,7 @@ class UsersJobFilter implements FilterInterface
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
$data['jobs']->toArray()
|
||||
$data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs']
|
||||
)
|
||||
),
|
||||
]];
|
||||
|
@@ -18,6 +18,7 @@ use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -83,7 +84,7 @@ class UsersScopeFilter implements FilterInterface
|
||||
', ',
|
||||
array_map(
|
||||
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
$data['scopes']->toArray()
|
||||
$data['scopes'] instanceof Collection ? $data['scopes']->toArray() : $data['scopes']
|
||||
)
|
||||
),
|
||||
]];
|
||||
|
@@ -0,0 +1,62 @@
|
||||
<?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\Tests\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\ActivityPresenceAggregator;
|
||||
use Chill\ActivityBundle\Repository\ActivityPresenceRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class ActivityPresenceAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
private ActivityPresenceRepositoryInterface $activityPresenceRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->translatableStringHelper = self::$container->get(TranslatableStringHelperInterface::class);
|
||||
$this->activityPresenceRepository = self::$container->get(ActivityPresenceRepositoryInterface::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return new ActivityPresenceAggregator($this->activityPresenceRepository, $this->translatableStringHelper);
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
return [
|
||||
[],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('count(activity.id)')
|
||||
->from(Activity::class, 'activity'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
||||
namespace Chill\ActivityBundle\Tests\Export\Aggregator;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator;
|
||||
use Chill\ActivityBundle\Export\Aggregator\CreatorJobAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
@@ -21,15 +21,15 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class JobScopeAggregatorTest extends AbstractAggregatorTest
|
||||
final class CreatorJobAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private JobScopeAggregator $aggregator;
|
||||
private CreatorJobAggregator $aggregator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$this->aggregator = self::$container->get(JobScopeAggregator::class);
|
||||
$this->aggregator = self::$container->get(CreatorJobAggregator::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
@@ -0,0 +1,57 @@
|
||||
<?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\Tests\Export\Aggregator\PersonAggregators;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Aggregator\PersonAggregators\PersonAggregator;
|
||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
||||
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class PersonAggregatorTest extends AbstractAggregatorTest
|
||||
{
|
||||
private LabelPersonHelper $labelPersonHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->labelPersonHelper = self::$container->get(LabelPersonHelper::class);
|
||||
}
|
||||
|
||||
public function getAggregator()
|
||||
{
|
||||
return new PersonAggregator($this->labelPersonHelper);
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
return [[]];
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
return [
|
||||
$em->createQueryBuilder()
|
||||
->select('count(activity.id)')
|
||||
->from(Activity::class, 'activity'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
<?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\Tests\Export\Export\LinkedToACP;
|
||||
|
||||
use Chill\ActivityBundle\Export\Declarations;
|
||||
use Chill\ActivityBundle\Export\Export\LinkedToACP\CountPersonsOnActivity;
|
||||
use Chill\MainBundle\Test\Export\AbstractExportTest;
|
||||
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class CountPersonsOnActivityTest extends AbstractExportTest
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
}
|
||||
|
||||
public function getExport()
|
||||
{
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
yield new CountPersonsOnActivity($em, $this->getParameters(true));
|
||||
yield new CountPersonsOnActivity($em, $this->getParameters(false));
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
return [[]];
|
||||
}
|
||||
|
||||
public function getModifiersCombination()
|
||||
{
|
||||
return [[
|
||||
Declarations::ACTIVITY,
|
||||
Declarations::ACTIVITY_ACP,
|
||||
PersonDeclarations::ACP_TYPE,
|
||||
PersonDeclarations::PERSON_TYPE,
|
||||
]];
|
||||
}
|
||||
}
|
@@ -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\Tests\Export\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Filter\ActivityPresenceFilter;
|
||||
use Chill\ActivityBundle\Repository\ActivityPresenceRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class ActivityPresenceFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
private TranslatorInterface $translator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
|
||||
$this->translator = self::$container->get(TranslatorInterface::class);
|
||||
$this->translatableStringHelper = self::$container->get(TranslatableStringHelperInterface::class);
|
||||
}
|
||||
|
||||
public function getFilter()
|
||||
{
|
||||
return new ActivityPresenceFilter($this->translatableStringHelper, $this->translator);
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$presences = self::$container->get(ActivityPresenceRepositoryInterface::class)
|
||||
->findAll();
|
||||
|
||||
return [
|
||||
[
|
||||
'presences' => $presences,
|
||||
],
|
||||
[
|
||||
'presences' => new ArrayCollection($presences),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::bootKernel();
|
||||
|
||||
$em = self::$container->get(EntityManagerInterface::class);
|
||||
|
||||
yield $em->createQueryBuilder()
|
||||
->select('count(activity.id)')
|
||||
->from(Activity::class, 'activity');
|
||||
|
||||
self::ensureKernelShutdown();
|
||||
}
|
||||
}
|
@@ -52,6 +52,7 @@ final class ActivityReasonFilterTest extends AbstractFilterTest
|
||||
public function getFormData()
|
||||
{
|
||||
self::bootKernel();
|
||||
$data = [];
|
||||
|
||||
$em = self::$container
|
||||
->get(EntityManagerInterface::class);
|
||||
@@ -62,10 +63,13 @@ final class ActivityReasonFilterTest extends AbstractFilterTest
|
||||
|
||||
// generate an array of 5 different combination of results
|
||||
for ($i = 0; 5 > $i; ++$i) {
|
||||
yield ['reasons' => new ArrayCollection(array_splice($reasons, ($i + 1) * -1))];
|
||||
$data[] = ['reasons' => new ArrayCollection(array_splice($reasons, ($i + 1) * -1))];
|
||||
$data[] = ['reasons' => array_splice($reasons, ($i + 1) * -1)];
|
||||
}
|
||||
|
||||
self::ensureKernelShutdown();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getQueryBuilders(): iterable
|
||||
|
@@ -57,6 +57,9 @@ final class ActivityTypeFilterTest extends AbstractFilterTest
|
||||
$data[] = [
|
||||
'types' => new ArrayCollection([$a]),
|
||||
];
|
||||
/*$data[] = [
|
||||
'types' => [$a],
|
||||
];*/
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@@ -0,0 +1,75 @@
|
||||
<?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\Tests\Export\Filter;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\ActivityBundle\Export\Filter\CreatorJobFilter;
|
||||
use Chill\MainBundle\Repository\UserJobRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\MainBundle\Test\Export\AbstractFilterTest;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class CreatorJobFilterTest extends AbstractFilterTest
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private TranslatableStringHelperInterface $translatableStringHelper;
|
||||
private TranslatorInterface $translator;
|
||||
private UserJobRepositoryInterface $userJobRepository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
self::bootKernel();
|
||||
|
||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
||||
$this->translatableStringHelper = self::$container->get(TranslatableStringHelperInterface::class);
|
||||
$this->translator = self::$container->get(TranslatorInterface::class);
|
||||
$this->userJobRepository = self::$container->get(UserJobRepositoryInterface::class);
|
||||
}
|
||||
|
||||
public function getFilter()
|
||||
{
|
||||
return new CreatorJobFilter(
|
||||
$this->translatableStringHelper,
|
||||
$this->translator,
|
||||
$this->userJobRepository
|
||||
);
|
||||
}
|
||||
|
||||
public function getFormData()
|
||||
{
|
||||
$this->setUp();
|
||||
$jobs = $this->userJobRepository->findAll();
|
||||
|
||||
return [
|
||||
['jobs' => $jobs],
|
||||
];
|
||||
}
|
||||
|
||||
public function getQueryBuilders()
|
||||
{
|
||||
self::setUp();
|
||||
|
||||
return [
|
||||
$this->entityManager->createQueryBuilder()
|
||||
->select('count(activity.id)')
|
||||
->from(Activity::class, 'activity')
|
||||
->join('activity.user', 'actuser'),
|
||||
];
|
||||
}
|
||||
}
|
@@ -16,6 +16,10 @@ services:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'list_activity_linked_to_person' }
|
||||
|
||||
Chill\ActivityBundle\Export\Export\LinkedToACP\CountPersonsOnActivity:
|
||||
tags:
|
||||
- { name: chill.export, alias: 'count_person_on_activity' }
|
||||
|
||||
chill.activity.export.count_activity_linked_to_acp:
|
||||
class: Chill\ActivityBundle\Export\Export\LinkedToACP\CountActivity
|
||||
tags:
|
||||
@@ -115,6 +119,10 @@ services:
|
||||
# affect all saved exports (unless we write a migration for that)
|
||||
- { name: chill.export_filter, alias: 'activity_userscope_filter' }
|
||||
|
||||
Chill\ActivityBundle\Export\Filter\CreatorJobFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_creatorjob_filter' }
|
||||
|
||||
Chill\ActivityBundle\Export\Filter\UsersJobFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_usersjob_filter' }
|
||||
@@ -131,6 +139,11 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'period_having_activity_betw_dates_filter' }
|
||||
|
||||
Chill\ActivityBundle\Export\Filter\ActivityPresenceFilter:
|
||||
tags:
|
||||
- { name: chill.export_filter, alias: 'activity_presence_filter' }
|
||||
|
||||
|
||||
## Aggregators
|
||||
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\ActivityReasonAggregator:
|
||||
tags:
|
||||
@@ -179,7 +192,7 @@ services:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_creator_scope_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\JobScopeAggregator:
|
||||
Chill\ActivityBundle\Export\Aggregator\CreatorJobAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_creator_job_aggregator }
|
||||
|
||||
@@ -214,3 +227,11 @@ services:
|
||||
Chill\ActivityBundle\Export\Aggregator\ACPAggregators\ByActivityTypeAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: acp_by_activity_type_aggregator }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\ActivityPresenceAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_presence_agg }
|
||||
|
||||
Chill\ActivityBundle\Export\Aggregator\PersonAggregators\PersonAggregator:
|
||||
tags:
|
||||
- { name: chill.export_aggregator, alias: activity_person_agg }
|
||||
|
@@ -332,6 +332,11 @@ docgen:
|
||||
myWorksOnly: Prendre en compte uniquement les actions d'accompagnement dont je suis référent
|
||||
|
||||
export:
|
||||
export:
|
||||
count_person_on_activity:
|
||||
title: Nombre d'usagers concernés par les échanges
|
||||
description: Compte le nombre d'usagers concernés par les échanges. Si un usager est présent dans plusieurs échanges, il n'est comptabilisé qu'une seule fois.
|
||||
header: Nombre d'usagers concernés par des échanges
|
||||
list:
|
||||
activity:
|
||||
users name: Nom des utilisateurs
|
||||
@@ -376,7 +381,11 @@ export:
|
||||
date mismatch: La date de fin de la période doit être supérieure à la date du début
|
||||
by_creator_scope:
|
||||
Filter activity by user scope: Filtrer les échanges par service du créateur de l'échange
|
||||
'Filtered activity by user scope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
|
||||
'Filtered activity by user scope: only %scopes%': "Filtré par service du créateur de l'échange: uniquement %scopes%"
|
||||
by_creator_job:
|
||||
job_form_label: Métiers
|
||||
Filter activity by user job: Filtrer les échanges par métier du créateur de l'échange
|
||||
'Filtered activity by user job: only %jobs%': "Filtré par service du créateur de l'échange: uniquement %jobs%"
|
||||
by_persons:
|
||||
Filter activity by persons: Filtrer les échanges par usager participant
|
||||
'Filtered activity by persons: only %persons%': 'Échanges filtrés par usagers participants: seulement %persons%'
|
||||
@@ -385,8 +394,16 @@ export:
|
||||
Sent or received: Envoyé ou reçu
|
||||
is sent: envoyé
|
||||
is received: reçu
|
||||
by_presence:
|
||||
Filter activity by activity presence: Filtrer les échanges par présence de l'usager
|
||||
presences: Présences
|
||||
'Filtered by activity presence: only %presences%': 'Filtré par présence de l''usager: seulement %presences%'
|
||||
|
||||
aggregator:
|
||||
person:
|
||||
by_person:
|
||||
title: Grouper les échanges par usager (dossier d'usager dans lequel l'échange est enregistré)
|
||||
person: Usager
|
||||
acp:
|
||||
by_activity_type:
|
||||
title: Grouper les parcours par type d'échange
|
||||
@@ -412,11 +429,14 @@ export:
|
||||
Group activity by creator scope: Grouper les échanges par service du créateur de l'échange
|
||||
Calc date: Date de calcul du service du créateur de l'échange
|
||||
by_creator_job:
|
||||
Group activity by creator job: Grouper les échanges par service du créateur de l'échange
|
||||
Calc date: Date de calcul du service du créateur de l'échange
|
||||
Group activity by creator job: Grouper les échanges par métier du créateur de l'échange
|
||||
Calc date: Date de calcul du métier du créateur de l'échange
|
||||
by_persons:
|
||||
Group activity by persons: Grouper les échanges par usager participant
|
||||
Persons: Usagers participants
|
||||
by_activity_presence:
|
||||
Group activity by presence: Grouper les échanges par présence de l'usager
|
||||
header: Présence de(s) usager(s)
|
||||
|
||||
generic_doc:
|
||||
filter:
|
||||
|
@@ -17,6 +17,7 @@ use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository;
|
||||
use Chill\AsideActivityBundle\Templating\Entity\CategoryRender;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -76,7 +77,7 @@ class ByActivityTypeFilter implements FilterInterface
|
||||
{
|
||||
$types = array_map(
|
||||
fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getTitle()),
|
||||
$data['types']->toArray()
|
||||
$data['types'] instanceof Collection ? $data['types']->toArray() : $data['types']
|
||||
);
|
||||
|
||||
return ['export.filter.Filtered by aside activity type: only %type%', [
|
||||
|
@@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserLocationType;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
@@ -54,7 +55,12 @@ final readonly class ByLocationFilter implements FilterInterface
|
||||
|
||||
public function describeAction($data, $format = 'string'): array
|
||||
{
|
||||
$locations = $data['locations']->map(fn (Location $l): string => $l->getName());
|
||||
$extractFunction = fn (Location $l): string => $l->getName();
|
||||
if ($data['locations'] instanceof Collection) {
|
||||
$locations = $data['locations']->map($extractFunction);
|
||||
} else {
|
||||
$locations = array_map($extractFunction, $data['locations']);
|
||||
}
|
||||
|
||||
return ['export.filter.Filtered by aside activity location: only %location%', [
|
||||
'%location%' => implode(', ', $locations),
|
||||
|
@@ -17,6 +17,7 @@ use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -80,7 +81,7 @@ class ByUserJobFilter implements FilterInterface
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserJob $job) => $this->translatableStringHelper->localize($job->getLabel()),
|
||||
$data['jobs']->toArray()
|
||||
$data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs']
|
||||
)
|
||||
),
|
||||
]];
|
||||
|
@@ -18,6 +18,7 @@ use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepositoryInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -83,7 +84,7 @@ class ByUserScopeFilter implements FilterInterface
|
||||
', ',
|
||||
array_map(
|
||||
fn (Scope $s) => $this->translatableStringHelper->localize($s->getName()),
|
||||
$data['scopes']->toArray()
|
||||
$data['scopes'] instanceof Collection ? $data['scopes']->toArray() : $data['scopes']
|
||||
)
|
||||
),
|
||||
]];
|
||||
|
@@ -356,20 +356,6 @@ final class CalendarSyncerTest extends TestCase
|
||||
}
|
||||
JSON;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// all tests should run when timezone = +02:00
|
||||
$brussels = new \DateTimeZone('Europe/Brussels');
|
||||
|
||||
if (7200 === $brussels->getOffset(new \DateTimeImmutable())) {
|
||||
date_default_timezone_set('Europe/Brussels');
|
||||
} else {
|
||||
date_default_timezone_set('Europe/Moscow');
|
||||
}
|
||||
}
|
||||
|
||||
public function testHandleAttendeesConfirmingCalendar(): void
|
||||
{
|
||||
$machineHttpClient = new MockHttpClient([
|
||||
|
@@ -30,7 +30,7 @@ class CustomFieldController extends AbstractController
|
||||
/**
|
||||
* Creates a new CustomField entity.
|
||||
*
|
||||
* @Route("/{_locale}/admin/customfield/new", name="customfield_new")
|
||||
* @Route("/{_locale}/admin/customfield/new", name="customfield_create")
|
||||
*/
|
||||
public function createAction(Request $request)
|
||||
{
|
||||
|
@@ -232,7 +232,7 @@ class CustomFieldsGroupController extends AbstractController
|
||||
/**
|
||||
* Finds and displays a CustomFieldsGroup entity.
|
||||
*
|
||||
* @Route("/{_locale}/admin/customfieldsgroup/{id}/show", name="customfieldsgroup/show")
|
||||
* @Route("/{_locale}/admin/customfieldsgroup/{id}/show", name="customfieldsgroup_show")
|
||||
*/
|
||||
public function showAction(mixed $id)
|
||||
{
|
||||
@@ -256,7 +256,7 @@ class CustomFieldsGroupController extends AbstractController
|
||||
/**
|
||||
* Edits an existing CustomFieldsGroup entity.
|
||||
*
|
||||
* @Route("/{_locale}/admin/customfieldsgroup/{id}/update", name="customfieldsgroup/update")
|
||||
* @Route("/{_locale}/admin/customfieldsgroup/{id}/update", name="customfieldsgroup_update")
|
||||
*/
|
||||
public function updateAction(Request $request, mixed $id)
|
||||
{
|
||||
@@ -383,7 +383,7 @@ class CustomFieldsGroupController extends AbstractController
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
|
||||
$customFieldsGroupIds = $em->createQuery('SELECT g.id FROM '
|
||||
.'ChillCustomFieldsBundle:CustomFieldsDefaultGroup d '
|
||||
.CustomFieldsDefaultGroup::class.' d '
|
||||
.'JOIN d.customFieldsGroup g')
|
||||
->getResult(Query::HYDRATE_SCALAR);
|
||||
|
||||
|
@@ -280,7 +280,7 @@ class CustomFieldChoice extends AbstractCustomField
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/choice.html.twig';
|
||||
|
||||
if ('csv' === $documentType) {
|
||||
$template = 'ChillCustomFieldsBundle:CustomFieldsRendering:choice.csv.twig';
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/choice.csv.twig';
|
||||
}
|
||||
|
||||
return $this->templating
|
||||
|
@@ -68,7 +68,7 @@ class CustomFieldDate extends AbstractCustomField
|
||||
{
|
||||
$validatorFunction = static function ($value, ExecutionContextInterface $context) {
|
||||
try {
|
||||
$date = new \DateTime($value);
|
||||
$date = new \DateTime((string) $value);
|
||||
} catch (\Exception) {
|
||||
$context->buildViolation('The expression "%expression%" is invalid', [
|
||||
'%expression%' => $value,
|
||||
@@ -125,7 +125,7 @@ class CustomFieldDate extends AbstractCustomField
|
||||
return $date->format('Y-m-d');
|
||||
|
||||
default:
|
||||
$template = 'ChillCustomFieldsBundle:CustomFieldsRendering:date.'
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/date.'
|
||||
.$documentType.'.twig';
|
||||
|
||||
return $this->templating
|
||||
|
@@ -96,7 +96,7 @@ class CustomFieldLongChoice extends AbstractCustomField
|
||||
public function render($value, CustomField $customField, $documentType = 'html')
|
||||
{
|
||||
$option = $this->deserialize($value, $customField);
|
||||
$template = 'ChillCustomFieldsBundle:CustomFieldsRendering:choice_long.'
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/choice_long.'
|
||||
.$documentType.'.twig';
|
||||
|
||||
return $this->templating
|
||||
|
@@ -95,7 +95,7 @@ class CustomFieldNumber extends AbstractCustomField
|
||||
|
||||
public function render($value, CustomField $customField, $documentType = 'html')
|
||||
{
|
||||
$template = 'ChillCustomFieldsBundle:CustomFieldsRendering:number.'
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/number.'
|
||||
.$documentType.'.twig';
|
||||
$options = $customField->getOptions();
|
||||
|
||||
|
@@ -89,7 +89,7 @@ class CustomFieldText extends AbstractCustomField
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/text.html.twig';
|
||||
|
||||
if ('csv' === $documentType) {
|
||||
$template = 'ChillCustomFieldsBundle:CustomFieldsRendering:text.csv.twig';
|
||||
$template = '@ChillCustomFields/CustomFieldsRendering/text.csv.twig';
|
||||
}
|
||||
|
||||
return $this->templating
|
||||
|
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\CustomFieldsBundle\Form;
|
||||
|
||||
use Chill\CustomFieldsBundle\Entity\CustomFieldsGroup;
|
||||
use Chill\CustomFieldsBundle\Form\DataTransformer\CustomFieldsGroupToIdTransformer;
|
||||
use Chill\CustomFieldsBundle\Service\CustomFieldProvider;
|
||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||
@@ -45,7 +46,7 @@ class CustomFieldType extends AbstractType
|
||||
|
||||
if ('entity' === $options['group_widget']) {
|
||||
$builder->add('customFieldsGroup', EntityType::class, [
|
||||
'class' => 'ChillCustomFieldsBundle:CustomFieldsGroup',
|
||||
'class' => CustomFieldsGroup::class,
|
||||
'choice_label' => fn ($g) => $this->translatableStringHelper->localize($g->getName()),
|
||||
]);
|
||||
} elseif ('hidden' === $options['group_widget']) {
|
||||
|
@@ -1,4 +1,8 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Chill\CustomFieldsBundle\Controller\:
|
||||
resource: '../../Controller'
|
||||
tags: ['controller.service_arguments']
|
||||
|
@@ -11,14 +11,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\DocGeneratorBundle\Serializer\Helper;
|
||||
|
||||
use Symfony\Component\Serializer\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
class NormalizeNullValueHelper
|
||||
{
|
||||
public function __construct(private readonly NormalizerInterface $normalizer, private readonly ?string $discriminatorType = null, private readonly ?string $discriminatorValue = null) {}
|
||||
|
||||
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [], ClassMetadata $classMetadata = null)
|
||||
public function normalize(array $attributes, string $format = 'docgen', ?array $context = [], ClassMetadataInterface $classMetadata = null)
|
||||
{
|
||||
$data = [];
|
||||
$data['isNull'] = true;
|
||||
@@ -44,7 +44,7 @@ class NormalizeNullValueHelper
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getContextForAttribute(string $key, array $initialContext, ?ClassMetadata $classMetadata): array
|
||||
private function getContextForAttribute(string $key, array $initialContext, ?ClassMetadataInterface $classMetadata): array
|
||||
{
|
||||
if (null === $classMetadata) {
|
||||
return $initialContext;
|
||||
|
@@ -19,6 +19,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Serializer\Exception\LogicException;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
|
||||
use Symfony\Component\Serializer\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
@@ -52,12 +53,15 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
||||
}
|
||||
|
||||
$metadata = $this->classMetadataFactory->getMetadataFor($classMetadataKey);
|
||||
if (!$metadata instanceof ClassMetadata) {
|
||||
throw new \LogicException('ClassMetadata should be the only one implementation for ClassMetadataInterface. See https://github.com/symfony/symfony/pull/17114');
|
||||
}
|
||||
$expectedGroups = \array_key_exists(AbstractNormalizer::GROUPS, $context) ?
|
||||
\is_array($context[AbstractNormalizer::GROUPS]) ? $context[AbstractNormalizer::GROUPS] : [$context[AbstractNormalizer::GROUPS]]
|
||||
: [];
|
||||
$attributes = \array_filter(
|
||||
$metadata->getAttributesMetadata(),
|
||||
static function (AttributeMetadata $a) use ($expectedGroups) {
|
||||
static function (AttributeMetadataInterface $a) use ($expectedGroups) {
|
||||
foreach ($a->getGroups() as $g) {
|
||||
if (\in_array($g, $expectedGroups, true)) {
|
||||
return true;
|
||||
@@ -119,7 +123,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
||||
return $type->getName();
|
||||
}
|
||||
if ($type instanceof \ReflectionIntersectionType) {
|
||||
foreach (array_map(fn (\ReflectionNamedType $t) => $t->getName(), $type->getTypes()) as $classString) {
|
||||
foreach (array_map(fn (\ReflectionType $t) => $t->getName(), $type->getTypes()) as $classString) {
|
||||
if (ReadableCollection::class === $classString) {
|
||||
return ReadableCollection::class;
|
||||
}
|
||||
@@ -211,7 +215,7 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|AttributeMetadata[] $attributes
|
||||
* @param array<AttributeMetadata> $attributes
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
|
@@ -304,7 +304,7 @@ class ExportController extends AbstractController
|
||||
FormType::class,
|
||||
$defaultFormData,
|
||||
[
|
||||
'method' => $isGenerate ? 'GET' : 'POST',
|
||||
'method' => $isGenerate ? Request::METHOD_GET : Request::METHOD_POST,
|
||||
'csrf_protection' => !$isGenerate,
|
||||
]
|
||||
);
|
||||
@@ -352,7 +352,7 @@ class ExportController extends AbstractController
|
||||
|
||||
$form = $this->createCreateFormExport($alias, 'export', $data, $savedExport);
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
@@ -406,7 +406,7 @@ class ExportController extends AbstractController
|
||||
|
||||
$form = $this->createCreateFormExport($alias, 'formatter', $data, $savedExport);
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
@@ -536,7 +536,7 @@ class ExportController extends AbstractController
|
||||
|
||||
$form = $this->createCreateFormExport($alias, 'centers', [], $savedExport);
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
if (Request::METHOD_POST === $request->getMethod()) {
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
|
@@ -0,0 +1,65 @@
|
||||
<?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\MainBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Form\UserPhonenumberType;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class UserProfileController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TranslatorInterface $translator,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* User profile that allows editing of phonenumber and visualization of certain data.
|
||||
*
|
||||
* @Route("/{_locale}/main/user/my-profile", name="chill_main_user_profile")
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$editForm = $this->createPhonenumberEditForm($user);
|
||||
$editForm->handleRequest($request);
|
||||
|
||||
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||
$phonenumber = $editForm->get('phonenumber')->getData();
|
||||
|
||||
$user->setPhonenumber($phonenumber);
|
||||
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
$this->addFlash('success', $this->translator->trans('user.profile.Phonenumber successfully updated!'));
|
||||
|
||||
return $this->redirectToRoute('chill_main_user_profile');
|
||||
}
|
||||
|
||||
return $this->render('@ChillMain/User/profile.html.twig', [
|
||||
'user' => $user,
|
||||
'form' => $editForm->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createPhonenumberEditForm(UserInterface $user): FormInterface
|
||||
{
|
||||
return $this->createForm(
|
||||
UserPhonenumberType::class,
|
||||
$user,
|
||||
)
|
||||
->add('submit', SubmitType::class, ['label' => $this->translator->trans('Save')]);
|
||||
}
|
||||
}
|
@@ -18,9 +18,11 @@ use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Doctrine\Common\Collections\Selectable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Chill\MainBundle\Validation\Constraint\PhonenumberConstraint;
|
||||
|
||||
/**
|
||||
* User.
|
||||
@@ -161,6 +163,15 @@ class User implements UserInterface, \Stringable
|
||||
*/
|
||||
private ?string $usernameCanonical = null;
|
||||
|
||||
/**
|
||||
* The user's mobile phone number.
|
||||
*
|
||||
* @ORM\Column(type="phone_number", nullable=true)
|
||||
*
|
||||
* @PhonenumberConstraint()
|
||||
*/
|
||||
private ?PhoneNumber $phonenumber = null;
|
||||
|
||||
/**
|
||||
* User constructor.
|
||||
*/
|
||||
@@ -419,6 +430,11 @@ class User implements UserInterface, \Stringable
|
||||
}
|
||||
}
|
||||
|
||||
public function getPhonenumber(): ?PhoneNumber
|
||||
{
|
||||
return $this->phonenumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException if the groupCenter is not in the collection
|
||||
*/
|
||||
@@ -639,4 +655,11 @@ class User implements UserInterface, \Stringable
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPhonenumber(?PhoneNumber $phonenumber): self
|
||||
{
|
||||
$this->phonenumber = $phonenumber;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -37,9 +37,13 @@ class RollingDateDataMapper implements DataMapperInterface
|
||||
{
|
||||
$forms = iterator_to_array($forms);
|
||||
|
||||
$viewData = new RollingDate(
|
||||
$forms['roll']->getData() ?? RollingDate::T_TODAY,
|
||||
$forms['fixedDate']->getData()
|
||||
);
|
||||
if (null === $forms['roll']->getData()) {
|
||||
$viewData = null;
|
||||
} else {
|
||||
$viewData = new RollingDate(
|
||||
$forms['roll']->getData(),
|
||||
$forms['fixedDate']->getData()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ class PickRollingDateType extends AbstractType
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => RollingDate::class,
|
||||
'empty_data' => new RollingDate(RollingDate::T_TODAY),
|
||||
'empty_data' => null,
|
||||
'constraints' => [
|
||||
new Callback($this->validate(...)),
|
||||
],
|
||||
@@ -66,6 +66,10 @@ class PickRollingDateType extends AbstractType
|
||||
|
||||
public function validate($data, ExecutionContextInterface $context, $payload): void
|
||||
{
|
||||
if (null === $data) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var RollingDate $data */
|
||||
if (RollingDate::T_FIXED_DATE === $data->getRoll() && null === $data->getFixedDate()) {
|
||||
$context
|
||||
|
36
src/Bundle/ChillMainBundle/Form/UserPhonenumberType.php
Normal file
36
src/Bundle/ChillMainBundle/Form/UserPhonenumberType.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class UserPhonenumberType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('phonenumber', ChillPhoneNumberType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => User::class,
|
||||
]);
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
|
||||
use Chill\MainBundle\Form\Type\PickCivilityType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
@@ -44,6 +45,9 @@ class UserType extends AbstractType
|
||||
->add('email', EmailType::class, [
|
||||
'required' => true,
|
||||
])
|
||||
->add('phonenumber', ChillPhoneNumberType::class, [
|
||||
'required' => false,
|
||||
])
|
||||
->add('label', TextType::class)
|
||||
->add('civility', PickCivilityType::class, [
|
||||
'required' => false,
|
||||
|
@@ -12,14 +12,15 @@ declare(strict_types=1);
|
||||
namespace Chill\MainBundle\Repository;
|
||||
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class UserJobRepository implements UserJobRepositoryInterface
|
||||
readonly class UserJobRepository implements UserJobRepositoryInterface
|
||||
{
|
||||
private readonly EntityRepository $repository;
|
||||
private EntityRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
public function __construct(EntityManagerInterface $em, private TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
$this->repository = $em->getRepository(UserJob::class);
|
||||
}
|
||||
@@ -42,6 +43,15 @@ class UserJobRepository implements UserJobRepositoryInterface
|
||||
return $this->repository->findBy(['active' => true]);
|
||||
}
|
||||
|
||||
public function findAllOrderedByName(): array
|
||||
{
|
||||
$jobs = $this->findAll();
|
||||
|
||||
usort($jobs, fn (UserJob $a, UserJob $b) => $this->translatableStringHelper->localize($a->getLabel()) <=> $this->translatableStringHelper->localize($b->getLabel()));
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
|
@@ -28,6 +28,13 @@ interface UserJobRepositoryInterface extends ObjectRepository
|
||||
*/
|
||||
public function findAllActive(): array;
|
||||
|
||||
/**
|
||||
* a list of UserJob ordered by name.
|
||||
*
|
||||
* @return array<UserJob>
|
||||
*/
|
||||
public function findAllOrderedByName(): array;
|
||||
|
||||
/**
|
||||
* @param mixed|null $limit
|
||||
* @param mixed|null $offset
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
// 3. Include remainder of required Bootstrap stylesheets
|
||||
@import "bootstrap/scss/variables";
|
||||
@import "bootstrap/scss/variables-dark";
|
||||
|
||||
// 4. Include any default map overrides here
|
||||
@import "custom/_maps";
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import {ShowHide} from 'ChillMainAssets/lib/show_hide/index';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(_e) {
|
||||
console.log('pick-rolling-date');
|
||||
document.querySelectorAll('div[data-rolling-date]').forEach( (picker) => {
|
||||
const
|
||||
roll_wrapper = picker.querySelector('div.roll-wrapper'),
|
||||
@@ -11,12 +10,8 @@ document.addEventListener('DOMContentLoaded', function(_e) {
|
||||
froms: [roll_wrapper],
|
||||
container: [fixed_wrapper],
|
||||
test: function (elems) {
|
||||
console.log('testing');
|
||||
console.log('elems', elems);
|
||||
for (let el of elems) {
|
||||
for (let select_roll of el.querySelectorAll('select[data-roll-picker]')) {
|
||||
console.log('select_roll', select_roll);
|
||||
console.log('value', select_roll.value);
|
||||
return select_roll.value === 'fixed_date';
|
||||
}
|
||||
}
|
||||
|
@@ -28,9 +28,7 @@
|
||||
{% block js %}
|
||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
||||
{{ encore_entry_script_tags('page_export') }}
|
||||
{% if export_alias == 'count_social_work_actions' %}
|
||||
{{ encore_entry_script_tags('vue_export_action_goal_result') }}
|
||||
{% endif %}
|
||||
{{ encore_entry_script_tags('vue_export_action_goal_result') }}
|
||||
{{ encore_entry_script_tags('mod_pick_rolling_date') }}
|
||||
{% endblock js %}
|
||||
|
||||
|
@@ -0,0 +1,58 @@
|
||||
{#
|
||||
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
|
||||
<info@champs-libres.coop> / <http://www.champs-libres.coop>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#}
|
||||
|
||||
|
||||
{% extends "@ChillMain/layout.html.twig" %}
|
||||
|
||||
|
||||
{% block title %}{{"My profile"|trans}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="justify-content-center col-10">
|
||||
<h1>{{ 'user.profile.title'|trans }}</h1>
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>{{ 'Job'|trans }}</dt>
|
||||
{% if user.getUserJob is not null %}
|
||||
<dd>{{ user.getUserJob.label|localize_translatable_string }}</dd>
|
||||
{% else %}
|
||||
<dd class="chill-no-data-statement">{{ 'user.profile.no job'|trans }}</dd>
|
||||
{% endif %}
|
||||
<dt>{{ 'Scope'|trans }}</dt>
|
||||
{% if user.getMainScope is not null %}
|
||||
<dd>{{ user.getMainScope.name|localize_translatable_string }}</dd>
|
||||
{% else %}
|
||||
<dd class="chill-no-data-statement">{{ 'user.profile.no scope'|trans }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<div>
|
||||
{{ form_start(form) }}
|
||||
{{ form_row(form.phonenumber) }}
|
||||
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
{{ form_widget(form.submit, { 'attr': { 'class': 'btn btn-save' } } ) }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@@ -29,6 +29,14 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
||||
$user = $this->security->getUser();
|
||||
|
||||
if ($user instanceof User) {
|
||||
$menu->addChild($this->translator->trans('user.profile.title'), [
|
||||
'route' => 'chill_main_user_profile',
|
||||
])
|
||||
->setExtras([
|
||||
'order' => -11_111_111,
|
||||
'icon' => 'user',
|
||||
]);
|
||||
|
||||
if (null !== $user->getCurrentLocation()) {
|
||||
$locationTextMenu = $user->getCurrentLocation()->getName();
|
||||
} else {
|
||||
|
@@ -18,6 +18,7 @@ use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
@@ -34,6 +35,7 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware
|
||||
'text_without_absent' => '',
|
||||
'label' => '',
|
||||
'email' => '',
|
||||
'isAbsent' => false,
|
||||
];
|
||||
|
||||
public function __construct(private readonly UserRender $userRender) {}
|
||||
@@ -61,9 +63,13 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware
|
||||
$context,
|
||||
['docgen:expects' => Civility::class, 'groups' => 'docgen:read']
|
||||
);
|
||||
$phonenumberContext = array_merge(
|
||||
$context,
|
||||
['docgen:expects' => PhoneNumber::class, 'groups' => 'docgen:read']
|
||||
);
|
||||
|
||||
if (null === $object && 'docgen' === $format) {
|
||||
return [...self::NULL_USER, 'civility' => $this->normalizer->normalize(null, $format, $civilityContext), 'user_job' => $this->normalizer->normalize(null, $format, $userJobContext), 'main_center' => $this->normalizer->normalize(null, $format, $centerContext), 'main_scope' => $this->normalizer->normalize(null, $format, $scopeContext), 'current_location' => $this->normalizer->normalize(null, $format, $locationContext), 'main_location' => $this->normalizer->normalize(null, $format, $locationContext)];
|
||||
return [...self::NULL_USER, 'phonenumber' => $this->normalizer->normalize(null, $format, $phonenumberContext), 'civility' => $this->normalizer->normalize(null, $format, $civilityContext), 'user_job' => $this->normalizer->normalize(null, $format, $userJobContext), 'main_center' => $this->normalizer->normalize(null, $format, $centerContext), 'main_scope' => $this->normalizer->normalize(null, $format, $scopeContext), 'current_location' => $this->normalizer->normalize(null, $format, $locationContext), 'main_location' => $this->normalizer->normalize(null, $format, $locationContext)];
|
||||
}
|
||||
|
||||
$data = [
|
||||
@@ -74,6 +80,7 @@ class UserNormalizer implements ContextAwareNormalizerInterface, NormalizerAware
|
||||
'text_without_absent' => $this->userRender->renderString($object, ['absence' => false]),
|
||||
'label' => $object->getLabel(),
|
||||
'email' => (string) $object->getEmail(),
|
||||
'phonenumber' => $this->normalizer->normalize($object->getPhonenumber(), $format, $phonenumberContext),
|
||||
'user_job' => $this->normalizer->normalize($object->getUserJob(), $format, $userJobContext),
|
||||
'main_center' => $this->normalizer->normalize($object->getMainCenter(), $format, $centerContext),
|
||||
'main_scope' => $this->normalizer->normalize($object->getMainScope(), $format, $scopeContext),
|
||||
|
@@ -0,0 +1,33 @@
|
||||
<?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\MainBundle\Tests\Controller;
|
||||
|
||||
use Chill\MainBundle\Test\PrepareClientTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class UserProfileControllerTest extends WebTestCase
|
||||
{
|
||||
use PrepareClientTrait;
|
||||
|
||||
public function testPage()
|
||||
{
|
||||
$client = $this->getClientAuthenticated();
|
||||
|
||||
$client->request('GET', '/fr/main/user/my-profile');
|
||||
$this->assertResponseIsSuccessful('Request GET /main/user/my-profile was successful');
|
||||
}
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
<?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 Serializer\Normalizer;
|
||||
|
||||
use Chill\MainBundle\Entity\Center;
|
||||
use Chill\MainBundle\Entity\Civility;
|
||||
use Chill\MainBundle\Entity\Location;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\UserJob;
|
||||
use Chill\MainBundle\Serializer\Normalizer\UserNormalizer;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumber;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\Serializer\Exception\ExceptionInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class UserNormalizerTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
/**
|
||||
* @throws NumberParseException
|
||||
*/
|
||||
public function dataProviderUserNormalizer()
|
||||
{
|
||||
$user = new User();
|
||||
$userNoPhone = new User();
|
||||
|
||||
$user
|
||||
->setUsername('SomeUser')
|
||||
->setLabel('SomeUser')
|
||||
->setPhonenumber(PhoneNumberUtil::getInstance()->parse('+32475928635'))
|
||||
->setEmail('some.user@chill.com');
|
||||
|
||||
$userNoPhone
|
||||
->setUsername('AnotherUser')
|
||||
->setLabel('AnotherUser');
|
||||
|
||||
yield [$user, 'docgen', ['docgen:expects' => User::class],
|
||||
[
|
||||
'id' => $user->getId(), // id
|
||||
'type' => 'user', // type
|
||||
'username' => 'SomeUser', // username
|
||||
'email' => 'some.user@chill.com', // email
|
||||
'text' => 'SomeUser', // text
|
||||
'label' => 'SomeUser', // label
|
||||
'phonenumber' => ['context' => PhoneNumber::class], // phonenumber
|
||||
'main_scope' => ['context' => Scope::class], // scope
|
||||
'user_job' => ['context' => UserJob::class], // user job
|
||||
'current_location' => ['context' => Location::class], // curent location
|
||||
'main_location' => ['context' => Location::class], // main location
|
||||
'civility' => ['context' => Civility::class], // civility
|
||||
'text_without_absent' => 'SomeUser',
|
||||
'isAbsent' => false,
|
||||
'main_center' => ['context' => Center::class],
|
||||
]];
|
||||
|
||||
yield [$userNoPhone, 'docgen', ['docgen:expects' => User::class],
|
||||
[
|
||||
'id' => $user->getId(), // id
|
||||
'type' => 'user', // type
|
||||
'username' => 'AnotherUser', // username
|
||||
'email' => '', // email
|
||||
'text' => 'AnotherUser', // text
|
||||
'label' => 'AnotherUser', // label
|
||||
'phonenumber' => ['context' => PhoneNumber::class], // phonenumber
|
||||
'main_scope' => ['context' => Scope::class], // scope
|
||||
'user_job' => ['context' => UserJob::class], // user job
|
||||
'current_location' => ['context' => Location::class], // curent location
|
||||
'main_location' => ['context' => Location::class], // main location
|
||||
'civility' => ['context' => Civility::class], // civility
|
||||
'text_without_absent' => 'AnotherUser',
|
||||
'isAbsent' => false,
|
||||
'main_center' => ['context' => Center::class],
|
||||
]];
|
||||
|
||||
yield [null, 'docgen', ['docgen:expects' => User::class], [
|
||||
'id' => '', // id
|
||||
'type' => 'user', // type
|
||||
'username' => '', // username
|
||||
'email' => '', // email
|
||||
'text' => '', // text
|
||||
'label' => '', // label
|
||||
'phonenumber' => ['context' => PhoneNumber::class], // phonenumber
|
||||
'main_scope' => ['context' => Scope::class], // scope
|
||||
'user_job' => ['context' => UserJob::class], // user job
|
||||
'current_location' => ['context' => Location::class], // curent location
|
||||
'main_location' => ['context' => Location::class], // main location
|
||||
'civility' => ['context' => Civility::class], // civility
|
||||
'text_without_absent' => '',
|
||||
'isAbsent' => false,
|
||||
'main_center' => ['context' => Center::class],
|
||||
]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderUserNormalizer
|
||||
*
|
||||
* @throws ExceptionInterface
|
||||
*/
|
||||
public function testNormalize(null|User $user, mixed $format, mixed $context, mixed $expected)
|
||||
{
|
||||
$userRender = $this->prophesize(UserRender::class);
|
||||
$userRender->renderString(Argument::type(User::class), Argument::type('array'))->willReturn($user ? $user->getLabel() : '');
|
||||
|
||||
$normalizer = new UserNormalizer($userRender->reveal());
|
||||
$normalizer->setNormalizer(new class () implements NormalizerInterface {
|
||||
public function normalize($object, string $format = null, array $context = [])
|
||||
{
|
||||
return ['context' => $context['docgen:expects'] ?? null];
|
||||
}
|
||||
|
||||
public function supportsNormalization($data, string $format = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
$this->assertEquals($expected, $normalizer->normalize($user, $format, $context));
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<?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\Migrations\Main;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Add phonenumber to user profile.
|
||||
*/
|
||||
final class Version20231020075524 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add phonenumber to user profile';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users ADD phonenumber VARCHAR(35) DEFAULT NULL');
|
||||
$this->addSql('COMMENT ON COLUMN users.phonenumber IS \'(DC2Type:phone_number)\'');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users DROP phonenumber');
|
||||
}
|
||||
}
|
@@ -44,6 +44,13 @@ address_fields: Données liées à l'adresse
|
||||
Datas: Données
|
||||
No title: Aucun titre
|
||||
|
||||
user:
|
||||
profile:
|
||||
title: Mon profil
|
||||
Phonenumber successfully updated!: Numéro de téléphone mis à jour!
|
||||
no job: Pas de métier assigné
|
||||
no scope: Pas de cercle assigné
|
||||
|
||||
inactive: inactif
|
||||
|
||||
Edit: Modifier
|
||||
|
@@ -39,6 +39,13 @@ Last updated by: Laatste update door
|
||||
on: "op "
|
||||
Last updated on: Laatste update op
|
||||
by_user: "door "
|
||||
lifecycleUpdate: Updates en creatie gebeurtenissen
|
||||
address_fields: Gegevens gelinked aan het adres
|
||||
Datas: Gegevens
|
||||
No title: Geen titel
|
||||
User profile: Mijn gebruikersprofiel
|
||||
Phonenumber successfully updated!: Telefoonnummer bijgewerkt!
|
||||
|
||||
|
||||
Edit: Bewerken
|
||||
Update: Updaten
|
||||
|
@@ -24,9 +24,19 @@ class PersonMoveRelationHandler implements PersonMoveSqlHandlerInterface
|
||||
|
||||
public function getSqls(string $className, string $field, Person $from, Person $to): array
|
||||
{
|
||||
/* Insert sql statement taking into account two cases.
|
||||
One where the person is the fromperson and another where the person is the toperson in the relationship.*/
|
||||
$insertSql = sprintf(<<<'SQL'
|
||||
INSERT INTO chill_person_relationships (id, relation_id, reverse, fromperson_id, toperson_id)
|
||||
SELECT nextval('chill_person_relationships_id_seq'), relation_id, reverse, fromperson_id, toperson_id
|
||||
INSERT INTO chill_person_relationships (id, relation_id, reverse, createdat, createdby_id, fromperson_id, toperson_id)
|
||||
SELECT nextval('chill_person_relationships_id_seq'), relation_id, reverse, createdat, createdby_id,
|
||||
CASE
|
||||
WHEN cpr.fromperson_id = %d THEN %d
|
||||
ELSE cpr.fromperson_id
|
||||
END as fromperson,
|
||||
CASE
|
||||
WHEN cpr.toperson_id = %d THEN %d
|
||||
ELSE cpr.toperson_id
|
||||
END as toperson
|
||||
FROM chill_person_relationships cpr
|
||||
WHERE fromperson_id = %d OR toperson_id = %d
|
||||
AND NOT EXISTS (
|
||||
@@ -35,7 +45,7 @@ class PersonMoveRelationHandler implements PersonMoveSqlHandlerInterface
|
||||
cpr2.fromperson_id = %d AND cpr2.toperson_id = %d
|
||||
OR cpr2.fromperson_id = %d AND cpr2.toperson_id = %d
|
||||
);
|
||||
SQL, $from->getId(), $from->getId(), $to->getId(), $from->getId(), $from->getId(), $to->getId());
|
||||
SQL, $from->getId(), $to->getId(), $from->getId(), $to->getId(), $from->getId(), $from->getId(), $to->getId(), $from->getId(), $from->getId(), $to->getId());
|
||||
|
||||
$deleteSql = [
|
||||
sprintf('DELETE FROM chill_person_relationships WHERE fromperson_id = %d', $from->getId()),
|
||||
|
@@ -82,7 +82,6 @@ class PersonDuplicateController extends \Symfony\Bundle\FrameworkBundle\Controll
|
||||
$connection->beginTransaction();
|
||||
|
||||
foreach ($sqls as $sql) {
|
||||
dump($sql);
|
||||
$connection->executeQuery($sql);
|
||||
}
|
||||
$connection->commit();
|
||||
|
@@ -392,9 +392,13 @@ class AccompanyingPeriodWork implements AccompanyingPeriodLinkedWithSocialIssues
|
||||
*/
|
||||
public function getReferrers(): ReadableCollection
|
||||
{
|
||||
return $this->referrersHistory
|
||||
$users = $this->referrersHistory
|
||||
->filter(fn (AccompanyingPeriodWorkReferrerHistory $h) => null === $h->getEndDate())
|
||||
->map(fn (AccompanyingPeriodWorkReferrerHistory $h) => $h->getUser());
|
||||
->map(fn (AccompanyingPeriodWorkReferrerHistory $h) => $h->getUser())
|
||||
->getValues()
|
||||
;
|
||||
|
||||
return new ArrayCollection(array_values($users));
|
||||
}
|
||||
|
||||
public function getReferrersHistory(): Collection
|
||||
|
@@ -56,7 +56,7 @@ final readonly class GeographicalUnitStatAggregator implements AggregatorInterfa
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
'IDENTITY(acp_geog_agg_address_person_location.person) = IDENTITY(acp_geog_agg_location_history.personLocation)',
|
||||
'acp_geog_agg_address_person_location.validFrom < :acp_geog_aggregator_date',
|
||||
'acp_geog_agg_address_person_location.validFrom <= :acp_geog_aggregator_date',
|
||||
$qb->expr()->orX(
|
||||
'acp_geog_agg_address_person_location.validTo > :acp_geog_aggregator_date',
|
||||
$qb->expr()->isNull('acp_geog_agg_address_person_location.validTo')
|
||||
@@ -83,7 +83,12 @@ final readonly class GeographicalUnitStatAggregator implements AggregatorInterfa
|
||||
'acp_geog_units'
|
||||
);
|
||||
|
||||
$qb->andWhere($qb->expr()->in('acp_geog_units.layer', ':acp_geog_unit_layer'));
|
||||
$qb->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('acp_geog_units'),
|
||||
$qb->expr()->in('acp_geog_units.layer', ':acp_geog_unit_layer')
|
||||
)
|
||||
);
|
||||
|
||||
$qb->setParameter('acp_geog_unit_layer', $data['level']);
|
||||
|
||||
|
@@ -19,11 +19,10 @@ use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ChildrenNumberAggregator implements AggregatorInterface
|
||||
{
|
||||
public function __construct(private readonly TranslatorInterface $translator, private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
public function __construct(private readonly RollingDateConverterInterface $rollingDateConverter) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
@@ -71,7 +70,7 @@ class ChildrenNumberAggregator implements AggregatorInterface
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return static function ($value): string {
|
||||
return static function (null|int|string $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'Number of children';
|
||||
}
|
||||
@@ -80,7 +79,7 @@ class ChildrenNumberAggregator implements AggregatorInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
return (string) $value;
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -35,21 +36,26 @@ class GeographicalUnitAggregator implements AggregatorInterface
|
||||
public function alterQuery(QueryBuilder $qb, $data): void
|
||||
{
|
||||
$qb
|
||||
->leftJoin('person.householdAddresses', 'person_geog_agg_current_household_address')
|
||||
->leftJoin('person_geog_agg_current_household_address.address', 'person_geog_agg_address')
|
||||
->leftJoin('person_geog_agg_address.geographicalUnits', 'person_geog_agg_geog_unit')
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_geog_agg_current_household_address'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte('person_geog_agg_current_household_address.validFrom', ':person_geog_agg_date'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_geog_agg_current_household_address.validTo'),
|
||||
$qb->expr()->gt('person_geog_agg_current_household_address.validTo', ':person_geog_agg_date')
|
||||
)
|
||||
->leftJoin(
|
||||
'person.householdAddresses',
|
||||
'person_geog_agg_current_household_address',
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte('person_geog_agg_current_household_address.validFrom', ':person_geog_agg_date'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_geog_agg_current_household_address.validTo'),
|
||||
$qb->expr()->gt('person_geog_agg_current_household_address.validTo', ':person_geog_agg_date')
|
||||
)
|
||||
)
|
||||
)
|
||||
->leftJoin(
|
||||
'person_geog_agg_current_household_address.address',
|
||||
'person_geog_agg_address'
|
||||
)
|
||||
->leftJoin(
|
||||
'person_geog_agg_address.geographicalUnits',
|
||||
'person_geog_agg_geog_unit'
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull('person_geog_agg_geog_unit'),
|
||||
|
@@ -0,0 +1,99 @@
|
||||
<?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\PersonBundle\Export\Aggregator\PersonAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
final readonly class PostalCodeAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'person_postal_code_agg';
|
||||
|
||||
public function __construct(
|
||||
private RollingDateConverterInterface $rollingDateConverter,
|
||||
) {}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.aggregator.person.by_postal_code.at_date',
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['calc_date' => new RollingDate(RollingDate::T_TODAY)];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return function (null|int|string $value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.person.by_postal_code.header';
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return [self::PREFIX.'_postal_code_code', self::PREFIX.'_postal_code_label'];
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return 'export.aggregator.person.by_postal_code.title';
|
||||
}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin(
|
||||
'person.householdAddresses',
|
||||
"{$p}_household_addresses",
|
||||
Join::WITH,
|
||||
"{$p}_household_addresses.validFrom <= :{$p}_calc_date AND ({$p}_household_addresses.validTo IS NULL OR {$p}_household_addresses.validTo > :{$p}_calc_date)"
|
||||
)
|
||||
->setParameter("{$p}_calc_date", $this->rollingDateConverter->convert($data['calc_date']))
|
||||
->leftJoin("{$p}_household_addresses.address", "{$p}_address")
|
||||
->leftJoin("{$p}_address.postcode", "{$p}_postal_code")
|
||||
->addSelect("{$p}_postal_code.code AS {$p}_postal_code_code")
|
||||
->addSelect("{$p}_postal_code.name AS {$p}_postal_code_label")
|
||||
->addGroupBy("{$p}_postal_code_code")
|
||||
->addGroupBy("{$p}_postal_code_label")
|
||||
;
|
||||
}
|
||||
|
||||
public function applyOn()
|
||||
{
|
||||
return Declarations::PERSON_TYPE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?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\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CreatorAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'acpw_aggr_creator';
|
||||
|
||||
public function __construct(
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly UserRender $userRender
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->addSelect("IDENTITY(acpw.createdBy) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.course_work.by_creator.Creator';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$r = $this->userRepository->find($value);
|
||||
|
||||
return $this->userRender->renderString($r, ['absence' => false, 'user_job' => false, 'main_scope' => false]);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::PREFIX.'_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course_work.by_creator.title';
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?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\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserJobHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\UserJobRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CreatorJobAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'acpw_aggr_creator_job';
|
||||
|
||||
public function __construct(
|
||||
private readonly UserJobRepository $jobRepository,
|
||||
private readonly TranslatableStringHelper $translatableStringHelper
|
||||
) {}
|
||||
|
||||
public function addRole(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function alterQuery(QueryBuilder $qb, $data)
|
||||
{
|
||||
$p = self::PREFIX;
|
||||
|
||||
$qb
|
||||
->leftJoin(
|
||||
UserJobHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.job) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.course_work.by_creator_job.Creator\'s job';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $j = $this->jobRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::PREFIX.'_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course_work.by_creator_job.title';
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?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\PersonBundle\Export\Aggregator\SocialWorkAggregators;
|
||||
|
||||
use Chill\MainBundle\Entity\User\UserScopeHistory;
|
||||
use Chill\MainBundle\Export\AggregatorInterface;
|
||||
use Chill\MainBundle\Repository\ScopeRepository;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelper;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\ORM\Query\Expr\Join;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CreatorScopeAggregator implements AggregatorInterface
|
||||
{
|
||||
private const PREFIX = 'acpw_aggr_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(
|
||||
UserScopeHistory::class,
|
||||
"{$p}_history",
|
||||
Join::WITH,
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->eq("{$p}_history.user", 'acpw.createdBy'),
|
||||
$qb->expr()->andX(
|
||||
$qb->expr()->lte("{$p}_history.startDate", 'acpw.createdAt'),
|
||||
$qb->expr()->orX(
|
||||
$qb->expr()->isNull("{$p}_history.endDate"),
|
||||
$qb->expr()->gt("{$p}_history.endDate", 'acpw.createdAt')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
|
||||
->addGroupBy("{$p}_select");
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, mixed $data)
|
||||
{
|
||||
return function ($value): string {
|
||||
if ('_header' === $value) {
|
||||
return 'export.aggregator.course_work.by_creator_scope.Creator\'s scope';
|
||||
}
|
||||
|
||||
if (null === $value || '' === $value || null === $s = $this->scopeRepository->find($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return [self::PREFIX.'_select'];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.aggregator.course_work.by_creator_scope.title';
|
||||
}
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
<?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\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class AvgDurationAPWorkPersonAssociatedOnAccompanyingPeriod implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
ParameterBagInterface $parameterBag,
|
||||
private readonly AccompanyingPeriodWorkRepository $accompanyingPeriodWorkRepository
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.export.avg_duration_acpw_associate_on_period.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_result' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'export.export.avg_duration_acpw_associate_on_period.header' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_result'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.export.avg_duration_acpw_associate_on_period.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->accompanyingPeriodWorkRepository->createQueryBuilder('acpw');
|
||||
|
||||
$qb
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acp.participations', 'acppart')
|
||||
->join('acppart.person', 'person');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
$qb->select('AVG(DATE_DIFF(COALESCE(acpw.endDate, CURRENT_DATE()), acpw.startDate)) AS export_result');
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
<?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\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class AvgDurationAPWorkPersonAssociatedOnWork implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
ParameterBagInterface $parameterBag,
|
||||
private readonly AccompanyingPeriodWorkRepository $accompanyingPeriodWorkRepository
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder) {}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.export.avg_duration_acpw_associate_on_work.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_result' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
return static fn ($value) => '_header' === $value ? 'export.export.avg_duration_acpw_associate_on_work.header' : $value;
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_result'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.export.avg_duration_acpw_associate_on_work.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->accompanyingPeriodWorkRepository->createQueryBuilder('acpw');
|
||||
|
||||
$qb
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acpw.persons', 'person');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
$qb->select('AVG(DATE_DIFF(COALESCE(acpw.endDate, CURRENT_DATE()), acpw.startDate)) AS export_result');
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -25,7 +25,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountAccompanyingPeriodWork implements ExportInterface, GroupedExportInterface
|
||||
class CountAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
@@ -53,7 +53,7 @@ class CountAccompanyingPeriodWork implements ExportInterface, GroupedExportInter
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Count social work actions by various parameters';
|
||||
return 'export.export.count_accompanying_period_work_associate_person.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
@@ -68,7 +68,7 @@ class CountAccompanyingPeriodWork implements ExportInterface, GroupedExportInter
|
||||
}
|
||||
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = $this->getTitle();
|
||||
$labels['_header'] = 'export.export.count_accompanying_period_work_associate_person.header';
|
||||
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
@@ -85,7 +85,7 @@ class CountAccompanyingPeriodWork implements ExportInterface, GroupedExportInter
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Count social work actions';
|
||||
return 'export.export.count_accompanying_period_work_associate_person.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
@@ -0,0 +1,139 @@
|
||||
<?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\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountAccompanyingPeriodWorkAssociatePersonOnWork implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder): void
|
||||
{
|
||||
// No form necessary?
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.export.count_accompanying_period_work_associate_work.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_result' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = 'export.export.count_accompanying_period_work_associate_work.header';
|
||||
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_result'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.export.count_accompanying_period_work_associate_work.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(AccompanyingPeriod\AccompanyingPeriodWork::class, 'acpw')
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acpw.persons', 'person');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
$qb->select('COUNT(DISTINCT acpw.id) as export_result');
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -26,7 +26,7 @@ use Doctrine\ORM\Query;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountHousehold implements ExportInterface, GroupedExportInterface
|
||||
class CountHouseholdInPeriod implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private const TR_PREFIX = 'export.export.nb_household_with_course.';
|
||||
private readonly bool $filterStatsByCenters;
|
||||
@@ -120,6 +120,7 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
|
||||
->join('person.accompanyingPeriodParticipations', 'acppart')
|
||||
->join('acppart.accompanyingPeriod', 'acp')
|
||||
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
|
||||
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
|
||||
->setParameter('count_household_at_date', $this->rollingDateConverter->convert($data['calc_date']));
|
||||
|
||||
$qb
|
||||
@@ -135,7 +136,6 @@ class CountHousehold implements ExportInterface, GroupedExportInterface
|
||||
'
|
||||
)
|
||||
)
|
||||
->andWhere('hmember.startDate <= :count_household_at_date AND (hmember.endDate IS NULL OR hmember.endDate > :count_household_at_date)')
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
@@ -0,0 +1,139 @@
|
||||
<?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\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
|
||||
use Chill\MainBundle\Export\ExportInterface;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class CountPersonOnAccompanyingPeriodWorkAssociatePersonOnWork implements ExportInterface, GroupedExportInterface
|
||||
{
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder): void
|
||||
{
|
||||
// No form necessary?
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes(): array
|
||||
{
|
||||
return [FormatterInterface::TYPE_TABULAR];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.export.count_person_on_acpw_associate_person_on_work.description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
if ('export_result' !== $key) {
|
||||
throw new \LogicException("the key {$key} is not used by this export");
|
||||
}
|
||||
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = 'export.export.count_person_on_acpw_associate_person_on_work.header';
|
||||
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
{
|
||||
return ['export_result'];
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.export.count_person_on_acpw_associate_person_on_work.title';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->em->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(AccompanyingPeriod\AccompanyingPeriodWork::class, 'acpw')
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acpw.persons', 'person');
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
$qb->select('COUNT(DISTINCT person.id) as export_result');
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
}
|
@@ -47,7 +47,7 @@ use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterface
|
||||
class ListAccompanyingPeriodWorkAssociatePersonOnAccompanyingPeriod implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'id',
|
||||
@@ -121,7 +121,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.list.acpw.List description';
|
||||
return 'export.list.acpw_associate_period.List description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
@@ -190,12 +190,12 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return dump($query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR));
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.list.acpw.List of accompanying period works';
|
||||
return 'export.list.acpw_associate_period.List of accompanying period works';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
@@ -206,6 +206,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
$calcDate = $data['calc_date'] ?? new RollingDate(RollingDate::T_TODAY);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
@@ -220,7 +221,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
|
||||
// get participants at the given date
|
||||
->andWhere('acppart.startDate <= :calc_date AND (acppart.endDate > :calc_date OR acppart.endDate IS NULL)')
|
||||
->setParameter('calc_date', $this->rollingDateConverter->convert($data['calc_date']));
|
||||
->setParameter('calc_date', $this->rollingDateConverter->convert($calcDate));
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
@@ -236,7 +237,7 @@ class ListAccompanyingPeriodWork implements ListInterface, GroupedExportInterfac
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($data['calc_date']));
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($calcDate));
|
||||
|
||||
return $qb;
|
||||
}
|
@@ -0,0 +1,349 @@
|
||||
<?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\PersonBundle\Export\Export;
|
||||
|
||||
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
|
||||
use Chill\MainBundle\Export\FormatterInterface;
|
||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
||||
use Chill\MainBundle\Export\Helper\AggregateStringHelper;
|
||||
use Chill\MainBundle\Export\Helper\DateTimeHelper;
|
||||
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
|
||||
use Chill\MainBundle\Export\Helper\UserHelper;
|
||||
use Chill\MainBundle\Export\ListInterface;
|
||||
use Chill\MainBundle\Form\Type\PickRollingDateType;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDate;
|
||||
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkReferrerHistory;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\UserHistory;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Goal;
|
||||
use Chill\PersonBundle\Entity\SocialWork\Result;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
|
||||
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
|
||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialActionRender;
|
||||
use Chill\PersonBundle\Templating\Entity\SocialIssueRender;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\ThirdPartyBundle\Export\Helper\LabelThirdPartyHelper;
|
||||
use Doctrine\ORM\AbstractQuery;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
class ListAccompanyingPeriodWorkAssociatePersonOnWork implements ListInterface, GroupedExportInterface
|
||||
{
|
||||
private const FIELDS = [
|
||||
'id',
|
||||
'socialActionId',
|
||||
'socialAction',
|
||||
'socialIssue',
|
||||
'acp_id',
|
||||
'acp_user',
|
||||
'startDate',
|
||||
'endDate',
|
||||
'goalsId',
|
||||
'goalsTitle',
|
||||
'goalResultsId',
|
||||
'goalResultsTitle',
|
||||
'resultsId',
|
||||
'resultsTitle',
|
||||
'evaluationsId',
|
||||
'evaluationsTitle',
|
||||
'note',
|
||||
'personsId',
|
||||
'personsName',
|
||||
'thirdParties',
|
||||
'handlingThierParty',
|
||||
// 'acpwReferrers',
|
||||
'referrers',
|
||||
'createdAt',
|
||||
'createdBy',
|
||||
'updatedAt',
|
||||
'updatedBy',
|
||||
];
|
||||
|
||||
private readonly bool $filterStatsByCenters;
|
||||
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly DateTimeHelper $dateTimeHelper,
|
||||
private readonly UserHelper $userHelper,
|
||||
private readonly LabelPersonHelper $personHelper,
|
||||
private readonly LabelThirdPartyHelper $thirdPartyHelper,
|
||||
private readonly TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
|
||||
private readonly SocialIssueRender $socialIssueRender,
|
||||
private readonly SocialIssueRepository $socialIssueRepository,
|
||||
private readonly SocialActionRender $socialActionRender,
|
||||
private readonly RollingDateConverterInterface $rollingDateConverter,
|
||||
private readonly AggregateStringHelper $aggregateStringHelper,
|
||||
private readonly SocialActionRepository $socialActionRepository,
|
||||
ParameterBagInterface $parameterBag,
|
||||
) {
|
||||
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder)
|
||||
{
|
||||
$builder
|
||||
->add('calc_date', PickRollingDateType::class, [
|
||||
'label' => 'export.list.acpw.Date of calculation for associated elements',
|
||||
'help' => 'export.list.acpw.help_description',
|
||||
'required' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFormDefaultData(): array
|
||||
{
|
||||
return ['calc_date' => new RollingDate(RollingDate::T_TODAY)];
|
||||
}
|
||||
|
||||
public function getAllowedFormattersTypes()
|
||||
{
|
||||
return [FormatterInterface::TYPE_LIST];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'export.list.acpw_associate_work.List description';
|
||||
}
|
||||
|
||||
public function getGroup(): string
|
||||
{
|
||||
return 'Exports of social work actions';
|
||||
}
|
||||
|
||||
public function getLabels($key, array $values, $data)
|
||||
{
|
||||
return match ($key) {
|
||||
'startDate', 'endDate', 'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel('export.list.acpw.'.$key),
|
||||
'socialAction' => function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.'.$key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialActionRender->renderString(
|
||||
$this->socialActionRepository->find($value),
|
||||
[]
|
||||
);
|
||||
},
|
||||
'socialIssue' => function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.'.$key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->socialIssueRender->renderString(
|
||||
$this->socialIssueRepository->find($value),
|
||||
[]
|
||||
);
|
||||
},
|
||||
'createdBy', 'updatedBy', 'acp_user' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.'.$key),
|
||||
'referrers' => $this->userHelper->getLabel($key, $values, 'export.list.acpw.'.$key),
|
||||
// 'acpwReferrers' => $this->userHelper->getLabelMulti($key, $values, 'export.list.acpw.' . $key),
|
||||
'personsName' => $this->personHelper->getLabelMulti($key, $values, 'export.list.acpw.'.$key),
|
||||
'handlingThierParty' => $this->thirdPartyHelper->getLabel($key, $values, 'export.list.acpw.'.$key),
|
||||
'thirdParties' => $this->thirdPartyHelper->getLabelMulti($key, $values, 'export.list.acpw.'.$key),
|
||||
'personsId', 'goalsId', 'goalResultsId', 'resultsId', 'evaluationsId' => $this->aggregateStringHelper->getLabelMulti($key, $values, 'export.list.acpw.'.$key),
|
||||
'goalsTitle', 'goalResultsTitle', 'resultsTitle', 'evaluationsTitle' => $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, 'export.list.acpw.'.$key),
|
||||
default => static function ($value) use ($key) {
|
||||
if ('_header' === $value) {
|
||||
return 'export.list.acpw.'.$key;
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public function getQueryKeys($data)
|
||||
{
|
||||
return self::FIELDS;
|
||||
}
|
||||
|
||||
public function getResult($query, $data)
|
||||
{
|
||||
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'export.list.acpw_associate_work.List of accompanying period works';
|
||||
}
|
||||
|
||||
public function getType(): string
|
||||
{
|
||||
return Declarations::SOCIAL_WORK_ACTION_TYPE;
|
||||
}
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
$calcDate = $data['calc_date'] ?? new RollingDate(RollingDate::T_TODAY);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(AccompanyingPeriodWork::class, 'acpw')
|
||||
->distinct()
|
||||
->select('acpw.id AS id')
|
||||
->join('acpw.accompanyingPeriod', 'acp')
|
||||
->join('acpw.persons', 'person')
|
||||
;
|
||||
|
||||
if ($this->filterStatsByCenters) {
|
||||
$qb
|
||||
->andWhere(
|
||||
$qb->expr()->exists(
|
||||
'SELECT 1 FROM '.PersonCenterHistory::class.' acl_count_person_history WHERE acl_count_person_history.person = person
|
||||
AND acl_count_person_history.center IN (:authorized_centers)
|
||||
'
|
||||
)
|
||||
)
|
||||
->setParameter('authorized_centers', $centers);
|
||||
}
|
||||
|
||||
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
|
||||
|
||||
$this->addSelectClauses($qb, $this->rollingDateConverter->convert($calcDate));
|
||||
|
||||
return $qb;
|
||||
}
|
||||
|
||||
public function requiredRole(): string
|
||||
{
|
||||
return AccompanyingPeriodVoter::STATS;
|
||||
}
|
||||
|
||||
public function supportsModifiers(): array
|
||||
{
|
||||
return [
|
||||
Declarations::SOCIAL_WORK_ACTION_TYPE,
|
||||
Declarations::ACP_TYPE,
|
||||
Declarations::PERSON_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
private function addSelectClauses(QueryBuilder $qb, \DateTimeImmutable $calcDate): void
|
||||
{
|
||||
// add regular fields
|
||||
foreach ([
|
||||
'startDate',
|
||||
'endDate',
|
||||
'note',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
] as $field) {
|
||||
$qb->addSelect(sprintf('acpw.%s AS %s', $field, $field));
|
||||
}
|
||||
|
||||
// those with identity
|
||||
foreach ([
|
||||
'createdBy',
|
||||
'updatedBy',
|
||||
'handlingThierParty',
|
||||
] as $field) {
|
||||
$qb->addSelect(sprintf('IDENTITY(acpw.%s) AS %s', $field, $field));
|
||||
}
|
||||
|
||||
// join socialaction
|
||||
$qb
|
||||
->join('acpw.socialAction', 'sa')
|
||||
->addSelect('sa.id AS socialActionId')
|
||||
->addSelect('sa.id AS socialAction')
|
||||
->addSelect('IDENTITY(sa.issue) AS socialIssue');
|
||||
|
||||
// join acp
|
||||
$qb
|
||||
->addSelect('acp.id AS acp_id')
|
||||
->addSelect('IDENTITY(acp.user) AS acp_user');
|
||||
|
||||
// persons
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(person_acpw_member.id) FROM '.Person::class.' person_acpw_member '
|
||||
.'WHERE person_acpw_member MEMBER OF acpw.persons) AS personsId')
|
||||
->addSelect('(SELECT AGGREGATE(person1_acpw_member.id) FROM '.Person::class.' person1_acpw_member '
|
||||
.'WHERE person1_acpw_member MEMBER OF acpw.persons) AS personsName');
|
||||
|
||||
// referrers => at date XXXX
|
||||
$qb
|
||||
->addSelect('(SELECT JSON_BUILD_OBJECT(\'uid\', IDENTITY(history.user), \'d\', history.startDate) FROM '.UserHistory::class.' history '.
|
||||
'WHERE history.accompanyingPeriod = acp AND history.startDate <= :calcDate AND (history.endDate IS NULL OR history.endDate > :calcDate)) AS referrers');
|
||||
|
||||
/*
|
||||
// acpwReferrers at date XXX
|
||||
$qb
|
||||
->addSelect('(
|
||||
SELECT IDENTITY(acpw_ref_history.accompanyingPeriodWork) AS acpw_ref_history_id,
|
||||
JSON_BUILD_OBJECT(\'uid\', IDENTITY(acpw_ref_history.user), \'d\', acpw_ref_history.startDate)
|
||||
FROM ' . AccompanyingPeriodWorkReferrerHistory::class . ' acpw_ref_history ' .
|
||||
'WHERE acpw_ref_history.accompanyingPeriodWork = acpw AND acpw_ref_history.startDate <= :calcDate AND (acpw_ref_history.endDate IS NULL or acpw_ref_history.endDate > :calcDate) GROUP BY acpw_ref_history_id) AS acpwReferrers'
|
||||
);
|
||||
*/
|
||||
|
||||
// thirdparties
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(tp.id) FROM '.ThirdParty::class.' tp '
|
||||
.'WHERE tp MEMBER OF acpw.thirdParties) AS thirdParties');
|
||||
|
||||
// goals
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(IDENTITY(goal.goal)) FROM '.AccompanyingPeriodWorkGoal::class.' goal '
|
||||
.'WHERE goal MEMBER OF acpw.goals) AS goalsId')
|
||||
->addSelect('(SELECT AGGREGATE(g.title) FROM '.AccompanyingPeriodWorkGoal::class.' goal1 '
|
||||
.'LEFT JOIN '.Goal::class.' g WITH goal1.goal = g.id WHERE goal1 MEMBER OF acpw.goals) AS goalsTitle');
|
||||
|
||||
// goals results
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(wr.id) FROM '.Result::class.' wr '
|
||||
.'JOIN '.AccompanyingPeriodWorkGoal::class.' wg WITH wr MEMBER OF wg.results '
|
||||
.'WHERE wg MEMBER OF acpw.goals) AS goalResultsId')
|
||||
->addSelect('(SELECT AGGREGATE(wr1.title) FROM '.Result::class.' wr1 '
|
||||
.'JOIN '.AccompanyingPeriodWorkGoal::class.' wg1 WITH wr1 MEMBER OF wg1.results '
|
||||
.'WHERE wg1 MEMBER OF acpw.goals) AS goalResultsTitle');
|
||||
|
||||
// results
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(result.id) FROM '.Result::class.' result '
|
||||
.'WHERE result MEMBER OF acpw.results ) AS resultsId ')
|
||||
->addSelect('(SELECT AGGREGATE (result1.title) FROM '.Result::class.' result1 '
|
||||
.'WHERE result1 MEMBER OF acpw.results ) AS resultsTitle ');
|
||||
|
||||
// evaluations
|
||||
$qb
|
||||
->addSelect('(SELECT AGGREGATE(IDENTITY(we.evaluation)) FROM '.AccompanyingPeriodWorkEvaluation::class.' we '
|
||||
.'WHERE we MEMBER OF acpw.accompanyingPeriodWorkEvaluations ) AS evaluationsId ')
|
||||
->addSelect('(SELECT AGGREGATE(ev.title) FROM '.AccompanyingPeriodWorkEvaluation::class.' we1 '
|
||||
.'LEFT JOIN '.Evaluation::class.' ev WITH we1.evaluation = ev.id '
|
||||
.'WHERE we1 MEMBER OF acpw.accompanyingPeriodWorkEvaluations ) AS evaluationsTitle ');
|
||||
|
||||
$qb->setParameter('calcDate', $calcDate);
|
||||
}
|
||||
}
|
@@ -332,13 +332,18 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou
|
||||
if (null === $value) {
|
||||
return '';
|
||||
}
|
||||
$decoded = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
if ('_header' === $value) {
|
||||
$label = $cfType->getChoices($cf)[$slugChoice];
|
||||
|
||||
return $this->translatableStringHelper->localize($cf->getName())
|
||||
.' | '.$label;
|
||||
.' | '.$label;
|
||||
}
|
||||
|
||||
try {
|
||||
$decoded = json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (\JsonException $e) {
|
||||
throw new \RuntimeException(sprintf('unable to decode json: %s, %s', json_last_error(), json_last_error_msg()), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if ('_other' === $slugChoice && $cfType->isChecked($cf, $slugChoice, $decoded)) {
|
||||
|
@@ -15,6 +15,7 @@ use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Export\FilterInterface;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
@@ -59,7 +60,13 @@ class CreatorFilter implements FilterInterface
|
||||
{
|
||||
return [
|
||||
'Filtered by creator: only %creators%', [
|
||||
'%creators%' => implode(', ', array_map(static fn (User $u) => $u->getLabel(), $data['accepted_creators'])),
|
||||
'%creators%' => implode(
|
||||
', ',
|
||||
array_map(
|
||||
static fn (User $u) => $u->getLabel(),
|
||||
$data['accepted_creators'] instanceof Collection ? $data['accepted_creators']->toArray() : $data['accepted_creators']
|
||||
)
|
||||
),
|
||||
], ];
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@ use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -68,7 +69,10 @@ class GeographicalUnitStatFilter implements FilterInterface
|
||||
'acp_geog_filter_date',
|
||||
$this->rollingDateConverter->convert($data['date_calc'])
|
||||
)
|
||||
->setParameter('acp_geog_filter_units', array_map(static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id, $data['units']));
|
||||
->setParameter('acp_geog_filter_units', array_map(
|
||||
static fn (SimpleGeographicalUnitDTO $unitDTO) => $unitDTO->id,
|
||||
$data['units'] instanceof Collection ? $data['units']->toArray() : $data['units']
|
||||
));
|
||||
}
|
||||
|
||||
public function applyOn(): string
|
||||
|
@@ -16,6 +16,7 @@ use Chill\PersonBundle\Export\Declarations;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
|
||||
use Chill\ThirdPartyBundle\Templating\Entity\ThirdPartyRender;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
@@ -52,7 +53,10 @@ final readonly class HandlingThirdPartyFilter implements FilterInterface
|
||||
[
|
||||
'%3parties%' => implode(
|
||||
', ',
|
||||
array_map(fn (ThirdParty $thirdParty) => $this->thirdPartyRender->renderString($thirdParty, []), $data['handling_3parties'])
|
||||
array_map(
|
||||
fn (ThirdParty $thirdParty) => $this->thirdPartyRender->renderString($thirdParty, []),
|
||||
$data['handling_3parties'] instanceof Collection ? $data['handling_3parties']->toArray() : $data['handling_3parties']
|
||||
)
|
||||
),
|
||||
],
|
||||
];
|
||||
|
@@ -21,6 +21,7 @@ use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodInfo;
|
||||
use Chill\PersonBundle\Export\Declarations;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -115,7 +116,7 @@ readonly class JobWorkingOnCourseFilter implements FilterInterface
|
||||
', ',
|
||||
array_map(
|
||||
fn (UserJob $userJob) => $this->translatableStringHelper->localize($userJob->getLabel()),
|
||||
$data['jobs']
|
||||
$data['jobs'] instanceof Collection ? $data['jobs']->toArray() : $data['jobs']
|
||||
)
|
||||
),
|
||||
'%start_date%' => $this->rollingDateConverter->convert($data['start_date'])?->format('d-m-Y'),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user