Compare commits

..

1 Commits

2101 changed files with 30111 additions and 30778 deletions

View File

@@ -1,5 +0,0 @@
kind: Feature
body: '[DX] move async-upload-bundle features into chill-bundles'
time: 2023-12-12T15:48:41.954970271+01:00
custom:
Issue: "221"

View File

@@ -1,20 +0,0 @@
## v2.10.0 - 2023-10-24
### Feature
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] Add a filter "grouping accompanying period by opening date" and "grouping accompanying period by closing date"
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a filter and aggregator on accompanying period work: group/filter by handling third party
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a filter and aggregator on activites: group/filter activities by people participating to the activities
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a grouping on accompanying period export: group by activity type associated to at least one activity within the accompanying period
* [export] sort filters and aggregators by title
* ([#179](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/179)) [export] create a parameter that will force to skip the filtering by center (ACL) when generating an export
### Fixed
* ([#177](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/177)) [export] fix date range selection on filter and grouping "by status of the course at date", on accompanying periods
### Résumé francophone des changements
- Ajout d'un regroupement sur les parcours: par date de cloture et d'ouverture;
- Ajouter d'un filtre et regroupement par tiers traitant sur les actions d'accompagnement;
- ajout d'un filtre et regroupement par usager participant sur les échanges
- ajout d'un regroupement: par type d'activité associé au parcours;
- trie les filtre et regroupements par ordre alphabétique dans els exports
- ajout d'un paramètre qui permet de désactiver le filtre par centre dans les exports
- correction de l'interface de date dans les filtres et regroupements "par statut du parcours à la date"

View File

@@ -1,3 +0,0 @@
## v2.10.1 - 2023-10-24
### Fixed
* Fix export controller when generating an export without any data in session

View File

@@ -1,3 +0,0 @@
## v2.10.2 - 2023-10-26
### Fixed
* ([#175](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/175)) Use injection of translator instead of ->get().

View File

@@ -1,3 +0,0 @@
## 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

View File

@@ -1,3 +0,0 @@
## v2.10.4 - 2023-10-26
### Fixed
* Fix null value constraint errors when merging relationships in doubles

View File

@@ -1,4 +0,0 @@
## 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"

View File

@@ -1,4 +0,0 @@
## 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

View File

@@ -1,6 +0,0 @@
## 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

View File

@@ -1,26 +0,0 @@
## 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

View File

@@ -1,3 +0,0 @@
## 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"

View File

@@ -1,9 +0,0 @@
## 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

View File

@@ -1,8 +0,0 @@
## 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

View File

@@ -1,5 +0,0 @@
## v2.14.1 - 2023-11-29
### Fixed
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone

View File

@@ -1,11 +0,0 @@
## v2.15.0 - 2023-12-11
### Feature
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
### Fixed
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#234](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/234)) Fix "filter aside activity by date"
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
# Select what we should cache between builds
cache:
paths:
- /vendor/
- tests/app/vendor/
- .cache
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
@@ -11,10 +11,6 @@ cache:
services:
- name: postgis/postgis:14-3.3-alpine
alias: db
command:
- postgres
- "-c"
- max_connections=1000
- name: redis
alias: redis
@@ -27,13 +23,13 @@ variables:
# configure database access
DATABASE_URL: postgresql://postgres:postgres@db:5432/postgres?serverVersion=14&charset=utf8
# fetch the chill-app using git submodules
# GIT_SUBMODULE_STRATEGY: recursive
GIT_SUBMODULE_STRATEGY: recursive
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_URL: redis://redis:6379
# change vendor dir to make the app install into tests/apps
COMPOSER_VENDOR_DIR: tests/app/vendor
DEFAULT_CARRIER_CODE: BE
# force a timezone
TZ: Europe/Brussels
stages:
- Composer install
@@ -54,7 +50,7 @@ build:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
code_style:
stage: Tests
@@ -68,7 +64,7 @@ code_style:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
phpstan_tests:
stage: Tests
@@ -82,14 +78,13 @@ phpstan_tests:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
rector_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
script:
- tests/console cache:clear
- bin/rector process --dry-run
- bin/rector --dry-run
cache:
paths:
- .cache/
@@ -97,7 +92,7 @@ rector_tests:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
# psalm_tests:
# stage: Tests
@@ -114,17 +109,19 @@ rector_tests:
unit_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
# until we fix testes
allow_failure: true
script:
- php tests/console doctrine:migrations:migrate -n --env=test
- php tests/console chill:db:sync-views --env=test
- php -d memory_limit=2G tests/console cache:clear --env=test
- php -d memory_limit=3G tests/console doctrine:fixtures:load -n
- php -d memory_limit=4G bin/phpunit --colors=never --exclude-group dbIntensive
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev
- php -d memory_limit=3G tests/app/bin/console doctrine:fixtures:load -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=test
- php -d memory_limit=4G bin/phpunit --colors=never
artifacts:
expire_in: 30 min
paths:
- bin
- vendor/
- tests/app/vendor/
release:
stage: Deploy

3
.gitmodules vendored
View File

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

View File

@@ -91,7 +91,7 @@ $rules = array_merge(
[
'@PhpCsFixer' => true,
'@PhpCsFixer:risky' => false,
'@Symfony' => true,
'@Symfony' => false,
'@Symfony:risky' => false,
'ordered_class_elements' => [
'order' => [
@@ -111,13 +111,13 @@ $rules = array_merge(
'method_private',
],
'sort_algorithm' => 'alpha',
],
]
],
$rules,
$riskyRules,
$untilFullSwitchToPhp8,
);
$rules['header_comment']['header'] = trim(file_get_contents(__DIR__.'/resource/header.txt'));
$rules['header_comment']['header'] = trim(file_get_contents(__DIR__ . '/resource/header.txt'));
return $config->setRules($rules);

View File

@@ -6,222 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.15.0 - 2023-12-11
### Feature
* ([#191](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/191)) Add export "number of household associate with an exchange"
* ([#235](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/235)) Export: add dates on the filter "filter course by activity type"
### Fixed
* ([#214](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/214)) Fix error when posting an empty comment on an accompanying period.
* ([#233](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/233)) Fix "filter evaluation by evaluation type" (and add select2 to the list of evaluation types to pick)
* ([#234](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/234)) Fix "filter aside activity by date"
* ([#228](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/228)) Fix export of activity for people created before the introduction of the createdAt column on person (during v1)
* ([#246](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/246)) Do not show activities, evaluations and social work when associated to a confidential accompanying period, except for the users which are allowed to see them
## v2.14.1 - 2023-11-29
### Fixed
* Export: fix list person with custom fields
* ([#100](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/100)) Add a paginator to budget elements (resource and charge types) in the admin
* Fix error in ListEvaluation when "handling agents" are alone
## 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().
## v2.10.1 - 2023-10-24
### Fixed
* Fix export controller when generating an export without any data in session
## v2.10.0 - 2023-10-24
### Feature
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] Add a filter "grouping accompanying period by opening date" and "grouping accompanying period by closing date"
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a filter and aggregator on accompanying period work: group/filter by handling third party
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a filter and aggregator on activites: group/filter activities by people participating to the activities
* ([#172](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/172)) [export] add a grouping on accompanying period export: group by activity type associated to at least one activity within the accompanying period
* [export] sort filters and aggregators by title
* ([#179](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/179)) [export] create a parameter that will force to skip the filtering by center (ACL) when generating an export
### Fixed
* ([#177](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/177)) [export] fix date range selection on filter and grouping "by status of the course at date", on accompanying periods
### Résumé francophone des changements
- Ajout d'un regroupement sur les parcours: par date de cloture et d'ouverture;
- Ajouter d'un filtre et regroupement par tiers traitant sur les actions d'accompagnement;
- ajout d'un filtre et regroupement par usager participant sur les échanges
- ajout d'un regroupement: par type d'activité associé au parcours;
- trie les filtre et regroupements par ordre alphabétique dans els exports
- ajout d'un paramètre qui permet de désactiver le filtre par centre dans les exports
- correction de l'interface de date dans les filtres et regroupements "par statut du parcours à la date"
## v2.9.2 - 2023-10-17
### Fixed
* Fix possible null values in string's entities
## v2.9.1 - 2023-10-17
### Fixed
* Fix the handling of activity form when editing or creating an activity in an accompanying period with multiple centers
## v2.9.0 - 2023-10-17
### Feature
* ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)) Add history to scopes and to jobs in administrator section. When user job or main scope of user is changed, automaticaly add a new row in history.
* ([#146](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/146)) Allow closing motives to be identified as 'canceling the accompanying period' + don't take canceled accompanying periods into account
* [export] add an aggregator for activities: group by job scope's creator aggregator
* DX: prepare the code for the upgrade to symfony 5.4
### Traductions francophones des principaux changements
- ajout de l'historique des services et métiers pour les utilisateurs. Les exports, filtres et regroupements sont adaptés pour tenir compte du métier et du service
de l'utilisateur au moment de l'échange, de sa désignation comme agent traitant de l'échange ou du moment du rendez-vous ([#147](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/147)))
- modification des motifs de cloture des parcours: ajout d'un chanmp "annule le parcours", qui permet d'indiquer que le motif "annule" le parcours. Les parcours annulés n'apparaissent
pas dans les statistiques
- ajouter d'un regroupement pour les échanges: grouper par métier et service du créateur de l'échange
### Possible BC break in configuration
This release remove the use of deprecated package [symfony/templating](https://symfony.com/components/Templating).
If you use this package in your own bundle (usually `src/` directory, or other dependencies), you should add this dependencies in your local composer.json (`composer require symfony/templating`).
But if you do not need this any more, you must ensure that the configuration key `framework.templating` is removed. This is usually located into `config/packages/framework.yaml`. [See here an example](https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic/commit/cc716beaecc239e6a189f3db62ea95f169a37505#diff-df607fe73ff82c569824a7392edf5e760e998efe)
## v2.8.0 - 2023-10-05
### Feature
* ([#162](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/162)) Reassigning list: when reassigning courses to a new user, the job associated with the course become the one of the new user (if any)
* Reassining list: the length of the list is increased to 100 courses
### Fixed
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) Fix filter "accompanying course by social action" to avoid duplication in list
* ([#164](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/164)) View a third party: avoid errors when a contact has a civility
* ([#163](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/163)) Fix the filters and aggregators on exports "count peoples"
* ([#143](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/143)) From the database, avoid the creation of location history for same period and at same dates
### Traduction francophone des principaux changements
- Fonctionnalité: Réassigner les parcours en lot: lorsque des parcours sont réassignés "en lot", les parcours sont maintenant associés au métier du nouveau référent;
- Correction: certaines causes qui créaient des doublons dans les listes ont été corrigées;
- Correction des associations entre l'export "nombre de personnes" et les filtres et regroupements associés
## v2.7.0 - 2023-09-27
### Feature
* ([#155](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/155)) The regulation list load accompanying periods by exact postal code (address associated with postal code), and not by the content of the postal code (postal code with same code's string)
### Fixed
* ([#142](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/142)) Fix the label of filter ActivityTypeFilter to a more obvious one
* ([#140](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/140)) [export] Fix association of filter "filter location by type" which did not appears on "list of activities"
## v2.6.3 - 2023-09-19
### Fixed
* Remove id property from document
mappedsuperclass
## v2.6.2 - 2023-09-18
### Fixed
* Fix doctrine mapping of AbstractTaskPlaceEvent and SingleTaskPlaceEvent: id property moved.
## v2.6.1 - 2023-09-14
### Fixed
* Filter out active centers in exports, which uses a different PickCenterType.
## v2.6.0 - 2023-09-14
### Feature
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Add locations in Aside Activity. By default, suggest user location, otherwise a select with all locations.
* ([#133](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/133)) Adapt Aside Activity exports: display location, filter by location, group by location
* Use the CRUD controller for center entity + add the isActive property to be able to mask instances of Center that are no longer in use.
### Fixed
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) reinstate the fusion of duplicate persons
* Missing translation in Work Actions exports
* Reimplement the mission type filter on tasks, only for instances that have a config parameter indicating true for this.
* ([#135](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/135)) Corrects a typing error in 2 filters, which caused an
error when trying to reedit a saved export
* ([#136](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/136)) [household] when moving a person to a sharing position to a not-sharing position on the same household on the same date, remove the previous household membership on the same household. This fix duplicate member.
* Add missing translation for comment field placeholder in repositionning household editor.
* Do not send an email to creator twice when adding a comment to a notification
* ([#107](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/107)) Fix gestion doublon functionality to work with chill bundles v2
### UX
* Uniformize badge-person in household banner (background, size)
## v2.5.3 - 2023-07-20
### Fixed
* ([#132](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/132)) Rendez-vous documents created would appear in all documents lists of all persons with an accompanying period. Or statements are now added to the where clause to filter out documents that come from unrelated accompanying period/ or person rendez-vous.

View File

@@ -40,7 +40,7 @@ About once a year, the core team discusses the opportunity to invite new members
### Core Membership Revocation
A Chill Core membership can be revoked for any of the following reasons:
A Symfony Core membership can be revoked for any of the following reasons:
- Refusal to follow the rules and policies stated in this document;
- Lack of activity for the past six months;

View File

@@ -8,10 +8,11 @@
"social worker"
],
"require": {
"php": "^8.2",
"php": "^7.4|^8.2",
"ext-json": "*",
"ext-openssl": "*",
"ext-redis": "*",
"champs-libres/async-uploader-bundle": "dev-sf4#d57134aee8e504a83c902ff0cf9f8d36ac418290",
"champs-libres/wopi-bundle": "dev-master@dev",
"champs-libres/wopi-lib": "dev-master@dev",
"doctrine/doctrine-bundle": "^2.1",
@@ -47,6 +48,7 @@
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4",
"symfony/serializer": "^5.3",
"symfony/templating": "^4.4",
"symfony/translation": "^4.4",
"symfony/twig-bundle": "^4.4",
"symfony/validator": "^4.4",
@@ -74,7 +76,7 @@
"phpunit/phpunit": ">= 7.5",
"psalm/plugin-phpunit": "^0.18.4",
"psalm/plugin-symfony": "^4.0.2",
"rector/rector": "^0.17.7",
"rector/rector": "^0.15.23",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^4.4",
"symfony/maker-bundle": "^1.20",
@@ -96,6 +98,7 @@
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FamilyMemberBundle\\": "src/Bundle/ChillFamilyMemberBundle",
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",
@@ -107,7 +110,7 @@
},
"autoload-dev": {
"psr-4": {
"App\\": "tests/",
"App\\": "tests/app/src/",
"Chill\\DocGeneratorBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\WopiBundle\\Tests\\": "src/Bundle/ChillDocGeneratorBundle/tests",
"Chill\\Utils\\Rector\\Tests\\": "utils/rector/tests"
@@ -120,15 +123,16 @@
"ocramius/package-versions": true,
"phpro/grumphp": true,
"phpstan/extension-installer": true,
"roave/you-are-using-it-wrong": true,
"symfony/runtime": true
"roave/you-are-using-it-wrong": true
},
"bin-dir": "bin",
"optimize-autoloader": true,
"sort-packages": true
"sort-packages": true,
"vendor-dir": "tests/app/vendor"
},
"scripts": {
"auto-scripts": {
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"cache:clear": "symfony-cmd"
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,13 @@
Configure Chill for calendar and absence synchronisation and SSO with Microsoft Graph (Outlook)
===============================================================================================
Configure Chill for calendar sync 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).
Please note that 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). When calendar sync is configured without SSL, the user's email address is the key to associate Chill's users with Microsoft's ones.
Configure SSO
-------------
@@ -48,7 +46,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
@@ -188,27 +186,20 @@ Configure chill app
Configure sync and calendar access
----------------------------------
Configure sync
--------------
The purpose of this configuration is the following:
The sync processe might be configured in the same app, or into a different app.
- 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.
The synchronization processes use Oauth2.0 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.
@@ -238,9 +229,8 @@ 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;
* 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`).
* 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.
At this step, you might choose to accept those permissions for all users, or let them do it by yourself.
@@ -311,7 +301,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: 166 KiB

After

Width:  |  Height:  |  Size: 149 KiB

View File

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

View File

@@ -21,6 +21,7 @@ These are alias conventions :
| | AccompanyingPeriodInfo::class | not existing (using custom WITH clause) | acpinfo |
| AccompanyingPeriodWork::class | | | acpw |
| | AccompanyingPeriodWorkEvaluation::class | acpw.accompanyingPeriodWorkEvaluations | workeval |
| | User::class | acpw.referrers | acpwuser |
| | SocialAction::class | acpw.socialAction | acpwsocialaction |
| | Goal::class | acpw.goals | goal |
| | Result::class | acpw.results | result |

View File

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

View File

@@ -2,13 +2,6 @@
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
@@ -19,9 +12,6 @@ return static function (RectorConfig $rectorConfig): void {
__DIR__ . '/src',
]);
$rectorConfig->symfonyContainerXml(__DIR__ . '/var/cache/dev/testsApp_KernelDevDebugContainer.xml');
$rectorConfig->symfonyContainerPhp(__DIR__ . '/tests/symfony-container.php');
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
@@ -31,25 +21,43 @@ return static function (RectorConfig $rectorConfig): void {
//define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
\Rector\Symfony\Set\SymfonyLevelSetList::UP_TO_SYMFONY_44,
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
\Rector\PHPUnit\Set\PHPUnitLevelSetList::UP_TO_PHPUNIT_90,
LevelSetList::UP_TO_PHP_74
]);
// some routes are added twice if it remains activated
// $rectorConfig->rule(\Rector\Symfony\Configs\Rector\ClassMethod\AddRouteAnnotationRector::class);
// chill rules
//$rectorConfig->rule(\Chill\Utils\Rector\Rector\ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector::class);
$rectorConfig->rule(\Chill\Utils\Rector\Rector\ChillBundleAddFormDefaultDataOnExportFilterAggregatorRector::class);
// skip some path...
$rectorConfig->skip([
// make rector stuck for some files
\Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector::class,
// we need to discuss this: are we going to have FALSE in tests instead of an error ?
\Rector\Php71\Rector\FuncCall\CountOnNullRector::class,
// we must adapt service definition
\Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector::class,
\Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector::class,
// must merge MR500 and review a typing of "ArrayCollection" in entities
\Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class,
// remove all PHP80 rules, in order to activate them one by one
\Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector::class,
\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class,
\Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class,
\Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class,
\Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class,
\Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class,
\Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector::class,
\Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector::class,
\Rector\Php80\Rector\Ternary\GetDebugTypeRector::class,
\Rector\Php80\Rector\FunctionLike\MixedTypeRector::class,
\Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector::class,
\Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector::class,
\Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class,
\Rector\Php80\Rector\ClassMethod\SetStateToStaticRector::class,
\Rector\Php80\Rector\NotIdentical\StrContainsRector::class,
\Rector\Php80\Rector\Identical\StrEndsWithRector::class,
\Rector\Php80\Rector\Identical\StrStartsWithRector::class,
\Rector\Php80\Rector\Class_\StringableForToStringRector::class,
\Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class,
\Rector\Php80\Rector\FunctionLike\UnionTypesRector::class
]);
};

View File

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

View File

@@ -18,6 +18,7 @@ use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Repository\ActivityTypeCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\ActivityBundle\Repository\ActivityUserJobRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\UserJob;
@@ -34,8 +35,11 @@ use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
@@ -45,6 +49,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
final class ActivityController extends AbstractController
{
@@ -67,14 +72,15 @@ final class ActivityController extends AbstractController
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
private readonly TranslatableStringHelperInterface $translatableStringHelper,
private readonly PaginatorFactory $paginatorFactory,
) {}
) {
}
/**
* Deletes a Activity entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/delete", name="chill_activity_activity_delete", methods={"GET", "POST", "DELETE"})
* @param mixed $id
*/
public function deleteAction(Request $request, mixed $id)
public function deleteAction(Request $request, $id)
{
$view = null;
@@ -87,10 +93,10 @@ final class ActivityController extends AbstractController
}
if ($activity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = '@ChillActivity/Activity/confirm_deleteAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:confirm_deleteAccompanyingCourse.html.twig';
$accompanyingPeriod = $activity->getAccompanyingPeriod();
} else {
$view = '@ChillActivity/Activity/confirm_deletePerson.html.twig';
$view = 'ChillActivityBundle:Activity:confirm_deletePerson.html.twig';
}
// TODO
@@ -98,7 +104,7 @@ final class ActivityController extends AbstractController
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if (Request::METHOD_DELETE === $request->getMethod()) {
if ($request->getMethod() === Request::METHOD_DELETE) {
$form->handleRequest($request);
if ($form->isValid()) {
@@ -131,6 +137,10 @@ final class ActivityController extends AbstractController
}
}
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'activity' => $activity,
'delete_form' => $form->createView(),
@@ -141,8 +151,6 @@ final class ActivityController extends AbstractController
/**
* Displays a form to edit an existing Activity entity.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/edit", name="chill_activity_activity_edit", methods={"GET", "POST", "PUT"})
*/
public function editAction(int $id, Request $request): Response
{
@@ -158,10 +166,10 @@ final class ActivityController extends AbstractController
$person = $entity->getPerson();
if ($entity->getAccompanyingPeriod() instanceof AccompanyingPeriod) {
$view = '@ChillActivity/Activity/editAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:editAccompanyingCourse.html.twig';
$accompanyingPeriod = $entity->getAccompanyingPeriod();
} else {
$view = '@ChillActivity/Activity/editPerson.html.twig';
$view = 'ChillActivityBundle:Activity:editPerson.html.twig';
}
// TODO
// $this->denyAccessUnlessGranted('CHILL_ACTIVITY_UPDATE', $entity);
@@ -222,6 +230,10 @@ final class ActivityController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
$activity_array = $this->serializer->normalize($entity, 'json', ['groups' => 'read']);
return $this->render($view, [
@@ -236,8 +248,6 @@ final class ActivityController extends AbstractController
/**
* Lists all Activity entities.
*
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/", name="chill_activity_activity_list")
*/
public function listAction(Request $request): Response
{
@@ -273,9 +283,9 @@ final class ActivityController extends AbstractController
'element_class' => Activity::class,
'action' => 'list',
]);
$this->eventDispatcher->dispatch($event, PrivacyEvent::PERSON_PRIVACY_EVENT);
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
$view = '@ChillActivity/Activity/listPerson.html.twig';
$view = 'ChillActivityBundle:Activity:listPerson.html.twig';
} elseif ($accompanyingPeriod instanceof AccompanyingPeriod) {
$this->denyAccessUnlessGranted(ActivityVoter::SEE, $accompanyingPeriod);
@@ -291,9 +301,9 @@ final class ActivityController extends AbstractController
$filterArgs
);
$view = '@ChillActivity/Activity/listAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:listAccompanyingCourse.html.twig';
} else {
throw new \LogicException('Unsupported');
throw new \LogicException("Unsupported");
}
return $this->render(
@@ -310,6 +320,7 @@ final class ActivityController extends AbstractController
private function buildFilterOrder(AccompanyingPeriod|Person $associated): FilterOrderHelper
{
$filterBuilder = $this->filterOrderHelperFactory->create(self::class);
$types = $this->activityACLAwareRepository->findActivityTypeByAssociated($associated);
$jobs = $this->activityACLAwareRepository->findUserJobByAssociated($associated);
@@ -323,28 +334,25 @@ final class ActivityController extends AbstractController
->addEntityChoice('activity_types', 'activity_filter.Types', \Chill\ActivityBundle\Entity\ActivityType::class, $types, [
'choice_label' => function (\Chill\ActivityBundle\Entity\ActivityType $activityType) {
$text = match ($activityType->hasCategory()) {
true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()).' > ',
true => $this->translatableStringHelper->localize($activityType->getCategory()->getName()) . ' > ',
false => '',
};
return $text.$this->translatableStringHelper->localize($activityType->getName());
},
return $text . $this->translatableStringHelper->localize($activityType->getName());
}
]);
}
if (1 < count($jobs)) {
$filterBuilder
->addEntityChoice('jobs', 'activity_filter.Jobs', UserJob::class, $jobs, [
'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel()),
'choice_label' => fn (UserJob $u) => $this->translatableStringHelper->localize($u->getLabel())
]);
}
return $filterBuilder->build();
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/new", name="chill_activity_activity_new", methods={"POST", "GET"})
*/
public function newAction(Request $request): Response
{
$view = null;
@@ -352,16 +360,16 @@ final class ActivityController extends AbstractController
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillActivity/Activity/newAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:newAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = '@ChillActivity/Activity/newPerson.html.twig';
$view = 'ChillActivityBundle:Activity:newPerson.html.twig';
}
$activityType_id = $request->get('activityType_id', 0);
$activityType = $this->activityTypeRepository->find($activityType_id);
if (isset($activityType) && !$activityType->isActive()) {
throw new \InvalidArgumentException('Activity type must be active');
throw new InvalidArgumentException('Activity type must be active');
}
$activityData = null;
@@ -396,45 +404,45 @@ final class ActivityController extends AbstractController
}
$entity->setActivityType($activityType);
$entity->setDate(new \DateTime('now'));
$entity->setDate(new DateTime('now'));
if ($request->query->has('activityData')) {
$activityData = $request->query->get('activityData');
if (\array_key_exists('durationTime', $activityData) && $activityType->getDurationTimeVisible() > 0) {
if (array_key_exists('durationTime', $activityData) && $activityType->getDurationTimeVisible() > 0) {
$durationTimeInMinutes = $activityData['durationTime'];
$hours = floor($durationTimeInMinutes / 60);
$minutes = $durationTimeInMinutes % 60;
$duration = \DateTime::createFromFormat('H:i', $hours.':'.$minutes);
$duration = DateTime::createFromFormat('H:i', $hours . ':' . $minutes);
if ($duration) {
$entity->setDurationTime($duration);
}
}
if (\array_key_exists('date', $activityData)) {
$date = \DateTime::createFromFormat('Y-m-d', $activityData['date']);
if (array_key_exists('date', $activityData)) {
$date = DateTime::createFromFormat('Y-m-d', $activityData['date']);
if ($date) {
$entity->setDate($date);
}
}
if (\array_key_exists('personsId', $activityData) && $activityType->getPersonsVisible() > 0) {
if (array_key_exists('personsId', $activityData) && $activityType->getPersonsVisible() > 0) {
foreach ($activityData['personsId'] as $personId) {
$concernedPerson = $this->personRepository->find($personId);
$entity->addPerson($concernedPerson);
}
}
if (\array_key_exists('professionalsId', $activityData) && $activityType->getThirdPartiesVisible() > 0) {
if (array_key_exists('professionalsId', $activityData) && $activityType->getThirdPartiesVisible() > 0) {
foreach ($activityData['professionalsId'] as $professionalsId) {
$professional = $this->thirdPartyRepository->find($professionalsId);
$entity->addThirdParty($professional);
}
}
if (\array_key_exists('usersId', $activityData) && $activityType->getUsersVisible() > 0) {
if (array_key_exists('usersId', $activityData) && $activityType->getUsersVisible() > 0) {
foreach ($activityData['usersId'] as $userId) {
$user = $this->userRepository->find($userId);
@@ -444,16 +452,16 @@ final class ActivityController extends AbstractController
}
}
if (\array_key_exists('location', $activityData) && $activityType->getLocationVisible() > 0) {
if (array_key_exists('location', $activityData) && $activityType->getLocationVisible() > 0) {
$location = $this->locationRepository->find($activityData['location']);
$entity->setLocation($location);
}
if (\array_key_exists('comment', $activityData) && $activityType->getCommentVisible() > 0) {
if (array_key_exists('comment', $activityData) && $activityType->getCommentVisible() > 0) {
$comment = new CommentEmbeddable();
$comment->setComment($activityData['comment']);
$comment->setUserId($this->getUser()->getid());
$comment->setDate(new \DateTime('now'));
$comment->setDate(new DateTime('now'));
$entity->setComment($comment);
}
}
@@ -525,9 +533,6 @@ final class ActivityController extends AbstractController
]);
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/select-type", name="chill_activity_activity_select_type")
*/
public function selectTypeAction(Request $request): Response
{
$view = null;
@@ -535,9 +540,9 @@ final class ActivityController extends AbstractController
[$person, $accompanyingPeriod] = $this->getEntity($request);
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillActivity/Activity/selectTypeAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:selectTypeAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = '@ChillActivity/Activity/selectTypePerson.html.twig';
$view = 'ChillActivityBundle:Activity:selectTypePerson.html.twig';
}
$data = [];
@@ -572,9 +577,6 @@ final class ActivityController extends AbstractController
]);
}
/**
* @\Symfony\Component\Routing\Annotation\Route(path="/{_locale}/activity/{id}/show", name="chill_activity_activity_show")
*/
public function showAction(Request $request, int $id): Response
{
$entity = $this->activityRepository->find($id);
@@ -587,11 +589,11 @@ final class ActivityController extends AbstractController
$person = $entity->getPerson();
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
$view = '@ChillActivity/Activity/showAccompanyingCourse.html.twig';
$view = 'ChillActivityBundle:Activity:showAccompanyingCourse.html.twig';
} elseif ($person instanceof Person) {
$view = '@ChillActivity/Activity/showPerson.html.twig';
$view = 'ChillActivityBundle:Activity:showPerson.html.twig';
} else {
throw new \RuntimeException('the activity should be linked with a period or person');
throw new RuntimeException('the activity should be linked with a period or person');
}
if (null !== $accompanyingPeriod) {
@@ -614,6 +616,10 @@ final class ActivityController extends AbstractController
$this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event);
*/
if (null === $view) {
throw $this->createNotFoundException('Template not found');
}
return $this->render($view, [
'person' => $person,
'accompanyingCourse' => $accompanyingPeriod,
@@ -673,8 +679,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');
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,11 +25,17 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
{
use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
private readonly \Faker\Generator $faker;
private EntityManagerInterface $em;
public function __construct(private readonly EntityManagerInterface $em)
/**
* @var \Faker\Generator
*/
private $faker;
public function __construct(EntityManagerInterface $em)
{
$this->faker = FakerFactory::create('fr_FR');
$this->em = $em;
}
public function getOrder()

View File

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

View File

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

View File

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

View File

@@ -42,13 +42,13 @@ class LoadActivityTypeCategory extends Fixture implements OrderedFixtureInterfac
];
foreach ($categories as $cat) {
echo 'Creating activity type category : '.$cat['ref']."\n";
echo 'Creating activity type category : ' . $cat['ref'] . "\n";
$newCat = (new ActivityTypeCategory())
->setName($cat['name']);
->setName(($cat['name']));
$manager->persist($newCat);
$reference = 'activity_type_cat_'.$cat['ref'];
$reference = 'activity_type_cat_' . $cat['ref'];
$this->addReference($reference, $newCat);
static::$references[] = $reference;

View File

@@ -20,6 +20,8 @@ use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Persistence\ObjectManager;
use function in_array;
/**
* Add a role CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE for all groups except administrative,
* and a role CHILL_ACTIVITY_SEE for administrative.
@@ -38,10 +40,10 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
foreach (LoadScopes::$references as $scopeRef) {
$scope = $this->getReference($scopeRef);
// create permission group
//create permission group
switch ($permissionsGroup->getName()) {
case 'social':
if ('administrative' === $scope->getName()['en']) {
if ($scope->getName()['en'] === 'administrative') {
break 2; // we do not want any power on administrative
}
@@ -49,7 +51,7 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
case 'administrative':
case 'direction':
if (\in_array($scope->getName()['en'], ['administrative', 'social'], true)) {
if (in_array($scope->getName()['en'], ['administrative', 'social'], true)) {
break 2; // we do not want any power on social or administrative
}
@@ -58,7 +60,7 @@ class LoadActivitytACL extends AbstractFixture implements OrderedFixtureInterfac
printf(
'Adding CHILL_ACTIVITY_UPDATE & CHILL_ACTIVITY_CREATE & CHILL_ACTIVITY_DELETE, and stats and list permissions to %s '
."permission group, scope '%s' \n",
. "permission group, scope '%s' \n",
$permissionsGroup->getName(),
$scope->getName()['en']
);

View File

@@ -32,7 +32,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
$container->setParameter('chill_activity.form.time_duration', $config['form']['time_duration']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
$loader->load('services.yaml');
$loader->load('services/export.yaml');
$loader->load('services/repositories.yaml');
@@ -73,7 +73,7 @@ class ChillActivityExtension extends Extension implements PrependExtensionInterf
*/
public function prependRoutes(ContainerBuilder $container)
{
// add routes for custom bundle
//add routes for custom bundle
$container->prependExtensionConfig('chill_main', [
'routing' => [
'resources' => [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ class ByActivityNumberAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data): void
{
$qb
->addSelect('(SELECT COUNT(activity.id) FROM '.Activity::class.' activity WHERE activity.accompanyingPeriod = acp) AS activity_by_number_aggregator')
->addSelect('(SELECT COUNT(activity.id) FROM ' . Activity::class . ' activity WHERE activity.accompanyingPeriod = acp) AS activity_by_number_aggregator')
->addGroupBy('activity_by_number_aggregator');
}
@@ -40,7 +40,6 @@ class ByActivityNumberAggregator implements AggregatorInterface
{
// No form needed
}
public function getFormDefaultData(): array
{
return [];

View File

@@ -1,122 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
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\Component\Form\FormBuilderInterface;
final readonly class ByActivityTypeAggregator implements AggregatorInterface
{
private const PREFIX = 'acp_by_activity_type_agg';
public function __construct(
private RollingDateConverterInterface $rollingDateConverter,
private ActivityTypeRepositoryInterface $activityTypeRepository,
private TranslatableStringHelperInterface $translatableStringHelper,
) {}
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('after_date', PickRollingDateType::class, [
'required' => false,
'label' => 'export.aggregator.acp.by_activity_type.after_date',
])
->add('before_date', PickRollingDateType::class, [
'required' => false,
'label' => 'export.aggregator.acp.by_activity_type.before_date',
]);
}
public function getFormDefaultData(): array
{
return [
'before_date' => null,
'after_date' => null,
];
}
public function getLabels($key, array $values, mixed $data)
{
return function (null|int|string $value): string {
if ('_header' === $value) {
return 'export.aggregator.acp.by_activity_type.activity_type';
}
if ('' === $value || null === $value || null === $activityType = $this->activityTypeRepository->find($value)) {
return '';
}
return $this->translatableStringHelper->localize($activityType->getName());
};
}
public function getQueryKeys($data)
{
return [self::PREFIX.'_actype_id'];
}
public function getTitle()
{
return 'export.aggregator.acp.by_activity_type.title';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
// we make a left join, with acp having at least one activity of the given type
$exists = 'EXISTS (SELECT 1 FROM '.Activity::class." {$p}_activity WHERE {$p}_activity.accompanyingPeriod = acp AND {$p}_activity.activityType = {$p}_activity_type";
if (null !== $data['after_date']) {
$exists .= " AND {$p}_activity.date > :{$p}_after_date";
$qb->setParameter("{$p}_after_date", $this->rollingDateConverter->convert($data['after_date']));
}
if (null !== $data['before_date']) {
$exists .= " AND {$p}_activity.date < :{$p}_before_date";
$qb->setParameter("{$p}_before_date", $this->rollingDateConverter->convert($data['before_date']));
}
$exists .= ')';
$qb->leftJoin(
ActivityType::class,
"{$p}_activity_type",
Join::WITH,
$exists
);
$qb
->addSelect("{$p}_activity_type.id AS {$p}_actype_id")
->addGroupBy("{$p}_actype_id");
}
public function applyOn()
{
return Declarations::ACP_TYPE;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,25 +9,29 @@ declare(strict_types=1);
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
namespace Chill\ActivityBundle\Export\Aggregator\ACPAggregators;
use Chill\ActivityBundle\Export\Declarations;
use Chill\MainBundle\Entity\User\UserScopeHistory;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\ScopeRepository;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
class CreatorScopeAggregator implements AggregatorInterface
{
private const PREFIX = 'acp_agg_creator_scope';
private ScopeRepository $scopeRepository;
private TranslatableStringHelper $translatableStringHelper;
public function __construct(
private readonly ScopeRepository $scopeRepository,
private readonly TranslatableStringHelper $translatableStringHelper
) {}
ScopeRepository $scopeRepository,
TranslatableStringHelper $translatableStringHelper
) {
$this->scopeRepository = $scopeRepository;
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
@@ -36,37 +40,23 @@ class CreatorScopeAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
if (!in_array('actcreator', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.createdBy', 'actcreator');
}
$qb
->leftJoin('activity.createdBy', "{$p}_user")
->leftJoin(
UserScopeHistory::class,
"{$p}_history",
Join::WITH,
$qb->expr()->eq("{$p}_history.user", "{$p}_user")
)
// scope_at based on activity.date
->andWhere(
$qb->expr()->andX(
$qb->expr()->lte("{$p}_history.startDate", 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull("{$p}_history.endDate"),
$qb->expr()->gt("{$p}_history.endDate", 'activity.date')
)
)
)
->addSelect("IDENTITY({$p}_history.scope) AS {$p}_select")
->addGroupBy("{$p}_select");
$qb->addSelect('IDENTITY(actcreator.mainScope) AS creatorscope_aggregator');
$qb->addGroupBy('creatorscope_aggregator');
}
public function applyOn(): string
{
return Declarations::ACTIVITY;
return Declarations::ACTIVITY_ACP;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder)
{
// no form
}
public function getFormDefaultData(): array
{
return [];
@@ -93,11 +83,11 @@ class CreatorScopeAggregator implements AggregatorInterface
public function getQueryKeys($data): array
{
return [self::PREFIX.'_select'];
return ['creatorscope_aggregator'];
}
public function getTitle(): string
{
return 'export.aggregator.activity.by_creator_scope.Group activity by creator scope';
return 'Group activity by creator scope';
}
}

View File

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

View File

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

View File

@@ -12,9 +12,14 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Repository\LocationRepository;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Closure;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use function in_array;
final readonly class ActivityLocationAggregator implements AggregatorInterface
{
@@ -27,7 +32,7 @@ final readonly class ActivityLocationAggregator implements AggregatorInterface
public function alterQuery(QueryBuilder $qb, $data)
{
if (!\in_array('actloc', $qb->getAllAliases(), true)) {
if (!in_array('actloc', $qb->getAllAliases(), true)) {
$qb->leftJoin('activity.location', 'actloc');
}
$qb->addSelect(sprintf('actloc.name AS %s', self::KEY));
@@ -43,13 +48,12 @@ final readonly class ActivityLocationAggregator implements AggregatorInterface
{
// no form required for this aggregator
}
public function getFormDefaultData(): array
{
return [];
}
public function getLabels($key, array $values, $data): \Closure
public function getLabels($key, array $values, $data): Closure
{
return function ($value): string {
if ('_header' === $value) {

View File

@@ -1,72 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,65 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator\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;
}
}

View File

@@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Aggregator;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Tests\Export\Aggregator\PersonsAggregatorTest;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\PersonBundle\Export\Helper\LabelPersonHelper;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
/**
* @see PersonsAggregatorTest
*/
final readonly class PersonsAggregator implements AggregatorInterface
{
private const PREFIX = 'act_persons_agg';
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)
{
if ($key !== self::PREFIX.'_pid') {
throw new \UnexpectedValueException('this key should not be handled: '.$key);
}
return $this->labelPersonHelper->getLabel($key, $values, 'export.aggregator.activity.by_persons.Persons');
}
public function getQueryKeys($data)
{
return [self::PREFIX.'_pid'];
}
public function getTitle()
{
return 'export.aggregator.activity.by_persons.Group activity by persons';
}
public function addRole(): ?string
{
return null;
}
public function alterQuery(QueryBuilder $qb, $data)
{
$p = self::PREFIX;
$qb
->leftJoin('activity.persons', "{$p}_p")
->addSelect("{$p}_p.id AS {$p}_pid")
->addGroupBy("{$p}_pid");
}
public function applyOn()
{
return Declarations::ACTIVITY;
}
}

View File

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

View File

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

View File

@@ -11,33 +11,34 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityRepository;
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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityDuration implements ExportInterface, GroupedExportInterface
{
private readonly bool $filterStatsByCenters;
protected EntityRepository $repository;
public function __construct(
private readonly ActivityRepository $activityRepository,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
$this->repository = $em->getRepository(Activity::class);
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getFormDefaultData(): array
{
return [];
@@ -61,7 +62,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_duration' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period duration' : $value;
@@ -91,27 +92,23 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
{
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->activityRepository->createQueryBuilder('activity');
$qb = $this->repository->createQueryBuilder('activity');
$qb
->join('activity.accompanyingPeriod', 'acp')
->select('AVG(activity.durationTime) as export_avg_activity_duration')
->andWhere($qb->expr()->isNotNull('activity.durationTime'));
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)
$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);
)
->setParameter('authorized_centers', $centers);
return $qb;
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -24,28 +23,23 @@ 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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
@@ -69,7 +63,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function getLabels($key, array $values, $data)
{
if ('export_avg_activity_visit_duration' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Average activities linked to an accompanying period visit duration' : $value;
@@ -106,20 +100,16 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
->select('AVG(activity.travelTime) as export_avg_activity_visit_duration')
->andWhere($qb->expr()->isNotNull('activity.travelTime'));
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)
$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);
)
->setParameter('authorized_centers', $centers);
return $qb;
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -24,25 +23,22 @@ 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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getFormDefaultData(): array
{
return [];
@@ -66,7 +62,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to an accompanying period' : $value;
@@ -100,20 +96,16 @@ class CountActivity implements ExportInterface, GroupedExportInterface
->createQueryBuilder('activity')
->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)
$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);
)
->setParameter('authorized_centers', $centers);
$qb->select('COUNT(DISTINCT activity.id) as export_count_activity');

View File

@@ -1,147 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\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\Household\HouseholdMember;
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;
final readonly class CountHouseholdOnActivity implements ExportInterface, GroupedExportInterface
{
private EntityRepository $repository;
private 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_household_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_household_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_household_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')
->join(
HouseholdMember::class,
'householdmember',
Query\Expr\Join::WITH,
'person.id = IDENTITY(householdmember.person) AND householdmember.startDate <= activity.date AND (householdmember.endDate IS NULL OR householdmember.endDate > activity.date)'
)
->join('householdmember.household', 'household');
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 household.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,
PersonDeclarations::HOUSEHOLD_TYPE,
];
}
}

View File

@@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\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,
];
}
}

View File

@@ -12,32 +12,38 @@ declare(strict_types=1);
namespace Chill\ActivityBundle\Export\Export\LinkedToACP;
use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Export\Export\ListActivityHelper;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\Helper\TranslatableStringExportLabelHelper;
use Chill\MainBundle\Export\ListInterface;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class ListActivity implements ListInterface, GroupedExportInterface
class ListActivity implements ListInterface, GroupedExportInterface
{
private EntityManagerInterface $entityManager;
private ListActivityHelper $helper;
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper;
public function __construct(
private ListActivityHelper $helper,
private EntityManagerInterface $entityManager,
private TranslatableStringExportLabelHelper $translatableStringExportLabelHelper,
private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
) {}
ListActivityHelper $helper,
EntityManagerInterface $entityManager,
TranslatableStringExportLabelHelper $translatableStringExportLabelHelper
) {
$this->helper = $helper;
$this->entityManager = $entityManager;
$this->translatableStringExportLabelHelper = $translatableStringExportLabelHelper;
}
public function buildForm(FormBuilderInterface $builder)
{
$this->helper->buildForm($builder);
}
public function getFormDefaultData(): array
{
return [];
@@ -50,7 +56,7 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
public function getDescription()
{
return ListActivityHelper::MSG_KEY.'List activities linked to an accompanying course';
return ListActivityHelper::MSG_KEY . 'List activities linked to an accompanying course';
}
public function getGroup(): string
@@ -60,17 +66,22 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
public function getLabels($key, array $values, $data)
{
return match ($key) {
'acpId' => static function ($value) {
if ('_header' === $value) {
return ListActivityHelper::MSG_KEY.'accompanying course id';
}
switch ($key) {
case 'acpId':
return static function ($value) {
if ('_header' === $value) {
return ListActivityHelper::MSG_KEY . 'accompanying course id';
}
return $value ?? '';
},
'scopesNames' => $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY.'course circles'),
default => $this->helper->getLabels($key, $values, $data),
};
return $value ?? '';
};
case 'scopesNames':
return $this->translatableStringExportLabelHelper->getLabelMulti($key, $values, ListActivityHelper::MSG_KEY . 'course circles');
default:
return $this->helper->getLabels($key, $values, $data);
}
}
public function getQueryKeys($data)
@@ -92,7 +103,7 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
public function getTitle()
{
return ListActivityHelper::MSG_KEY.'List activity linked to a course';
return ListActivityHelper::MSG_KEY . 'List activity linked to a course';
}
public function getType()
@@ -112,26 +123,30 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
->join('activity.accompanyingPeriod', 'acp')
->leftJoin('acp.participations', 'acppart')
->leftJoin('acppart.person', 'person')
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL');
$this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $data);
$qb
->andWhere('acppart.startDate != acppart.endDate OR acppart.endDate IS NULL')
->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)
'
)
)
// some grouping are necessary
->addGroupBy('acp.id')
->addOrderBy('activity.date')
->addOrderBy('activity.id');
->addOrderBy('activity.id')
->setParameter('authorized_centers', $centers);
$this->helper->addSelect($qb);
// add select for this step
$qb
->addSelect('acp.id AS acpId')
->addSelect('(SELECT AGGREGATE(acpScope.name) FROM '.Scope::class.' acpScope WHERE acpScope MEMBER OF acp.scopes) AS scopesNames')
->addSelect('(SELECT AGGREGATE(acpScope.name) FROM ' . Scope::class . ' acpScope WHERE acpScope MEMBER OF acp.scopes) AS scopesNames')
->addGroupBy('scopesNames');
AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);
return $qb;
}
@@ -145,7 +160,6 @@ final readonly class ListActivity implements ListInterface, GroupedExportInterfa
return array_merge(
$this->helper->supportsModifiers(),
[
Declarations::ACTIVITY,
\Chill\PersonBundle\Export\Declarations::ACP_TYPE,
]
);

View File

@@ -14,7 +14,6 @@ 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;
@@ -24,27 +23,23 @@ 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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
@@ -68,7 +63,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_duration' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period duration' : $value;
@@ -105,20 +100,16 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
$qb->select('SUM(activity.durationTime) as export_sum_activity_duration')
->andWhere($qb->expr()->isNotNull('activity.durationTime'));
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)
$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);
)
->setParameter('authorized_centers', $centers);
return $qb;
}

View File

@@ -14,7 +14,6 @@ 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;
@@ -24,27 +23,23 @@ 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 LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class SumActivityVisitDuration implements ExportInterface, GroupedExportInterface
{
protected EntityRepository $repository;
private readonly bool $filterStatsByCenters;
public function __construct(
EntityManagerInterface $em,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em
) {
$this->repository = $em->getRepository(Activity::class);
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder)
{
// TODO: Implement buildForm() method.
}
public function getFormDefaultData(): array
{
return [];
@@ -68,7 +63,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function getLabels($key, array $values, $data)
{
if ('export_sum_activity_visit_duration' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Sum activities linked to an accompanying period visit duration' : $value;
@@ -105,20 +100,16 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
$qb->select('SUM(activity.travelTime) as export_sum_activity_visit_duration')
->andWhere($qb->expr()->isNotNull('activity.travelTime'));
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)
$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);
)
->setParameter('authorized_centers', $centers);
return $qb;
}

View File

@@ -19,22 +19,22 @@ use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use LogicException;
use Symfony\Component\Form\FormBuilderInterface;
class CountActivity implements ExportInterface, GroupedExportInterface
{
private readonly bool $filterStatsByCenters;
protected ActivityRepository $activityRepository;
public function __construct(
private readonly ActivityRepository $activityRepository,
ParameterBagInterface $parameterBag,
ActivityRepository $activityRepository
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder) {}
public function buildForm(FormBuilderInterface $builder)
{
}
public function getFormDefaultData(): array
{
return [];
@@ -58,7 +58,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function getLabels($key, array $values, $data)
{
if ('export_count_activity' !== $key) {
throw new \LogicException("the key {$key} is not used by this export");
throw new LogicException("the key {$key} is not used by this export");
}
return static fn ($value) => '_header' === $value ? 'Number of activities linked to a person' : $value;
@@ -90,25 +90,23 @@ class CountActivity implements ExportInterface, GroupedExportInterface
$qb = $this->activityRepository
->createQueryBuilder('activity')
->join('activity.person', 'person');
->join('activity.person', 'person')
->join('person.centerHistory', 'centerHistory');
$qb->select('COUNT(activity.id) as export_count_activity');
if ($this->filterStatsByCenters) {
$qb
->join('person.centerHistory', 'centerHistory')
->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gt('centerHistory.endDate', 'activity.date')
)
$qb
->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gt('centerHistory.endDate', 'activity.date')
)
)
->andWhere($qb->expr()->in('centerHistory.center', ':centers'))
->setParameter('centers', $centers);
}
)
->andWhere($qb->expr()->in('centerHistory.center', ':centers'))
->setParameter('centers', $centers);
return $qb;
}

View File

@@ -1,138 +0,0 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Export\Export\LinkedToPerson;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityRepository;
use Chill\ActivityBundle\Security\Authorization\ActivityStatsVoter;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class CountHouseholdOnActivity implements ExportInterface, GroupedExportInterface
{
private bool $filterStatsByCenters;
public function __construct(
private ActivityRepository $activityRepository,
ParameterBagInterface $parameterBag,
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
}
public function buildForm(FormBuilderInterface $builder) {}
public function getFormDefaultData(): array
{
return [];
}
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_TABULAR];
}
public function getDescription()
{
return 'export.export.count_household_on_activity_person.description';
}
public function getGroup(): string
{
return 'Exports of activities linked to a person';
}
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_household_on_activity_person.header' : $value;
}
public function getQueryKeys($data)
{
return ['export_count_activity'];
}
public function getResult($query, $data)
{
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
}
public function getTitle()
{
return 'export.export.count_household_on_activity_person.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->activityRepository
->createQueryBuilder('activity')
->join('activity.person', 'person')
->join(
HouseholdMember::class,
'householdmember',
Query\Expr\Join::WITH,
'person = householdmember.person AND householdmember.startDate <= activity.date AND (householdmember.endDate IS NULL OR householdmember.endDate > activity.date)'
)
->join('householdmember.household', 'household');
$qb->select('COUNT(DISTINCT household.id) as export_count_activity');
if ($this->filterStatsByCenters) {
$qb
->join('person.centerHistory', 'centerHistory')
->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gt('centerHistory.endDate', 'activity.date')
)
)
)
->andWhere($qb->expr()->in('centerHistory.center', ':centers'))
->setParameter('centers', $centers);
}
return $qb;
}
public function requiredRole(): string
{
return ActivityStatsVoter::STATS;
}
public function supportsModifiers()
{
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
PersonDeclarations::PERSON_TYPE,
PersonDeclarations::HOUSEHOLD_TYPE,
];
}
}

View File

@@ -20,18 +20,24 @@ use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations as PersonDeclarations;
use DateTime;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
use function count;
use function in_array;
class ListActivity implements ListInterface, GroupedExportInterface
{
protected EntityManagerInterface $entityManager;
protected array $fields = [
'id',
'date',
@@ -45,16 +51,23 @@ class ListActivity implements ListInterface, GroupedExportInterface
'person_lastname',
'person_id',
];
private readonly bool $filterStatsByCenters;
protected TranslatableStringHelperInterface $translatableStringHelper;
protected TranslatorInterface $translator;
private ActivityRepository $activityRepository;
public function __construct(
protected EntityManagerInterface $entityManager,
protected TranslatorInterface $translator,
protected TranslatableStringHelperInterface $translatableStringHelper,
private readonly ActivityRepository $activityRepository,
ParameterBagInterface $parameterBag,
EntityManagerInterface $em,
TranslatorInterface $translator,
TranslatableStringHelperInterface $translatableStringHelper,
ActivityRepository $activityRepository
) {
$this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
$this->entityManager = $em;
$this->translator = $translator;
$this->translatableStringHelper = $translatableStringHelper;
$this->activityRepository = $activityRepository;
}
public function buildForm(FormBuilderInterface $builder)
@@ -66,7 +79,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
'label' => 'Fields to include in export',
'constraints' => [new Callback([
'callback' => static function ($selected, ExecutionContextInterface $context) {
if (0 === \count($selected)) {
if (count($selected) === 0) {
$context->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
@@ -75,7 +88,6 @@ class ListActivity implements ListInterface, GroupedExportInterface
])],
]);
}
public function getFormDefaultData(): array
{
return [];
@@ -105,7 +117,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'date';
}
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
$date = DateTime::createFromFormat('Y-m-d H:i:s', $value);
return $date->format('d-m-Y');
};
@@ -129,11 +141,11 @@ class ListActivity implements ListInterface, GroupedExportInterface
$activity = $activityRepository->find($value);
return implode(', ', array_map(fn (ActivityReason $r) => '"'.
return implode(', ', array_map(fn (ActivityReason $r) => '"' .
$this->translatableStringHelper->localize($r->getCategory()->getName())
.' > '.
. ' > ' .
$this->translatableStringHelper->localize($r->getName())
.'"', $activity->getReasons()->toArray()));
. '"', $activity->getReasons()->toArray()));
};
case 'circle_name':
@@ -142,7 +154,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'circle';
}
return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR));
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
};
case 'type_name':
@@ -151,7 +163,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'activity type';
}
return $this->translatableStringHelper->localize(json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR));
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
};
default:
@@ -190,7 +202,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
$centers = array_map(static fn ($el) => $el['center'], $acl);
// throw an error if any fields are present
if (!\array_key_exists('fields', $data)) {
if (!array_key_exists('fields', $data)) {
throw new InvalidArgumentException('Any fields have been checked.');
}
@@ -198,25 +210,23 @@ class ListActivity implements ListInterface, GroupedExportInterface
$qb
->from('ChillActivityBundle:Activity', 'activity')
->join('activity.person', 'actperson');
->join('activity.person', 'actperson')
->join('actperson.centerHistory', 'centerHistory');
if ($this->filterStatsByCenters) {
$qb->join('actperson.centerHistory', 'centerHistory');
$qb->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gt('centerHistory.endDate', 'activity.date')
)
$qb->where(
$qb->expr()->andX(
$qb->expr()->lte('centerHistory.startDate', 'activity.date'),
$qb->expr()->orX(
$qb->expr()->isNull('centerHistory.endDate'),
$qb->expr()->gt('centerHistory.endDate', 'activity.date')
)
)
->andWhere($qb->expr()->in('centerHistory.center', ':centers'))
->setParameter('centers', $centers);
}
)
->andWhere($qb->expr()->in('centerHistory.center', ':centers'))
->setParameter('centers', $centers);
foreach ($this->fields as $f) {
if (\in_array($f, $data['fields'], true)) {
if (in_array($f, $data['fields'], true)) {
switch ($f) {
case 'id':
$qb->addSelect('activity.id AS id');
@@ -289,7 +299,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return [
Declarations::ACTIVITY,
Declarations::ACTIVITY_PERSON,
PersonDeclarations::PERSON_TYPE,
//PersonDeclarations::PERSON_TYPE,
];
}
}

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