mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-10-15 17:59:43 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
801f799e45
|
|||
98b8f3dcff
|
|||
a333a0312a
|
|||
0288fd22cf
|
|||
1dd4398c43
|
|||
4f4b3dbb44
|
|||
1c3e6e0dba
|
|||
e7ca81e057
|
|||
9687debb57
|
|||
769504c497
|
|||
811364e139
|
|||
deffc5e4db
|
|||
40ecaab5b4
|
|||
f7be53f790
|
|||
6fb01b19ec
|
|||
b8ecff4f08
|
|||
4c340dd086
|
|||
8f1955c536
|
|||
c9c15cdd56
|
5
.changes/unreleased/Feature-20230707-123609.yaml
Normal file
5
.changes/unreleased/Feature-20230707-123609.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add a list for people with their associated course'
|
||||||
|
time: 2023-07-07T12:36:09.596469063+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "125"
|
6
.changes/unreleased/Feature-20230707-124132.yaml
Normal file
6
.changes/unreleased/Feature-20230707-124132.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add ordering by person''s lastname or course opening date in list
|
||||||
|
which concerns accompanying course or peoples'
|
||||||
|
time: 2023-07-07T12:41:32.112725962+02:00
|
||||||
|
custom:
|
||||||
|
Issue: ""
|
5
.changes/unreleased/Feature-20230711-150055.yaml
Normal file
5
.changes/unreleased/Feature-20230711-150055.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[Export] allow to group activities by localisation'
|
||||||
|
time: 2023-07-11T15:00:55.770070399+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "128"
|
5
.changes/unreleased/Feature-20230711-155929.yaml
Normal file
5
.changes/unreleased/Feature-20230711-155929.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kind: Feature
|
||||||
|
body: '[export] Add a filter "filter course having an activity between two dates"'
|
||||||
|
time: 2023-07-11T15:59:29.065329834+02:00
|
||||||
|
custom:
|
||||||
|
Issue: "129"
|
@@ -1,37 +0,0 @@
|
|||||||
## v2.5.0 - 2023-07-14
|
|
||||||
### Feature
|
|
||||||
* Allow filtering on the basis of a user within general tasks lists
|
|
||||||
* ([#120](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/120)) Adding OrderFilter to the list of social actions.
|
|
||||||
* ([#125](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/125)) [export] Add a list for people with their associated course
|
|
||||||
* [export] Add ordering by person's lastname or course opening date in list which concerns accompanying course or peoples
|
|
||||||
* ([#128](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/128)) [Export] allow to group activities by localisation
|
|
||||||
* ([#129](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/129)) [export] Add a filter "filter course having an activity between two dates"
|
|
||||||
* ([#112](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/112)) [addresses] Add a cronjob to re-associate addresses with addresses reference every 6 hours
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* reimplement the visualization of all calculator results
|
|
||||||
* ([#117](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/117)) Repair my unread notification list with actions and evaluations documents
|
|
||||||
* ([#126](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/126)) Correct bug in thirdparty API search query: simplify address joins clause for child and parent kind
|
|
||||||
|
|
||||||
### DX
|
|
||||||
* Documentation for database principles
|
|
||||||
* [cronjob] when a cronjob is executed, it may return an array of data that will be passed as argument on the next execution
|
|
||||||
|
|
||||||
### UX
|
|
||||||
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Better integration of address details button: look, position, title tag
|
|
||||||
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Show address detail button on person and household banners
|
|
||||||
* Improve residential address position on show onthefly modale
|
|
||||||
|
|
||||||
### Traduction francophone des principaux changements
|
|
||||||
|
|
||||||
* Ajout d'un filtre "par utilisateur" aux pages de tâche
|
|
||||||
* Filtre des actions d'accompagnement par date, type, intervenant
|
|
||||||
* export: liste des usagers concernés avec détail de leurs parcours
|
|
||||||
* export: ajout d'un regroupement des échanges par localisation
|
|
||||||
* export: ajout d'un filtre "parcours ayant reçu un échange entre deux dates"
|
|
||||||
* ajout d'une tâche cron pour associer les adresses à une adresse de référence
|
|
||||||
* correction: réparation de la liste des notifications sur la page d'accueil, dans le cas où une notification concerne une action ou un document dans une évaluation
|
|
||||||
* correction: réparation de la recherche des tiers ayant des codes postaux similaires entre les parents et enfants
|
|
||||||
* meilleure intégration du bouton "détail d'une adresse": améliration de la taille et de la position
|
|
||||||
* bouton permettant de visualiser les détails d'une adresse (modale avec carte) dans la bannière "Usager" et "Ménage"
|
|
||||||
* amélioration de la modale permettant de voir les détails d'un usager: les adresses de résidence sont dans la continuité des autres adresses, et non plus dans une colonne séparée
|
|
@@ -1,3 +0,0 @@
|
|||||||
## v2.5.1 - 2023-07-14
|
|
||||||
### Fixed
|
|
||||||
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match
|
|
@@ -7,7 +7,7 @@ versionFormat: '## {{.Version}} - {{.Time.Format "2006-01-02"}}'
|
|||||||
kindFormat: '### {{.Kind}}'
|
kindFormat: '### {{.Kind}}'
|
||||||
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
|
# Note: it is possible to add a `.custom.Long` text manually into the yaml file produced by `changie new`. This will add a long description.
|
||||||
changeFormat: >-
|
changeFormat: >-
|
||||||
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}} {{ if and (.Custom.Long) (not (eq .Custom.Long "")) }}
|
* {{ if not (eq .Custom.Issue "") }}([#{{ .Custom.Issue }}](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/{{ .Custom.Issue }})) {{ end }}{{.Body}} {{ if not (eq .Custom.Long "") }}
|
||||||
|
|
||||||
{{ .Custom.Long }}{{ end }}
|
{{ .Custom.Long }}{{ end }}
|
||||||
custom:
|
custom:
|
||||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@@ -6,48 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
## v2.5.1 - 2023-07-14
|
|
||||||
### Fixed
|
|
||||||
* [collate addresses] block collating addresses to another address reference where the address reference is already the best match
|
|
||||||
|
|
||||||
## v2.5.0 - 2023-07-14
|
|
||||||
### Feature
|
|
||||||
* Allow filtering on the basis of a user within general tasks lists
|
|
||||||
* ([#120](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/120)) Adding OrderFilter to the list of social actions.
|
|
||||||
* ([#125](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/125)) [export] Add a list for people with their associated course
|
|
||||||
* [export] Add ordering by person's lastname or course opening date in list which concerns accompanying course or peoples
|
|
||||||
* ([#128](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/128)) [Export] allow to group activities by localisation
|
|
||||||
* ([#129](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/129)) [export] Add a filter "filter course having an activity between two dates"
|
|
||||||
* ([#112](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/112)) [addresses] Add a cronjob to re-associate addresses with addresses reference every 6 hours
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
* reimplement the visualization of all calculator results
|
|
||||||
* ([#117](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/117)) Repair my unread notification list with actions and evaluations documents
|
|
||||||
* ([#126](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/126)) Correct bug in thirdparty API search query: simplify address joins clause for child and parent kind
|
|
||||||
|
|
||||||
### DX
|
|
||||||
* Documentation for database principles
|
|
||||||
* [cronjob] when a cronjob is executed, it may return an array of data that will be passed as argument on the next execution
|
|
||||||
|
|
||||||
### UX
|
|
||||||
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Better integration of address details button: look, position, title tag
|
|
||||||
* ([#93](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/93)) Show address detail button on person and household banners
|
|
||||||
* Improve residential address position on show onthefly modale
|
|
||||||
|
|
||||||
### Traduction francophone des principaux changements
|
|
||||||
|
|
||||||
* Ajout d'un filtre "par utilisateur" aux pages de tâche
|
|
||||||
* Filtre des actions d'accompagnement par date, type, intervenant
|
|
||||||
* export: liste des usagers concernés avec détail de leurs parcours
|
|
||||||
* export: ajout d'un regroupement des échanges par localisation
|
|
||||||
* export: ajout d'un filtre "parcours ayant reçu un échange entre deux dates"
|
|
||||||
* ajout d'une tâche cron pour associer les adresses à une adresse de référence
|
|
||||||
* correction: réparation de la liste des notifications sur la page d'accueil, dans le cas où une notification concerne une action ou un document dans une évaluation
|
|
||||||
* correction: réparation de la recherche des tiers ayant des codes postaux similaires entre les parents et enfants
|
|
||||||
* meilleure intégration du bouton "détail d'une adresse": améliration de la taille et de la position
|
|
||||||
* bouton permettant de visualiser les détails d'une adresse (modale avec carte) dans la bannière "Usager" et "Ménage"
|
|
||||||
* amélioration de la modale permettant de voir les détails d'un usager: les adresses de résidence sont dans la continuité des autres adresses, et non plus dans une colonne séparée
|
|
||||||
|
|
||||||
## v2.4.0 - 2023-07-07
|
## v2.4.0 - 2023-07-07
|
||||||
|
|
||||||
### Feature
|
### Feature
|
||||||
|
@@ -1,84 +0,0 @@
|
|||||||
|
|
||||||
.. database-principles:
|
|
||||||
|
|
||||||
Principes de la base de données
|
|
||||||
###############################
|
|
||||||
|
|
||||||
Cette page donne une compréhension globale de la base de donnée de Chill, et explique quelques détails d'implémentations qui permettent d'accélérer les traitements à partir de la base de donnée, ou de l'exploiter plus aisément.
|
|
||||||
|
|
||||||
Cette page est rédigée en français.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
La stabilité du schéma de la base de donnée n'est pas garantie.
|
|
||||||
|
|
||||||
Toutefois, ce dernier évolue relativement peu. Il est rare que des tables ou des colonnes soient supprimées ou renommées. Mais il n'est pas garanti que cela puisse arriver.
|
|
||||||
|
|
||||||
Généralités
|
|
||||||
===========
|
|
||||||
|
|
||||||
Une liste commentée de toutes les tables :download:`est disponible au format CSV <./database/table_list.csv`.
|
|
||||||
|
|
||||||
Schéma et conventions de nommage
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Au début de l'histoire de Chill, les schémas postgresql n'étaient pas exploités. Les données étaient stockées dans le schéma :code:`public`.
|
|
||||||
|
|
||||||
Par la suite, des nouveaux bundles sont apparus, et les tables ont été classées dans des schémas dédiés.
|
|
||||||
|
|
||||||
A l'heure actuelle:
|
|
||||||
|
|
||||||
- pour les anciens bundle, ceux qui ont déjà des tables dans le schéma public, les nouvelles tables sont ajoutées à ce schéma. Elles sont préfixées par :code:`chill_<nom du bundle>_`;
|
|
||||||
- pour les bundles plus récents, les tables sont créées dans le schéma dédié
|
|
||||||
|
|
||||||
Données avec de l'historicité
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Certaines données sont historisées:
|
|
||||||
|
|
||||||
- les référents d'un parcours;
|
|
||||||
- les statuts d'un parcours;
|
|
||||||
- la liaison entre les centres et les usagers;
|
|
||||||
- etc.
|
|
||||||
|
|
||||||
Dans ces cas-là, Chill crée généralement deux colonnes, qui sont habituellement nommées :code:`startDate` et :code:`endDate`. Lorsque la colonne :code:`endDate` est à :code:`NULL`, cela signifie que la période n'est pas "fermée". La colonne :code:`startDate` n'est pas nullable.
|
|
||||||
|
|
||||||
Dans certains cas, la donnée actuelle (référent d'un parcours, par exemple) est également répétée au niveau de la table en elle-même. Par exemple, la table des parcours :code:`chill_person_accompanying_period` comporte une colonne :code:`step` (le statut du parcours) et :code:`user_id` (id du référent) en plus de l'historique. Bien que redondant, cela simplifie les traitements.
|
|
||||||
|
|
||||||
Relations particulières
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Usagers, ménages, adresses
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Les usagers ont une adresse au travers des ménages: dans l'interface, l'adresse est inscrite dans le dossier du ménage, et elle est "donnée" aux usagers membres du ménage, **et** qui partagent l'adresse de ce ménage. En effet, il est possible que des usagers "appartiennent" à un ménage sans y être domicilié: c'est le cas, par exemple, des enfants en garde alternée.
|
|
||||||
|
|
||||||
L'historique de l'appartenance des usagers au ménage est conservée, de même que l'historique des adresses pour un même ménage.
|
|
||||||
|
|
||||||
Les tables en jeu sont les suivantes:
|
|
||||||
|
|
||||||
- la table :code:`chill_person_person` liste les usagers;
|
|
||||||
- la table :code:`chill_person_household_members` liste les appartenances au ménage: il s'agit de la jointure entre les usagers et les ménages:
|
|
||||||
- les colonnes :code:`startDate` et :code:`endDate` indiquent la date de début et la date de fin de l'appartenance;
|
|
||||||
- la colonne :code:`shareHousehold` indique si l'utilisateur partage l'adresse du ménage (si oui, sa valeur est :code:`TRUE`)
|
|
||||||
- la table :code:`chill_person_household` liste les ménages
|
|
||||||
- la table :code:`chill_person_household_to_addresses` associe les ménages aux adresses;
|
|
||||||
- la table :code:`chill_main_address` contient les adresses, en indiquant la date de début de validité (:code:`validFrom`) et la fin de validité (:code:`validTo`).
|
|
||||||
|
|
||||||
Pour simplifier la résolution des adresses et des usagers, deux vues ont été mises en œuvre:
|
|
||||||
|
|
||||||
- la vue :code:`view_chill_person_household_address` reprend, pour chaque usager, l'historique des appartenances au ménage découpée par l'historique des adresses d'un ménage.
|
|
||||||
Autrement dit, une ligne est créée à chaque fois qu'un usager change de ménage, ou qu'un ménage change d'adresse. Il est donc possible de retrouver l'historique complet des adresses pour un usager donné via cette table.
|
|
||||||
- la vue :code:`view_chill_person_current_address` reprend l'adresse actuelle des usagers.
|
|
||||||
|
|
||||||
Adresses et unités géographiques
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
Chill propose des statistiques sur la localisation des adresses par rapport à des zones géographiques (:code:`chill_main_geographical_unit`).
|
|
||||||
|
|
||||||
Comme la résolution géographique des adresses est coûteuse en CPU et en temps de traitement, une vue matérialisée a été créée: :code:`view_chill_main_address_geographical_unit`. Elle est rafraichie quotidiennement dans la base de donnée de production.
|
|
||||||
|
|
||||||
Liste des tables et commentaires
|
|
||||||
================================
|
|
||||||
|
|
||||||
Une liste commentée de toutes les tables :download:`est disponible au format CSV <./database/table_list.csv`.
|
|
@@ -1,155 +0,0 @@
|
|||||||
order,table_schema,table_name,commentaire
|
|
||||||
1,chill_3party,party_category,Catégorie de tiers
|
|
||||||
2,chill_3party,party_center,Association entre les tiers et les centres (déprécié)
|
|
||||||
3,chill_3party,party_profession,Profession du tiers (déprécié)
|
|
||||||
4,chill_3party,third_party,Tiers
|
|
||||||
5,chill_3party,thirdparty_category,association tiers - catégories
|
|
||||||
6,chill_asideactivity,asideactivity,Activités annexes
|
|
||||||
7,chill_asideactivity,asideactivitycategory,Catégories d'activités annexes
|
|
||||||
8,chill_budget,charge,Charges du budget
|
|
||||||
9,chill_budget,charge_type,Types de charges
|
|
||||||
10,chill_budget,resource,Ressources du budget
|
|
||||||
11,chill_budget,resource_type,Types de ressources
|
|
||||||
12,chill_calendar,calendar,Rendez-vous
|
|
||||||
13,chill_calendar,calendar_doc,Document du rendez-vous
|
|
||||||
14,chill_calendar,calendar_range,Plage de disponibilité
|
|
||||||
15,chill_calendar,calendar_to_persons,association rendez-vous - usagers
|
|
||||||
16,chill_calendar,calendar_to_thirdparties,association rendez-vous - tiers
|
|
||||||
17,chill_calendar,cancel_reason,Motifs d'annulations
|
|
||||||
18,chill_calendar,invite,Invitation aux rendez-vous
|
|
||||||
19,chill_doc,accompanyingcourse_document,Documents associés aux parcours
|
|
||||||
20,chill_doc,document_category,Catégories de documents
|
|
||||||
21,chill_doc,person_document,Documents associés à l'usagers
|
|
||||||
22,chill_doc,stored_object,Documents
|
|
||||||
23,chill_task,recurring_task,Tâches récurrentes (non utilisé)
|
|
||||||
24,chill_task,single_task,Tâches
|
|
||||||
25,chill_task,single_task_place_event,Historique des transitions des tâches
|
|
||||||
26,chill_vendee,adressederelais,
|
|
||||||
27,chill_vendee,center_polygon
|
|
||||||
28,chill_vendee,entourage,
|
|
||||||
29,chill_vendee,geographical_unit
|
|
||||||
30,chill_vendee,geographical_unit_association
|
|
||||||
31,chill_vendee,mobilite
|
|
||||||
32,chill_vendee,niveauetude
|
|
||||||
33,chill_vendee,security_profile
|
|
||||||
34,chill_vendee,security_profile_action
|
|
||||||
35,chill_vendee,security_profile_jobs
|
|
||||||
36,chill_vendee,situationprofessionelle
|
|
||||||
37,chill_vendee,statutlogement
|
|
||||||
38,chill_vendee,tempsdetravail
|
|
||||||
39,chill_vendee,titredesejour
|
|
||||||
40,chill_vendee,vendee_person
|
|
||||||
41,chill_vendee,vendee_person_mineur
|
|
||||||
42,chill_vendee,vendeeperson_entourage
|
|
||||||
43,chill_vendee,vendeepersonmineur_adressederelais
|
|
||||||
44,public,accompanying_periods_scopes,Services associés aux parcours
|
|
||||||
45,public,activity,Échanges
|
|
||||||
46,public,activity_activityreason,s
|
|
||||||
47,public,activity_person,
|
|
||||||
48,public,activity_storedobject,
|
|
||||||
49,public,activity_thirdparty,
|
|
||||||
50,public,activity_user,
|
|
||||||
51,public,activityreason,Sujets d'échange
|
|
||||||
52,public,activityreasoncategory,Catégories de sujets
|
|
||||||
53,public,activitytpresence,Présence aux échanges
|
|
||||||
54,public,activitytype,Types d'échanges
|
|
||||||
55,public,activitytypecategory,Catégories de types d'échanges
|
|
||||||
56,public,centers,"Centres (territoires, agences, etc.)"
|
|
||||||
57,public,chill_activity_activity_chill_person_socialaction,
|
|
||||||
58,public,chill_activity_activity_chill_person_socialissue
|
|
||||||
59,public,chill_docgen_template,Gabarits de documents
|
|
||||||
60,public,chill_main_address,Adresses
|
|
||||||
61,public,chill_main_address_legacy,Anciennes adresses (dépréciés)
|
|
||||||
62,public,chill_main_address_reference,Adresses de référence
|
|
||||||
63,public,chill_main_civility,Civilités
|
|
||||||
64,public,chill_main_cronjob_execution,Dernière exécution des tâche cron
|
|
||||||
65,public,chill_main_geographical_unit,Unités géographiques
|
|
||||||
66,public,chill_main_geographical_unit_layer,Couches d'unités géographiques
|
|
||||||
67,public,chill_main_location,Localisations
|
|
||||||
68,public,chill_main_location_type,Types de localisations
|
|
||||||
69,public,chill_main_notification,Notifications
|
|
||||||
70,public,chill_main_notification_addresses_unread
|
|
||||||
71,public,chill_main_notification_addresses_user
|
|
||||||
72,public,chill_main_notification_comment,
|
|
||||||
73,public,chill_main_postal_code,Code postaux
|
|
||||||
74,public,chill_main_saved_export,Exports enregistrés
|
|
||||||
75,public,chill_main_user_job,Métiers
|
|
||||||
76,public,chill_main_workflow_entity,Workflows
|
|
||||||
77,public,chill_main_workflow_entity_comment
|
|
||||||
78,public,chill_main_workflow_entity_step,Etapes du workflow
|
|
||||||
79,public,chill_main_workflow_entity_step_cc_user,
|
|
||||||
80,public,chill_main_workflow_entity_step_user
|
|
||||||
81,public,chill_main_workflow_entity_step_user_by_accesskey,
|
|
||||||
82,public,chill_main_workflow_entity_subscriber_to_final,
|
|
||||||
83,public,chill_main_workflow_entity_subscriber_to_step
|
|
||||||
84,public,chill_person_accompanying_period,Parcours d'accompagnement
|
|
||||||
85,public,chill_person_accompanying_period_closingmotive,Motifs de cloture des parcours
|
|
||||||
86,public,chill_person_accompanying_period_comment,Commentaires des parcours
|
|
||||||
87,public,chill_person_accompanying_period_location_history,Historique de la localisatio ndes parcours
|
|
||||||
88,public,chill_person_accompanying_period_origin,Origine des parcours
|
|
||||||
89,public,chill_person_accompanying_period_participation,Appartenance des usagers au parcours
|
|
||||||
90,public,chill_person_accompanying_period_resource,Personnes ressources d'un parcours
|
|
||||||
91,public,chill_person_accompanying_period_social_issues,
|
|
||||||
92,public,chill_person_accompanying_period_step_history
|
|
||||||
93,public,chill_person_accompanying_period_user_history
|
|
||||||
94,public,chill_person_accompanying_period_work,Actions d'accompagnements
|
|
||||||
95,public,chill_person_accompanying_period_work_evaluation,Évaluations (dans les actions d'accompagnements)
|
|
||||||
96,public,chill_person_accompanying_period_work_evaluation_document,Documents des évaluations
|
|
||||||
97,public,chill_person_accompanying_period_work_goal,Objectifs d'une actions
|
|
||||||
98,public,chill_person_accompanying_period_work_goal_result,Objectifs et résultats d'une action
|
|
||||||
99,public,chill_person_accompanying_period_work_person,Usagers associés à une actions
|
|
||||||
100,public,chill_person_accompanying_period_work_referrer,Référents d'une actions
|
|
||||||
101,public,chill_person_accompanying_period_work_result,Résultats d'une action
|
|
||||||
102,public,chill_person_accompanying_period_work_third_party,Tiers traitants d'une action
|
|
||||||
103,public,chill_person_alt_name,"Noms supplémentaires d'un usager (nom marital, etc.)"
|
|
||||||
104,public,chill_person_household,Ménages
|
|
||||||
105,public,chill_person_household_composition,
|
|
||||||
106,public,chill_person_household_composition_type,Types de composition de ménage
|
|
||||||
107,public,chill_person_household_members,Membres du ménages
|
|
||||||
108,public,chill_person_household_position,Positions dans le ménage
|
|
||||||
109,public,chill_person_household_to_addresses,Association adresses - ménages
|
|
||||||
110,public,chill_person_marital_status,Etats civils
|
|
||||||
111,public,chill_person_not_duplicate,
|
|
||||||
112,public,chill_person_person,Usagers
|
|
||||||
113,public,chill_person_person_center_history,Historique des centres d'un usagers
|
|
||||||
114,public,chill_person_persons_to_addresses,Déprécié
|
|
||||||
115,public,chill_person_phone,Numéros d etéléphone supplémentaires d'un usager
|
|
||||||
116,public,chill_person_relations,Types de relations de filiation
|
|
||||||
117,public,chill_person_relationships,Relations de filiations
|
|
||||||
118,public,chill_person_residential_address,Adresses de résidences
|
|
||||||
119,public,chill_person_resource,Personnes ressources (pour les personnes)
|
|
||||||
120,public,chill_person_resource_kind,Type de personnes ressources
|
|
||||||
121,public,chill_person_social_action,Liste des actions d'accompagnement
|
|
||||||
122,public,chill_person_social_action_goal,Objectifs associés à une action
|
|
||||||
123,public,chill_person_social_action_result,Résultats associés à une action
|
|
||||||
124,public,chill_person_social_issue,Problématiques sociales
|
|
||||||
125,public,chill_person_social_work_evaluation,Evaluations disponibles
|
|
||||||
126,public,chill_person_social_work_evaluation_action,Associations entre les évaluations et les actions
|
|
||||||
127,public,chill_person_social_work_goal,Objectifs disponibles pour une actions
|
|
||||||
128,public,chill_person_social_work_goal_result,Objectifs et résultats disponible pour une action
|
|
||||||
129,public,chill_person_social_work_result,Résultats disponibles pour une action
|
|
||||||
130,public,country,Pays
|
|
||||||
131,public,custom_field_long_choice_options,
|
|
||||||
132,public,customfield
|
|
||||||
133,public,customfieldsdefaultgroup
|
|
||||||
134,public,customfieldsgroup
|
|
||||||
135,public,geography_columns,Table liée à postgis
|
|
||||||
136,public,geometry_columns,Table liée à postgis
|
|
||||||
137,public,group_centers,
|
|
||||||
138,public,language,Langues
|
|
||||||
139,public,messenger_messages,Table système
|
|
||||||
140,public,migration_versions,Table système
|
|
||||||
141,public,permission_groups
|
|
||||||
142,public,permissionsgroup_rolescope
|
|
||||||
143,public,persons_spoken_languages
|
|
||||||
144,public,regroupment,Regroupement de centres
|
|
||||||
145,public,regroupment_center,
|
|
||||||
146,public,role_scopes,
|
|
||||||
147,public,scopes,Services
|
|
||||||
148,public,spatial_ref_sys,Table système (postgis)
|
|
||||||
149,public,user_groupcenter,
|
|
||||||
150,public,users,Utilisateurs
|
|
||||||
151,public,view_chill_person_accompanying_period_info,
|
|
||||||
152,public,view_chill_person_current_address
|
|
||||||
153,public,view_chill_person_household_address
|
|
||||||
154,public,view_chill_person_person_center_history_current
|
|
Can't render this file because it has a wrong number of fields in line 28.
|
@@ -36,7 +36,6 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
|
|||||||
Assets <assets.rst>
|
Assets <assets.rst>
|
||||||
Cron Jobs <cronjob.rst>
|
Cron Jobs <cronjob.rst>
|
||||||
Info about entities <entity-info.rst>
|
Info about entities <entity-info.rst>
|
||||||
Info about database (in French) <database-principles.rst>
|
|
||||||
|
|
||||||
Layout and UI
|
Layout and UI
|
||||||
**************
|
**************
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
{% macro table_elements(elements, type) %}
|
{% macro table_elements(elements, family) %}
|
||||||
|
|
||||||
<table class="table table-bordered border-dark budget-table">
|
<table class="table table-bordered border-dark budget-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="{{ type }} el-type">{{ 'Budget element type'|trans }}</th>
|
<th class="{{ family }} el-type">{{ 'Budget element type'|trans }}</th>
|
||||||
<th class="{{ type }}">{{ 'Amount'|trans }}</th>
|
<th class="{{ family }}">{{ 'Amount'|trans }}</th>
|
||||||
<th class="{{ type }}">{{ 'Validity period'|trans }}</th>
|
<th class="{{ family }}">{{ 'Validity period'|trans }}</th>
|
||||||
<th class="{{ type }}"> </th>
|
<th class="{{ family }}"> </th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -39,17 +38,17 @@
|
|||||||
<ul class="record_actions">
|
<ul class="record_actions">
|
||||||
{% if is_granted('CHILL_BUDGET_ELEMENT_SEE', f) %}
|
{% if is_granted('CHILL_BUDGET_ELEMENT_SEE', f) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('chill_budget_' ~ type ~ '_view', { 'id': f.id } ) }}" class="btn btn-sm btn-show"></a>
|
<a href="{{ path('chill_budget_' ~ family ~ '_view', { 'id': f.id } ) }}" class="btn btn-sm btn-show"></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_granted('CHILL_BUDGET_ELEMENT_UPDATE', f) %}
|
{% if is_granted('CHILL_BUDGET_ELEMENT_UPDATE', f) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('chill_budget_' ~ type ~'_edit', { 'id': f.id } ) }}" class="btn btn-sm btn-edit"></a>
|
<a href="{{ path('chill_budget_' ~ family ~'_edit', { 'id': f.id } ) }}" class="btn btn-sm btn-edit"></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_granted('CHILL_BUDGET_ELEMENT_DELETE', f) %}
|
{% if is_granted('CHILL_BUDGET_ELEMENT_DELETE', f) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ path('chill_budget_' ~ type ~ '_delete', { 'id': f.id } ) }}" class="btn btn-sm btn-delete"></a>
|
<a href="{{ path('chill_budget_' ~ family ~ '_delete', { 'id': f.id } ) }}" class="btn btn-sm btn-delete"></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -70,7 +69,7 @@
|
|||||||
</table>
|
</table>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro table_results(actualCharges, actualResources, results) %}
|
{% macro table_results(actualCharges, actualResources) %}
|
||||||
|
|
||||||
{% set totalCharges = 0 %}
|
{% set totalCharges = 0 %}
|
||||||
{% for c in actualCharges %}
|
{% for c in actualCharges %}
|
||||||
@@ -98,20 +97,6 @@
|
|||||||
{{ result|format_currency('EUR') }}
|
{{ result|format_currency('EUR') }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% for result in results %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ result.label }}</td>
|
|
||||||
<td>
|
|
||||||
{% if result.type == 'currency' %}
|
|
||||||
{{ result.result|format_currency('EUR') }}
|
|
||||||
{% elseif result.type == 'percentage' %}
|
|
||||||
{{ result.result|round(2, 'ceil') ~ '%' }}
|
|
||||||
{% else %}
|
|
||||||
{{ result.result|round(2, 'common') }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<h3 class="subtitle">{{ 'Budget calculator'|trans }}</h3>
|
<h3 class="subtitle">{{ 'Budget calculator'|trans }}</h3>
|
||||||
{{ table_results(charges, resources, results) }}
|
{{ table_results(charges, resources) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_granted('CHILL_BUDGET_ELEMENT_CREATE', person) %}
|
{% if is_granted('CHILL_BUDGET_ELEMENT_CREATE', person) %}
|
||||||
|
@@ -46,7 +46,8 @@ class AccompanyingCourseDocumentWorkflowHandler implements EntityWorkflowHandler
|
|||||||
|
|
||||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||||
{
|
{
|
||||||
$course = $this->getRelatedEntity($entityWorkflow)?->getCourse();
|
$course = $this->getRelatedEntity($entityWorkflow)
|
||||||
|
->getCourse();
|
||||||
$persons = [];
|
$persons = [];
|
||||||
|
|
||||||
if (null !== $course) {
|
if (null !== $course) {
|
||||||
|
@@ -19,13 +19,5 @@ interface CronJobInterface
|
|||||||
|
|
||||||
public function getKey(): string;
|
public function getKey(): string;
|
||||||
|
|
||||||
/**
|
public function run(): void;
|
||||||
* Execute the cronjob
|
|
||||||
*
|
|
||||||
* If data is returned, this data is passed as argument on the next execution
|
|
||||||
*
|
|
||||||
* @param array $lastExecutionData the data which was returned from the previous execution
|
|
||||||
* @return array|null optionally return an array with the same data than the previous execution
|
|
||||||
*/
|
|
||||||
public function run(array $lastExecutionData): null|array;
|
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@ namespace Chill\MainBundle\Cron;
|
|||||||
use Chill\MainBundle\Entity\CronJobExecution;
|
use Chill\MainBundle\Entity\CronJobExecution;
|
||||||
use Chill\MainBundle\Repository\CronJobExecutionRepositoryInterface;
|
use Chill\MainBundle\Repository\CronJobExecutionRepositoryInterface;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\DBAL\Types\Types;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -47,8 +46,6 @@ class CronManager implements CronManagerInterface
|
|||||||
|
|
||||||
private const UPDATE_BEFORE_EXEC = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastStart = :now WHERE cr.key = :key';
|
private const UPDATE_BEFORE_EXEC = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastStart = :now WHERE cr.key = :key';
|
||||||
|
|
||||||
private const UPDATE_LAST_EXECUTION_DATA = 'UPDATE ' . CronJobExecution::class . ' cr SET cr.lastExecutionData = :data WHERE cr.key = :key';
|
|
||||||
|
|
||||||
private CronJobExecutionRepositoryInterface $cronJobExecutionRepository;
|
private CronJobExecutionRepositoryInterface $cronJobExecutionRepository;
|
||||||
|
|
||||||
private EntityManagerInterface $entityManager;
|
private EntityManagerInterface $entityManager;
|
||||||
@@ -88,9 +85,6 @@ class CronManager implements CronManagerInterface
|
|||||||
foreach ($orderedJobs as $job) {
|
foreach ($orderedJobs as $job) {
|
||||||
if ($job->canRun($lasts[$job->getKey()] ?? null)) {
|
if ($job->canRun($lasts[$job->getKey()] ?? null)) {
|
||||||
if (array_key_exists($job->getKey(), $lasts)) {
|
if (array_key_exists($job->getKey(), $lasts)) {
|
||||||
|
|
||||||
$executionData = $lasts[$job->getKey()]->getLastExecutionData();
|
|
||||||
|
|
||||||
$this->entityManager
|
$this->entityManager
|
||||||
->createQuery(self::UPDATE_BEFORE_EXEC)
|
->createQuery(self::UPDATE_BEFORE_EXEC)
|
||||||
->setParameters([
|
->setParameters([
|
||||||
@@ -102,17 +96,12 @@ class CronManager implements CronManagerInterface
|
|||||||
$execution = new CronJobExecution($job->getKey());
|
$execution = new CronJobExecution($job->getKey());
|
||||||
$this->entityManager->persist($execution);
|
$this->entityManager->persist($execution);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
$executionData = $execution->getLastExecutionData();
|
|
||||||
}
|
}
|
||||||
$this->entityManager->clear();
|
$this->entityManager->clear();
|
||||||
|
|
||||||
// note: at this step, the entity manager does not have any entity CronJobExecution
|
|
||||||
// into his internal memory
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->logger->info(sprintf('%sWill run job', self::LOG_PREFIX), ['job' => $job->getKey()]);
|
$this->logger->info(sprintf('%sWill run job', self::LOG_PREFIX), ['job' => $job->getKey()]);
|
||||||
$result = $job->run($executionData);
|
$job->run();
|
||||||
|
|
||||||
$this->entityManager
|
$this->entityManager
|
||||||
->createQuery(self::UPDATE_AFTER_EXEC)
|
->createQuery(self::UPDATE_AFTER_EXEC)
|
||||||
@@ -123,14 +112,6 @@ class CronManager implements CronManagerInterface
|
|||||||
])
|
])
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
if (null !== $result) {
|
|
||||||
$this->entityManager
|
|
||||||
->createQuery(self::UPDATE_LAST_EXECUTION_DATA)
|
|
||||||
->setParameter('data', $result, Types::JSON)
|
|
||||||
->setParameter('key', $job->getKey(), Types::STRING)
|
|
||||||
->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->logger->info(sprintf('%sSuccessfully run job', self::LOG_PREFIX), ['job' => $job->getKey()]);
|
$this->logger->info(sprintf('%sSuccessfully run job', self::LOG_PREFIX), ['job' => $job->getKey()]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -152,7 +133,7 @@ class CronManager implements CronManagerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{0: array<CronJobInterface>, 1: array<string, CronJobExecution>}
|
* @return array<0: CronJobInterface[], 1: array<string, CronJobExecution>>
|
||||||
*/
|
*/
|
||||||
private function getOrderedJobs(): array
|
private function getOrderedJobs(): array
|
||||||
{
|
{
|
||||||
@@ -193,7 +174,7 @@ class CronManager implements CronManagerInterface
|
|||||||
{
|
{
|
||||||
foreach ($this->jobs as $job) {
|
foreach ($this->jobs as $job) {
|
||||||
if ($job->getKey() === $forceJob) {
|
if ($job->getKey() === $forceJob) {
|
||||||
$job->run([]);
|
$job->run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ class CronJobExecution
|
|||||||
private string $key;
|
private string $key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @var DateTimeImmutable
|
||||||
* @ORM\Column(type="datetime_immutable", nullable=true, options={"default": null})
|
* @ORM\Column(type="datetime_immutable", nullable=true, options={"default": null})
|
||||||
*/
|
*/
|
||||||
private ?DateTimeImmutable $lastEnd = null;
|
private ?DateTimeImmutable $lastEnd = null;
|
||||||
@@ -45,11 +46,6 @@ class CronJobExecution
|
|||||||
*/
|
*/
|
||||||
private ?int $lastStatus = null;
|
private ?int $lastStatus = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* @ORM\Column(type="json", options={"default": "'{}'::jsonb", "jsonb": true})
|
|
||||||
*/
|
|
||||||
private array $lastExecutionData = [];
|
|
||||||
|
|
||||||
public function __construct(string $key)
|
public function __construct(string $key)
|
||||||
{
|
{
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
@@ -96,16 +92,4 @@ class CronJobExecution
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLastExecutionData(): array
|
|
||||||
{
|
|
||||||
return $this->lastExecutionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLastExecutionData(array $lastExecutionData): CronJobExecution
|
|
||||||
{
|
|
||||||
$this->lastExecutionData = $lastExecutionData;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ declare(strict_types=1);
|
|||||||
namespace Chill\MainBundle\Form\Type\Listing;
|
namespace Chill\MainBundle\Form\Type\Listing;
|
||||||
|
|
||||||
use Chill\MainBundle\Form\Type\ChillDateType;
|
use Chill\MainBundle\Form\Type\ChillDateType;
|
||||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
|
||||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
@@ -115,28 +114,6 @@ final class FilterOrderType extends \Symfony\Component\Form\AbstractType
|
|||||||
|
|
||||||
$builder->add($singleCheckBoxBuilder);
|
$builder->add($singleCheckBoxBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([] !== $helper->getUserPickers()) {
|
|
||||||
$userPickersBuilder = $builder->create('user_pickers', null, ['compound' => true]);
|
|
||||||
|
|
||||||
foreach ($helper->getUserPickers() as $name => [
|
|
||||||
'label' => $label, 'options' => $opts
|
|
||||||
]) {
|
|
||||||
|
|
||||||
$userPickersBuilder->add(
|
|
||||||
$name,
|
|
||||||
PickUserDynamicType::class,
|
|
||||||
[
|
|
||||||
'multiple' => true,
|
|
||||||
'label' => $label,
|
|
||||||
...$opts,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->add($userPickersBuilder);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function buildCheckboxChoices(array $choices, array $trans = []): array
|
public static function buildCheckboxChoices(array $choices, array $trans = []): array
|
||||||
|
@@ -66,10 +66,6 @@ export default {
|
|||||||
return appMessages.fr.the_activity;
|
return appMessages.fr.the_activity;
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||||
return appMessages.fr.the_course;
|
return appMessages.fr.the_course;
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork':
|
|
||||||
return appMessages.fr.the_action;
|
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument':
|
|
||||||
return appMessages.fr.the_evaluation_document;
|
|
||||||
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
||||||
return appMessages.fr.the_workflow;
|
return appMessages.fr.the_workflow;
|
||||||
default:
|
default:
|
||||||
@@ -82,10 +78,6 @@ export default {
|
|||||||
return `/fr/activity/${n.relatedEntityId}/show`
|
return `/fr/activity/${n.relatedEntityId}/show`
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod':
|
||||||
return `/fr/parcours/${n.relatedEntityId}`
|
return `/fr/parcours/${n.relatedEntityId}`
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork':
|
|
||||||
return `/fr/person/accompanying-period/work/${n.relatedEntityId}/show`
|
|
||||||
case 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument':
|
|
||||||
return `/fr/person/accompanying-period/work/evaluation/document/${n.relatedEntityId}/show`
|
|
||||||
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
case 'Chill\\MainBundle\\Entity\\Workflow\\EntityWorkflow':
|
||||||
return `/fr/main/workflow/${n.relatedEntityId}/show`
|
return `/fr/main/workflow/${n.relatedEntityId}/show`
|
||||||
default:
|
default:
|
||||||
|
@@ -46,7 +46,6 @@ const appMessages = {
|
|||||||
the_course: "le parcours",
|
the_course: "le parcours",
|
||||||
the_action: "l'action",
|
the_action: "l'action",
|
||||||
the_evaluation: "l'évaluation",
|
the_evaluation: "l'évaluation",
|
||||||
the_evaluation_document: "le document",
|
|
||||||
the_task: "la tâche",
|
the_task: "la tâche",
|
||||||
the_workflow: "le workflow",
|
the_workflow: "le workflow",
|
||||||
StartDate: "Date d'ouverture",
|
StartDate: "Date d'ouverture",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<span v-if="data.working_ref_status === 'to_review'" class="badge bg-danger address-details-button-warning">L'adresse de référence a été modifiée</span>
|
<span v-if="data.working_ref_status === 'to_review'" class="badge bg-danger address-details-button-warning">L'adresse de référence a été modifiée</span>
|
||||||
<a v-if="data.loading === false" @click.prevent="clickOrOpen" class="btn btn-sm address-details-button" title="Plus de détails">
|
<a v-if="data.loading === false" @click.prevent="clickOrOpen" class="btn btn-misc address-details-button">
|
||||||
<span class="fa fa-map-o"></span>
|
<span class="fa fa-map"></span> <!-- button -->
|
||||||
</a>
|
</a>
|
||||||
<span v-if="data.loading" class="fa fa-spin fa-spinner "></span>
|
<span v-if="data.loading" class="fa fa-spin fa-spinner "></span>
|
||||||
<AddressModal :address="data.working_address" @update-address="onUpdateAddress" ref="address_modal"></AddressModal>
|
<AddressModal :address="data.working_address" @update-address="onUpdateAddress" ref="address_modal"></AddressModal>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<component :is="component" class="chill-entity entity-address">
|
<component :is="component" class="chill-entity entity-address my-3">
|
||||||
|
|
||||||
<component :is="component" class="address" :class="multiline">
|
<component :is="component" class="address" :class="multiline">
|
||||||
|
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if form.dateRanges is defined %}
|
{% if form.dateRanges is defined %}
|
||||||
{% set btnSubmit = 1 %}
|
{% set btnSubmit = 1 %}
|
||||||
{% if form.dateRanges|length > 0 %}
|
{% if form.dateRanges|length > 0 %}
|
||||||
@@ -41,7 +40,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if form.checkboxes is defined %}
|
{% if form.checkboxes is defined %}
|
||||||
{% set btnSubmit = 1 %}
|
{% set btnSubmit = 1 %}
|
||||||
{% if form.checkboxes|length > 0 %}
|
{% if form.checkboxes|length > 0 %}
|
||||||
@@ -58,7 +56,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if form.entity_choices is defined %}
|
{% if form.entity_choices is defined %}
|
||||||
{% set btnSubmit = 1 %}
|
{% set btnSubmit = 1 %}
|
||||||
{% if form.entity_choices |length > 0 %}
|
{% if form.entity_choices |length > 0 %}
|
||||||
@@ -77,25 +74,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if form.user_pickers is defined %}
|
|
||||||
{% set btnSubmit = 1 %}
|
|
||||||
{% if form.user_pickers.children|length > 0 %}
|
|
||||||
{% for name, options in form.user_pickers %}
|
|
||||||
<div class="row my-2">
|
|
||||||
{% if form.user_pickers[name].vars.label is not same as(false) %}
|
|
||||||
{{ form_label(form.user_pickers[name]) }}
|
|
||||||
{% else %}
|
|
||||||
{{ form_label(form.user_pickers[name].vars.label) }}
|
|
||||||
{% endif %}
|
|
||||||
<div class="col-sm-8 pt-2">
|
|
||||||
{{ form_widget(form.user_pickers[name]) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if form.single_checkboxes is defined %}
|
{% if form.single_checkboxes is defined %}
|
||||||
{% set btnSubmit = 1 %}
|
{% set btnSubmit = 1 %}
|
||||||
{% for name, _o in form.single_checkboxes %}
|
{% for name, _o in form.single_checkboxes %}
|
||||||
@@ -113,10 +91,8 @@
|
|||||||
<button type="submit" class="btn btn-sm btn-misc"><i class="fa fa-fw fa-filter"></i>{{ 'Filter'|trans }}</button>
|
<button type="submit" class="btn btn-sm btn-misc"><i class="fa fa-fw fa-filter"></i>{{ 'Filter'|trans }}</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if active|length > 0 %}
|
{% if active|length > 0 %}
|
||||||
<div class="activeFilters mt-3">
|
<div class="activeFilters mt-3">
|
||||||
{% for f in active %}
|
{% for f in active %}
|
||||||
|
@@ -1,144 +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\MainBundle\Service\AddressGeographicalUnit;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Connection;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
|
|
||||||
final readonly class CollateAddressWithReferenceOrPostalCode implements CollateAddressWithReferenceOrPostalCodeInterface
|
|
||||||
{
|
|
||||||
private const LOG_PREFIX = '[collate addresses] ';
|
|
||||||
/**
|
|
||||||
* For the address having an "invented" postal code, find the postal code "reference" with the same code,
|
|
||||||
* and the most similar name. When two reference code match, we add
|
|
||||||
*
|
|
||||||
* This query intentionally includes also address with reference, as the reference may be wrong.
|
|
||||||
*/
|
|
||||||
private const FORCE_ORIGINAL_POSTAL_CODE = <<<'SQL'
|
|
||||||
WITH recollate AS (
|
|
||||||
SELECT * FROM (
|
|
||||||
SELECT cma.id AS address_id, cmpc.id, cmpc.label, cmpc.code, cmpc_reference.id AS cmpc_reference_id, cmpc_reference.label, cmpc_reference.code,
|
|
||||||
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY(cmpc.label, cmpc_reference.label) DESC, cmpc_reference.id ASC) AS ranked
|
|
||||||
FROM
|
|
||||||
chill_main_address cma JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
|
|
||||||
chill_main_postal_code cmpc_reference
|
|
||||||
WHERE
|
|
||||||
-- use only postal code which are reference
|
|
||||||
cmpc_reference.id != cmpc.id AND cmpc_reference.origin = 0
|
|
||||||
-- only where cmpc is created manually
|
|
||||||
AND cmpc.origin != 0
|
|
||||||
-- only when postal code match
|
|
||||||
AND TRIM(REPLACE(LOWER(cmpc.code), ' ', '')) = LOWER(cmpc_reference.code)
|
|
||||||
AND cmpc.country_id = cmpc_reference.country_id
|
|
||||||
AND cma.id > :since_id -- to set the first id
|
|
||||||
) sq
|
|
||||||
WHERE ranked = 1)
|
|
||||||
UPDATE chill_main_address SET postcode_id = cmpc_reference_id FROM recollate WHERE recollate.address_id = chill_main_address.id;
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* associate the address with the most similar address reference.
|
|
||||||
*
|
|
||||||
* This query intentionally ignores the existing addressreference_id, to let fixing the address match the
|
|
||||||
* most similar address reference.
|
|
||||||
*/
|
|
||||||
private const FORCE_MOST_SIMILAR_ADDRESS_REFERENCE = <<<'SQL'
|
|
||||||
WITH recollate AS (
|
|
||||||
SELECT * FROM (
|
|
||||||
SELECT cma.id AS address_id, cma.streetnumber, cma.street, cmpc.code, cmpc.label, cmar.id AS address_reference_id, cmar.streetnumber, cmar.street, cmpc_reference.code, cmpc_reference.label,
|
|
||||||
similarity(cma.street, cmar.street),
|
|
||||||
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY (cma.street, cmar.street) DESC, SIMILARITY (cma.streetnumber, cmar.streetnumber), cmar.id ASC) AS ranked
|
|
||||||
FROM
|
|
||||||
chill_main_address cma
|
|
||||||
JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
|
|
||||||
chill_main_address_reference cmar JOIN chill_main_postal_code cmpc_reference ON cmar.postcode_id = cmpc_reference.id
|
|
||||||
WHERE
|
|
||||||
-- only if cmpc is a reference (must be matched before executing this query)
|
|
||||||
cma.postcode_id = cmar.postcode_id
|
|
||||||
-- join cmpc to cma
|
|
||||||
AND SIMILARITY(LOWER(cma.street), LOWER(cmar.street)) > 0.6 AND LOWER(cma.streetnumber) = LOWER(cmar.streetnumber)
|
|
||||||
-- only addresses which match the address reference - let the user decide if the reference has changed
|
|
||||||
AND cma.refstatus = 'match'
|
|
||||||
-- only the most recent
|
|
||||||
AND cma.id > :since_id
|
|
||||||
) AS sq
|
|
||||||
WHERE ranked = 1
|
|
||||||
)
|
|
||||||
UPDATE chill_main_address SET addressreference_id = recollate.address_reference_id FROM recollate WHERE chill_main_address.id = recollate.address_id;
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
private const UPDATE_POINT = <<<'SQL'
|
|
||||||
WITH address_geom AS (
|
|
||||||
SELECT cma.id AS address_id, COALESCE(cmar.point, cmpc.center) AS point
|
|
||||||
FROM chill_main_address cma
|
|
||||||
LEFT JOIN chill_main_address_reference cmar ON cma.addressreference_id = cmar.id
|
|
||||||
LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id
|
|
||||||
WHERE cma.id > :since_id
|
|
||||||
)
|
|
||||||
UPDATE chill_main_address SET point = address_geom.point FROM address_geom WHERE address_geom.address_id = chill_main_address.id
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
private const MAX_ADDRESS_ID = <<<'SQL'
|
|
||||||
SELECT MAX(id) AS max_id FROM chill_main_address;
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private Connection $connection,
|
|
||||||
private LoggerInterface $logger,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function __invoke(int $sinceId = 0): int
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
[
|
|
||||||
$postCodeSetReferenceFromMostSimilar,
|
|
||||||
$addressReferenceMatch,
|
|
||||||
$pointUpdates,
|
|
||||||
$lastId,
|
|
||||||
] = $this->connection->transactional(function () use ($sinceId) {
|
|
||||||
$postCodeSetReferenceFromMostSimilar = $this->connection->executeStatement(self::FORCE_ORIGINAL_POSTAL_CODE, ['since_id' => $sinceId]);
|
|
||||||
$addressReferenceMatch = $this->connection->executeStatement(self::FORCE_MOST_SIMILAR_ADDRESS_REFERENCE, ['since_id' => $sinceId]);
|
|
||||||
$pointUpdates = $this->connection->executeStatement(self::UPDATE_POINT, ['since_id' => $sinceId]);
|
|
||||||
$lastId = $this->connection->fetchOne(self::MAX_ADDRESS_ID);
|
|
||||||
|
|
||||||
return [
|
|
||||||
$postCodeSetReferenceFromMostSimilar,
|
|
||||||
$addressReferenceMatch,
|
|
||||||
$pointUpdates,
|
|
||||||
$lastId,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
$this->logger->error(self::LOG_PREFIX . "error while re-collating addresses", [
|
|
||||||
'message' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString()
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->logger->info(self::LOG_PREFIX . "Collate the addresses with reference", [
|
|
||||||
'set_postcode_from_most_similar' => $postCodeSetReferenceFromMostSimilar,
|
|
||||||
'address_reference_match' => $addressReferenceMatch,
|
|
||||||
'point_update' => $pointUpdates,
|
|
||||||
'since_id' => $sinceId,
|
|
||||||
'last_id' => $lastId,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $lastId;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,50 +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\MainBundle\Service\AddressGeographicalUnit;
|
|
||||||
|
|
||||||
use Chill\MainBundle\Cron\CronJobInterface;
|
|
||||||
use Chill\MainBundle\Entity\CronJobExecution;
|
|
||||||
use Symfony\Component\Clock\ClockInterface;
|
|
||||||
|
|
||||||
final readonly class CollateAddressWithReferenceOrPostalCodeCronJob implements CronJobInterface
|
|
||||||
{
|
|
||||||
private const LAST_MAX_ID = 'last-max-id';
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private ClockInterface $clock,
|
|
||||||
private CollateAddressWithReferenceOrPostalCodeInterface $collateAddressWithReferenceOrPostalCode,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canRun(?CronJobExecution $cronJobExecution): bool
|
|
||||||
{
|
|
||||||
if (null === $cronJobExecution) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$now = $this->clock->now();
|
|
||||||
|
|
||||||
return $now->sub(new \DateInterval('PT6H')) > $cronJobExecution->getLastStart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getKey(): string
|
|
||||||
{
|
|
||||||
return 'collate-address';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
|
||||||
{
|
|
||||||
$maxId = ($this->collateAddressWithReferenceOrPostalCode)($lastExecutionData[self::LAST_MAX_ID] ?? 0);
|
|
||||||
|
|
||||||
return [self::LAST_MAX_ID => $maxId];
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,20 +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\MainBundle\Service\AddressGeographicalUnit;
|
|
||||||
|
|
||||||
interface CollateAddressWithReferenceOrPostalCodeInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @throws \Throwable
|
|
||||||
*/
|
|
||||||
public function __invoke(int $sinceId = 0): int;
|
|
||||||
}
|
|
@@ -49,10 +49,8 @@ class RefreshAddressToGeographicalUnitMaterializedViewCronJob implements CronJob
|
|||||||
return 'refresh-materialized-view-address-to-geog-units';
|
return 'refresh-materialized-view-address-to-geog-units';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
public function run(): void
|
||||||
{
|
{
|
||||||
$this->connection->executeQuery('REFRESH MATERIALIZED VIEW view_chill_main_address_geographical_unit');
|
$this->connection->executeQuery('REFRESH MATERIALIZED VIEW view_chill_main_address_geographical_unit');
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Templating\Listing;
|
namespace Chill\MainBundle\Templating\Listing;
|
||||||
|
|
||||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
@@ -21,7 +20,6 @@ final readonly class FilterOrderGetActiveFilterHelper
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private TranslatorInterface $translator,
|
private TranslatorInterface $translator,
|
||||||
private PropertyAccessorInterface $propertyAccessor,
|
private PropertyAccessorInterface $propertyAccessor,
|
||||||
private UserRender $userRender,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +73,6 @@ final readonly class FilterOrderGetActiveFilterHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($filterOrderHelper->getUserPickers() as $name => ['label' => $label, 'options' => $options]) {
|
|
||||||
foreach ($filterOrderHelper->getUserPickerData($name) as $user) {
|
|
||||||
$result[] = ['value' => $this->userRender->renderString($user, []), 'label' => (string) $label, 'position' => FilterOrderPositionEnum::UserPicker->value, 'name' => $name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($filterOrderHelper->getSingleCheckbox() as $name => ['label' => $label]) {
|
foreach ($filterOrderHelper->getSingleCheckbox() as $name => ['label' => $label]) {
|
||||||
if (true === $filterOrderHelper->getSingleCheckboxData($name)) {
|
if (true === $filterOrderHelper->getSingleCheckboxData($name)) {
|
||||||
$result[] = ['label' => '', 'value' => $this->translator->trans($label), 'position' => FilterOrderPositionEnum::SingleCheckbox->value, 'name' => $name];
|
$result[] = ['label' => '', 'value' => $this->translator->trans($label), 'position' => FilterOrderPositionEnum::SingleCheckbox->value, 'name' => $name];
|
||||||
|
@@ -11,14 +11,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\MainBundle\Templating\Listing;
|
namespace Chill\MainBundle\Templating\Listing;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Chill\MainBundle\Form\Type\Listing\FilterOrderType;
|
use Chill\MainBundle\Form\Type\Listing\FilterOrderType;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Symfony\Component\Form\FormFactoryInterface;
|
use Symfony\Component\Form\FormFactoryInterface;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
use function array_merge;
|
use function array_merge;
|
||||||
|
use function count;
|
||||||
|
|
||||||
final class FilterOrderHelper
|
final class FilterOrderHelper
|
||||||
{
|
{
|
||||||
@@ -46,10 +51,6 @@ final class FilterOrderHelper
|
|||||||
*/
|
*/
|
||||||
private array $entityChoices = [];
|
private array $entityChoices = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, array{label: string, options: array}>
|
|
||||||
*/
|
|
||||||
private array $userPickers = [];
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly FormFactoryInterface $formFactory,
|
private readonly FormFactoryInterface $formFactory,
|
||||||
@@ -79,14 +80,6 @@ final class FilterOrderHelper
|
|||||||
return $this->entityChoices;
|
return $this->entityChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addUserPicker(string $name, ?string $label = null, array $options = []): self
|
|
||||||
{
|
|
||||||
$this->userPickers[$name] = ['label' => $label, 'options' => $options];
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = [], array $options = []): self
|
public function addCheckbox(string $name, array $choices, ?array $default = [], ?array $trans = [], array $options = []): self
|
||||||
{
|
{
|
||||||
if ([] === $trans) {
|
if ([] === $trans) {
|
||||||
@@ -121,19 +114,6 @@ final class FilterOrderHelper
|
|||||||
->handleRequest($this->requestStack->getCurrentRequest());
|
->handleRequest($this->requestStack->getCurrentRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserPickers(): array
|
|
||||||
{
|
|
||||||
return $this->userPickers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return list<User>
|
|
||||||
*/
|
|
||||||
public function getUserPickerData(string $name): array
|
|
||||||
{
|
|
||||||
return $this->getFormData()['user_pickers'][$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasCheckboxData(string $name): bool
|
public function hasCheckboxData(string $name): bool
|
||||||
{
|
{
|
||||||
return array_key_exists($name, $this->checkboxes);
|
return array_key_exists($name, $this->checkboxes);
|
||||||
@@ -223,8 +203,7 @@ final class FilterOrderHelper
|
|||||||
'checkboxes' => [],
|
'checkboxes' => [],
|
||||||
'dateRanges' => [],
|
'dateRanges' => [],
|
||||||
'single_checkboxes' => [],
|
'single_checkboxes' => [],
|
||||||
'entity_choices' => [],
|
'entity_choices' => []
|
||||||
'user_pickers' => []
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($this->hasSearchBox()) {
|
if ($this->hasSearchBox()) {
|
||||||
@@ -248,10 +227,6 @@ final class FilterOrderHelper
|
|||||||
$r['entity_choices'][$name] = ($c['options']['multiple'] ?? true) ? [] : null;
|
$r['entity_choices'][$name] = ($c['options']['multiple'] ?? true) ? [] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->userPickers as $name => $u) {
|
|
||||||
$r['user_pickers'][$name] = ($u['options']['multiple'] ?? true) ? [] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $r;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,11 +39,6 @@ class FilterOrderHelperBuilder
|
|||||||
*/
|
*/
|
||||||
private array $entityChoices = [];
|
private array $entityChoices = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, array{label: string, options: array}>
|
|
||||||
*/
|
|
||||||
private array $userPickers = [];
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
FormFactoryInterface $formFactory,
|
FormFactoryInterface $formFactory,
|
||||||
RequestStack $requestStack,
|
RequestStack $requestStack,
|
||||||
@@ -90,13 +85,6 @@ class FilterOrderHelperBuilder
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addUserPicker(string $name, ?string $label = null, ?array $options = []): self
|
|
||||||
{
|
|
||||||
$this->userPickers[$name] = ['label' => $label, 'options' => $options];
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function build(): FilterOrderHelper
|
public function build(): FilterOrderHelper
|
||||||
{
|
{
|
||||||
$helper = new FilterOrderHelper(
|
$helper = new FilterOrderHelper(
|
||||||
@@ -138,17 +126,6 @@ class FilterOrderHelperBuilder
|
|||||||
$helper->addDateRange($name, $label, $from, $to);
|
$helper->addDateRange($name, $label, $from, $to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach (
|
|
||||||
$this->userPickers as $name => [
|
|
||||||
'label' => $label,
|
|
||||||
'options' => $options
|
|
||||||
]
|
|
||||||
) {
|
|
||||||
$helper->addUserPicker($name, $label, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $helper;
|
return $helper;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,5 +18,4 @@ enum FilterOrderPositionEnum: string
|
|||||||
case DateRange = 'date_range';
|
case DateRange = 'date_range';
|
||||||
case EntityChoice = 'entity_choice';
|
case EntityChoice = 'entity_choice';
|
||||||
case SingleCheckbox = 'single_checkbox';
|
case SingleCheckbox = 'single_checkbox';
|
||||||
case UserPicker = 'user_picker';
|
|
||||||
}
|
}
|
||||||
|
@@ -1,87 +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 Cron;
|
|
||||||
|
|
||||||
use Chill\MainBundle\Cron\CronJobInterface;
|
|
||||||
use Chill\MainBundle\Cron\CronManager;
|
|
||||||
use Chill\MainBundle\Entity\CronJobExecution;
|
|
||||||
use Chill\MainBundle\Repository\CronJobExecutionRepository;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
|
||||||
use Psr\Log\NullLogger;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
class CronJobDatabaseInteractionTest extends KernelTestCase
|
|
||||||
{
|
|
||||||
use ProphecyTrait;
|
|
||||||
|
|
||||||
private EntityManagerInterface $entityManager;
|
|
||||||
|
|
||||||
private CronJobExecutionRepository $cronJobExecutionRepository;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
self::bootKernel();
|
|
||||||
$this->entityManager = self::$container->get(EntityManagerInterface::class);
|
|
||||||
$this->cronJobExecutionRepository = self::$container->get(CronJobExecutionRepository::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCompleteLifeCycle(): void
|
|
||||||
{
|
|
||||||
$cronjob = $this->prophesize(CronJobInterface::class);
|
|
||||||
$cronjob->canRun(null)->willReturn(true);
|
|
||||||
$cronjob->canRun(Argument::type(CronJobExecution::class))->willReturn(true);
|
|
||||||
$cronjob->getKey()->willReturn('test-with-data');
|
|
||||||
$cronjob->run([])->willReturn(['test' => 'execution-0']);
|
|
||||||
$cronjob->run(['test' => 'execution-0'])->willReturn(['test' => 'execution-1']);
|
|
||||||
|
|
||||||
$cronjob->run([])->shouldBeCalledOnce();
|
|
||||||
$cronjob->run(['test' => 'execution-0'])->shouldBeCalledOnce();
|
|
||||||
|
|
||||||
$manager = new CronManager(
|
|
||||||
$this->cronJobExecutionRepository,
|
|
||||||
$this->entityManager,
|
|
||||||
[$cronjob->reveal()],
|
|
||||||
new NullLogger()
|
|
||||||
);
|
|
||||||
|
|
||||||
// run a first time
|
|
||||||
$manager->run();
|
|
||||||
|
|
||||||
// run a second time
|
|
||||||
$manager->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class JobWithReturn implements CronJobInterface
|
|
||||||
{
|
|
||||||
public function canRun(?CronJobExecution $cronJobExecution): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getKey(): string
|
|
||||||
{
|
|
||||||
return 'with-data';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
|
||||||
{
|
|
||||||
return ['data' => 'test'];
|
|
||||||
}
|
|
||||||
}
|
|
@@ -40,7 +40,7 @@ final class CronManagerTest extends TestCase
|
|||||||
$jobToExecute = $this->prophesize(CronJobInterface::class);
|
$jobToExecute = $this->prophesize(CronJobInterface::class);
|
||||||
$jobToExecute->getKey()->willReturn('to-exec');
|
$jobToExecute->getKey()->willReturn('to-exec');
|
||||||
$jobToExecute->canRun(Argument::type(CronJobExecution::class))->willReturn(true);
|
$jobToExecute->canRun(Argument::type(CronJobExecution::class))->willReturn(true);
|
||||||
$jobToExecute->run([])->shouldBeCalled();
|
$jobToExecute->run()->shouldBeCalled();
|
||||||
|
|
||||||
$executions = [
|
$executions = [
|
||||||
['key' => $jobOld1->getKey(), 'lastStart' => new DateTimeImmutable('yesterday'), 'lastEnd' => new DateTimeImmutable('1 hours ago'), 'lastStatus' => CronJobExecution::SUCCESS],
|
['key' => $jobOld1->getKey(), 'lastStart' => new DateTimeImmutable('yesterday'), 'lastEnd' => new DateTimeImmutable('1 hours ago'), 'lastStatus' => CronJobExecution::SUCCESS],
|
||||||
@@ -64,7 +64,7 @@ final class CronManagerTest extends TestCase
|
|||||||
$jobAlreadyExecuted = new JobCanRun('k');
|
$jobAlreadyExecuted = new JobCanRun('k');
|
||||||
$jobNeverExecuted = $this->prophesize(CronJobInterface::class);
|
$jobNeverExecuted = $this->prophesize(CronJobInterface::class);
|
||||||
$jobNeverExecuted->getKey()->willReturn('never-executed');
|
$jobNeverExecuted->getKey()->willReturn('never-executed');
|
||||||
$jobNeverExecuted->run([])->shouldBeCalled();
|
$jobNeverExecuted->run()->shouldBeCalled();
|
||||||
$jobNeverExecuted->canRun(null)->willReturn(true);
|
$jobNeverExecuted->canRun(null)->willReturn(true);
|
||||||
|
|
||||||
$executions = [
|
$executions = [
|
||||||
@@ -86,7 +86,7 @@ final class CronManagerTest extends TestCase
|
|||||||
$jobAlreadyExecuted = new JobCanRun('k');
|
$jobAlreadyExecuted = new JobCanRun('k');
|
||||||
$jobNeverExecuted = $this->prophesize(CronJobInterface::class);
|
$jobNeverExecuted = $this->prophesize(CronJobInterface::class);
|
||||||
$jobNeverExecuted->getKey()->willReturn('never-executed');
|
$jobNeverExecuted->getKey()->willReturn('never-executed');
|
||||||
$jobNeverExecuted->run([])->shouldBeCalled();
|
$jobNeverExecuted->run()->shouldBeCalled();
|
||||||
$jobNeverExecuted->canRun(null)->willReturn(true);
|
$jobNeverExecuted->canRun(null)->willReturn(true);
|
||||||
|
|
||||||
$executions = [
|
$executions = [
|
||||||
@@ -178,9 +178,8 @@ class JobCanRun implements CronJobInterface
|
|||||||
return $this->key;
|
return $this->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
public function run(): void
|
||||||
{
|
{
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +195,7 @@ class JobCannotRun implements CronJobInterface
|
|||||||
return 'job-b';
|
return 'job-b';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
public function run(): void
|
||||||
{
|
{
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,68 +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 Services\AddressGeographicalUnit;
|
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\CronJobExecution;
|
|
||||||
use Chill\MainBundle\Service\AddressGeographicalUnit\CollateAddressWithReferenceOrPostalCodeCronJob;
|
|
||||||
use Chill\MainBundle\Service\AddressGeographicalUnit\CollateAddressWithReferenceOrPostalCodeInterface;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
|
||||||
use Symfony\Component\Clock\MockClock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
class CollateAddressWithReferenceOrPostalCodeCronJobTest extends TestCase
|
|
||||||
{
|
|
||||||
use ProphecyTrait;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider provideDataCanRun
|
|
||||||
*/
|
|
||||||
public function testCanRun(\DateTimeImmutable $now, ?\DateTimeImmutable $lastExecution, bool $expected): void
|
|
||||||
{
|
|
||||||
$execution = match ($lastExecution) {
|
|
||||||
null => null,
|
|
||||||
default => (new CronJobExecution('collate-address'))->setLastStart($lastExecution),
|
|
||||||
};
|
|
||||||
|
|
||||||
$clock = new MockClock($now);
|
|
||||||
$collator = $this->prophesize(CollateAddressWithReferenceOrPostalCodeInterface::class);
|
|
||||||
|
|
||||||
$job = new CollateAddressWithReferenceOrPostalCodeCronJob($clock, $collator->reveal());
|
|
||||||
|
|
||||||
self::assertEquals($expected, $job->canRun($execution));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRun(): void
|
|
||||||
{
|
|
||||||
$clock = new MockClock();
|
|
||||||
$collator = $this->prophesize(CollateAddressWithReferenceOrPostalCodeInterface::class);
|
|
||||||
$collator->__invoke(0)->shouldBeCalledOnce();
|
|
||||||
$collator->__invoke(0)->willReturn(1);
|
|
||||||
|
|
||||||
$job = new CollateAddressWithReferenceOrPostalCodeCronJob($clock, $collator->reveal());
|
|
||||||
|
|
||||||
$actual = $job->run(['last-max-id' => 0]);
|
|
||||||
self::assertEquals(['last-max-id' => 1], $actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function provideDataCanRun(): iterable
|
|
||||||
{
|
|
||||||
yield [new \DateTimeImmutable('2023-07-10T12:00:00'), new \DateTimeImmutable('2023-07-10T11:00:00'), false];
|
|
||||||
yield [new \DateTimeImmutable('2023-07-10T12:00:00'), new \DateTimeImmutable('2023-07-10T05:00:00'), true];
|
|
||||||
yield [new \DateTimeImmutable('2023-07-10T12:00:00'), new \DateTimeImmutable('2023-07-01T12:00:00'), true];
|
|
||||||
yield [new \DateTimeImmutable('2023-07-10T12:00:00'), null, true];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,44 +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 Services\AddressGeographicalUnit;
|
|
||||||
|
|
||||||
use Chill\MainBundle\Service\AddressGeographicalUnit\CollateAddressWithReferenceOrPostalCode;
|
|
||||||
use Doctrine\DBAL\Connection;
|
|
||||||
use Psr\Log\NullLogger;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
class CollateAddressWithReferenceOrPostalCodeTest extends KernelTestCase
|
|
||||||
{
|
|
||||||
private Connection $connection;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
self::bootKernel();
|
|
||||||
$this->connection = self::$container->get(Connection::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRun(): void
|
|
||||||
{
|
|
||||||
$collator = new CollateAddressWithReferenceOrPostalCode(
|
|
||||||
$this->connection,
|
|
||||||
new NullLogger()
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = $collator(0);
|
|
||||||
|
|
||||||
self::assertGreaterThan(0, $result);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,33 +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\Migrations\Main;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20230711152947 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Add data to ';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_main_cronjob_execution ADD lastExecutionData JSONB DEFAULT \'{}\'::jsonb NOT NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_main_cronjob_execution DROP COLUMN lastExecutionData');
|
|
||||||
}
|
|
||||||
}
|
|
@@ -39,10 +39,8 @@ readonly class AccompanyingPeriodStepChangeCronjob implements CronJobInterface
|
|||||||
return 'accompanying-period-step-change';
|
return 'accompanying-period-step-change';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(array $lastExecutionData): null|array
|
public function run(): void
|
||||||
{
|
{
|
||||||
($this->requestor)();
|
($this->requestor)();
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,39 +11,45 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Controller;
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
use Chill\MainBundle\Entity\UserJob;
|
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
|
||||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
|
||||||
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
|
|
||||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
|
||||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
use Symfony\Component\Form\Form;
|
use Symfony\Component\Form\Form;
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Serializer\SerializerInterface;
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
final class AccompanyingCourseWorkController extends AbstractController
|
class AccompanyingCourseWorkController extends AbstractController
|
||||||
{
|
{
|
||||||
|
private LoggerInterface $chillLogger;
|
||||||
|
|
||||||
|
private PaginatorFactory $paginator;
|
||||||
|
|
||||||
|
private SerializerInterface $serializer;
|
||||||
|
|
||||||
|
private TranslatorInterface $trans;
|
||||||
|
|
||||||
|
private AccompanyingPeriodWorkRepository $workRepository;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly TranslatorInterface $trans,
|
TranslatorInterface $trans,
|
||||||
private readonly SerializerInterface $serializer,
|
SerializerInterface $serializer,
|
||||||
private readonly AccompanyingPeriodWorkRepository $workRepository,
|
AccompanyingPeriodWorkRepository $workRepository,
|
||||||
private readonly PaginatorFactory $paginator,
|
PaginatorFactory $paginator,
|
||||||
private readonly LoggerInterface $chillLogger,
|
LoggerInterface $chillLogger
|
||||||
private readonly TranslatableStringHelperInterface $translatableStringHelper,
|
|
||||||
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory
|
|
||||||
) {
|
) {
|
||||||
|
$this->trans = $trans;
|
||||||
|
$this->serializer = $serializer;
|
||||||
|
$this->workRepository = $workRepository;
|
||||||
|
$this->paginator = $paginator;
|
||||||
|
$this->chillLogger = $chillLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,21 +162,11 @@ final class AccompanyingCourseWorkController extends AbstractController
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);
|
$this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period);
|
||||||
|
|
||||||
$filter = $this->buildFilterOrder($period);
|
|
||||||
|
|
||||||
$filterData = [
|
|
||||||
'types' => $filter->hasEntityChoice('typesFilter') ? $filter->getEntityChoiceData('typesFilter') : [],
|
|
||||||
'before' => $filter->getDateRangeData('dateFilter')['to'],
|
|
||||||
'after' => $filter->getDateRangeData('dateFilter')['from'],
|
|
||||||
'user' => $filter->getUserPickerData('userFilter')
|
|
||||||
];
|
|
||||||
|
|
||||||
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
$totalItems = $this->workRepository->countByAccompanyingPeriod($period);
|
||||||
$paginator = $this->paginator->create($totalItems);
|
$paginator = $this->paginator->create($totalItems);
|
||||||
|
|
||||||
$works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
|
$works = $this->workRepository->findByAccompanyingPeriodOpenFirst(
|
||||||
$period,
|
$period,
|
||||||
$filterData,
|
|
||||||
$paginator->getItemsPerPage(),
|
$paginator->getItemsPerPage(),
|
||||||
$paginator->getCurrentPageFirstItemNumber()
|
$paginator->getCurrentPageFirstItemNumber()
|
||||||
);
|
);
|
||||||
@@ -179,7 +175,6 @@ final class AccompanyingCourseWorkController extends AbstractController
|
|||||||
'accompanyingCourse' => $period,
|
'accompanyingCourse' => $period,
|
||||||
'works' => $works,
|
'works' => $works,
|
||||||
'paginator' => $paginator,
|
'paginator' => $paginator,
|
||||||
'filter' => $filter
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +199,7 @@ final class AccompanyingCourseWorkController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createDeleteForm(int $id): FormInterface
|
private function createDeleteForm(int $id): Form
|
||||||
{
|
{
|
||||||
$params = [];
|
$params = [];
|
||||||
$params['id'] = $id;
|
$params['id'] = $id;
|
||||||
@@ -215,26 +210,4 @@ final class AccompanyingCourseWorkController extends AbstractController
|
|||||||
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
->add('submit', SubmitType::class, ['label' => 'Delete'])
|
||||||
->getForm();
|
->getForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildFilterOrder($associatedPeriod): FilterOrderHelper
|
|
||||||
{
|
|
||||||
$filterBuilder = $this->filterOrderHelperFactory->create(self::class);
|
|
||||||
$types = $this->workRepository->findActionTypeByPeriod($associatedPeriod);
|
|
||||||
|
|
||||||
$filterBuilder
|
|
||||||
->addDateRange('dateFilter', 'accompanying_course_work.date_filter');
|
|
||||||
|
|
||||||
if (1 < count($types)) {
|
|
||||||
$filterBuilder
|
|
||||||
->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [
|
|
||||||
'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$filterBuilder
|
|
||||||
->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false])
|
|
||||||
;
|
|
||||||
|
|
||||||
return $filterBuilder->build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +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\PersonBundle\Controller;
|
|
||||||
|
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
|
|
||||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
|
||||||
use Symfony\Component\Security\Core\Security;
|
|
||||||
|
|
||||||
class AccompanyingCourseWorkEvaluationDocumentController extends AbstractController
|
|
||||||
{
|
|
||||||
public function __construct(private Security $security)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Route(
|
|
||||||
* "{_locale}/person/accompanying-period/work/evaluation/document/{id}/show",
|
|
||||||
* name="chill_person_accompanying_period_work_evaluation_document_show",
|
|
||||||
* methods={"GET"}
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
public function showAccompanyingCourseWork(AccompanyingPeriodWorkEvaluationDocument $document): Response
|
|
||||||
{
|
|
||||||
$work = $document->getAccompanyingPeriodWorkEvaluation()->getAccompanyingPeriodWork();
|
|
||||||
|
|
||||||
return $this->redirectToRoute(
|
|
||||||
$this->security->isGranted(AccompanyingPeriodWorkVoter::UPDATE, $work) ?
|
|
||||||
'chill_person_accompanying_period_work_edit' : 'chill_person_accompanying_period_work_show',
|
|
||||||
[
|
|
||||||
'id' => $work->getId()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -95,103 +95,29 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository
|
|||||||
* * then, closed works
|
* * then, closed works
|
||||||
*
|
*
|
||||||
* @return AccompanyingPeriodWork[]
|
* @return AccompanyingPeriodWork[]
|
||||||
* @param array{types?: list<SocialAction>, user?: list<User>, after?: null|\DateTimeImmutable, before?: null|\DateTimeImmutable} $filters
|
|
||||||
*/
|
*/
|
||||||
public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, array $filters, int $limit = 10, int $offset = 0): array
|
public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, int $limit = 10, int $offset = 0): array
|
||||||
{
|
{
|
||||||
$rsm = new ResultSetMappingBuilder($this->em);
|
$rsm = new ResultSetMappingBuilder($this->em);
|
||||||
$rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w');
|
$rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w');
|
||||||
|
|
||||||
$sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w
|
$sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w
|
||||||
LEFT JOIN chill_person_accompanying_period_work_referrer AS rw ON accompanyingperiodwork_id = w.id
|
WHERE accompanyingPeriod_id = :periodId
|
||||||
WHERE accompanyingPeriod_id = :periodId";
|
ORDER BY
|
||||||
|
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
||||||
// implement filters
|
startdate DESC,
|
||||||
|
enddate DESC,
|
||||||
if ([] !== ($filters['types'] ?? [])) {
|
id DESC
|
||||||
$sql .= " AND w.socialaction_id IN (:types)";
|
LIMIT :limit OFFSET :offset";
|
||||||
}
|
|
||||||
|
|
||||||
if ([] !== ($filters['user'] ?? [])) {
|
|
||||||
$sql .= " AND rw.user_id IN ("
|
|
||||||
. implode(
|
|
||||||
', ',
|
|
||||||
// we add a user_xx for each key of the 'user' list
|
|
||||||
array_map(fn (User $u, int $idx) => ':user_' . $idx, $filters['user'], array_keys($filters['user']))
|
|
||||||
)
|
|
||||||
. ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
$sql .= " AND daterange(:after::date, :before::date) && daterange(w.startDate, w.endDate)";
|
|
||||||
|
|
||||||
// if the start and end date were inversed, we inverse the order to avoid an error
|
|
||||||
if (null !== ($filters['after'] ?? null) && null !== ($filters['before']) && $filters['after'] > $filters['before']) {
|
|
||||||
$before = $filters['after'];
|
|
||||||
$after = $filters['before'];
|
|
||||||
} else {
|
|
||||||
$before = $filters['before'];
|
|
||||||
$after = $filters['after'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// set limit and offset
|
|
||||||
$sql .= " ORDER BY
|
|
||||||
CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC,
|
|
||||||
startdate DESC,
|
|
||||||
enddate DESC,
|
|
||||||
id DESC";
|
|
||||||
|
|
||||||
$sql .= " LIMIT :limit OFFSET :offset";
|
|
||||||
|
|
||||||
$typeIds = [];
|
|
||||||
foreach ($filters['types'] as $type) {
|
|
||||||
$typeIds[] = $type->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
$nq = $this->em->createNativeQuery($sql, $rsm)
|
$nq = $this->em->createNativeQuery($sql, $rsm)
|
||||||
->setParameter('periodId', $period->getId(), Types::INTEGER)
|
->setParameter('periodId', $period->getId(), Types::INTEGER)
|
||||||
->setParameter('types', $typeIds)
|
|
||||||
->setParameter('after', $after)
|
|
||||||
->setParameter('before', $before)
|
|
||||||
->setParameter('limit', $limit, Types::INTEGER)
|
->setParameter('limit', $limit, Types::INTEGER)
|
||||||
->setParameter('offset', $offset, Types::INTEGER);
|
->setParameter('offset', $offset, Types::INTEGER);
|
||||||
|
|
||||||
foreach ($filters['user'] as $key => $user) {
|
|
||||||
$nq->setParameter('user_' . $key, $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $nq->getResult();
|
return $nq->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a list of types of social actions associated to the accompanying period
|
|
||||||
*
|
|
||||||
* @return array<SocialAction>
|
|
||||||
*/
|
|
||||||
public function findActionTypeByPeriod(AccompanyingPeriod $period): array
|
|
||||||
{
|
|
||||||
$in = $this->em->createQueryBuilder();
|
|
||||||
$in
|
|
||||||
->select('1')
|
|
||||||
->from(AccompanyingPeriodWork::class, 'apw');
|
|
||||||
|
|
||||||
|
|
||||||
$in->andWhere('apw.accompanyingPeriod = :period')->setParameter('period', $period);
|
|
||||||
|
|
||||||
|
|
||||||
// join between the embedded exist query and the main query
|
|
||||||
$in->andWhere('apw.socialAction = sa');
|
|
||||||
|
|
||||||
$qb = $this->em->createQueryBuilder()->setParameters($in->getParameters());
|
|
||||||
$qb
|
|
||||||
->select('sa')
|
|
||||||
->from(SocialAction::class, 'sa')
|
|
||||||
->where(
|
|
||||||
$qb->expr()->exists($in->getDQL())
|
|
||||||
);
|
|
||||||
|
|
||||||
return $qb->getQuery()->getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array
|
||||||
{
|
{
|
||||||
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
return $this->buildQueryNearEndDateByUser($user, $since, $until)
|
||||||
|
@@ -68,45 +68,27 @@
|
|||||||
<ul class="list-content fa-ul">
|
<ul class="list-content fa-ul">
|
||||||
<li v-if="person.current_household_id">
|
<li v-if="person.current_household_id">
|
||||||
<i class="fa fa-li fa-map-marker"></i>
|
<i class="fa fa-li fa-map-marker"></i>
|
||||||
<address-render-box v-if="person.current_household_address" :address="person.current_household_address" :isMultiline="isMultiline"></address-render-box>
|
<address-render-box v-if="person.current_household_address"
|
||||||
<p v-else class="chill-no-data-statement">{{ $t('renderbox.household_without_address') }}</p>
|
:address="person.current_household_address"
|
||||||
|
:isMultiline="isMultiline">
|
||||||
|
</address-render-box>
|
||||||
|
<p v-else class="chill-no-data-statement">
|
||||||
|
{{ $t('renderbox.household_without_address') }}
|
||||||
|
</p>
|
||||||
<a v-if="options.addHouseholdLink === true"
|
<a v-if="options.addHouseholdLink === true"
|
||||||
:href="getCurrentHouseholdUrl"
|
:href="getCurrentHouseholdUrl"
|
||||||
:title="$t('persons_associated.show_household_number', {id: person.current_household_id})">
|
:title="$t('persons_associated.show_household_number', {id: person.current_household_id})">
|
||||||
<span class="badge rounded-pill bg-chill-beige">
|
<span class="badge rounded-pill bg-chill-beige">
|
||||||
<i class="fa fa-fw fa-home"></i><!--{{ $t('persons_associated.show_household') }}-->
|
<i class="fa fa-fw fa-home"></i><!--{{ $t('persons_associated.show_household') }}-->
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li v-else-if="options.addNoData">
|
<li v-else-if="options.addNoData">
|
||||||
<i class="fa fa-li fa-map-marker"></i><p class="chill-no-data-statement">{{ $t('renderbox.no_data') }}</p>
|
<i class="fa fa-li fa-map-marker"></i>
|
||||||
|
<p class="chill-no-data-statement">
|
||||||
|
{{ $t('renderbox.no_data') }}
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<template v-if="this.showResidentialAddresses && (person.current_residential_addresses || []).length > 0">
|
|
||||||
<li v-for="(addr, i) in person.current_residential_addresses" :key="i">
|
|
||||||
<i class="fa fa-li fa-map-marker"></i>
|
|
||||||
<div v-if="addr.address">
|
|
||||||
<span class="item-key">{{ $t('renderbox.residential_address') }}:</span>
|
|
||||||
<div style="margin-top: -1em;">
|
|
||||||
<address-render-box :address="addr.address" :isMultiline="isMultiline"></address-render-box>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="addr.hostPerson" class="mt-3">
|
|
||||||
<p>{{ $t('renderbox.located_at') }}:</p>
|
|
||||||
<span class="chill-entity entity-person badge-person">
|
|
||||||
<person-text v-if="addr.hostPerson" :person="addr.hostPerson"></person-text>
|
|
||||||
</span>
|
|
||||||
<address-render-box v-if="addr.hostPerson.address" :address="addr.hostPerson.address" :isMultiline="isMultiline"></address-render-box>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="addr.hostThirdParty" class="mt-3">
|
|
||||||
<p>{{ $t('renderbox.located_at') }}:</p>
|
|
||||||
<span class="chill-entity entity-person badge-thirdparty">
|
|
||||||
<third-party-text v-if="addr.hostThirdParty" :thirdparty="addr.hostThirdParty"></third-party-text>
|
|
||||||
</span>
|
|
||||||
<address-render-box v-if="addr.hostThirdParty.address" :address="addr.hostThirdParty.address" :isMultiline="isMultiline"></address-render-box>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<li v-if="person.email">
|
<li v-if="person.email">
|
||||||
<i class="fa fa-li fa-envelope-o"></i>
|
<i class="fa fa-li fa-envelope-o"></i>
|
||||||
@@ -149,6 +131,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="item-col mx-3"
|
||||||
|
v-if="this.showResidentialAddresses && (person.current_residential_addresses || []).length > 0">
|
||||||
|
<div class="float-button bottom">
|
||||||
|
<div class="box">
|
||||||
|
<ul class="list-content fa-ul">
|
||||||
|
<li v-for="(addr, i) in person.current_residential_addresses" :key="i">
|
||||||
|
<i class="fa fa-li fa-map-marker"></i>
|
||||||
|
<div v-if="addr.address">
|
||||||
|
<address-render-box
|
||||||
|
:address="addr.address"
|
||||||
|
:isMultiline="isMultiline">
|
||||||
|
</address-render-box>
|
||||||
|
<p>({{ $t('renderbox.residential_address') }})</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="addr.hostPerson" class="mt-3">
|
||||||
|
<p>{{ $t('renderbox.located_at') }}:</p>
|
||||||
|
<span class="chill-entity entity-person badge-person">
|
||||||
|
<person-text
|
||||||
|
v-if="addr.hostPerson"
|
||||||
|
:person="addr.hostPerson"
|
||||||
|
></person-text>
|
||||||
|
</span>
|
||||||
|
<address-render-box v-if="addr.hostPerson.address"
|
||||||
|
:address="addr.hostPerson.address"
|
||||||
|
:isMultiline="isMultiline">
|
||||||
|
</address-render-box>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="addr.hostThirdParty" class="mt-3">
|
||||||
|
<p>{{ $t('renderbox.located_at') }}:</p>
|
||||||
|
<span class="chill-entity entity-person badge-thirdparty">
|
||||||
|
<third-party-text
|
||||||
|
v-if="addr.hostThirdParty"
|
||||||
|
:thirdparty="addr.hostThirdParty"
|
||||||
|
></third-party-text>
|
||||||
|
</span>
|
||||||
|
<address-render-box v-if="addr.hostThirdParty.address"
|
||||||
|
:address="addr.hostThirdParty.address"
|
||||||
|
:isMultiline="isMultiline">
|
||||||
|
</address-render-box>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot name="end-bloc"></slot>
|
<slot name="end-bloc"></slot>
|
||||||
|
@@ -5,23 +5,18 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
{{ encore_entry_script_tags('mod_entity_workflow_pick') }}
|
||||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
{{ encore_entry_link_tags('mod_entity_workflow_pick') }}
|
||||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="accompanying-course-work">
|
<div class="accompanying-course-work">
|
||||||
|
|
||||||
<h1>{{ block('title') }}</h1>
|
<h1>{{ block('title') }}</h1>
|
||||||
|
|
||||||
{{ filter|chill_render_filter_order_helper }}
|
|
||||||
|
|
||||||
{% if works|length == 0 %}
|
{% if works|length == 0 %}
|
||||||
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}</p>
|
<p class="chill-no-data-statement">{{ 'accompanying_course_work.Any work'|trans }}</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@@ -59,7 +59,7 @@
|
|||||||
<span class=" d-block d-sm-inline-block">
|
<span class=" d-block d-sm-inline-block">
|
||||||
{{ address|chill_entity_render_box({
|
{{ address|chill_entity_render_box({
|
||||||
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
|
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
|
||||||
'details_button': true
|
'details_button': false
|
||||||
}) }}
|
}) }}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -56,12 +56,13 @@
|
|||||||
{%- if address is not null -%}
|
{%- if address is not null -%}
|
||||||
{{ address|chill_entity_render_box({
|
{{ address|chill_entity_render_box({
|
||||||
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
|
'render': 'inline', 'multiline': false, 'with_picto': true, 'with_delimiter': true,
|
||||||
'details_button': true
|
'details_button': false
|
||||||
}) }}
|
}) }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{% if person.getCurrentHousehold is not null %}
|
{% if person.getCurrentHousehold is not null %}
|
||||||
<a href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}"
|
<a class="btn household-link text-end"
|
||||||
class="btn btn-sm household-link" title="{{ 'Show household'|trans }}">
|
href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id' : person.getCurrentHousehold.id } ) }}"
|
||||||
|
title="{{ 'Show household'|trans }}">
|
||||||
<i class="fa fa-lg fa-home"></i>
|
<i class="fa fa-lg fa-home"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@@ -47,7 +47,8 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte
|
|||||||
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
public function getEntityData(EntityWorkflow $entityWorkflow, array $options = []): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'persons' => $this->getRelatedEntity($entityWorkflow)?->getPersons() ?? [],
|
'persons' => $this->getRelatedEntity($entityWorkflow)
|
||||||
|
->getPersons(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -913,9 +913,6 @@ accompanying_course_work:
|
|||||||
social_evaluation: Évaluation
|
social_evaluation: Évaluation
|
||||||
private_comment: Commentaire privé
|
private_comment: Commentaire privé
|
||||||
timeSpent: Temps de rédaction
|
timeSpent: Temps de rédaction
|
||||||
date_filter: Filtrer par date
|
|
||||||
types_filter: Filtrer par type d'action
|
|
||||||
user_filter: Filtrer par intervenant
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@@ -26,7 +26,6 @@ use Chill\TaskBundle\Event\TaskEvent;
|
|||||||
use Chill\TaskBundle\Event\UI\UIEvent;
|
use Chill\TaskBundle\Event\UI\UIEvent;
|
||||||
use Chill\TaskBundle\Form\SingleTaskType;
|
use Chill\TaskBundle\Form\SingleTaskType;
|
||||||
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
|
use Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface;
|
||||||
use Chill\TaskBundle\Repository\SingleTaskStateRepository;
|
|
||||||
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
use Chill\TaskBundle\Security\Authorization\TaskVoter;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -72,8 +71,7 @@ final class SingleTaskController extends AbstractController
|
|||||||
EventDispatcherInterface $eventDispatcher,
|
EventDispatcherInterface $eventDispatcher,
|
||||||
TimelineBuilder $timelineBuilder,
|
TimelineBuilder $timelineBuilder,
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
FilterOrderHelperFactoryInterface $filterOrderHelperFactory
|
||||||
private SingleTaskStateRepository $singleTaskStateRepository
|
|
||||||
) {
|
) {
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->timelineBuilder = $timelineBuilder;
|
$this->timelineBuilder = $timelineBuilder;
|
||||||
@@ -301,17 +299,13 @@ final class SingleTaskController extends AbstractController
|
|||||||
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
|
$this->denyAccessUnlessGranted(TaskVoter::SHOW, null);
|
||||||
|
|
||||||
$filterOrder = $this->buildFilterOrder();
|
$filterOrder = $this->buildFilterOrder();
|
||||||
|
|
||||||
$filteredUsers = $filterOrder->getUserPickerData('userPicker');
|
|
||||||
|
|
||||||
$flags = array_merge(
|
$flags = array_merge(
|
||||||
$filterOrder->getCheckboxData('status'),
|
$filterOrder->getCheckboxData('status'),
|
||||||
array_map(static fn ($i) => 'state_' . $i, $filterOrder->hasCheckboxData('states') ? $filterOrder->getCheckboxData('states') : [])
|
array_map(static fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||||
);
|
);
|
||||||
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
|
$nb = $this->singleTaskAclAwareRepository->countByAllViewable(
|
||||||
$filterOrder->getQueryString(),
|
$filterOrder->getQueryString(),
|
||||||
$flags,
|
$flags
|
||||||
$filteredUsers
|
|
||||||
);
|
);
|
||||||
$paginator = $this->paginatorFactory->create($nb);
|
$paginator = $this->paginatorFactory->create($nb);
|
||||||
|
|
||||||
@@ -319,7 +313,6 @@ final class SingleTaskController extends AbstractController
|
|||||||
$tasks = $this->singleTaskAclAwareRepository->findByAllViewable(
|
$tasks = $this->singleTaskAclAwareRepository->findByAllViewable(
|
||||||
$filterOrder->getQueryString(),
|
$filterOrder->getQueryString(),
|
||||||
$flags,
|
$flags,
|
||||||
$filteredUsers,
|
|
||||||
$paginator->getCurrentPageFirstItemNumber(),
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
$paginator->getItemsPerPage(),
|
$paginator->getItemsPerPage(),
|
||||||
[
|
[
|
||||||
@@ -353,7 +346,7 @@ final class SingleTaskController extends AbstractController
|
|||||||
$filterOrder = $this->buildFilterOrder();
|
$filterOrder = $this->buildFilterOrder();
|
||||||
$flags = array_merge(
|
$flags = array_merge(
|
||||||
$filterOrder->getCheckboxData('status'),
|
$filterOrder->getCheckboxData('status'),
|
||||||
array_map(static fn ($i) => 'state_' . $i, $filterOrder->hasCheckboxData('states') ? $filterOrder->getCheckboxData('states') : [])
|
array_map(static fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||||
);
|
);
|
||||||
$nb = $this->singleTaskAclAwareRepository->countByCourse(
|
$nb = $this->singleTaskAclAwareRepository->countByCourse(
|
||||||
$course,
|
$course,
|
||||||
@@ -402,7 +395,7 @@ final class SingleTaskController extends AbstractController
|
|||||||
$filterOrder = $this->buildFilterOrder();
|
$filterOrder = $this->buildFilterOrder();
|
||||||
$flags = array_merge(
|
$flags = array_merge(
|
||||||
$filterOrder->getCheckboxData('status'),
|
$filterOrder->getCheckboxData('status'),
|
||||||
array_map(static fn ($i) => 'state_' . $i, $filterOrder->hasCheckboxData('states') ? $filterOrder->getCheckboxData('states') : [])
|
array_map(static fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||||
);
|
);
|
||||||
$nb = $this->singleTaskAclAwareRepository->countByPerson(
|
$nb = $this->singleTaskAclAwareRepository->countByPerson(
|
||||||
$person,
|
$person,
|
||||||
@@ -454,10 +447,10 @@ final class SingleTaskController extends AbstractController
|
|||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
||||||
|
|
||||||
$filterOrder = $this->buildFilterOrder(false);
|
$filterOrder = $this->buildFilterOrder();
|
||||||
$flags = array_merge(
|
$flags = array_merge(
|
||||||
$filterOrder->getCheckboxData('status'),
|
$filterOrder->getCheckboxData('status'),
|
||||||
array_map(static fn ($i) => 'state_' . $i, $filterOrder->hasCheckboxData('states') ? $filterOrder->getCheckboxData('states') : [])
|
array_map(static fn ($i) => 'state_' . $i, $filterOrder->getCheckboxData('states'))
|
||||||
);
|
);
|
||||||
$nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(
|
$nb = $this->singleTaskAclAwareRepository->countByCurrentUsersTasks(
|
||||||
$filterOrder->getQueryString(),
|
$filterOrder->getQueryString(),
|
||||||
@@ -669,7 +662,7 @@ final class SingleTaskController extends AbstractController
|
|||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildFilterOrder($includeFilterByUser = true): FilterOrderHelper
|
private function buildFilterOrder(): FilterOrderHelper
|
||||||
{
|
{
|
||||||
$statuses = ['no-alert', 'warning', 'alert'];
|
$statuses = ['no-alert', 'warning', 'alert'];
|
||||||
$statusTrans = [
|
$statusTrans = [
|
||||||
@@ -677,26 +670,17 @@ final class SingleTaskController extends AbstractController
|
|||||||
'Tasks near deadline',
|
'Tasks near deadline',
|
||||||
'Tasks over deadline',
|
'Tasks over deadline',
|
||||||
];
|
];
|
||||||
|
$states = [
|
||||||
|
// todo: get a list of possible states dynamically
|
||||||
|
'new', 'in_progress', 'closed', 'canceled',
|
||||||
|
];
|
||||||
|
|
||||||
$filterBuilder = $this->filterOrderHelperFactory
|
return $this->filterOrderHelperFactory
|
||||||
->create(self::class)
|
->create(self::class)
|
||||||
->addSearchBox()
|
->addSearchBox()
|
||||||
->addCheckbox('status', $statuses, $statuses, $statusTrans);
|
->addCheckbox('status', $statuses, $statuses, $statusTrans)
|
||||||
|
->addCheckbox('states', $states, ['new', 'in_progress'])
|
||||||
$states = $this->singleTaskStateRepository->findAllExistingStates();
|
->build();
|
||||||
$checked = array_values(array_filter($states, fn (string $state) => !in_array($state, ['closed', 'canceled', 'validated'], true)));
|
|
||||||
|
|
||||||
if ([] !== $states) {
|
|
||||||
$filterBuilder
|
|
||||||
->addCheckbox('states', $states, $checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($includeFilterByUser) {
|
|
||||||
$filterBuilder
|
|
||||||
->addUserPicker('userPicker', 'Filter by user', ['multiple' => true, 'required' => false]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $filterBuilder->build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -51,37 +51,18 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
|||||||
|
|
||||||
public function buildBaseQuery(
|
public function buildBaseQuery(
|
||||||
?string $pattern = null,
|
?string $pattern = null,
|
||||||
?array $flags = [],
|
?array $flags = []
|
||||||
?array $users = []
|
|
||||||
): QueryBuilder {
|
): QueryBuilder {
|
||||||
$qb = $this->em->createQueryBuilder();
|
$qb = $this->em->createQueryBuilder();
|
||||||
$qb
|
$qb
|
||||||
->from(SingleTask::class, 't');
|
->from(SingleTask::class, 't');
|
||||||
|
|
||||||
if (null !== $pattern && '' !== $pattern) {
|
if (!empty($pattern)) {
|
||||||
$qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))'))
|
$qb->andWhere($qb->expr()->like('LOWER(UNACCENT(t.title))', 'LOWER(UNACCENT(:pattern))'))
|
||||||
->setParameter('pattern', '%' . $pattern . '%');
|
->setParameter('pattern', '%' . $pattern . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $users && count($users) > 0) {
|
if (count($flags) > 0) {
|
||||||
$orXUser = $qb->expr()->orX();
|
|
||||||
|
|
||||||
foreach ($users as $key => $user) {
|
|
||||||
$orXUser->add(
|
|
||||||
$qb->expr()->eq('t.assignee', ':user_' . $key)
|
|
||||||
);
|
|
||||||
|
|
||||||
$qb->setParameter('user_' . $key, $user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($orXUser->count() > 0) {
|
|
||||||
$qb->andWhere($orXUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $qb;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null !== $flags && count($flags) > 0) {
|
|
||||||
$orXDate = $qb->expr()->orX();
|
$orXDate = $qb->expr()->orX();
|
||||||
$orXState = $qb->expr()->orX();
|
$orXState = $qb->expr()->orX();
|
||||||
$now = new DateTime();
|
$now = new DateTime();
|
||||||
@@ -202,10 +183,9 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
|||||||
|
|
||||||
public function countByAllViewable(
|
public function countByAllViewable(
|
||||||
?string $pattern = null,
|
?string $pattern = null,
|
||||||
?array $flags = [],
|
?array $flags = []
|
||||||
?array $users = []
|
|
||||||
): int {
|
): int {
|
||||||
$qb = $this->buildBaseQuery($pattern, $flags, $users);
|
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->addACLGlobal($qb)
|
->addACLGlobal($qb)
|
||||||
@@ -251,12 +231,11 @@ final class SingleTaskAclAwareRepository implements SingleTaskAclAwareRepository
|
|||||||
public function findByAllViewable(
|
public function findByAllViewable(
|
||||||
?string $pattern = null,
|
?string $pattern = null,
|
||||||
?array $flags = [],
|
?array $flags = [],
|
||||||
?array $users = [],
|
|
||||||
?int $start = 0,
|
?int $start = 0,
|
||||||
?int $limit = 50,
|
?int $limit = 50,
|
||||||
?array $orderBy = []
|
?array $orderBy = []
|
||||||
): array {
|
): array {
|
||||||
$qb = $this->buildBaseQuery($pattern, $flags, $users);
|
$qb = $this->buildBaseQuery($pattern, $flags);
|
||||||
$qb = $this->addACLGlobal($qb);
|
$qb = $this->addACLGlobal($qb);
|
||||||
|
|
||||||
return $this->getResult($qb, $start, $limit, $orderBy);
|
return $this->getResult($qb, $start, $limit, $orderBy);
|
||||||
|
@@ -18,8 +18,7 @@ interface SingleTaskAclAwareRepositoryInterface
|
|||||||
{
|
{
|
||||||
public function countByAllViewable(
|
public function countByAllViewable(
|
||||||
?string $pattern = null,
|
?string $pattern = null,
|
||||||
?array $flags = [],
|
?array $flags = []
|
||||||
?array $users = []
|
|
||||||
): int;
|
): int;
|
||||||
|
|
||||||
public function countByCourse(
|
public function countByCourse(
|
||||||
@@ -39,7 +38,6 @@ interface SingleTaskAclAwareRepositoryInterface
|
|||||||
public function findByAllViewable(
|
public function findByAllViewable(
|
||||||
?string $pattern = null,
|
?string $pattern = null,
|
||||||
?array $flags = [],
|
?array $flags = [],
|
||||||
?array $users = [],
|
|
||||||
?int $start = 0,
|
?int $start = 0,
|
||||||
?int $limit = 50,
|
?int $limit = 50,
|
||||||
?array $orderBy = []
|
?array $orderBy = []
|
||||||
|
@@ -1,47 +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\TaskBundle\Repository;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Connection;
|
|
||||||
use Doctrine\DBAL\Exception;
|
|
||||||
|
|
||||||
class SingleTaskStateRepository
|
|
||||||
{
|
|
||||||
private const FIND_ALL_STATES = <<<'SQL'
|
|
||||||
SELECT DISTINCT jsonb_array_elements_text(current_states) FROM chill_task.single_task
|
|
||||||
SQL;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private Connection $connection
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a list of all states associated to at least one single task in the database
|
|
||||||
*
|
|
||||||
* @return list<string>
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function findAllExistingStates(): array
|
|
||||||
{
|
|
||||||
$states = [];
|
|
||||||
|
|
||||||
foreach ($this->connection->fetchAllNumeric(self::FIND_ALL_STATES) as $row) {
|
|
||||||
if ('' !== $row[0] && null !== $row[0]) {
|
|
||||||
$states[] = $row[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $states;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -27,10 +27,8 @@
|
|||||||
{% block css %}
|
{% block css %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_link_tags('page_task_list') }}
|
{{ encore_entry_link_tags('page_task_list') }}
|
||||||
{{ encore_entry_link_tags('mod_pickentity_type') }}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
{{ encore_entry_script_tags('page_task_list') }}
|
{{ encore_entry_script_tags('page_task_list') }}
|
||||||
{{ encore_entry_script_tags('mod_pickentity_type') }}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -1,8 +1,4 @@
|
|||||||
services:
|
services:
|
||||||
_defaults:
|
|
||||||
autowire: true
|
|
||||||
autoconfigure: true
|
|
||||||
|
|
||||||
chill_task.single_task_repository:
|
chill_task.single_task_repository:
|
||||||
class: Chill\TaskBundle\Repository\SingleTaskRepository
|
class: Chill\TaskBundle\Repository\SingleTaskRepository
|
||||||
factory: ['@doctrine.orm.entity_manager', getRepository]
|
factory: ['@doctrine.orm.entity_manager', getRepository]
|
||||||
@@ -14,8 +10,8 @@ services:
|
|||||||
- "@chill.main.security.authorization.helper"
|
- "@chill.main.security.authorization.helper"
|
||||||
Chill\TaskBundle\Repository\SingleTaskRepository: '@chill_task.single_task_repository'
|
Chill\TaskBundle\Repository\SingleTaskRepository: '@chill_task.single_task_repository'
|
||||||
|
|
||||||
Chill\TaskBundle\Repository\SingleTaskAclAwareRepository: ~
|
Chill\TaskBundle\Repository\SingleTaskAclAwareRepository:
|
||||||
|
autowire: true
|
||||||
|
autoconfigure: true
|
||||||
|
|
||||||
Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface: '@Chill\TaskBundle\Repository\SingleTaskAclAwareRepository'
|
Chill\TaskBundle\Repository\SingleTaskAclAwareRepositoryInterface: '@Chill\TaskBundle\Repository\SingleTaskAclAwareRepository'
|
||||||
|
|
||||||
Chill\TaskBundle\Repository\SingleTaskStateRepository: ~
|
|
||||||
|
@@ -65,7 +65,6 @@ Not assigned: Aucun utilisateur assigné
|
|||||||
For person: Pour
|
For person: Pour
|
||||||
By: Par
|
By: Par
|
||||||
Any tasks: Aucune tâche
|
Any tasks: Aucune tâche
|
||||||
Filter by user: Filtrer par utilisateur(s)
|
|
||||||
|
|
||||||
# transitions - default task definition
|
# transitions - default task definition
|
||||||
"new": "nouvelle"
|
"new": "nouvelle"
|
||||||
|
@@ -71,9 +71,12 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
|||||||
->setSelectKey('tparty')
|
->setSelectKey('tparty')
|
||||||
->setSelectJsonbMetadata("jsonb_build_object('id', tparty.id)")
|
->setSelectJsonbMetadata("jsonb_build_object('id', tparty.id)")
|
||||||
->setFromClause('chill_3party.third_party AS tparty
|
->setFromClause('chill_3party.third_party AS tparty
|
||||||
|
LEFT JOIN chill_main_address cma ON cma.id = tparty.address_id
|
||||||
|
LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id
|
||||||
LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id
|
LEFT JOIN chill_3party.third_party AS parent ON tparty.parent_id = parent.id
|
||||||
LEFT JOIN chill_main_address cma ON cma.id = COALESCE(parent.address_id, tparty.address_id)
|
LEFT JOIN chill_main_address cma_p ON parent.address_id = cma_p.id
|
||||||
LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id');
|
LEFT JOIN chill_main_postal_code cmpc_p ON cma_p.postcode_id = cmpc.id')
|
||||||
|
->andWhereClause('tparty.active IS TRUE');
|
||||||
|
|
||||||
$strs = explode(' ', $pattern);
|
$strs = explode(' ', $pattern);
|
||||||
$wheres = [];
|
$wheres = [];
|
||||||
@@ -99,8 +102,9 @@ class ThirdPartyApiSearch implements SearchApiInterface
|
|||||||
(parent.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int
|
(parent.canonicalized LIKE '%s' || LOWER(UNACCENT(?)) || '%')::int
|
||||||
) + " .
|
) + " .
|
||||||
// take postcode label into account, but lower than the canonicalized field
|
// take postcode label into account, but lower than the canonicalized field
|
||||||
"COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)";
|
"COALESCE((LOWER(UNACCENT(cmpc.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0) + " .
|
||||||
$pertinenceArgs[] = [$str, $str, $str, $str, $str];
|
"COALESCE((LOWER(UNACCENT(cmpc_p.label)) LIKE '%' || LOWER(UNACCENT(?)) || '%')::int * 0.3, 0)";
|
||||||
|
$pertinenceArgs[] = [$str, $str, $str, $str, $str, $str];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user