mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-07 20:58:24 +00:00
Compare commits
2 Commits
master
...
280-add-mi
| Author | SHA1 | Date | |
|---|---|---|---|
| 980036fd3d | |||
| a70f8a0579 |
6
.changes/unreleased/DX-20251030-123732.yaml
Normal file
6
.changes/unreleased/DX-20251030-123732.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
kind: DX
|
||||||
|
body: Add missing fixtures for proper loading of AccompanyingPeriods
|
||||||
|
time: 2025-10-30T12:37:32.824593456+01:00
|
||||||
|
custom:
|
||||||
|
Issue: "280"
|
||||||
|
SchemaChange: No schema change
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: Create invitation list in user menu
|
|
||||||
time: 2025-08-08T12:08:02.446361367+02:00
|
|
||||||
custom:
|
|
||||||
Issue: "385"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Feature
|
|
||||||
body: Add columns for comments linked to an activity in the activity list export
|
|
||||||
time: 2025-10-29T15:25:10.493968528+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "404"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'Fix: display also social actions linked to parents of the selected social issue'
|
|
||||||
time: 2025-10-29T12:43:55.008647232+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "451"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'Fix: export actions and their results in csv even when action does not have any goals attached to it.'
|
|
||||||
time: 2025-10-29T14:38:36.195220844+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "453"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: Fix the possibility to delete a workflow
|
|
||||||
time: 2025-11-04T13:51:08.113234488+01:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
||||||
SchemaChange: Drop or rename table or columns, or enforce new constraint that must be manually fixed
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: Fix the fusion of thirdparty properties that are located in another schema than public for TO_ONE relations + add extra loop for MANY_TO_MANY relations where thirdparty is the source instead of the target
|
|
||||||
time: 2025-11-06T12:36:41.555991969+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "457"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: Fix suggestion of referrer when creating notification for accompanyingPeriodWorkDocument
|
|
||||||
time: 2025-11-06T16:16:05.861813041+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "428"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Change the terms 'cercle' and 'centre' to 'service', and 'territoire' respectively.
|
|
||||||
time: 2025-10-06T12:39:32.514056818+02:00
|
|
||||||
custom:
|
|
||||||
Issue: "425"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Improve the ux for selecting whether user wants to be notified of the final step of a workflow or all steps
|
|
||||||
time: 2025-10-29T11:08:04.077020411+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "542"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Expand timeSpent choices for evaluation document and translate them to user locale or fallback 'fr'
|
|
||||||
time: 2025-10-30T18:09:19.373907522+01:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Change the order of display for results and objectives in the social work/action form
|
|
||||||
time: 2025-11-03T13:15:54.837971477+01:00
|
|
||||||
custom:
|
|
||||||
Issue: "455"
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
kind: UX
|
|
||||||
body: Wrap text when it is too long within badges
|
|
||||||
time: 2025-11-07T15:17:36.104379989+01:00
|
|
||||||
custom:
|
|
||||||
Issue: ""
|
|
||||||
SchemaChange: No schema change
|
|
||||||
@@ -780,7 +780,7 @@ Fix color of Chill footer
|
|||||||
- ajout d'un filtre et regroupement par usager participant sur les échanges
|
- ajout d'un filtre et regroupement par usager participant sur les échanges
|
||||||
- ajout d'un regroupement: par type d'activité associé au parcours;
|
- ajout d'un regroupement: par type d'activité associé au parcours;
|
||||||
- trie les filtre et regroupements par ordre alphabétique dans els exports
|
- trie les filtre et regroupements par ordre alphabétique dans els exports
|
||||||
- ajout d'un paramètre qui permet de désactiver le filtre par territoire dans les exports
|
- ajout d'un paramètre qui permet de désactiver le filtre par centre dans les exports
|
||||||
- correction de l'interface de date dans les filtres et regroupements "par statut du parcours à la date"
|
- correction de l'interface de date dans les filtres et regroupements "par statut du parcours à la date"
|
||||||
|
|
||||||
## v2.9.2 - 2023-10-17
|
## v2.9.2 - 2023-10-17
|
||||||
@@ -960,7 +960,7 @@ error when trying to reedit a saved export
|
|||||||
- ajout d'un regroupement par métier des intervenants sur un parcours;
|
- ajout d'un regroupement par métier des intervenants sur un parcours;
|
||||||
- ajout d'un regroupement par service des intervenants sur un parcours;
|
- ajout d'un regroupement par service des intervenants sur un parcours;
|
||||||
- ajout d'un regroupement par utilisateur intervenant sur un parcours
|
- ajout d'un regroupement par utilisateur intervenant sur un parcours
|
||||||
- ajout d'un regroupement "par territoire de l'usager";
|
- ajout d'un regroupement "par centre de l'usager";
|
||||||
- ajout d'un filtre "par métier intervenant sur un parcours";
|
- ajout d'un filtre "par métier intervenant sur un parcours";
|
||||||
- ajout d'un filtre "par service intervenant sur un parcours";
|
- ajout d'un filtre "par service intervenant sur un parcours";
|
||||||
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
|
- création d'un rôle spécifique pour voir les parcours confidentiels (et séparer de celui de la liste qui permet de ré-assigner les parcours en lot);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"ext-openssl": "*",
|
"ext-openssl": "*",
|
||||||
"ext-redis": "*",
|
"ext-redis": "*",
|
||||||
"ext-zlib": "*",
|
"ext-zlib": "*",
|
||||||
"champs-libres/wopi-bundle": "dev-symfony-v5@dev",
|
"champs-libres/wopi-bundle": "dev-master@dev",
|
||||||
"champs-libres/wopi-lib": "dev-master@dev",
|
"champs-libres/wopi-lib": "dev-master@dev",
|
||||||
"doctrine/data-fixtures": "^1.8",
|
"doctrine/data-fixtures": "^1.8",
|
||||||
"doctrine/doctrine-bundle": "^2.1",
|
"doctrine/doctrine-bundle": "^2.1",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||||
|
loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true],
|
||||||
ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true],
|
ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
|
||||||
@@ -36,5 +37,4 @@ return [
|
|||||||
Chill\WopiBundle\ChillWopiBundle::class => ['all' => true],
|
Chill\WopiBundle\ChillWopiBundle::class => ['all' => true],
|
||||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
|
Symfony\UX\Translator\UxTranslatorBundle::class => ['all' => true],
|
||||||
loophp\PsrHttpMessageBridgeBundle\PsrHttpMessageBridgeBundle::class => ['all' => true],
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
chill_aside_activity:
|
|
||||||
show_concerned_persons_count: hidden
|
|
||||||
@@ -23,8 +23,8 @@ class "Document" {
|
|||||||
- text description
|
- text description
|
||||||
- ArrayCollection_DocumentCategory categories
|
- ArrayCollection_DocumentCategory categories
|
||||||
- varchar_150 content #link to openstack
|
- varchar_150 content #link to openstack
|
||||||
- Territoire territoire
|
- Center center
|
||||||
- Service service
|
- Cercle cercle
|
||||||
- User user
|
- User user
|
||||||
- DateTime date # Creation date
|
- DateTime date # Creation date
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Certaines données sont historisées:
|
|||||||
|
|
||||||
- les référents d'un parcours;
|
- les référents d'un parcours;
|
||||||
- les statuts d'un parcours;
|
- les statuts d'un parcours;
|
||||||
- la liaison entre les territoires et les usagers;
|
- la liaison entre les centres et les usagers;
|
||||||
- etc.
|
- 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 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.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
order,table_schema,table_name,commentaire
|
order,table_schema,table_name,commentaire
|
||||||
1,chill_3party,party_category,Catégorie de tiers
|
1,chill_3party,party_category,Catégorie de tiers
|
||||||
2,chill_3party,party_center,Association entre les tiers et les territoires (déprécié)
|
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é)
|
3,chill_3party,party_profession,Profession du tiers (déprécié)
|
||||||
4,chill_3party,third_party,Tiers
|
4,chill_3party,third_party,Tiers
|
||||||
5,chill_3party,thirdparty_category,association tiers - catégories
|
5,chill_3party,thirdparty_category,association tiers - catégories
|
||||||
@@ -54,7 +54,7 @@ order,table_schema,table_name,commentaire
|
|||||||
53,public,activitytpresence,Présence aux échanges
|
53,public,activitytpresence,Présence aux échanges
|
||||||
54,public,activitytype,Types d'échanges
|
54,public,activitytype,Types d'échanges
|
||||||
55,public,activitytypecategory,Catégories de types d'échanges
|
55,public,activitytypecategory,Catégories de types d'échanges
|
||||||
56,public,centers,"Territoires (territoires, agences, etc.)"
|
56,public,centers,"Centres (territoires, agences, etc.)"
|
||||||
57,public,chill_activity_activity_chill_person_socialaction,
|
57,public,chill_activity_activity_chill_person_socialaction,
|
||||||
58,public,chill_activity_activity_chill_person_socialissue
|
58,public,chill_activity_activity_chill_person_socialissue
|
||||||
59,public,chill_docgen_template,Gabarits de documents
|
59,public,chill_docgen_template,Gabarits de documents
|
||||||
@@ -111,7 +111,7 @@ order,table_schema,table_name,commentaire
|
|||||||
110,public,chill_person_marital_status,Etats civils
|
110,public,chill_person_marital_status,Etats civils
|
||||||
111,public,chill_person_not_duplicate,
|
111,public,chill_person_not_duplicate,
|
||||||
112,public,chill_person_person,Usagers
|
112,public,chill_person_person,Usagers
|
||||||
113,public,chill_person_person_center_history,Historique des territoires d'un usagers
|
113,public,chill_person_person_center_history,Historique des centres d'un usagers
|
||||||
114,public,chill_person_persons_to_addresses,Déprécié
|
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
|
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
|
116,public,chill_person_relations,Types de relations de filiation
|
||||||
@@ -142,7 +142,7 @@ order,table_schema,table_name,commentaire
|
|||||||
141,public,permission_groups
|
141,public,permission_groups
|
||||||
142,public,permissionsgroup_rolescope
|
142,public,permissionsgroup_rolescope
|
||||||
143,public,persons_spoken_languages
|
143,public,persons_spoken_languages
|
||||||
144,public,regroupment,Regroupement de territoires
|
144,public,regroupment,Regroupement de centres
|
||||||
145,public,regroupment_center,
|
145,public,regroupment_center,
|
||||||
146,public,role_scopes,
|
146,public,role_scopes,
|
||||||
147,public,scopes,Services
|
147,public,scopes,Services
|
||||||
|
|||||||
|
Can't render this file because it has a wrong number of fields in line 28.
|
@@ -66,9 +66,6 @@ class ListActivityHelper
|
|||||||
->leftJoin('activity.location', 'location')
|
->leftJoin('activity.location', 'location')
|
||||||
->addSelect('location.name AS locationName')
|
->addSelect('location.name AS locationName')
|
||||||
->addSelect('activity.sentReceived')
|
->addSelect('activity.sentReceived')
|
||||||
->addSelect('activity.comment.comment AS commentText')
|
|
||||||
->addSelect('activity.comment.date AS commentDate')
|
|
||||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', activity.comment.userId, \'d\', activity.comment.date) AS commentUser')
|
|
||||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
|
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.createdBy), \'d\', activity.createdAt) AS createdBy')
|
||||||
->addSelect('activity.createdAt')
|
->addSelect('activity.createdAt')
|
||||||
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
|
->addSelect('JSON_BUILD_OBJECT(\'uid\', IDENTITY(activity.updatedBy), \'d\', activity.updatedAt) AS updatedBy')
|
||||||
@@ -90,8 +87,6 @@ class ListActivityHelper
|
|||||||
'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
|
'createdAt', 'updatedAt' => $this->dateTimeHelper->getLabel($key),
|
||||||
'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
|
'createdBy', 'updatedBy' => $this->userHelper->getLabel($key, $values, $key),
|
||||||
'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY.$key),
|
'date' => $this->dateTimeHelper->getLabel(self::MSG_KEY.$key),
|
||||||
'commentDate' => $this->dateTimeHelper->getLabel(self::MSG_KEY.'comment_date'),
|
|
||||||
'commentUser' => $this->userHelper->getLabel($key, $values, self::MSG_KEY.'comment_user'),
|
|
||||||
'attendeeName' => function ($value) {
|
'attendeeName' => function ($value) {
|
||||||
if ('_header' === $value) {
|
if ('_header' === $value) {
|
||||||
return 'Attendee';
|
return 'Attendee';
|
||||||
@@ -181,9 +176,6 @@ class ListActivityHelper
|
|||||||
'usersNames',
|
'usersNames',
|
||||||
'thirdPartiesIds',
|
'thirdPartiesIds',
|
||||||
'thirdPartiesNames',
|
'thirdPartiesNames',
|
||||||
'commentText',
|
|
||||||
'commentDate',
|
|
||||||
'commentUser',
|
|
||||||
'createdBy',
|
'createdBy',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'updatedBy',
|
'updatedBy',
|
||||||
|
|||||||
@@ -43,23 +43,11 @@ export default {
|
|||||||
span.badge {
|
span.badge {
|
||||||
@include badge_social($social-action-color);
|
@include badge_social($social-action-color);
|
||||||
font-size: 95%;
|
font-size: 95%;
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 100%;
|
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
text-align: left;
|
max-width: 100%; /* Adjust as needed */
|
||||||
line-height: 1.2em;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
&::before {
|
white-space: nowrap;
|
||||||
position: absolute;
|
|
||||||
left: 11px;
|
|
||||||
top: 0;
|
|
||||||
margin: 0 0.3em 0 -0.75em;
|
|
||||||
}
|
|
||||||
position: relative;
|
|
||||||
padding-left: 1.5em;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -43,22 +43,7 @@ export default {
|
|||||||
span.badge {
|
span.badge {
|
||||||
@include badge_social($social-issue-color);
|
@include badge_social($social-issue-color);
|
||||||
font-size: 95%;
|
font-size: 95%;
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
word-break: break-word;
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 100%;
|
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
left: 11px;
|
|
||||||
top: 0;
|
|
||||||
margin: 0 0.3em 0 -0.75em;
|
|
||||||
}
|
|
||||||
position: relative;
|
|
||||||
padding-left: 1.5em;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Attendee: Présence de l'usager
|
|||||||
attendee: présence de l'usager
|
attendee: présence de l'usager
|
||||||
list_reasons: liste des sujets
|
list_reasons: liste des sujets
|
||||||
user_username: nom de l'utilisateur
|
user_username: nom de l'utilisateur
|
||||||
circle_name: nom du service
|
circle_name: nom du cercle
|
||||||
Remark: Commentaire
|
Remark: Commentaire
|
||||||
No comments: Aucun commentaire
|
No comments: Aucun commentaire
|
||||||
Add a new activity: Ajouter une nouvel échange
|
Add a new activity: Ajouter une nouvel échange
|
||||||
@@ -20,7 +20,7 @@ not present: absent
|
|||||||
Delete: Supprimer
|
Delete: Supprimer
|
||||||
Update: Mettre à jour
|
Update: Mettre à jour
|
||||||
Update activity: Modifier l'échange
|
Update activity: Modifier l'échange
|
||||||
Scope: Service
|
Scope: Cercle
|
||||||
Activity data: Données de l'échange
|
Activity data: Données de l'échange
|
||||||
Activity location: Localisation de l'échange
|
Activity location: Localisation de l'échange
|
||||||
No reason associated: Aucun sujet
|
No reason associated: Aucun sujet
|
||||||
@@ -398,15 +398,13 @@ export:
|
|||||||
sent received: Envoyé ou reçu
|
sent received: Envoyé ou reçu
|
||||||
emergency: Urgence
|
emergency: Urgence
|
||||||
accompanying course id: Identifiant du parcours
|
accompanying course id: Identifiant du parcours
|
||||||
course circles: Services du parcours
|
course circles: Cercles du parcours
|
||||||
travelTime: Durée de déplacement
|
travelTime: Durée de déplacement
|
||||||
durationTime: Durée
|
durationTime: Durée
|
||||||
id: Identifiant
|
id: Identifiant
|
||||||
List activities linked to an accompanying course: Liste les échanges liés à un parcours en fonction de différents filtres.
|
List activities linked to an accompanying course: Liste les échanges liés à un parcours en fonction de différents filtres.
|
||||||
List activity linked to a course: Liste des échanges liés à un parcours
|
List activity linked to a course: Liste des échanges liés à un parcours
|
||||||
commentText: Commentaire
|
|
||||||
comment_date: Date de la dernière édition du commentaire
|
|
||||||
comment_user: Dernière édition par
|
|
||||||
|
|
||||||
filter:
|
filter:
|
||||||
activity:
|
activity:
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
|
|||||||
$config = $this->processConfiguration($configuration, $configs);
|
$config = $this->processConfiguration($configuration, $configs);
|
||||||
|
|
||||||
$container->setParameter('chill_aside_activity.form.time_duration', $config['form']['time_duration']);
|
$container->setParameter('chill_aside_activity.form.time_duration', $config['form']['time_duration']);
|
||||||
$container->setParameter('chill_aside_activity.show_concerned_persons_count', 'visible' === $config['show_concerned_persons_count']);
|
|
||||||
|
|
||||||
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
|
||||||
$loader->load('services.yaml');
|
$loader->load('services.yaml');
|
||||||
@@ -39,24 +38,6 @@ final class ChillAsideActivityExtension extends Extension implements PrependExte
|
|||||||
{
|
{
|
||||||
$this->prependRoute($container);
|
$this->prependRoute($container);
|
||||||
$this->prependCruds($container);
|
$this->prependCruds($container);
|
||||||
$this->prependTwigConfig($container);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prependTwigConfig(ContainerBuilder $container)
|
|
||||||
{
|
|
||||||
// Get the configuration for this bundle
|
|
||||||
$chillAsideActivityConfig = $container->getExtensionConfig($this->getAlias());
|
|
||||||
$config = $this->processConfiguration($this->getConfiguration($chillAsideActivityConfig, $container), $chillAsideActivityConfig);
|
|
||||||
|
|
||||||
// Add configuration to twig globals
|
|
||||||
$twigConfig = [
|
|
||||||
'globals' => [
|
|
||||||
'chill_aside_activity_config' => [
|
|
||||||
'show_concerned_persons_count' => 'visible' === $config['show_concerned_persons_count'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$container->prependExtensionConfig('twig', $twigConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function prependCruds(ContainerBuilder $container)
|
protected function prependCruds(ContainerBuilder $container)
|
||||||
|
|||||||
@@ -141,12 +141,6 @@ class Configuration implements ConfigurationInterface
|
|||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
|
||||||
->enumNode('show_concerned_persons_count')
|
|
||||||
->values(['hidden', 'visible'])
|
|
||||||
->defaultValue('hidden')
|
|
||||||
->info('Show the concerned persons count field in aside activity forms and views')
|
|
||||||
->end()
|
|
||||||
->end();
|
->end();
|
||||||
|
|
||||||
return $treeBuilder;
|
return $treeBuilder;
|
||||||
|
|||||||
@@ -62,10 +62,6 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||||
private User $updatedBy;
|
private User $updatedBy;
|
||||||
|
|
||||||
#[Assert\GreaterThanOrEqual(0)]
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: true)]
|
|
||||||
private ?int $concernedPersonsCount = 0;
|
|
||||||
|
|
||||||
public function getAgent(): ?User
|
public function getAgent(): ?User
|
||||||
{
|
{
|
||||||
return $this->agent;
|
return $this->agent;
|
||||||
@@ -190,16 +186,4 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConcernedPersonsCount(): ?int
|
|
||||||
{
|
|
||||||
return $this->concernedPersonsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setConcernedPersonsCount(?int $concernedPersonsCount): self
|
|
||||||
{
|
|
||||||
$this->concernedPersonsCount = $concernedPersonsCount;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +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\AsideActivityBundle\Export\Aggregator;
|
|
||||||
|
|
||||||
use Chill\AsideActivityBundle\Export\Declarations;
|
|
||||||
use Chill\MainBundle\Export\AggregatorInterface;
|
|
||||||
use Doctrine\ORM\QueryBuilder;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
|
|
||||||
class ByConcernedPersonsCountAggregator implements AggregatorInterface
|
|
||||||
{
|
|
||||||
public function addRole(): ?string
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
|
|
||||||
{
|
|
||||||
$qb->addSelect('aside.concernedPersonsCount AS by_concerned_persons_count_aggregator')
|
|
||||||
->addGroupBy('by_concerned_persons_count_aggregator');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function applyOn(): string
|
|
||||||
{
|
|
||||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder): void
|
|
||||||
{
|
|
||||||
// No form needed
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNormalizationVersion(): int
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function normalizeFormData(array $formData): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function denormalizeFormData(array $formData, int $fromVersion): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFormDefaultData(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data): callable
|
|
||||||
{
|
|
||||||
return function ($value): string {
|
|
||||||
if ('_header' === $value) {
|
|
||||||
return 'export.aggregator.Concerned persons count';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $value) {
|
|
||||||
return 'export.aggregator.No concerned persons count specified';
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string) $value;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQueryKeys($data): array
|
|
||||||
{
|
|
||||||
return ['by_concerned_persons_count_aggregator'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle(): string
|
|
||||||
{
|
|
||||||
return 'export.aggregator.Group by concerned persons count';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,116 +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\AsideActivityBundle\Export\Export;
|
|
||||||
|
|
||||||
use Chill\AsideActivityBundle\Export\Declarations;
|
|
||||||
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
|
|
||||||
use Chill\AsideActivityBundle\Security\AsideActivityVoter;
|
|
||||||
use Chill\MainBundle\Export\ExportInterface;
|
|
||||||
use Chill\MainBundle\Export\FormatterInterface;
|
|
||||||
use Chill\MainBundle\Export\GroupedExportInterface;
|
|
||||||
use Doctrine\ORM\Query;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
|
|
||||||
class SumConcernedPersonsCountAsideActivity implements ExportInterface, GroupedExportInterface
|
|
||||||
{
|
|
||||||
public function __construct(private readonly AsideActivityRepository $repository) {}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder) {}
|
|
||||||
|
|
||||||
public function getNormalizationVersion(): int
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function normalizeFormData(array $formData): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function denormalizeFormData(array $formData, int $fromVersion): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFormDefaultData(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAllowedFormattersTypes(): array
|
|
||||||
{
|
|
||||||
return [FormatterInterface::TYPE_TABULAR];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'export.Sum concerned persons count for aside activities';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getGroup(): string
|
|
||||||
{
|
|
||||||
return 'export.Exports of aside activities';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLabels($key, array $values, $data)
|
|
||||||
{
|
|
||||||
if ('export_sum_concerned_persons_count' !== $key) {
|
|
||||||
throw new \LogicException("the key {$key} is not used by this export");
|
|
||||||
}
|
|
||||||
|
|
||||||
$labels = array_combine($values, $values);
|
|
||||||
$labels['_header'] = $this->getTitle();
|
|
||||||
|
|
||||||
return static fn ($value) => $labels[$value];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQueryKeys($data): array
|
|
||||||
{
|
|
||||||
return ['export_sum_concerned_persons_count'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
|
|
||||||
{
|
|
||||||
return $query->getQuery()->getResult(Query::HYDRATE_SCALAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle(): string
|
|
||||||
{
|
|
||||||
return 'export.Sum concerned persons count for aside activities';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getType(): string
|
|
||||||
{
|
|
||||||
return Declarations::ASIDE_ACTIVITY_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
|
|
||||||
{
|
|
||||||
$qb = $this->repository->createQueryBuilder('aside');
|
|
||||||
|
|
||||||
$qb->select('SUM(COALESCE(aside.concernedPersonsCount, 0)) as export_sum_concerned_persons_count');
|
|
||||||
|
|
||||||
return $qb;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function requiredRole(): string
|
|
||||||
{
|
|
||||||
return AsideActivityVoter::STATS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supportsModifiers(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
Declarations::ASIDE_ACTIVITY_TYPE,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,6 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
|||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
@@ -30,13 +29,11 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
|||||||
final class AsideActivityFormType extends AbstractType
|
final class AsideActivityFormType extends AbstractType
|
||||||
{
|
{
|
||||||
private readonly array $timeChoices;
|
private readonly array $timeChoices;
|
||||||
private readonly bool $showConcernedPersonsCount;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ParameterBagInterface $parameterBag,
|
ParameterBagInterface $parameterBag,
|
||||||
) {
|
) {
|
||||||
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
|
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
|
||||||
$this->showConcernedPersonsCount = $parameterBag->get('chill_aside_activity.show_concerned_persons_count');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
@@ -79,16 +76,6 @@ final class AsideActivityFormType extends AbstractType
|
|||||||
->add('location', PickUserLocationType::class)
|
->add('location', PickUserLocationType::class)
|
||||||
;
|
;
|
||||||
|
|
||||||
if ($this->showConcernedPersonsCount) {
|
|
||||||
$builder->add('concernedPersonsCount', IntegerType::class, [
|
|
||||||
'label' => 'Concerned persons count',
|
|
||||||
'required' => false,
|
|
||||||
'attr' => [
|
|
||||||
'min' => 0,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (['duration'] as $fieldName) {
|
foreach (['duration'] as $fieldName) {
|
||||||
$builder->get($fieldName)
|
$builder->get($fieldName)
|
||||||
->addModelTransformer($durationTimeTransformer);
|
->addModelTransformer($durationTimeTransformer);
|
||||||
|
|||||||
@@ -42,11 +42,6 @@
|
|||||||
{%- if entity.location.name is defined -%}
|
{%- if entity.location.name is defined -%}
|
||||||
<div><i class="fa fa-fw fa-map-marker"></i>{{ entity.location.name }}</div>
|
<div><i class="fa fa-fw fa-map-marker"></i>{{ entity.location.name }}</div>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
{%- if entity.concernedPersonsCount > 0 -%}
|
|
||||||
<div><i class="fa fa-fw fa-user"></i>{{ entity.concernedPersonsCount }}</div>
|
|
||||||
{%- endif -%}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="item-col" style="justify-content: flex-end;">
|
<div class="item-col" style="justify-content: flex-end;">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
|||||||
@@ -38,11 +38,6 @@
|
|||||||
<dt class="inline">{{ 'Duration'|trans }}</dt>
|
<dt class="inline">{{ 'Duration'|trans }}</dt>
|
||||||
<dd>{{ entity.duration|date('H:i') }}</dd>
|
<dd>{{ entity.duration|date('H:i') }}</dd>
|
||||||
|
|
||||||
{% if chill_aside_activity_config.show_concerned_persons_count == 'visible' %}
|
|
||||||
<dt class="inline">{{ 'Concerned persons count'|trans }}</dt>
|
|
||||||
<dd>{{ entity.concernedPersonsCount }}</dd>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<dt class="inline">{{ 'Remark'|trans }}</dt>
|
<dt class="inline">{{ 'Remark'|trans }}</dt>
|
||||||
{%- if entity.note is empty -%}
|
{%- if entity.note is empty -%}
|
||||||
<dd>
|
<dd>
|
||||||
|
|||||||
@@ -1,49 +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\AsideActivityBundle\Tests\Export\Aggregator;
|
|
||||||
|
|
||||||
use Chill\AsideActivityBundle\Entity\AsideActivity;
|
|
||||||
use Chill\AsideActivityBundle\Export\Aggregator\ByConcernedPersonsCountAggregator;
|
|
||||||
use Chill\MainBundle\Test\Export\AbstractAggregatorTest;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
class ByConcernedPersonsCountAggregatorTest extends AbstractAggregatorTest
|
|
||||||
{
|
|
||||||
public function getAggregator()
|
|
||||||
{
|
|
||||||
return new ByConcernedPersonsCountAggregator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFormData(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getQueryBuilders(): iterable
|
|
||||||
{
|
|
||||||
self::bootKernel();
|
|
||||||
$em = self::getContainer()->get(EntityManagerInterface::class);
|
|
||||||
|
|
||||||
return [
|
|
||||||
$em->createQueryBuilder()
|
|
||||||
->select('count(aside.id)')
|
|
||||||
->from(AsideActivity::class, 'aside'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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\AsideActivityBundle\Tests\Export\Export;
|
|
||||||
|
|
||||||
use Chill\AsideActivityBundle\Export\Export\SumConcernedPersonsCountAsideActivity;
|
|
||||||
use Chill\AsideActivityBundle\Repository\AsideActivityRepository;
|
|
||||||
use Chill\MainBundle\Test\Export\AbstractExportTest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
final class SumConcernedPersonsCountAsideActivityTest extends AbstractExportTest
|
|
||||||
{
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
self::bootKernel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getExport()
|
|
||||||
{
|
|
||||||
$repository = self::getContainer()->get(AsideActivityRepository::class);
|
|
||||||
|
|
||||||
yield new SumConcernedPersonsCountAsideActivity($repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFormData(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getModifiersCombination(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['aside_activity'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,10 +20,6 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- { name: chill.export, alias: 'avg_aside_activity_duration' }
|
- { name: chill.export, alias: 'avg_aside_activity_duration' }
|
||||||
|
|
||||||
Chill\AsideActivityBundle\Export\Export\SumConcernedPersonsCountAsideActivity:
|
|
||||||
tags:
|
|
||||||
- { name: chill.export, alias: 'sum_aside_activity_concerned_persons_count' }
|
|
||||||
|
|
||||||
## Filters
|
## Filters
|
||||||
chill.aside_activity.export.date_filter:
|
chill.aside_activity.export.date_filter:
|
||||||
class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter
|
class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter
|
||||||
@@ -74,7 +70,3 @@ services:
|
|||||||
Chill\AsideActivityBundle\Export\Aggregator\ByLocationAggregator:
|
Chill\AsideActivityBundle\Export\Aggregator\ByLocationAggregator:
|
||||||
tags:
|
tags:
|
||||||
- { name: chill.export_aggregator, alias: 'aside_activity_location_aggregator' }
|
- { name: chill.export_aggregator, alias: 'aside_activity_location_aggregator' }
|
||||||
|
|
||||||
Chill\AsideActivityBundle\Export\Aggregator\ByConcernedPersonsCountAggregator:
|
|
||||||
tags:
|
|
||||||
- { name: chill.export_aggregator, alias: 'aside_activity_concerned_persons_count_aggregator' }
|
|
||||||
|
|||||||
@@ -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\AsideActivity;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20251006113048 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Add concernedPersonsCount property to AsideActivity entity';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_asideactivity.asideactivity ADD concernedPersonsCount INT DEFAULT 0');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_asideactivity.AsideActivity DROP concernedPersonsCount');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,7 +27,6 @@ Emergency: Urgent
|
|||||||
by: "Par "
|
by: "Par "
|
||||||
location: Lieu
|
location: Lieu
|
||||||
Asideactivity location: Localisation de l'activité
|
Asideactivity location: Localisation de l'activité
|
||||||
Concerned persons count: Nombre d'usager concernés
|
|
||||||
|
|
||||||
# Crud
|
# Crud
|
||||||
crud:
|
crud:
|
||||||
@@ -178,7 +177,7 @@ export:
|
|||||||
agent_id: Utilisateur
|
agent_id: Utilisateur
|
||||||
creator_id: Créateur
|
creator_id: Créateur
|
||||||
main_scope: Service principal de l'utilisateur
|
main_scope: Service principal de l'utilisateur
|
||||||
main_center: Territoire principal de l'utilisateur
|
main_center: Centre principal de l'utilisateur
|
||||||
aside_activity_type: Catégorie d'activité annexe
|
aside_activity_type: Catégorie d'activité annexe
|
||||||
date: Date
|
date: Date
|
||||||
duration: Durée
|
duration: Durée
|
||||||
@@ -191,7 +190,6 @@ export:
|
|||||||
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
|
Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères
|
||||||
Average aside activities duration: Durée moyenne des activités annexes
|
Average aside activities duration: Durée moyenne des activités annexes
|
||||||
Sum aside activities duration: Durée des activités annexes
|
Sum aside activities duration: Durée des activités annexes
|
||||||
Sum concerned persons count for aside activities: Nombre d'usager concernés par les activités annexes
|
|
||||||
filter:
|
filter:
|
||||||
Filter by aside activity date: Filtrer les activités annexes par date
|
Filter by aside activity date: Filtrer les activités annexes par date
|
||||||
Filter by aside activity type: Filtrer les activités annexes par type d'activité
|
Filter by aside activity type: Filtrer les activités annexes par type d'activité
|
||||||
@@ -212,8 +210,6 @@ export:
|
|||||||
'Filtered by aside activity location: only %location%': "Filtré par localisation: uniquement %location%"
|
'Filtered by aside activity location: only %location%': "Filtré par localisation: uniquement %location%"
|
||||||
aggregator:
|
aggregator:
|
||||||
Group by aside activity type: Grouper les activités annexes par type d'activité
|
Group by aside activity type: Grouper les activités annexes par type d'activité
|
||||||
Group by concerned persons count: Grouper les activités annexes par nombre d'usagers conernés
|
|
||||||
Concerned persons count: Nombre d'usagers concernés
|
|
||||||
Aside activity type: Type d'activité annexe
|
Aside activity type: Type d'activité annexe
|
||||||
by_user_job:
|
by_user_job:
|
||||||
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
|
Aggregate by user job: Grouper les activités annexes par métier des utilisateurs
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ namespace Chill\CalendarBundle\Controller;
|
|||||||
|
|
||||||
use Chill\CalendarBundle\Entity\Calendar;
|
use Chill\CalendarBundle\Entity\Calendar;
|
||||||
use Chill\CalendarBundle\Form\CalendarType;
|
use Chill\CalendarBundle\Form\CalendarType;
|
||||||
use Chill\CalendarBundle\Form\CancelType;
|
|
||||||
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
|
use Chill\CalendarBundle\RemoteCalendar\Connector\RemoteCalendarConnectorInterface;
|
||||||
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
|
use Chill\CalendarBundle\Repository\CalendarACLAwareRepositoryInterface;
|
||||||
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
use Chill\CalendarBundle\Security\Voter\CalendarVoter;
|
||||||
@@ -31,7 +30,6 @@ use Chill\PersonBundle\Repository\PersonRepository;
|
|||||||
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
|
||||||
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
use Chill\PersonBundle\Security\Authorization\PersonVoter;
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use http\Exception\UnexpectedValueException;
|
use http\Exception\UnexpectedValueException;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
@@ -62,7 +60,6 @@ class CalendarController extends AbstractController
|
|||||||
private readonly UserRepositoryInterface $userRepository,
|
private readonly UserRepositoryInterface $userRepository,
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
|
||||||
private readonly EntityManagerInterface $em,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,55 +111,6 @@ class CalendarController extends AbstractController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/{_locale}/calendar/calendar/{id}/cancel', name: 'chill_calendar_calendar_cancel')]
|
|
||||||
public function cancelAction(Calendar $calendar, Request $request): Response
|
|
||||||
{
|
|
||||||
// Deal with sms being sent or not
|
|
||||||
// Communicate cancellation with the remote calendar.
|
|
||||||
|
|
||||||
$this->denyAccessUnlessGranted(CalendarVoter::EDIT, $calendar);
|
|
||||||
|
|
||||||
[$person, $accompanyingPeriod] = [$calendar->getPerson(), $calendar->getAccompanyingPeriod()];
|
|
||||||
|
|
||||||
$form = $this->createForm(CancelType::class, $calendar);
|
|
||||||
$form->add('submit', SubmitType::class);
|
|
||||||
|
|
||||||
if ($accompanyingPeriod instanceof AccompanyingPeriod) {
|
|
||||||
$view = '@ChillCalendar/Calendar/cancelCalendarByAccompanyingCourse.html.twig';
|
|
||||||
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_period', ['id' => $accompanyingPeriod->getId()]);
|
|
||||||
} elseif ($person instanceof Person) {
|
|
||||||
$view = '@ChillCalendar/Calendar/cancelCalendarByPerson.html.twig';
|
|
||||||
$redirectRoute = $this->generateUrl('chill_calendar_calendar_list_by_person', ['id' => $person->getId()]);
|
|
||||||
} else {
|
|
||||||
throw new \RuntimeException('nor person or accompanying period');
|
|
||||||
}
|
|
||||||
|
|
||||||
$form->handleRequest($request);
|
|
||||||
|
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
|
||||||
|
|
||||||
$this->logger->notice('A calendar event has been cancelled', [
|
|
||||||
'by_user' => $this->getUser()->getUsername(),
|
|
||||||
'calendar_id' => $calendar->getId(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$calendar->setStatus($calendar::STATUS_CANCELED);
|
|
||||||
$calendar->setSmsStatus($calendar::SMS_CANCEL_PENDING);
|
|
||||||
$this->em->flush();
|
|
||||||
|
|
||||||
$this->addFlash('success', $this->translator->trans('chill_calendar.calendar_canceled'));
|
|
||||||
|
|
||||||
return new RedirectResponse($redirectRoute);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->render($view, [
|
|
||||||
'calendar' => $calendar,
|
|
||||||
'form' => $form->createView(),
|
|
||||||
'accompanyingCourse' => $accompanyingPeriod,
|
|
||||||
'person' => $person,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a calendar item.
|
* Edit a calendar item.
|
||||||
*/
|
*/
|
||||||
@@ -318,7 +266,7 @@ class CalendarController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->getUser() instanceof User) {
|
if (!$this->getUser() instanceof User) {
|
||||||
throw new UnauthorizedHttpException('you are not a user');
|
throw new UnauthorizedHttpException('you are not an user');
|
||||||
}
|
}
|
||||||
|
|
||||||
$view = '@ChillCalendar/Calendar/listByUser.html.twig';
|
$view = '@ChillCalendar/Calendar/listByUser.html.twig';
|
||||||
|
|||||||
@@ -1,58 +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\CalendarBundle\Controller;
|
|
||||||
|
|
||||||
use Chill\CalendarBundle\Entity\Calendar;
|
|
||||||
use Chill\CalendarBundle\Repository\InviteRepository;
|
|
||||||
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
|
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
|
||||||
|
|
||||||
class MyInvitationsController extends AbstractController
|
|
||||||
{
|
|
||||||
public function __construct(private readonly InviteRepository $inviteRepository, private readonly PaginatorFactory $paginator, private readonly DocGeneratorTemplateRepositoryInterface $docGeneratorTemplateRepository) {}
|
|
||||||
|
|
||||||
#[Route(path: '/{_locale}/calendar/invitations/my', name: 'chill_calendar_invitations_list_my')]
|
|
||||||
public function myInvitations(Request $request): Response
|
|
||||||
{
|
|
||||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
||||||
|
|
||||||
$user = $this->getUser();
|
|
||||||
|
|
||||||
if (!$user instanceof User) {
|
|
||||||
throw new UnauthorizedHttpException('you are not a user');
|
|
||||||
}
|
|
||||||
|
|
||||||
$total = count($this->inviteRepository->findBy(['user' => $user]));
|
|
||||||
$paginator = $this->paginator->create($total);
|
|
||||||
|
|
||||||
$invitations = $this->inviteRepository->findBy(
|
|
||||||
['user' => $user],
|
|
||||||
['createdAt' => 'DESC'],
|
|
||||||
$paginator->getItemsPerPage(),
|
|
||||||
$paginator->getCurrentPageFirstItemNumber()
|
|
||||||
);
|
|
||||||
|
|
||||||
$view = '@ChillCalendar/Invitations/listByUser.html.twig';
|
|
||||||
|
|
||||||
return $this->render($view, [
|
|
||||||
'invitations' => $invitations,
|
|
||||||
'paginator' => $paginator,
|
|
||||||
'templates' => $this->docGeneratorTemplateRepository->findByEntity(Calendar::class),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ class LoadCancelReason extends Fixture implements FixtureGroupInterface
|
|||||||
$arr = [
|
$arr = [
|
||||||
['name' => CancelReason::CANCELEDBY_USER],
|
['name' => CancelReason::CANCELEDBY_USER],
|
||||||
['name' => CancelReason::CANCELEDBY_PERSON],
|
['name' => CancelReason::CANCELEDBY_PERSON],
|
||||||
['name' => CancelReason::CANCELEDBY_OTHER],
|
['name' => CancelReason::CANCELEDBY_DONOTCOUNT],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($arr as $a) {
|
foreach ($arr as $a) {
|
||||||
|
|||||||
@@ -269,11 +269,6 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
|||||||
return $this->cancelReason;
|
return $this->cancelReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isCanceled(): bool
|
|
||||||
{
|
|
||||||
return null !== $this->cancelReason;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCenters(): ?iterable
|
public function getCenters(): ?iterable
|
||||||
{
|
{
|
||||||
return match ($this->getContext()) {
|
return match ($this->getContext()) {
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
#[ORM\Table(name: 'chill_calendar.cancel_reason')]
|
#[ORM\Table(name: 'chill_calendar.cancel_reason')]
|
||||||
class CancelReason
|
class CancelReason
|
||||||
{
|
{
|
||||||
final public const CANCELEDBY_OTHER = 'CANCELEDBY_OTHER';
|
final public const CANCELEDBY_DONOTCOUNT = 'CANCELEDBY_DONOTCOUNT';
|
||||||
|
|
||||||
final public const CANCELEDBY_PERSON = 'CANCELEDBY_PERSON';
|
final public const CANCELEDBY_PERSON = 'CANCELEDBY_PERSON';
|
||||||
|
|
||||||
final public const CANCELEDBY_USER = 'CANCELEDBY_USER';
|
final public const CANCELEDBY_USER = 'CANCELEDBY_USER';
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => true])]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN)]
|
||||||
private bool $active = true;
|
private ?bool $active = null;
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::STRING, length: 255)]
|
||||||
private ?string $canceledBy = null;
|
private ?string $canceledBy = null;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use Chill\CalendarBundle\Entity\CancelReason;
|
|||||||
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
use Chill\MainBundle\Form\Type\TranslatableStringFormType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
@@ -28,14 +28,7 @@ class CancelReasonType extends AbstractType
|
|||||||
->add('active', CheckboxType::class, [
|
->add('active', CheckboxType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
])
|
])
|
||||||
->add('canceledBy', ChoiceType::class, [
|
->add('canceledBy', TextType::class);
|
||||||
'choices' => [
|
|
||||||
'chill_calendar.canceled_by.user' => CancelReason::CANCELEDBY_USER,
|
|
||||||
'chill_calendar.canceled_by.person' => CancelReason::CANCELEDBY_PERSON,
|
|
||||||
'chill_calendar.canceled_by.other' => CancelReason::CANCELEDBY_OTHER,
|
|
||||||
],
|
|
||||||
'required' => true,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
|||||||
@@ -1,42 +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\CalendarBundle\Form;
|
|
||||||
|
|
||||||
use Chill\CalendarBundle\Entity\Calendar;
|
|
||||||
use Chill\CalendarBundle\Entity\CancelReason;
|
|
||||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
|
|
||||||
class CancelType extends AbstractType
|
|
||||||
{
|
|
||||||
public function __construct(private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
||||||
{
|
|
||||||
$builder->add('cancelReason', EntityType::class, [
|
|
||||||
'class' => CancelReason::class,
|
|
||||||
'required' => true,
|
|
||||||
'choice_label' => fn (CancelReason $cancelReason) => $this->translatableStringHelper->localize($cancelReason->getName()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
|
||||||
{
|
|
||||||
$resolver->setDefaults([
|
|
||||||
'data_class' => Calendar::class,
|
|
||||||
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,13 +25,6 @@ class UserMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
if ($this->security->isGranted('ROLE_USER')) {
|
if ($this->security->isGranted('ROLE_USER')) {
|
||||||
$menu->addChild('My calendar list', [
|
$menu->addChild('My calendar list', [
|
||||||
'route' => 'chill_calendar_calendar_list_my',
|
'route' => 'chill_calendar_calendar_list_my',
|
||||||
])
|
|
||||||
->setExtras([
|
|
||||||
'order' => 8,
|
|
||||||
'icon' => 'tasks',
|
|
||||||
]);
|
|
||||||
$menu->addChild('invite.list.title', [
|
|
||||||
'route' => 'chill_calendar_invitations_list_my',
|
|
||||||
])
|
])
|
||||||
->setExtras([
|
->setExtras([
|
||||||
'order' => 9,
|
'order' => 9,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ namespace Chill\CalendarBundle\Messenger\Doctrine;
|
|||||||
use Chill\CalendarBundle\Entity\Calendar;
|
use Chill\CalendarBundle\Entity\Calendar;
|
||||||
use Chill\CalendarBundle\Messenger\Message\CalendarMessage;
|
use Chill\CalendarBundle\Messenger\Message\CalendarMessage;
|
||||||
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;
|
use Chill\CalendarBundle\Messenger\Message\CalendarRemovedMessage;
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Doctrine\ORM\Event\PostPersistEventArgs;
|
use Doctrine\ORM\Event\PostPersistEventArgs;
|
||||||
use Doctrine\ORM\Event\PostRemoveEventArgs;
|
use Doctrine\ORM\Event\PostRemoveEventArgs;
|
||||||
use Doctrine\ORM\Event\PostUpdateEventArgs;
|
use Doctrine\ORM\Event\PostUpdateEventArgs;
|
||||||
@@ -32,17 +31,6 @@ class CalendarEntityListener
|
|||||||
{
|
{
|
||||||
public function __construct(private readonly MessageBusInterface $messageBus, private readonly Security $security) {}
|
public function __construct(private readonly MessageBusInterface $messageBus, private readonly Security $security) {}
|
||||||
|
|
||||||
private function getAuthenticatedUser(): User
|
|
||||||
{
|
|
||||||
$user = $this->security->getUser();
|
|
||||||
|
|
||||||
if (!$user instanceof User) {
|
|
||||||
throw new \LogicException('Expected an instance of User.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function postPersist(Calendar $calendar, PostPersistEventArgs $args): void
|
public function postPersist(Calendar $calendar, PostPersistEventArgs $args): void
|
||||||
{
|
{
|
||||||
if (!$calendar->preventEnqueueChanges) {
|
if (!$calendar->preventEnqueueChanges) {
|
||||||
@@ -50,7 +38,7 @@ class CalendarEntityListener
|
|||||||
new CalendarMessage(
|
new CalendarMessage(
|
||||||
$calendar,
|
$calendar,
|
||||||
CalendarMessage::CALENDAR_PERSIST,
|
CalendarMessage::CALENDAR_PERSIST,
|
||||||
$this->getAuthenticatedUser()
|
$this->security->getUser()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -62,7 +50,7 @@ class CalendarEntityListener
|
|||||||
$this->messageBus->dispatch(
|
$this->messageBus->dispatch(
|
||||||
new CalendarRemovedMessage(
|
new CalendarRemovedMessage(
|
||||||
$calendar,
|
$calendar,
|
||||||
$this->getAuthenticatedUser()
|
$this->security->getUser()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -70,19 +58,12 @@ class CalendarEntityListener
|
|||||||
|
|
||||||
public function postUpdate(Calendar $calendar, PostUpdateEventArgs $args): void
|
public function postUpdate(Calendar $calendar, PostUpdateEventArgs $args): void
|
||||||
{
|
{
|
||||||
if ($calendar->getStatus() === $calendar::STATUS_CANCELED) {
|
if (!$calendar->preventEnqueueChanges) {
|
||||||
$this->messageBus->dispatch(
|
|
||||||
new CalendarRemovedMessage(
|
|
||||||
$calendar,
|
|
||||||
$this->getAuthenticatedUser()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} elseif (!$calendar->preventEnqueueChanges) {
|
|
||||||
$this->messageBus->dispatch(
|
$this->messageBus->dispatch(
|
||||||
new CalendarMessage(
|
new CalendarMessage(
|
||||||
$calendar,
|
$calendar,
|
||||||
CalendarMessage::CALENDAR_UPDATE,
|
CalendarMessage::CALENDAR_UPDATE,
|
||||||
$this->getAuthenticatedUser()
|
$this->security->getUser()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ class CalendarRemovedMessage
|
|||||||
|
|
||||||
public function getRemoteId(): string
|
public function getRemoteId(): string
|
||||||
{
|
{
|
||||||
dump($this->remoteId);
|
|
||||||
|
|
||||||
return $this->remoteId;
|
return $this->remoteId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ class CalendarRepository implements ObjectRepository
|
|||||||
$qb->expr()->eq('c.mainUser', ':user'),
|
$qb->expr()->eq('c.mainUser', ':user'),
|
||||||
$qb->expr()->gte('c.startDate', ':startDate'),
|
$qb->expr()->gte('c.startDate', ':startDate'),
|
||||||
$qb->expr()->lte('c.endDate', ':endDate'),
|
$qb->expr()->lte('c.endDate', ':endDate'),
|
||||||
$qb->expr()->isNull('c.cancelReason'),
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
->setParameters([
|
->setParameters([
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class InviteRepository implements ObjectRepository
|
|||||||
/**
|
/**
|
||||||
* @return array|Invite[]
|
* @return array|Invite[]
|
||||||
*/
|
*/
|
||||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null)
|
||||||
{
|
{
|
||||||
return $this->entityRepository->findBy($criteria, $orderBy, $limit, $offset);
|
return $this->entityRepository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
services:
|
services:
|
||||||
Chill\CalendarBundle\Controller\:
|
Chill\CalendarBundle\Controller\:
|
||||||
autowire: true
|
autowire: true
|
||||||
autoconfigure: true
|
|
||||||
resource: '../../../Controller'
|
resource: '../../../Controller'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
{# list used in context of person, accompanyingPeriod or user #}
|
{# list used in context of person or accompanyingPeriod #}
|
||||||
|
|
||||||
<div class="item-bloc">
|
{% if calendarItems|length > 0 %}
|
||||||
<div class="item-row main">
|
<div class="flex-table list-records context-accompanyingCourse">
|
||||||
<div class="item-col">
|
|
||||||
<div class="wrap-header">
|
{% for calendar in calendarItems %}
|
||||||
<div class="wl-row">
|
|
||||||
{% if calendar.status == 'canceled' %}
|
<div class="item-bloc">
|
||||||
<div class="badge rounded-pill bg-danger">
|
<div class="item-row main">
|
||||||
<span>{{ 'chill_calendar.canceled'|trans }}: </span>
|
<div class="item-col">
|
||||||
<span>{{ calendar.cancelReason.name|localize_translatable_string }}</span>
|
<div class="wrap-header">
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="wl-row">
|
<div class="wl-row">
|
||||||
<div class="wl-col title">
|
<div class="wl-col title">
|
||||||
<p class="date-label">
|
<p class="date-label">
|
||||||
{% if calendar.status == 'canceled' %}
|
|
||||||
<del>
|
|
||||||
{% endif %}
|
|
||||||
{% if context == 'person' and calendar.context == 'accompanying_period' %}
|
{% if context == 'person' and calendar.context == 'accompanying_period' %}
|
||||||
<a href="{{ chill_path_add_return_path('chill_person_accompanying_course_index', {'accompanying_period_id': calendar.accompanyingPeriod.id}) }}" style="text-decoration: none;">
|
<a href="{{ chill_path_add_return_path('chill_person_accompanying_course_index', {'accompanying_period_id': calendar.accompanyingPeriod.id}) }}" style="text-decoration: none;">
|
||||||
<span class="badge bg-primary">
|
<span class="badge bg-primary">
|
||||||
@@ -25,9 +19,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if calendar.status == 'canceled' %}
|
|
||||||
<del>
|
|
||||||
{% endif %}
|
|
||||||
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
|
{% if calendar.endDate.diff(calendar.startDate).days >= 1 %}
|
||||||
{{ calendar.startDate|format_datetime('short', 'short') }}
|
{{ calendar.startDate|format_datetime('short', 'short') }}
|
||||||
- {{ calendar.endDate|format_datetime('short', 'short') }}
|
- {{ calendar.endDate|format_datetime('short', 'short') }}
|
||||||
@@ -35,46 +26,44 @@
|
|||||||
{{ calendar.startDate|format_datetime('short', 'short') }}
|
{{ calendar.startDate|format_datetime('short', 'short') }}
|
||||||
- {{ calendar.endDate|format_datetime('none', 'short') }}
|
- {{ calendar.endDate|format_datetime('none', 'short') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if calendar.status == 'canceled' %}
|
</p>
|
||||||
</del>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="duration short-message">
|
<div class="duration short-message">
|
||||||
<i class="fa fa-fw fa-hourglass-end"></i>
|
<i class="fa fa-fw fa-hourglass-end"></i>
|
||||||
{{ calendar.duration|date('%H:%I') }}
|
{{ calendar.duration|date('%H:%I') }}
|
||||||
{% if false == calendar.sendSMS or null == calendar.sendSMS %}
|
{% if false == calendar.sendSMS or null == calendar.sendSMS %}
|
||||||
<!-- no sms will be sent -->
|
<!-- no sms will be send -->
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if calendar.smsStatus == 'sms_sent' %}
|
{% if calendar.smsStatus == 'sms_sent' %}
|
||||||
<span title="{{ 'SMS already sent'|trans }}" class="badge bg-info">
|
<span title="{{ 'SMS already sent'|trans }}" class="badge bg-info">
|
||||||
<i class="fa fa-check "></i>
|
<i class="fa fa-check "></i>
|
||||||
<i class="fa fa-envelope "></i>
|
<i class="fa fa-envelope "></i>
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span title="{{ 'Will send SMS'|trans }}" class="badge bg-info">
|
<span title="{{ 'Will send SMS'|trans }}" class="badge bg-info">
|
||||||
<i class="fa fa-envelope "></i>
|
<i class="fa fa-envelope "></i>
|
||||||
<i class="fa fa-hourglass-end "></i>
|
<i class="fa fa-hourglass-end "></i>
|
||||||
</span>
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-col">
|
||||||
|
<ul class="list-content">
|
||||||
|
{% if calendar.mainUser is not empty %}
|
||||||
|
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box({'at_date': calendar.startDate}) }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="item-col">
|
{% if calendar.comment.comment is not empty
|
||||||
<ul class="list-content">
|
|
||||||
{% if calendar.mainUser is not empty %}
|
|
||||||
<span class="badge-user">{{ calendar.mainUser|chill_entity_render_box({'at_date': calendar.startDate}) }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if calendar.comment.comment is not empty
|
|
||||||
or calendar.users|length > 0
|
or calendar.users|length > 0
|
||||||
or calendar.thirdParties|length > 0
|
or calendar.thirdParties|length > 0
|
||||||
or calendar.users|length > 0 %}
|
or calendar.users|length > 0 %}
|
||||||
@@ -87,133 +76,131 @@
|
|||||||
} %}
|
} %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if calendar.comment.comment is not empty %}
|
|
||||||
<div class="item-row details separator">
|
|
||||||
<div class="item-col comment">
|
|
||||||
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if calendar.location is not empty %}
|
|
||||||
<div class="item-row separator">
|
|
||||||
<div>
|
|
||||||
{% if calendar.location.address is not same as(null) and calendar.location.name is not empty %}
|
|
||||||
<i class="fa fa-map-marker"></i>{% endif %}
|
|
||||||
{% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %}
|
|
||||||
{% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %}
|
|
||||||
<i class="fa fa-map-marker"></i>{% endif %}
|
|
||||||
{% if calendar.location.phonenumber1 is not empty %}<i
|
|
||||||
class="fa fa-phone"></i> {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %}
|
|
||||||
{% if calendar.location.phonenumber2 is not empty %}<i
|
|
||||||
class="fa fa-phone"></i> {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if calendar.documents is not empty %}
|
|
||||||
<div class="item-row separator column">
|
|
||||||
<div>
|
|
||||||
{{ include('@ChillCalendar/Calendar/_documents.twig.html') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if calendar.activity is not null %}
|
|
||||||
<div class="item-row separator">
|
|
||||||
<div class="item-col">
|
|
||||||
<div class="wrap-list">
|
|
||||||
<div class="wl-row">
|
|
||||||
<div class="wl-col title"><h3>{{ 'Activity'|trans }}</h3></div>
|
|
||||||
<div class="wl-col list activity-linked">
|
|
||||||
<h2 class="badge-title">
|
|
||||||
<span class="title_label"></span>
|
|
||||||
<span class="title_action">
|
|
||||||
{{ calendar.activity.type.name | localize_translatable_string }}
|
|
||||||
|
|
||||||
{% if calendar.activity.emergency %}
|
|
||||||
<span class="badge bg-danger rounded-pill fs-6 float-end">{{ 'Emergency'|trans|upper }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<ul class="record_actions">
|
|
||||||
<li class="cancel">
|
|
||||||
<span class="createdBy">
|
|
||||||
{{ 'Created by'|trans }}
|
|
||||||
<b>{{ calendar.activity.createdBy|chill_entity_render_string({'at_date': calendar.activity.createdAt}) }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
{% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %}
|
|
||||||
<li>
|
|
||||||
<a href="{{ chill_path_add_return_path('chill_activity_activity_show', {'id': calendar.activity.id}) }}" class="btn btn-sm btn-show" ></a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
|
{% if calendar.comment.comment is not empty %}
|
||||||
|
<div class="item-row details separator">
|
||||||
|
<div class="item-col comment">
|
||||||
|
{{ calendar.comment|chill_entity_render_box( { 'limit_lines': 3, 'metadata': false } ) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="item-row separator">
|
|
||||||
<ul class="record_actions">
|
|
||||||
{% if is_granted('CHILL_CALENDAR_DOC_EDIT', calendar) and calendar.status is not constant('STATUS_CANCELED', calendar) %}
|
|
||||||
{% if templates|length == 0 %}
|
|
||||||
<li>
|
|
||||||
<a class="btn btn-create"
|
|
||||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
|
||||||
{{ 'chill_calendar.Add a document'|trans }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li>
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-create dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
{{ 'chill_calendar.Add a document'|trans }}
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item"
|
|
||||||
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
|
||||||
{{ 'chill_calendar.Upload a document'|trans }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% for template in templates %}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item"
|
|
||||||
href="{{ chill_path_add_return_path('chill_docgenerator_generate_from_template', {'template': template.id, 'entityClassName': 'Chill\\CalendarBundle\\Entity\\Calendar', 'entityId': calendar.id}) }}"
|
|
||||||
>
|
|
||||||
{{ template.name|localize_translatable_string }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if calendar.activity is null and (
|
|
||||||
(calendar.context == 'accompanying_period' and is_granted('CHILL_ACTIVITY_CREATE', calendar.accompanyingPeriod))
|
|
||||||
or
|
|
||||||
(calendar.context == 'person' and is_granted('CHILL_ACTIVITY_CREATE', calendar.person))
|
|
||||||
)
|
|
||||||
and calendar.status is not constant('STATUS_CANCELED', calendar)
|
|
||||||
%}
|
|
||||||
<li>
|
|
||||||
<a class="btn btn-create"
|
|
||||||
href="{{ chill_path_add_return_path('chill_calendar_calendar_to_activity', { 'id': calendar.id }) }}">
|
|
||||||
{{ 'Transform to activity'|trans }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if calendar.isInvited(app.user) and not calendar.isCanceled %}
|
{% if calendar.location is not empty %}
|
||||||
|
<div class="item-row separator">
|
||||||
|
<div>
|
||||||
|
{% if calendar.location.address is not same as(null) and calendar.location.name is not empty %}
|
||||||
|
<i class="fa fa-map-marker"></i>{% endif %}
|
||||||
|
{% if calendar.location.name is not empty %}{{ calendar.location.name }}{% endif %}
|
||||||
|
{% if calendar.location.address is not same as(null) %}{{ calendar.location.address|chill_entity_render_box({'multiline': false, 'with_picto': (calendar.location.name is empty)}) }}{% else %}
|
||||||
|
<i class="fa fa-map-marker"></i>{% endif %}
|
||||||
|
{% if calendar.location.phonenumber1 is not empty %}<i
|
||||||
|
class="fa fa-phone"></i> {{ calendar.location.phonenumber1|chill_format_phonenumber }}{% endif %}
|
||||||
|
{% if calendar.location.phonenumber2 is not empty %}<i
|
||||||
|
class="fa fa-phone"></i> {{ calendar.location.phonenumber2|chill_format_phonenumber }}{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="item-row separator column">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
{{ include('@ChillCalendar/Calendar/_documents.twig.html') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if calendar.activity is not null %}
|
||||||
|
<div class="item-row separator">
|
||||||
|
<div class="item-col">
|
||||||
|
<div class="wrap-list">
|
||||||
|
<div class="wl-row">
|
||||||
|
<div class="wl-col title"><h3>{{ 'Activity'|trans }}</h3></div>
|
||||||
|
<div class="wl-col list activity-linked">
|
||||||
|
<h2 class="badge-title">
|
||||||
|
<span class="title_label"></span>
|
||||||
|
<span class="title_action">
|
||||||
|
{{ calendar.activity.type.name | localize_translatable_string }}
|
||||||
|
|
||||||
|
{% if calendar.activity.emergency %}
|
||||||
|
<span class="badge bg-danger rounded-pill fs-6 float-end">{{ 'Emergency'|trans|upper }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<ul class="record_actions">
|
||||||
|
<li class="cancel">
|
||||||
|
<span class="createdBy">
|
||||||
|
{{ 'Created by'|trans }}
|
||||||
|
<b>{{ calendar.activity.createdBy|chill_entity_render_string({'at_date': calendar.activity.createdAt}) }}</b>, {{ 'on'|trans }} {{ calendar.activity.createdAt|format_datetime('short', 'short') }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% if is_granted('CHILL_ACTIVITY_SEE', calendar.activity) %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ chill_path_add_return_path('chill_activity_activity_show', {'id': calendar.activity.id}) }}" class="btn btn-sm btn-show" ></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="item-row separator">
|
||||||
|
<ul class="record_actions">
|
||||||
|
{% if is_granted('CHILL_CALENDAR_DOC_EDIT', calendar) %}
|
||||||
|
{% if templates|length == 0 %}
|
||||||
|
<li>
|
||||||
|
<a class="btn btn-create"
|
||||||
|
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
||||||
|
{{ 'chill_calendar.Add a document'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-create dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
{{ 'chill_calendar.Add a document'|trans }}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item"
|
||||||
|
href="{{ chill_path_add_return_path('chill_calendar_calendardoc_new', {'id': calendar.id }) }}">
|
||||||
|
{{ 'chill_calendar.Upload a document'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% for template in templates %}
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item"
|
||||||
|
href="{{ chill_path_add_return_path('chill_docgenerator_generate_from_template', {'template': template.id, 'entityClassName': 'Chill\\CalendarBundle\\Entity\\Calendar', 'entityId': calendar.id}) }}"
|
||||||
|
>
|
||||||
|
{{ template.name|localize_translatable_string }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if calendar.activity is null and (
|
||||||
|
(calendar.context == 'accompanying_period' and is_granted('CHILL_ACTIVITY_CREATE', calendar.accompanyingPeriod))
|
||||||
|
or
|
||||||
|
(calendar.context == 'person' and is_granted('CHILL_ACTIVITY_CREATE', calendar.person))
|
||||||
|
)
|
||||||
|
%}
|
||||||
|
<li>
|
||||||
|
<a class="btn btn-create"
|
||||||
|
href="{{ chill_path_add_return_path('chill_calendar_calendar_to_activity', { 'id': calendar.id }) }}">
|
||||||
|
{{ 'Transform to activity'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if (calendar.isInvited(app.user)) %}
|
||||||
{% set invite = calendar.inviteForUser(app.user) %}
|
{% set invite = calendar.inviteForUser(app.user) %}
|
||||||
<li>
|
<li>
|
||||||
<div invite-answer data-status="{{ invite.status|e('html_attr') }}"
|
<div invite-answer data-status="{{ invite.status|e('html_attr') }}"
|
||||||
@@ -226,18 +213,12 @@
|
|||||||
class="btn btn-show "></a>
|
class="btn btn-show "></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', calendar) %}
|
||||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_EDIT', calendar) and calendar.status is not constant('STATUS_CANCELED', calendar) %}
|
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', { 'id': calendar.id }) }}"
|
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_edit', { 'id': calendar.id }) }}"
|
||||||
class="btn btn-update "></a>
|
class="btn btn-update "></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_cancel', { 'id': calendar.id } ) }}"
|
|
||||||
class="btn btn-action"><i class="bi bi-x-circle"></i> {{ 'Cancel'|trans }}</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if is_granted('CHILL_CALENDAR_CALENDAR_DELETE', calendar) %}
|
{% if is_granted('CHILL_CALENDAR_CALENDAR_DELETE', calendar) %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_delete', { 'id': calendar.id } ) }}"
|
<a href="{{ chill_path_add_return_path('chill_calendar_calendar_delete', { 'id': calendar.id } ) }}"
|
||||||
@@ -246,8 +227,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if calendarItems|length < paginator.getTotalItems %}
|
||||||
|
{{ chill_pagination(paginator) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
{% extends "@ChillPerson/AccompanyingCourse/layout.html.twig" %}
|
|
||||||
|
|
||||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
|
||||||
|
|
||||||
{% block title 'chill_calendar.cancel_calendar_item'|trans %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{{ form_start(form) }}
|
|
||||||
|
|
||||||
{{ form_row(form.cancelReason) }}
|
|
||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
|
||||||
<li class="cancel">
|
|
||||||
<a
|
|
||||||
class="btn btn-cancel"
|
|
||||||
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'id': accompanyingCourse.id } )}}"
|
|
||||||
>
|
|
||||||
{{ 'Cancel'|trans|chill_return_path_label }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{ form_widget(form.submit, { 'attr' : { 'class': 'btn btn-save' }, 'label': 'Save' } ) }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{ form_end(form) }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{% extends "@ChillPerson/Person/layout.html.twig" %}
|
|
||||||
|
|
||||||
{% set activeRouteKey = 'chill_calendar_calendar_list' %}
|
|
||||||
|
|
||||||
{% block title 'chill_calendar.cancel_calendar_item'|trans %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{{ form_start(form) }}
|
|
||||||
|
|
||||||
{{ form_row(form.cancelReason) }}
|
|
||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
|
||||||
<li class="cancel">
|
|
||||||
<a
|
|
||||||
class="btn btn-cancel"
|
|
||||||
href="{{ chill_return_path_or('chill_calendar_calendar_list', { 'id': person.id } )}}"
|
|
||||||
>
|
|
||||||
{{ 'Cancel'|trans|chill_return_path_label }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{ form_widget(form.submit, { 'attr' : { 'class': 'btn btn-save' }, 'label': 'Save' } ) }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{ form_end(form) }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -34,18 +34,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if calendarItems|length > 0 %}
|
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }}
|
||||||
<div class="flex-table list-records context-accompanyingCourse">
|
|
||||||
{% for calendar in calendarItems %}
|
|
||||||
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'accompanying_course'}) }}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if calendarItems|length < paginator.getTotalItems %}
|
|
||||||
{{ chill_pagination(paginator) }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
|
|||||||
@@ -33,17 +33,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if calendarItems|length > 0 %}
|
{{ include ('@ChillCalendar/Calendar/_list.html.twig', {context: 'person'}) }}
|
||||||
<div class="flex-table list-records context-person">
|
|
||||||
{% for calendar in calendarItems %}
|
|
||||||
{{ include ('@ChillCalendar/Calendar/_list.html.twig', {context: 'person'}) }}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if calendarItems|length < paginator.getTotalItems %}
|
|
||||||
{{ chill_pagination(paginator) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<ul class="record_actions sticky-form-buttons">
|
<ul class="record_actions sticky-form-buttons">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
{% block table_entities_thead_tr %}
|
{% block table_entities_thead_tr %}
|
||||||
<th>{{ 'Id'|trans }}</th>
|
<th>{{ 'Id'|trans }}</th>
|
||||||
<th>{{ 'Name'|trans }}</th>
|
<th>{{ 'Name'|trans }}</th>
|
||||||
<th>{{ 'Canceled by'|trans }}</th>
|
<th>{{ 'canceledBy'|trans }}</th>
|
||||||
<th>{{ 'active'|trans }}</th>
|
<th>{{ 'active'|trans }}</th>
|
||||||
<th> </th>
|
<th> </th>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
{% extends "@ChillMain/layout.html.twig" %}
|
|
||||||
|
|
||||||
{% set activeRouteKey = 'chill_calendar_invitations_list' %}
|
|
||||||
|
|
||||||
{% block title %}{{ 'invite.list.title'|trans }}{% endblock title %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{{ 'invite.list.title'|trans }}</h1>
|
|
||||||
|
|
||||||
{% if invitations|length == 0 %}
|
|
||||||
<p class="chill-no-data-statement">
|
|
||||||
{{ "invite.list.none"|trans }}
|
|
||||||
</p>
|
|
||||||
{% else %}
|
|
||||||
<div class="flex-table list-records">
|
|
||||||
{% for invitation in invitations %}
|
|
||||||
{% set calendar = invitation.getCalendar %}
|
|
||||||
{{ include('@ChillCalendar/Calendar/_list.html.twig', {context: 'user'}) }}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if invitations|length < paginator.getTotalItems %}
|
|
||||||
{{ chill_pagination(paginator) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
{{ parent() }}
|
|
||||||
{{ encore_entry_script_tags('mod_answer') }}
|
|
||||||
{{ encore_entry_script_tags('mod_document_action_buttons_group') }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block css %}
|
|
||||||
{{ parent() }}
|
|
||||||
{{ encore_entry_link_tags('mod_answer') }}
|
|
||||||
{{ encore_entry_link_tags('mod_document_action_buttons_group') }}
|
|
||||||
{% endblock %}
|
|
||||||
@@ -19,7 +19,6 @@ declare(strict_types=1);
|
|||||||
namespace Chill\CalendarBundle\Service\ShortMessageNotification;
|
namespace Chill\CalendarBundle\Service\ShortMessageNotification;
|
||||||
|
|
||||||
use Chill\CalendarBundle\Entity\Calendar;
|
use Chill\CalendarBundle\Entity\Calendar;
|
||||||
use Chill\CalendarBundle\Entity\CancelReason;
|
|
||||||
use libphonenumber\PhoneNumberFormat;
|
use libphonenumber\PhoneNumberFormat;
|
||||||
use libphonenumber\PhoneNumberUtil;
|
use libphonenumber\PhoneNumberUtil;
|
||||||
use Symfony\Component\Notifier\Message\SmsMessage;
|
use Symfony\Component\Notifier\Message\SmsMessage;
|
||||||
@@ -58,7 +57,7 @@ class DefaultShortMessageForCalendarBuilder implements ShortMessageForCalendarBu
|
|||||||
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
|
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
|
||||||
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message.txt.twig', ['calendar' => $calendar]),
|
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message.txt.twig', ['calendar' => $calendar]),
|
||||||
);
|
);
|
||||||
} elseif (Calendar::SMS_CANCEL_PENDING === $calendar->getSmsStatus() && (null === $calendar->getCancelReason() || CancelReason::CANCELEDBY_PERSON !== $calendar->getCancelReason()->getCanceledBy())) {
|
} elseif (Calendar::SMS_CANCEL_PENDING === $calendar->getSmsStatus()) {
|
||||||
$toUsers[] = new SmsMessage(
|
$toUsers[] = new SmsMessage(
|
||||||
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
|
$this->phoneUtil->format($person->getMobilenumber(), PhoneNumberFormat::E164),
|
||||||
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message_canceled.txt.twig', ['calendar' => $calendar]),
|
$this->engine->render('@ChillCalendar/CalendarShortMessage/short_message_canceled.txt.twig', ['calendar' => $calendar]),
|
||||||
|
|||||||
@@ -1,292 +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\CalendarBundle\Tests\Controller;
|
|
||||||
|
|
||||||
use Chill\CalendarBundle\Controller\MyInvitationsController;
|
|
||||||
use Chill\CalendarBundle\Entity\Calendar;
|
|
||||||
use Chill\CalendarBundle\Entity\Invite;
|
|
||||||
use Chill\CalendarBundle\Repository\InviteRepository;
|
|
||||||
use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface;
|
|
||||||
use Chill\MainBundle\Entity\User;
|
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
|
||||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use Prophecy\PhpUnit\ProphecyTrait;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
|
||||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
|
||||||
use Twig\Environment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
final class MyInvitationsControllerTest extends TestCase
|
|
||||||
{
|
|
||||||
use ProphecyTrait;
|
|
||||||
|
|
||||||
private MyInvitationsController $controller;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
// Create prophecies for dependencies
|
|
||||||
$inviteRepository = $this->prophesize(InviteRepository::class);
|
|
||||||
$paginatorFactory = $this->prophesize(PaginatorFactory::class);
|
|
||||||
$docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
|
|
||||||
|
|
||||||
// Create controller instance
|
|
||||||
$this->controller = new MyInvitationsController(
|
|
||||||
$inviteRepository->reveal(),
|
|
||||||
$paginatorFactory->reveal(),
|
|
||||||
$docGeneratorTemplateRepository->reveal()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up necessary services for AbstractController
|
|
||||||
$authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
|
|
||||||
$tokenStorage = $this->prophesize(TokenStorageInterface::class);
|
|
||||||
$twig = $this->prophesize(Environment::class);
|
|
||||||
|
|
||||||
// Use reflection to set the container
|
|
||||||
$reflection = new \ReflectionClass($this->controller);
|
|
||||||
$containerProperty = $reflection->getParentClass()->getProperty('container');
|
|
||||||
$containerProperty->setAccessible(true);
|
|
||||||
|
|
||||||
// Create a mock container
|
|
||||||
$container = $this->prophesize(\Psr\Container\ContainerInterface::class);
|
|
||||||
$container->has('security.authorization_checker')->willReturn(true);
|
|
||||||
$container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
|
|
||||||
$container->has('security.token_storage')->willReturn(true);
|
|
||||||
$container->get('security.token_storage')->willReturn($tokenStorage->reveal());
|
|
||||||
$container->has('twig')->willReturn(true);
|
|
||||||
$container->get('twig')->willReturn($twig->reveal());
|
|
||||||
|
|
||||||
$containerProperty->setValue($this->controller, $container->reveal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMyInvitationsReturnsCorrectAmountOfInvitations(): void
|
|
||||||
{
|
|
||||||
// Create test user
|
|
||||||
$user = new User();
|
|
||||||
$user->setUsername('testuser');
|
|
||||||
|
|
||||||
// Create test invitations
|
|
||||||
$invite1 = new Invite();
|
|
||||||
$invite1->setUser($user);
|
|
||||||
$invite1->setStatus(Invite::PENDING);
|
|
||||||
|
|
||||||
$invite2 = new Invite();
|
|
||||||
$invite2->setUser($user);
|
|
||||||
$invite2->setStatus(Invite::ACCEPTED);
|
|
||||||
|
|
||||||
$invite3 = new Invite();
|
|
||||||
$invite3->setUser($user);
|
|
||||||
$invite3->setStatus(Invite::DECLINED);
|
|
||||||
|
|
||||||
$allInvitations = [$invite1, $invite2, $invite3];
|
|
||||||
$paginatedInvitations = [$invite1, $invite2]; // First page with 2 items per page
|
|
||||||
|
|
||||||
// Set up repository prophecies
|
|
||||||
$inviteRepository = $this->prophesize(InviteRepository::class);
|
|
||||||
$inviteRepository->findBy(['user' => $user])->willReturn($allInvitations);
|
|
||||||
$inviteRepository->findBy(
|
|
||||||
['user' => $user],
|
|
||||||
['createdAt' => 'DESC'],
|
|
||||||
2, // items per page
|
|
||||||
0 // offset
|
|
||||||
)->willReturn($paginatedInvitations);
|
|
||||||
|
|
||||||
// Set up paginator prophecies
|
|
||||||
$paginator = $this->prophesize(PaginatorInterface::class);
|
|
||||||
$paginator->getItemsPerPage()->willReturn(2);
|
|
||||||
$paginator->getCurrentPageFirstItemNumber()->willReturn(0);
|
|
||||||
|
|
||||||
$paginatorFactory = $this->prophesize(PaginatorFactory::class);
|
|
||||||
$paginatorFactory->create(3)->willReturn($paginator->reveal());
|
|
||||||
|
|
||||||
// Set up doc generator repository
|
|
||||||
$docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
|
|
||||||
$docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]);
|
|
||||||
|
|
||||||
// Create controller with mocked dependencies
|
|
||||||
$controller = new MyInvitationsController(
|
|
||||||
$inviteRepository->reveal(),
|
|
||||||
$paginatorFactory->reveal(),
|
|
||||||
$docGeneratorTemplateRepository->reveal()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up authorization checker to return true for ROLE_USER
|
|
||||||
$authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
|
|
||||||
$authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true);
|
|
||||||
|
|
||||||
// Set up token storage to return user
|
|
||||||
$token = $this->prophesize(TokenInterface::class);
|
|
||||||
$token->getUser()->willReturn($user);
|
|
||||||
$tokenStorage = $this->prophesize(TokenStorageInterface::class);
|
|
||||||
$tokenStorage->getToken()->willReturn($token->reveal());
|
|
||||||
|
|
||||||
// Set up twig to return a response
|
|
||||||
$twig = $this->prophesize(Environment::class);
|
|
||||||
$twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [
|
|
||||||
'invitations' => $paginatedInvitations,
|
|
||||||
'paginator' => $paginator->reveal(),
|
|
||||||
'templates' => [],
|
|
||||||
])->willReturn('rendered content');
|
|
||||||
|
|
||||||
// Set up container
|
|
||||||
$container = $this->prophesize(\Psr\Container\ContainerInterface::class);
|
|
||||||
$container->has('security.authorization_checker')->willReturn(true);
|
|
||||||
$container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
|
|
||||||
$container->has('security.token_storage')->willReturn(true);
|
|
||||||
$container->get('security.token_storage')->willReturn($tokenStorage->reveal());
|
|
||||||
$container->has('twig')->willReturn(true);
|
|
||||||
$container->get('twig')->willReturn($twig->reveal());
|
|
||||||
|
|
||||||
// Use reflection to set the container
|
|
||||||
$reflection = new \ReflectionClass($controller);
|
|
||||||
$containerProperty = $reflection->getParentClass()->getProperty('container');
|
|
||||||
$containerProperty->setAccessible(true);
|
|
||||||
$containerProperty->setValue($controller, $container->reveal());
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
$request = new Request();
|
|
||||||
|
|
||||||
// Execute the action
|
|
||||||
$response = $controller->myInvitations($request);
|
|
||||||
|
|
||||||
// Assert that response is successful
|
|
||||||
self::assertInstanceOf(Response::class, $response);
|
|
||||||
self::assertSame(200, $response->getStatusCode());
|
|
||||||
self::assertSame('rendered content', $response->getContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMyInvitationsPageLoads(): void
|
|
||||||
{
|
|
||||||
// Create test user
|
|
||||||
$user = new User();
|
|
||||||
$user->setUsername('testuser');
|
|
||||||
|
|
||||||
// Set up repository prophecies - no invitations
|
|
||||||
$inviteRepository = $this->prophesize(InviteRepository::class);
|
|
||||||
$inviteRepository->findBy(['user' => $user])->willReturn([]);
|
|
||||||
$inviteRepository->findBy(
|
|
||||||
['user' => $user],
|
|
||||||
['createdAt' => 'DESC'],
|
|
||||||
20, // default items per page
|
|
||||||
0 // offset
|
|
||||||
)->willReturn([]);
|
|
||||||
|
|
||||||
// Set up paginator prophecies
|
|
||||||
$paginator = $this->prophesize(PaginatorInterface::class);
|
|
||||||
$paginator->getItemsPerPage()->willReturn(20);
|
|
||||||
$paginator->getCurrentPageFirstItemNumber()->willReturn(0);
|
|
||||||
|
|
||||||
$paginatorFactory = $this->prophesize(PaginatorFactory::class);
|
|
||||||
$paginatorFactory->create(0)->willReturn($paginator->reveal());
|
|
||||||
|
|
||||||
// Set up doc generator repository
|
|
||||||
$docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
|
|
||||||
$docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]);
|
|
||||||
|
|
||||||
// Create controller with mocked dependencies
|
|
||||||
$controller = new MyInvitationsController(
|
|
||||||
$inviteRepository->reveal(),
|
|
||||||
$paginatorFactory->reveal(),
|
|
||||||
$docGeneratorTemplateRepository->reveal()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up authorization checker to return true for ROLE_USER
|
|
||||||
$authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
|
|
||||||
$authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true);
|
|
||||||
|
|
||||||
// Set up token storage to return user
|
|
||||||
$token = $this->prophesize(TokenInterface::class);
|
|
||||||
$token->getUser()->willReturn($user);
|
|
||||||
$tokenStorage = $this->prophesize(TokenStorageInterface::class);
|
|
||||||
$tokenStorage->getToken()->willReturn($token->reveal());
|
|
||||||
|
|
||||||
// Set up twig to return a response
|
|
||||||
$twig = $this->prophesize(Environment::class);
|
|
||||||
$twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [
|
|
||||||
'invitations' => [],
|
|
||||||
'paginator' => $paginator->reveal(),
|
|
||||||
'templates' => [],
|
|
||||||
])->willReturn('empty page content');
|
|
||||||
|
|
||||||
// Set up container
|
|
||||||
$container = $this->prophesize(\Psr\Container\ContainerInterface::class);
|
|
||||||
$container->has('security.authorization_checker')->willReturn(true);
|
|
||||||
$container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
|
|
||||||
$container->has('security.token_storage')->willReturn(true);
|
|
||||||
$container->get('security.token_storage')->willReturn($tokenStorage->reveal());
|
|
||||||
$container->has('twig')->willReturn(true);
|
|
||||||
$container->get('twig')->willReturn($twig->reveal());
|
|
||||||
|
|
||||||
// Use reflection to set the container
|
|
||||||
$reflection = new \ReflectionClass($controller);
|
|
||||||
$containerProperty = $reflection->getParentClass()->getProperty('container');
|
|
||||||
$containerProperty->setAccessible(true);
|
|
||||||
$containerProperty->setValue($controller, $container->reveal());
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
$request = new Request();
|
|
||||||
|
|
||||||
// Execute the action
|
|
||||||
$response = $controller->myInvitations($request);
|
|
||||||
|
|
||||||
// Assert that page loads successfully
|
|
||||||
self::assertInstanceOf(Response::class, $response);
|
|
||||||
self::assertSame(200, $response->getStatusCode());
|
|
||||||
self::assertSame('empty page content', $response->getContent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMyInvitationsRequiresAuthentication(): void
|
|
||||||
{
|
|
||||||
// Create controller with minimal dependencies
|
|
||||||
$inviteRepository = $this->prophesize(InviteRepository::class);
|
|
||||||
$paginatorFactory = $this->prophesize(PaginatorFactory::class);
|
|
||||||
$docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class);
|
|
||||||
|
|
||||||
$controller = new MyInvitationsController(
|
|
||||||
$inviteRepository->reveal(),
|
|
||||||
$paginatorFactory->reveal(),
|
|
||||||
$docGeneratorTemplateRepository->reveal()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up authorization checker to return false for ROLE_USER
|
|
||||||
$authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class);
|
|
||||||
$authorizationChecker->isGranted('ROLE_USER')->willReturn(false);
|
|
||||||
$authorizationChecker->isGranted('ROLE_USER', null)->willReturn(false);
|
|
||||||
|
|
||||||
// Set up container
|
|
||||||
$container = $this->prophesize(\Psr\Container\ContainerInterface::class);
|
|
||||||
$container->has('security.authorization_checker')->willReturn(true);
|
|
||||||
$container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal());
|
|
||||||
|
|
||||||
// Use reflection to set the container
|
|
||||||
$reflection = new \ReflectionClass($controller);
|
|
||||||
$containerProperty = $reflection->getParentClass()->getProperty('container');
|
|
||||||
$containerProperty->setAccessible(true);
|
|
||||||
$containerProperty->setValue($controller, $container->reveal());
|
|
||||||
|
|
||||||
// Create request
|
|
||||||
$request = new Request();
|
|
||||||
|
|
||||||
// Expect AccessDeniedException
|
|
||||||
$this->expectException(\Symfony\Component\Security\Core\Exception\AccessDeniedException::class);
|
|
||||||
|
|
||||||
// Execute the action
|
|
||||||
$controller->myInvitations($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,8 @@ Will send SMS: Un SMS de rappel sera envoyé
|
|||||||
Will not send SMS: Aucun SMS de rappel ne sera envoyé
|
Will not send SMS: Aucun SMS de rappel ne sera envoyé
|
||||||
SMS already sent: Un SMS a été envoyé
|
SMS already sent: Un SMS a été envoyé
|
||||||
|
|
||||||
Canceled by: Annulé par
|
canceledBy: supprimé par
|
||||||
|
Canceled by: supprimé par
|
||||||
Calendar configuration: Gestion des rendez-vous
|
Calendar configuration: Gestion des rendez-vous
|
||||||
|
|
||||||
crud:
|
crud:
|
||||||
@@ -43,14 +44,6 @@ crud:
|
|||||||
title_edit: Modifier le motif d'annulation
|
title_edit: Modifier le motif d'annulation
|
||||||
|
|
||||||
chill_calendar:
|
chill_calendar:
|
||||||
canceled: Annulé
|
|
||||||
cancel_reason: Raison d'annulation
|
|
||||||
cancel_calendar_item: Annuler rendez-vous
|
|
||||||
calendar_canceled: Le rendez-vous a été annulé
|
|
||||||
canceled_by:
|
|
||||||
user: Utilisateur
|
|
||||||
person: Usager
|
|
||||||
other: Autre
|
|
||||||
Document: Document d'un rendez-vous
|
Document: Document d'un rendez-vous
|
||||||
form:
|
form:
|
||||||
The main user is mandatory. He will organize the appointment.: L'utilisateur principal est obligatoire. Il est l'organisateur de l'événement.
|
The main user is mandatory. He will organize the appointment.: L'utilisateur principal est obligatoire. Il est l'organisateur de l'événement.
|
||||||
@@ -93,9 +86,6 @@ invite:
|
|||||||
declined: Refusé
|
declined: Refusé
|
||||||
pending: En attente
|
pending: En attente
|
||||||
tentative: Accepté provisoirement
|
tentative: Accepté provisoirement
|
||||||
list:
|
|
||||||
none: Il n'y aucun invitation
|
|
||||||
title: Mes invitations
|
|
||||||
|
|
||||||
# exports
|
# exports
|
||||||
Exports of calendar: Exports des rendez-vous
|
Exports of calendar: Exports des rendez-vous
|
||||||
|
|||||||
@@ -20,9 +20,4 @@ use Doctrine\Persistence\ObjectRepository;
|
|||||||
interface DocGeneratorTemplateRepositoryInterface extends ObjectRepository
|
interface DocGeneratorTemplateRepositoryInterface extends ObjectRepository
|
||||||
{
|
{
|
||||||
public function countByEntity(string $entity): int;
|
public function countByEntity(string $entity): int;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array|DocGeneratorTemplate[]
|
|
||||||
*/
|
|
||||||
public function findByEntity(string $entity, ?int $start = 0, ?int $limit = 50): array;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ final class EventController extends AbstractController
|
|||||||
'class' => Center::class,
|
'class' => Center::class,
|
||||||
'choices' => $centers,
|
'choices' => $centers,
|
||||||
'placeholder' => $this->translator->trans('Pick a center'),
|
'placeholder' => $this->translator->trans('Pick a center'),
|
||||||
'label' => 'To which territory should the event be associated ?',
|
'label' => 'To which centre should the event be associated ?',
|
||||||
])
|
])
|
||||||
->add('submit', SubmitType::class, [
|
->add('submit', SubmitType::class, [
|
||||||
'label' => 'Next step',
|
'label' => 'Next step',
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ CHILL_EVENT_PARTICIPATION_SEE_DETAILS: Voir le détail d'une participation
|
|||||||
|
|
||||||
# TODO check place to put this
|
# TODO check place to put this
|
||||||
Next step: Étape suivante
|
Next step: Étape suivante
|
||||||
To which territory should the event be associated ?: À quel territoire doit être associé l'événement ?
|
To which centre should the event be associated ?: À quel centre doit être associé l'événement ?
|
||||||
|
|
||||||
# timeline
|
# timeline
|
||||||
past: passé
|
past: passé
|
||||||
@@ -151,7 +151,7 @@ event:
|
|||||||
filter:
|
filter:
|
||||||
event_types: Par types d'événement
|
event_types: Par types d'événement
|
||||||
event_dates: Par date d'événement
|
event_dates: Par date d'événement
|
||||||
center: Par territoire
|
center: Par centre
|
||||||
by_responsable: Par responsable
|
by_responsable: Par responsable
|
||||||
pick_responsable: Filtrer par responsables
|
pick_responsable: Filtrer par responsables
|
||||||
budget:
|
budget:
|
||||||
@@ -188,7 +188,7 @@ event_id: Identifiant
|
|||||||
event_name: Nom
|
event_name: Nom
|
||||||
event_date: Date
|
event_date: Date
|
||||||
event_type: Type d'évenement
|
event_type: Type d'évenement
|
||||||
event_center: Territoire
|
event_center: Centre
|
||||||
event_moderator: Responsable
|
event_moderator: Responsable
|
||||||
event_participants_count: Nombre de participants
|
event_participants_count: Nombre de participants
|
||||||
event_location: Localisation
|
event_location: Localisation
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class CRUDController extends AbstractController
|
|||||||
Resolver::class => Resolver::class,
|
Resolver::class => Resolver::class,
|
||||||
SerializerInterface::class => SerializerInterface::class,
|
SerializerInterface::class => SerializerInterface::class,
|
||||||
FilterOrderHelperFactoryInterface::class => FilterOrderHelperFactoryInterface::class,
|
FilterOrderHelperFactoryInterface::class => FilterOrderHelperFactoryInterface::class,
|
||||||
|
ManagerRegistry::class => ManagerRegistry::class,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -673,7 +674,7 @@ class CRUDController extends AbstractController
|
|||||||
|
|
||||||
protected function getManagerRegistry(): ManagerRegistry
|
protected function getManagerRegistry(): ManagerRegistry
|
||||||
{
|
{
|
||||||
return $this->container->get('doctrine');
|
return $this->container->get(ManagerRegistry::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?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\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\Location;
|
||||||
|
use Chill\MainBundle\Entity\LocationType;
|
||||||
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
|
||||||
|
class LoadAdministrativeLocation extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
{
|
||||||
|
final public const ADMINISTRATIVE_LOCATION = 'administrative_location';
|
||||||
|
|
||||||
|
public function getOrder(): int
|
||||||
|
{
|
||||||
|
return 9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(ObjectManager $manager): void
|
||||||
|
{
|
||||||
|
$o = new Location();
|
||||||
|
/** @var LocationType $locationType */
|
||||||
|
$locationType = $this->getReference(LoadLocationType::LOCATION_TYPE.'_0');
|
||||||
|
$o->setLocationType($locationType);
|
||||||
|
$o->setName('Commune de Bruxelles');
|
||||||
|
$o->setAvailableForUsers(true);
|
||||||
|
|
||||||
|
$manager->persist($o);
|
||||||
|
|
||||||
|
$this->addReference(self::ADMINISTRATIVE_LOCATION, $o);
|
||||||
|
echo "Adding one Administrative Location\n";
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ class LoadLocationType extends AbstractFixture implements ContainerAwareInterfac
|
|||||||
{
|
{
|
||||||
private ?ContainerInterface $container = null;
|
private ?ContainerInterface $container = null;
|
||||||
|
|
||||||
|
final public const LOCATION_TYPE = 'location_type';
|
||||||
|
|
||||||
public function getOrder(): int
|
public function getOrder(): int
|
||||||
{
|
{
|
||||||
return 52;
|
return 52;
|
||||||
@@ -53,13 +55,15 @@ class LoadLocationType extends AbstractFixture implements ContainerAwareInterfac
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($arr as $a) {
|
foreach ($arr as $index => $a) {
|
||||||
$locationType = (new LocationType())
|
$locationType = (new LocationType())
|
||||||
->setTitle($a['name'])
|
->setTitle($a['name'])
|
||||||
->setAvailableForUsers(true)
|
->setAvailableForUsers(true)
|
||||||
->setActive(true)
|
->setActive(true)
|
||||||
->setAddressRequired($a['address_required']);
|
->setAddressRequired($a['address_required']);
|
||||||
$manager->persist($locationType);
|
$manager->persist($locationType);
|
||||||
|
|
||||||
|
$this->addReference(self::LOCATION_TYPE.'_'.$index, $locationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
$manager->flush();
|
$manager->flush();
|
||||||
|
|||||||
41
src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadUserJob.php
Normal file
41
src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadUserJob.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?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\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
|
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||||
|
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||||
|
use Doctrine\Persistence\ObjectManager;
|
||||||
|
|
||||||
|
class LoadUserJob extends AbstractFixture implements OrderedFixtureInterface
|
||||||
|
{
|
||||||
|
final public const USER_JOB = 'user_job';
|
||||||
|
private array $socialWorker = ['en' => 'social worker', 'fr' => 'travailleur social'];
|
||||||
|
|
||||||
|
public function getOrder(): int
|
||||||
|
{
|
||||||
|
return 9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function load(ObjectManager $manager): void
|
||||||
|
{
|
||||||
|
$o = new UserJob();
|
||||||
|
$o->setLabel($this->socialWorker);
|
||||||
|
|
||||||
|
$manager->persist($o);
|
||||||
|
|
||||||
|
$this->addReference(self::USER_JOB, $o);
|
||||||
|
echo "Adding one AccompanyingPeriod User Job\n";
|
||||||
|
|
||||||
|
$manager->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -123,7 +123,7 @@ class EntityWorkflowStep
|
|||||||
/**
|
/**
|
||||||
* @var Collection<int, EntityWorkflowStepHold>
|
* @var Collection<int, EntityWorkflowStepHold>
|
||||||
*/
|
*/
|
||||||
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class, cascade: ['remove'])]
|
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class)]
|
||||||
private Collection $holdsOnStep;
|
private Collection $holdsOnStep;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use Symfony\Component\Routing\RouterInterface;
|
|||||||
/**
|
/**
|
||||||
* Create paginator instances.
|
* Create paginator instances.
|
||||||
*/
|
*/
|
||||||
class PaginatorFactory implements PaginatorFactoryInterface
|
final readonly class PaginatorFactory implements PaginatorFactoryInterface
|
||||||
{
|
{
|
||||||
final public const DEFAULT_CURRENT_PAGE_KEY = 'page';
|
final public const DEFAULT_CURRENT_PAGE_KEY = 'page';
|
||||||
|
|
||||||
@@ -29,16 +29,16 @@ class PaginatorFactory implements PaginatorFactoryInterface
|
|||||||
/**
|
/**
|
||||||
* the request stack.
|
* the request stack.
|
||||||
*/
|
*/
|
||||||
private readonly RequestStack $requestStack,
|
private RequestStack $requestStack,
|
||||||
/**
|
/**
|
||||||
* the router and generator for url.
|
* the router and generator for url.
|
||||||
*/
|
*/
|
||||||
private readonly RouterInterface $router,
|
private RouterInterface $router,
|
||||||
/**
|
/**
|
||||||
* the default item per page. This may be overriden by
|
* the default item per page. This may be overriden by
|
||||||
* the request or inside the paginator.
|
* the request or inside the paginator.
|
||||||
*/
|
*/
|
||||||
private readonly int $itemPerPage = 20,
|
private int $itemPerPage = 20,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="d-grid gap-2 my-3">
|
<div class="d-grid gap-2 my-3">
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-primary text-start d-flex align-items-center"
|
class="btn btn-misc"
|
||||||
:class="{ active: subscriberFinal }"
|
|
||||||
type="button"
|
type="button"
|
||||||
@click="
|
v-if="!subscriberFinal"
|
||||||
subscribeTo(
|
@click="subscribeTo('subscribe', 'final')"
|
||||||
subscriberFinal ? 'unsubscribe' : 'subscribe',
|
|
||||||
'final',
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<i
|
<i class="fa fa-check fa-fw"></i>
|
||||||
class="fa fa-fw me-2"
|
{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}
|
||||||
:class="subscriberFinal ? 'fa-check-square-o' : 'fa-square-o'"
|
|
||||||
></i>
|
|
||||||
<span>{{ trans(WORKFLOW_SUBSCRIBE_FINAL) }}</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-primary text-start d-flex align-items-center"
|
class="btn btn-misc"
|
||||||
:class="{ active: subscriberStep }"
|
|
||||||
type="button"
|
type="button"
|
||||||
@click="
|
v-if="subscriberFinal"
|
||||||
subscribeTo(
|
@click="subscribeTo('unsubscribe', 'final')"
|
||||||
subscriberStep ? 'unsubscribe' : 'subscribe',
|
|
||||||
'step',
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<i
|
<i class="fa fa-times fa-fw"></i>
|
||||||
class="fa fa-fw me-2"
|
{{ trans(WORKFLOW_UNSUBSCRIBE_FINAL) }}
|
||||||
:class="subscriberStep ? 'fa-check-square-o' : 'fa-square-o'"
|
</button>
|
||||||
></i>
|
<button
|
||||||
<span>{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}</span>
|
class="btn btn-misc"
|
||||||
|
type="button"
|
||||||
|
v-if="!subscriberStep"
|
||||||
|
@click="subscribeTo('subscribe', 'step')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-check fa-fw"></i>
|
||||||
|
{{ trans(WORKFLOW_SUBSCRIBE_ALL_STEPS) }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-misc"
|
||||||
|
type="button"
|
||||||
|
v-if="subscriberStep"
|
||||||
|
@click="subscribeTo('unsubscribe', 'step')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-times fa-fw"></i>
|
||||||
|
{{ trans(WORKFLOW_UNSUBSCRIBE_ALL_STEPS) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -43,7 +45,9 @@ import { defineProps, defineEmits } from "vue";
|
|||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
WORKFLOW_SUBSCRIBE_FINAL,
|
WORKFLOW_SUBSCRIBE_FINAL,
|
||||||
|
WORKFLOW_UNSUBSCRIBE_FINAL,
|
||||||
WORKFLOW_SUBSCRIBE_ALL_STEPS,
|
WORKFLOW_SUBSCRIBE_ALL_STEPS,
|
||||||
|
WORKFLOW_UNSUBSCRIBE_ALL_STEPS,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// props
|
// props
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<span class="dt">{{ 'Scope'|trans }}/{{ 'center'|trans }}:</span>
|
<span class="dt">cercle/centre:</span>
|
||||||
{% if entity.mainScope %}
|
{% if entity.mainScope %}
|
||||||
{{ entity.mainScope.name|localize_translatable_string }}
|
{{ entity.mainScope.name|localize_translatable_string }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class AdminUserMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
'route' => 'chill_crud_center_index',
|
'route' => 'chill_crud_center_index',
|
||||||
])->setExtras(['order' => 1010]);
|
])->setExtras(['order' => 1010]);
|
||||||
|
|
||||||
$menu->addChild('Regroupements des territoires', [
|
$menu->addChild('Regroupements des centres', [
|
||||||
'route' => 'chill_crud_regroupment_index',
|
'route' => 'chill_crud_regroupment_index',
|
||||||
])->setExtras(['order' => 1015]);
|
])->setExtras(['order' => 1015]);
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ final class ScopeControllerTest extends WebTestCase
|
|||||||
$client->getResponse()->getStatusCode(),
|
$client->getResponse()->getStatusCode(),
|
||||||
'Unexpected HTTP status code for GET /fr/admin/scope/'
|
'Unexpected HTTP status code for GET /fr/admin/scope/'
|
||||||
);
|
);
|
||||||
$crawler = $client->click($crawler->selectLink('Créer un nouveau service')->link());
|
$crawler = $client->click($crawler->selectLink('Créer un nouveau cercle')->link());
|
||||||
// Fill in the form and submit it
|
// Fill in the form and submit it
|
||||||
$form = $crawler->selectButton('Créer')->form([
|
$form = $crawler->selectButton('Créer')->form([
|
||||||
'chill_mainbundle_scope[name][fr]' => 'Test en fr',
|
'chill_mainbundle_scope[name][fr]' => 'Test en fr',
|
||||||
|
|||||||
@@ -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\Migrations\Main;
|
|
||||||
|
|
||||||
use Doctrine\DBAL\Schema\Schema;
|
|
||||||
use Doctrine\Migrations\AbstractMigration;
|
|
||||||
|
|
||||||
final class Version20251104124123 extends AbstractMigration
|
|
||||||
{
|
|
||||||
public function getDescription(): string
|
|
||||||
{
|
|
||||||
return 'Delete on cascade EntityWorkflowStepHold';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function up(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_hold
|
|
||||||
DROP CONSTRAINT fk_1be2e7c73b21e9c');
|
|
||||||
|
|
||||||
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_hold
|
|
||||||
ADD CONSTRAINT fk_1be2e7c73b21e9c
|
|
||||||
FOREIGN KEY (step_id)
|
|
||||||
REFERENCES chill_main_workflow_entity_step (id)
|
|
||||||
ON DELETE CASCADE');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
|
||||||
{
|
|
||||||
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_hold
|
|
||||||
DROP CONSTRAINT fk_1be2e7c73b21e9c');
|
|
||||||
|
|
||||||
$this->addSql('ALTER TABLE chill_main_workflow_entity_step_hold
|
|
||||||
ADD CONSTRAINT fk_1be2e7c73b21e9c
|
|
||||||
FOREIGN KEY (step_id)
|
|
||||||
REFERENCES chill_main_workflow_entity_step (id)');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -127,20 +127,6 @@ duration:
|
|||||||
few {# minutes}
|
few {# minutes}
|
||||||
other {# minutes}
|
other {# minutes}
|
||||||
}
|
}
|
||||||
hour: >-
|
|
||||||
{h, plural,
|
|
||||||
=0 {Aucune durée}
|
|
||||||
one {# heure}
|
|
||||||
few {# heures}
|
|
||||||
other {# heures}
|
|
||||||
}
|
|
||||||
day: >-
|
|
||||||
{d, plural,
|
|
||||||
=0 {Aucune durée}
|
|
||||||
one {# jour}
|
|
||||||
few {# jours}
|
|
||||||
other {# jours}
|
|
||||||
}
|
|
||||||
|
|
||||||
filter_order:
|
filter_order:
|
||||||
by_date:
|
by_date:
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ user:
|
|||||||
title: Mon profil
|
title: Mon profil
|
||||||
Profile successfully updated!: Votre profil a été mis à jour!
|
Profile successfully updated!: Votre profil a été mis à jour!
|
||||||
no job: Pas de métier assigné
|
no job: Pas de métier assigné
|
||||||
no scope: Pas de service assigné
|
no scope: Pas de cercle assigné
|
||||||
notification_preferences: Préférences pour mes notifications
|
notification_preferences: Préférences pour mes notifications
|
||||||
|
|
||||||
user_group:
|
user_group:
|
||||||
@@ -102,9 +102,9 @@ createdAt: Créé le
|
|||||||
createdBy: Créé par
|
createdBy: Créé par
|
||||||
|
|
||||||
#elements used in software
|
#elements used in software
|
||||||
centers: territoires
|
centers: centres
|
||||||
Centers: Territoires
|
Centers: Centres
|
||||||
center: territoire
|
center: centre
|
||||||
comment: commentaire
|
comment: commentaire
|
||||||
Comment: Commentaire
|
Comment: Commentaire
|
||||||
Comments: Commentaires
|
Comments: Commentaires
|
||||||
@@ -227,12 +227,12 @@ Location Menu: Localisations et types de localisation
|
|||||||
Management of location: Gestion des localisations et types de localisation
|
Management of location: Gestion des localisations et types de localisation
|
||||||
|
|
||||||
#admin section for center's administration
|
#admin section for center's administration
|
||||||
Create a new center: Créer une nouveau territoire
|
Create a new center: Créer un nouveau centre
|
||||||
Center list: Liste des territoires
|
Center list: Liste des centres
|
||||||
Center edit: Édition d'un territoire
|
Center edit: Édition d'un centre
|
||||||
Center creation: Création d'un territoire
|
Center creation: Création d'un centre
|
||||||
New center: Nouveau territoire
|
New center: Nouveau centre
|
||||||
Center: Territoire
|
Center: Centre
|
||||||
|
|
||||||
#admin section for permissions group
|
#admin section for permissions group
|
||||||
Permissions group list: Groupes de permissions
|
Permissions group list: Groupes de permissions
|
||||||
@@ -246,15 +246,15 @@ New permission group: Nouveau groupe de permissions
|
|||||||
PermissionsGroup "%name%" edit: Modification du groupe de permission '%name%'
|
PermissionsGroup "%name%" edit: Modification du groupe de permission '%name%'
|
||||||
Role: Rôle
|
Role: Rôle
|
||||||
Choose amongst roles: Choisir un rôle
|
Choose amongst roles: Choisir un rôle
|
||||||
Choose amongst scopes: Choisir un service
|
Choose amongst scopes: Choisir un cercle
|
||||||
Add permission: Ajouter les permissions
|
Add permission: Ajouter les permissions
|
||||||
This group does not provide any permission: Ce groupe n'attribue aucune permission
|
This group does not provide any permission: Ce groupe n'attribue aucune permission
|
||||||
The role '%role%' has been removed: Le rôle "%role%" a été enlevé de ce groupe de permission
|
The role '%role%' has been removed: Le rôle "%role%" a été enlevé de ce groupe de permission
|
||||||
The role '%role%' on circle '%scope%' has been removed: Le rôle "%role%" sur le service "%scope%" a été enlevé de ce groupe de permission
|
The role '%role%' on circle '%scope%' has been removed: Le rôle "%role%" sur le cercle "%scope%" a été enlevé de ce groupe de permission
|
||||||
Unclassified: Non classifié
|
Unclassified: Non classifié
|
||||||
Help to pick role and scope: Certains rôles ne nécessitent pas de service.
|
Help to pick role and scope: Certains rôles ne nécessitent pas de cercle.
|
||||||
The role need scope: Ce rôle nécessite un service.
|
The role need scope: Ce rôle nécessite un cercle.
|
||||||
The role does not need scope: Ce rôle ne nécessite pas de service !
|
The role does not need scope: Ce rôle ne nécessite pas de cercle !
|
||||||
|
|
||||||
#admin section for users
|
#admin section for users
|
||||||
User configuration: Gestion des utilisateurs
|
User configuration: Gestion des utilisateurs
|
||||||
@@ -270,7 +270,7 @@ Grant new permissions: Ajout de permissions
|
|||||||
Add a new groupCenter: Ajout de permissions
|
Add a new groupCenter: Ajout de permissions
|
||||||
The permissions have been successfully added to the user: Les permissions ont été accordées à l'utilisateur
|
The permissions have been successfully added to the user: Les permissions ont été accordées à l'utilisateur
|
||||||
The permissions where removed.: Les permissions ont été enlevées.
|
The permissions where removed.: Les permissions ont été enlevées.
|
||||||
Center & groups: Territoire et groupes
|
Center & groups: Centre et groupes
|
||||||
User %username%: Utilisateur %username%
|
User %username%: Utilisateur %username%
|
||||||
Add a new user: Ajouter un nouvel utilisateur
|
Add a new user: Ajouter un nouvel utilisateur
|
||||||
The permissions have been added: Les permissions ont été ajoutées
|
The permissions have been added: Les permissions ont été ajoutées
|
||||||
@@ -280,13 +280,13 @@ Back to the user edition: Retour au formulaire d'édition
|
|||||||
Password successfully updated!: Mot de passe mis à jour
|
Password successfully updated!: Mot de passe mis à jour
|
||||||
Flags: Drapeaux
|
Flags: Drapeaux
|
||||||
Main location: Localisation principale
|
Main location: Localisation principale
|
||||||
Main scope: Service
|
Main scope: Cercle
|
||||||
Main center: Territoire
|
Main center: Centre
|
||||||
user job: Métier de l'utilisateur
|
user job: Métier de l'utilisateur
|
||||||
Job: Métier
|
Job: Métier
|
||||||
Jobs: Métiers
|
Jobs: Métiers
|
||||||
Choose a main center: Choisir un territoire
|
Choose a main center: Choisir un centre
|
||||||
Choose a main scope: Choisir un service
|
Choose a main scope: Choisir un cercle
|
||||||
choose a job: Choisir un métier
|
choose a job: Choisir un métier
|
||||||
choose a location: Choisir une localisation
|
choose a location: Choisir une localisation
|
||||||
|
|
||||||
@@ -302,12 +302,12 @@ Current location successfully updated: Localisation actuelle mise à jour
|
|||||||
Pick a location: Choisir un lieu
|
Pick a location: Choisir un lieu
|
||||||
|
|
||||||
#admin section for circles (old: scopes)
|
#admin section for circles (old: scopes)
|
||||||
List circles: Services
|
List circles: Cercles
|
||||||
New circle: Nouveau service
|
New circle: Nouveau cercle
|
||||||
Circle: Service
|
Circle: Cercle
|
||||||
Circle edit: Modification du service
|
Circle edit: Modification du cercle
|
||||||
Circle creation: Création d'un service
|
Circle creation: Création d'un cercle
|
||||||
Create a new circle: Créer un nouveau service
|
Create a new circle: Créer un nouveau cercle
|
||||||
|
|
||||||
#admin section for location
|
#admin section for location
|
||||||
Location: Localisation
|
Location: Localisation
|
||||||
@@ -347,9 +347,9 @@ Country list: Liste des pays
|
|||||||
Country code: Code du pays
|
Country code: Code du pays
|
||||||
|
|
||||||
# circles / scopes
|
# circles / scopes
|
||||||
Choose the circle: Choisir le service
|
Choose the circle: Choisir le cercle
|
||||||
Scope: Service
|
Scope: Cercle
|
||||||
Scopes: Services
|
Scopes: Cercles
|
||||||
|
|
||||||
#export
|
#export
|
||||||
|
|
||||||
@@ -357,14 +357,14 @@ Scopes: Services
|
|||||||
Exports list: Liste des exports
|
Exports list: Liste des exports
|
||||||
Create an export: Créer un export
|
Create an export: Créer un export
|
||||||
#export creation step 'center' : pick a center
|
#export creation step 'center' : pick a center
|
||||||
Pick centers: Choisir les territoires
|
Pick centers: Choisir les centres
|
||||||
Pick a center: Choisir un territoire
|
Pick a center: Choisir un centre
|
||||||
The export will contains only data from the picked centers.: L'export ne contiendra que les données des territoires choisis.
|
The export will contains only data from the picked centers.: L'export ne contiendra que les données des centres choisis.
|
||||||
This will eventually restrict your possibilities in filtering the data.: Les possibilités de filtrages seront adaptées aux droits de consultation pour les territoires choisis.
|
This will eventually restrict your possibilities in filtering the data.: Les possibilités de filtrages seront adaptées aux droits de consultation pour les centres choisis.
|
||||||
Go to export options: Vers la préparation de l'export
|
Go to export options: Vers la préparation de l'export
|
||||||
Pick aggregated centers: Regroupement de territoires
|
Pick aggregated centers: Regroupement de centres
|
||||||
uncheck all centers: Désélectionner tous les territoires
|
uncheck all centers: Désélectionner tous les centres
|
||||||
check all centers: Sélectionner tous les territoires
|
check all centers: Sélectionner tous les centres
|
||||||
# export creation step 'export' : choose aggregators, filtering and formatter
|
# export creation step 'export' : choose aggregators, filtering and formatter
|
||||||
Formatter: Mise en forme
|
Formatter: Mise en forme
|
||||||
Choose the formatter: Choisissez le format d'export voulu.
|
Choose the formatter: Choisissez le format d'export voulu.
|
||||||
@@ -510,10 +510,10 @@ crud:
|
|||||||
title_edit: Modifier un regroupement
|
title_edit: Modifier un regroupement
|
||||||
center:
|
center:
|
||||||
index:
|
index:
|
||||||
title: Liste des territoires
|
title: Liste des centres
|
||||||
add_new: Ajouter un territoire
|
add_new: Ajouter un centre
|
||||||
title_new: Nouveau territoire
|
title_new: Nouveau centre
|
||||||
title_edit: Modifier un territoire
|
title_edit: Modifier un centre
|
||||||
news_item:
|
news_item:
|
||||||
index:
|
index:
|
||||||
title: Liste des actualités
|
title: Liste des actualités
|
||||||
@@ -862,7 +862,7 @@ absence:
|
|||||||
admin:
|
admin:
|
||||||
users:
|
users:
|
||||||
export_list_csv: Liste des utilisateurs (format CSV)
|
export_list_csv: Liste des utilisateurs (format CSV)
|
||||||
export_permissions_csv: Association utilisateurs - groupes de permissions - territoire (format CSV)
|
export_permissions_csv: Association utilisateurs - groupes de permissions - centre (format CSV)
|
||||||
export:
|
export:
|
||||||
id: Identifiant
|
id: Identifiant
|
||||||
username: Nom d'utilisateur
|
username: Nom d'utilisateur
|
||||||
@@ -872,8 +872,8 @@ admin:
|
|||||||
civility_abbreviation: Abbréviation civilité
|
civility_abbreviation: Abbréviation civilité
|
||||||
civility_name: Civilité
|
civility_name: Civilité
|
||||||
label: Label
|
label: Label
|
||||||
mainCenter_id: Identifiant territoire principal
|
mainCenter_id: Identifiant centre principal
|
||||||
mainCenter_name: Territoire principal
|
mainCenter_name: Centre principal
|
||||||
mainScope_id: Identifiant service principal
|
mainScope_id: Identifiant service principal
|
||||||
mainScope_name: Service principal
|
mainScope_name: Service principal
|
||||||
userJob_id: Identifiant métier
|
userJob_id: Identifiant métier
|
||||||
@@ -883,8 +883,8 @@ admin:
|
|||||||
mainLocation_id: Identifiant localisation principale
|
mainLocation_id: Identifiant localisation principale
|
||||||
mainLocation_name: Localisation principale
|
mainLocation_name: Localisation principale
|
||||||
absenceStart: Absent à partir du
|
absenceStart: Absent à partir du
|
||||||
center_id: Identifiant du territoire
|
center_id: Identifiant du centre
|
||||||
center_name: Territoire
|
center_name: Centre
|
||||||
permissionsGroup_id: Identifiant du groupe de permissions
|
permissionsGroup_id: Identifiant du groupe de permissions
|
||||||
permissionsGroup_name: Groupe de permissions
|
permissionsGroup_name: Groupe de permissions
|
||||||
job_scope_histories:
|
job_scope_histories:
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# role_scope constraint
|
# role_scope constraint
|
||||||
# scope presence
|
# scope presence
|
||||||
The role "%role%" require to be associated with a scope.: Le rôle "%role%" doit être associé à un service.
|
The role "%role%" require to be associated with a scope.: Le rôle "%role%" doit être associé à un cercle.
|
||||||
The role "%role%" should not be associated with a scope.: Le rôle "%role%" ne doit pas être associé à un service.
|
The role "%role%" should not be associated with a scope.: Le rôle "%role%" ne doit pas être associé à un cercle.
|
||||||
"The password must contains one letter, one capitalized letter, one number and one special character as *[@#$%!,;:+\"'-/{}~=µ()£]). Other characters are allowed.": "Le mot de passe doit contenir une majuscule, une minuscule, et au moins un caractère spécial parmi *[@#$%!,;:+\"'-/{}~=µ()£]). Les autres caractères sont autorisés."
|
"The password must contains one letter, one capitalized letter, one number and one special character as *[@#$%!,;:+\"'-/{}~=µ()£]). Other characters are allowed.": "Le mot de passe doit contenir une majuscule, une minuscule, et au moins un caractère spécial parmi *[@#$%!,;:+\"'-/{}~=µ()£]). Les autres caractères sont autorisés."
|
||||||
The password fields must match: Les mots de passe doivent correspondre
|
The password fields must match: Les mots de passe doivent correspondre
|
||||||
The password must be greater than {{ limit }} characters: "[1,Inf] Le mot de passe doit contenir au moins {{ limit }} caractères"
|
The password must be greater than {{ limit }} characters: "[1,Inf] Le mot de passe doit contenir au moins {{ limit }} caractères"
|
||||||
|
|
||||||
A permission is already present for the same role and scope: Une permission est déjà présente pour le même rôle et service.
|
A permission is already present for the same role and scope: Une permission est déjà présente pour le même rôle et cercle.
|
||||||
|
|
||||||
#UserCircleConsistency
|
#UserCircleConsistency
|
||||||
"{{ username }} is not allowed to see entities published in this circle": "{{ username }} n'est pas autorisé à voir l'élément publié dans ce service."
|
"{{ username }} is not allowed to see entities published in this circle": "{{ username }} n'est pas autorisé à voir l'élément publié dans ce cercle."
|
||||||
|
|
||||||
The user in cc cannot be a dest user in the same workflow step: Un utilisateur en Cc ne peut pas être un utilisateur qui valide.
|
The user in cc cannot be a dest user in the same workflow step: Un utilisateur en Cc ne peut pas être un utilisateur qui valide.
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,19 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\DataFixtures\ORM;
|
namespace Chill\PersonBundle\DataFixtures\ORM;
|
||||||
|
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadAdministrativeLocation;
|
||||||
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
|
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
|
||||||
|
use Chill\MainBundle\DataFixtures\ORM\LoadUserJob;
|
||||||
use Chill\MainBundle\Entity\Address;
|
use Chill\MainBundle\Entity\Address;
|
||||||
use Chill\MainBundle\Entity\Center;
|
use Chill\MainBundle\Entity\Center;
|
||||||
use Chill\MainBundle\Entity\Country;
|
use Chill\MainBundle\Entity\Country;
|
||||||
use Chill\MainBundle\Entity\Gender;
|
use Chill\MainBundle\Entity\Gender;
|
||||||
use Chill\MainBundle\Entity\GenderEnum;
|
use Chill\MainBundle\Entity\GenderEnum;
|
||||||
|
use Chill\MainBundle\Entity\Location;
|
||||||
use Chill\MainBundle\Entity\PostalCode;
|
use Chill\MainBundle\Entity\PostalCode;
|
||||||
use Chill\MainBundle\Entity\Scope;
|
use Chill\MainBundle\Entity\Scope;
|
||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
|
use Chill\MainBundle\Entity\UserJob;
|
||||||
use Chill\MainBundle\Repository\CenterRepository;
|
use Chill\MainBundle\Repository\CenterRepository;
|
||||||
use Chill\MainBundle\Repository\CountryRepository;
|
use Chill\MainBundle\Repository\CountryRepository;
|
||||||
use Chill\MainBundle\Repository\GenderRepository;
|
use Chill\MainBundle\Repository\GenderRepository;
|
||||||
@@ -362,6 +366,10 @@ class LoadPeople extends AbstractFixture implements ContainerAwareInterface, Ord
|
|||||||
$origin = $this->getReference(LoadAccompanyingPeriodOrigin::ACCOMPANYING_PERIOD_ORIGIN, AccompanyingPeriod\Origin::class);
|
$origin = $this->getReference(LoadAccompanyingPeriodOrigin::ACCOMPANYING_PERIOD_ORIGIN, AccompanyingPeriod\Origin::class);
|
||||||
$accompanyingPeriod->setOrigin($origin);
|
$accompanyingPeriod->setOrigin($origin);
|
||||||
$accompanyingPeriod->setIntensity('regular');
|
$accompanyingPeriod->setIntensity('regular');
|
||||||
|
$userJob = $this->getReference(LoadUserJob::USER_JOB, UserJob::class);
|
||||||
|
$accompanyingPeriod->setJob($userJob);
|
||||||
|
$administrativeLocation = $this->getReference(LoadAdministrativeLocation::ADMINISTRATIVE_LOCATION, Location::class);
|
||||||
|
$accompanyingPeriod->setAdministrativeLocation($administrativeLocation);
|
||||||
$accompanyingPeriod->setAddressLocation($this->createAddress());
|
$accompanyingPeriod->setAddressLocation($this->createAddress());
|
||||||
$manager->persist($accompanyingPeriod->getAddressLocation());
|
$manager->persist($accompanyingPeriod->getAddressLocation());
|
||||||
$workflow = $this->workflowRegistry->get($accompanyingPeriod);
|
$workflow = $this->workflowRegistry->get($accompanyingPeriod);
|
||||||
|
|||||||
@@ -122,8 +122,6 @@ class SocialIssue
|
|||||||
* get all the ancestors of the social issue.
|
* get all the ancestors of the social issue.
|
||||||
*
|
*
|
||||||
* @param bool $includeThis if the array in the result must include the present SocialIssue
|
* @param bool $includeThis if the array in the result must include the present SocialIssue
|
||||||
*
|
|
||||||
* @return list<SocialIssue>
|
|
||||||
*/
|
*/
|
||||||
public function getAncestors(bool $includeThis = true): array
|
public function getAncestors(bool $includeThis = true): array
|
||||||
{
|
{
|
||||||
@@ -178,7 +176,7 @@ class SocialIssue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, SocialAction> All the descendant social actions of the entity
|
* @return Collection|SocialAction[] All the descendant social actions of the entity
|
||||||
*/
|
*/
|
||||||
public function getDescendantsSocialActions(): Collection
|
public function getDescendantsSocialActions(): Collection
|
||||||
{
|
{
|
||||||
@@ -241,23 +239,18 @@ class SocialIssue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, SocialAction> All the social actions of the entity, it's
|
* @return Collection<SocialAction> All the descendant social actions of all
|
||||||
* the descendants and it's parents
|
* the descendants of the entity
|
||||||
*/
|
*/
|
||||||
public function getRecursiveSocialActions(): Collection
|
public function getRecursiveSocialActions(): Collection
|
||||||
{
|
{
|
||||||
$recursiveSocialActions = new ArrayCollection();
|
$recursiveSocialActions = new ArrayCollection();
|
||||||
|
|
||||||
// Get social actions from parent issues
|
|
||||||
foreach ($this->getAncestors(false) as $ancestor) {
|
|
||||||
foreach ($ancestor->getDescendantsSocialActions() as $descendant) {
|
|
||||||
$recursiveSocialActions->add($descendant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->getDescendantsWithThis() as $socialIssue) {
|
foreach ($this->getDescendantsWithThis() as $socialIssue) {
|
||||||
foreach ($socialIssue->getDescendantsSocialActions() as $descendant) {
|
foreach ($socialIssue->getDescendantsSocialActions() as $descendant) {
|
||||||
$recursiveSocialActions->add($descendant);
|
if (!$recursiveSocialActions->contains($descendant)) {
|
||||||
|
$recursiveSocialActions->add($descendant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ const appMessages = {
|
|||||||
firstName: "Prénom",
|
firstName: "Prénom",
|
||||||
lastName: "Nom",
|
lastName: "Nom",
|
||||||
birthdate: "Date de naissance",
|
birthdate: "Date de naissance",
|
||||||
center: "Territoire",
|
center: "Centre",
|
||||||
phonenumber: "Téléphone",
|
phonenumber: "Téléphone",
|
||||||
mobilenumber: "Mobile",
|
mobilenumber: "Mobile",
|
||||||
altNames: "Autres noms",
|
altNames: "Autres noms",
|
||||||
|
|||||||
@@ -52,32 +52,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 1. Goals with results that were already selected/saved to the entity -->
|
<!-- results which are not attached to an objective -->
|
||||||
<div v-for="g in goalsPicked" :key="g.goal.id">
|
|
||||||
<div class="item-title" @click="removeGoal(g)">
|
|
||||||
<span class="removable">{{
|
|
||||||
localizeString(g.goal.title)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<add-result :goal="g.goal" destination="goal"></add-result>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 2. Results without objectives that were already selected/saved to the entity -->
|
|
||||||
<div v-if="hasResultsForAction">
|
<div v-if="hasResultsForAction">
|
||||||
<div
|
<div class="results_without_objective">
|
||||||
class="results_without_objective"
|
|
||||||
style="
|
|
||||||
background: repeating-linear-gradient(
|
|
||||||
45deg,
|
|
||||||
#e6e6e6,
|
|
||||||
#e6e6e6 10px,
|
|
||||||
#f3f3f3 0,
|
|
||||||
#f3f3f3 20px
|
|
||||||
);
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ $t("results_without_objective") }}
|
{{ $t("results_without_objective") }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -88,7 +65,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. Selector for objectives with results -->
|
<!-- results which **are** attached to an objective -->
|
||||||
|
<div v-for="g in goalsPicked" :key="g.goal.id">
|
||||||
|
<div class="item-title" @click="removeGoal(g)">
|
||||||
|
<span class="removable">{{
|
||||||
|
localizeString(g.goal.title)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<add-result :goal="g.goal" destination="goal"></add-result>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="accordion" id="expandedSuggestions">
|
<div class="accordion" id="expandedSuggestions">
|
||||||
<div
|
<div
|
||||||
v-if="availableForCheckGoal.length > 0"
|
v-if="availableForCheckGoal.length > 0"
|
||||||
@@ -151,8 +138,6 @@
|
|||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 4. Selector for results without objectives is already included above in section 2 -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="evaluations" class="action-row">
|
<div id="evaluations" class="action-row">
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
@click="
|
@click="
|
||||||
goToGenerateDocumentNotification(
|
goToGenerateDocumentNotification(
|
||||||
d,
|
d,
|
||||||
true,
|
false,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -67,117 +67,37 @@ const store = useStore();
|
|||||||
|
|
||||||
const $toast = useToast();
|
const $toast = useToast();
|
||||||
|
|
||||||
const timeSpentValues = [
|
const timeSpentChoices = [
|
||||||
60,
|
{ text: "1 minute", value: 60 },
|
||||||
120,
|
{ text: "2 minutes", value: 120 },
|
||||||
180,
|
{ text: "3 minutes", value: 180 },
|
||||||
240,
|
{ text: "4 minutes", value: 240 },
|
||||||
300,
|
{ text: "5 minutes", value: 300 },
|
||||||
600,
|
{ text: "10 minutes", value: 600 },
|
||||||
900,
|
{ text: "15 minutes", value: 900 },
|
||||||
1200,
|
{ text: "20 minutes", value: 1200 },
|
||||||
1500,
|
{ text: "25 minutes", value: 1500 },
|
||||||
1800,
|
{ text: "30 minutes", value: 1800 },
|
||||||
2700,
|
{ text: "45 minutes", value: 2700 },
|
||||||
3600,
|
{ text: "1 hour", value: 3600 },
|
||||||
4500,
|
{ text: "1 hour 15 minutes", value: 4500 },
|
||||||
5400,
|
{ text: "1 hour 30 minutes", value: 5400 },
|
||||||
6300,
|
{ text: "1 hour 45 minutes", value: 6300 },
|
||||||
7200,
|
{ text: "2 hours", value: 7200 },
|
||||||
9000,
|
{ text: "2 hours 30 minutes", value: 9000 },
|
||||||
10800,
|
{ text: "3 hours", value: 10800 },
|
||||||
12600,
|
{ text: "3 hours 30 minutes", value: 12600 },
|
||||||
14400,
|
{ text: "4 hours", value: 14400 },
|
||||||
16200,
|
{ text: "4 hours 30 minutes", value: 16200 },
|
||||||
18000,
|
{ text: "5 hours", value: 18000 },
|
||||||
19800,
|
{ text: "5 hours 30 minutes", value: 19800 },
|
||||||
21600,
|
{ text: "6 hours", value: 21600 },
|
||||||
23400,
|
{ text: "6 hours 30 minutes", value: 23400 },
|
||||||
25200,
|
{ text: "7 hours", value: 25200 },
|
||||||
27000,
|
{ text: "7 hours 30 minutes", value: 27000 },
|
||||||
28800,
|
{ text: "8 hours", value: 28800 },
|
||||||
43200,
|
|
||||||
57600,
|
|
||||||
72000,
|
|
||||||
86400,
|
|
||||||
100800,
|
|
||||||
115200,
|
|
||||||
129600,
|
|
||||||
144000, // goes from 1 minute to 40 hours
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const formatDuration = (seconds, locale) => {
|
|
||||||
const currentLocale = locale || navigator.language || "fr";
|
|
||||||
|
|
||||||
const totalHours = Math.floor(seconds / 3600);
|
|
||||||
const remainingMinutes = Math.floor((seconds % 3600) / 60);
|
|
||||||
|
|
||||||
if (totalHours >= 8) {
|
|
||||||
const days = Math.floor(totalHours / 8);
|
|
||||||
const remainingHours = totalHours % 8;
|
|
||||||
|
|
||||||
const parts = [];
|
|
||||||
|
|
||||||
if (days > 0) {
|
|
||||||
parts.push(
|
|
||||||
new Intl.NumberFormat(currentLocale, {
|
|
||||||
style: "unit",
|
|
||||||
unit: "day",
|
|
||||||
unitDisplay: "long",
|
|
||||||
}).format(days),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingHours > 0) {
|
|
||||||
parts.push(
|
|
||||||
new Intl.NumberFormat(currentLocale, {
|
|
||||||
style: "unit",
|
|
||||||
unit: "hour",
|
|
||||||
unitDisplay: "long",
|
|
||||||
}).format(remainingHours),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// For less than 8 hours, use hour and minute format
|
|
||||||
const parts = [];
|
|
||||||
|
|
||||||
if (totalHours > 0) {
|
|
||||||
parts.push(
|
|
||||||
new Intl.NumberFormat(currentLocale, {
|
|
||||||
style: "unit",
|
|
||||||
unit: "hour",
|
|
||||||
unitDisplay: "long",
|
|
||||||
}).format(totalHours),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingMinutes > 0) {
|
|
||||||
parts.push(
|
|
||||||
new Intl.NumberFormat(currentLocale, {
|
|
||||||
style: "unit",
|
|
||||||
unit: "minute",
|
|
||||||
unitDisplay: "long",
|
|
||||||
}).format(remainingMinutes),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(parts);
|
|
||||||
console.log(parts.join(" "));
|
|
||||||
|
|
||||||
return parts.join(" ");
|
|
||||||
};
|
|
||||||
|
|
||||||
const timeSpentChoices = computed(() => {
|
|
||||||
const locale = "fr";
|
|
||||||
return timeSpentValues.map((value) => ({
|
|
||||||
text: formatDuration(value, locale),
|
|
||||||
value: parseInt(value),
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
const startDate = computed({
|
const startDate = computed({
|
||||||
get() {
|
get() {
|
||||||
return props.evaluation.startDate;
|
return props.evaluation.startDate;
|
||||||
@@ -274,7 +194,7 @@ function updateWarningInterval(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTimeSpent(value) {
|
function updateTimeSpent(value) {
|
||||||
timeSpent.value = parseInt(value);
|
timeSpent.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateComment(value) {
|
function updateComment(value) {
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ const visMessages = {
|
|||||||
return "Né·e le";
|
return "Né·e le";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
center_id: "Identifiant du territoire",
|
center_id: "Identifiant du centre",
|
||||||
center_type: "Type de territoire",
|
center_type: "Type de centre",
|
||||||
center_name: "Territoire", // vendée
|
center_name: "Territoire", // vendée
|
||||||
phonenumber: "Téléphone",
|
phonenumber: "Téléphone",
|
||||||
mobilenumber: "Mobile",
|
mobilenumber: "Mobile",
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ const personMessages = {
|
|||||||
return "Né·e le";
|
return "Né·e le";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
center_id: "Identifiant du territoire",
|
center_id: "Identifiant du centre",
|
||||||
center_type: "Type de territoire",
|
center_type: "Type de centre",
|
||||||
center_name: "Territoire", // vendée
|
center_name: "Territoire", // vendée
|
||||||
phonenumber: "Téléphone",
|
phonenumber: "Téléphone",
|
||||||
mobilenumber: "Mobile",
|
mobilenumber: "Mobile",
|
||||||
@@ -53,8 +53,8 @@ const personMessages = {
|
|||||||
"Un nouveau ménage va être créé. L'usager sera membre de ce ménage.",
|
"Un nouveau ménage va être créé. L'usager sera membre de ce ménage.",
|
||||||
},
|
},
|
||||||
center: {
|
center: {
|
||||||
placeholder: "Choisissez un territoire",
|
placeholder: "Choisissez un centre",
|
||||||
title: "territoire",
|
title: "Centre",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
error_only_one_person: "Une seule personne peut être sélectionnée !",
|
error_only_one_person: "Une seule personne peut être sélectionnée !",
|
||||||
|
|||||||
@@ -2,6 +2,30 @@
|
|||||||
# OPTIONS
|
# OPTIONS
|
||||||
# - displayContent: [short|long] default: short
|
# - displayContent: [short|long] default: short
|
||||||
#}
|
#}
|
||||||
|
{% if w.results|length > 0 %}
|
||||||
|
|
||||||
|
<table class="obj-res-eval">
|
||||||
|
<thead>
|
||||||
|
<th class="obj"><h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4></th>
|
||||||
|
<th class="res"><h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4></th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="obj">
|
||||||
|
<p class="chill-no-data-statement">{{ 'accompanying_course_work.results without objective'|trans }}</p>
|
||||||
|
</td>
|
||||||
|
<td class="res">
|
||||||
|
<ul class="result_list">
|
||||||
|
{% for r in w.results %}
|
||||||
|
<li>{{ r.title|localize_translatable_string }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if w.goals|length > 0 %}
|
{% if w.goals|length > 0 %}
|
||||||
<table class="obj-res-eval">
|
<table class="obj-res-eval">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -33,31 +57,6 @@
|
|||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if w.results|length > 0 %}
|
|
||||||
|
|
||||||
<table class="obj-res-eval">
|
|
||||||
<thead>
|
|
||||||
<th class="obj"><h4 class="title_label">{{ 'accompanying_course_work.goal'|trans }}</h4></th>
|
|
||||||
<th class="res"><h4 class="title_label">{{ 'accompanying_course_work.results'|trans }}</h4></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="obj">
|
|
||||||
<p class="chill-no-data-statement">{{ 'accompanying_course_work.results without objective'|trans }}</p>
|
|
||||||
</td>
|
|
||||||
<td class="res">
|
|
||||||
<ul class="result_list">
|
|
||||||
{% for r in w.results %}
|
|
||||||
<li>{{ r.title|localize_translatable_string }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% if w.accompanyingPeriodWorkEvaluations|length > 0 %}
|
{% if w.accompanyingPeriodWorkEvaluations|length > 0 %}
|
||||||
<table class="obj-res-eval">
|
<table class="obj-res-eval">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -217,29 +216,9 @@
|
|||||||
|
|
||||||
{% if e.timeSpent is not null and e.timeSpent > 0 %}
|
{% if e.timeSpent is not null and e.timeSpent > 0 %}
|
||||||
<li>
|
<li>
|
||||||
{% set totalHours = (e.timeSpent / 3600)|round(0, 'floor') %}
|
{% set minutes = (e.timeSpent / 60) %}
|
||||||
{% set totalMinutes = ((e.timeSpent % 3600) / 60)|round(0, 'floor') %}
|
<span
|
||||||
|
class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span> {{ 'duration.minute'|trans({ '{m}' : minutes }) }}
|
||||||
<span class="item-key">{{ 'accompanying_course_work.timeSpent'|trans ~ ' : ' }}</span>
|
|
||||||
|
|
||||||
{% if totalHours >= 8 %}
|
|
||||||
{% set days = (totalHours / 8)|round(0, 'floor') %}
|
|
||||||
{% set remainingHours = totalHours % 8 %}
|
|
||||||
|
|
||||||
{% if days > 0 %}
|
|
||||||
{{ 'duration.day'|trans({ '{d}' : days }) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if remainingHours > 0 %}
|
|
||||||
{{ 'duration.hour'|trans({ '{h}' : remainingHours }) }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
{% if totalHours > 0 %}
|
|
||||||
{{ 'duration.hour'|trans({ '{h}' : totalHours }) }}
|
|
||||||
{% endif %}
|
|
||||||
{% if totalMinutes > 0 %}
|
|
||||||
{{ 'duration.minute'|trans({ '{m}' : totalMinutes }) }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
</li>
|
||||||
{% elseif displayContent is defined and displayContent == 'long' %}
|
{% elseif displayContent is defined and displayContent == 'long' %}
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -42,41 +42,28 @@ final readonly class SocialActionCSVExportService
|
|||||||
$csv->insertOne($headers);
|
$csv->insertOne($headers);
|
||||||
|
|
||||||
foreach ($actions as $action) {
|
foreach ($actions as $action) {
|
||||||
$hasGoals = !$action->getGoals()->isEmpty();
|
if ($action->getGoals()->isEmpty() && $action->getResults()->isEmpty() && $action->getEvaluations()->isEmpty()) {
|
||||||
$hasResults = !$action->getResults()->isEmpty();
|
|
||||||
$hasEvaluations = !$action->getEvaluations()->isEmpty();
|
|
||||||
|
|
||||||
// If action has no goals, results, or evaluations, insert a single row
|
|
||||||
if (!$hasGoals && !$hasResults && !$hasEvaluations) {
|
|
||||||
$csv->insertOne($this->formatRow($action));
|
$csv->insertOne($this->formatRow($action));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process goals and their results
|
foreach ($action->getGoals() as $goal) {
|
||||||
if ($hasGoals) {
|
if ($goal->getResults()->isEmpty()) {
|
||||||
foreach ($action->getGoals() as $goal) {
|
$csv->insertOne($this->formatRow($action, $goal));
|
||||||
if ($goal->getResults()->isEmpty()) {
|
}
|
||||||
$csv->insertOne($this->formatRow($action, $goal));
|
|
||||||
} else {
|
foreach ($goal->getResults() as $goalResult) {
|
||||||
foreach ($goal->getResults() as $goalResult) {
|
$csv->insertOne($this->formatRow($action, $goal, $goalResult));
|
||||||
$csv->insertOne($this->formatRow($action, $goal, $goalResult));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process results that are linked to this action (regardless of whether they have goals elsewhere)
|
foreach ($action->getResults() as $result) {
|
||||||
if ($hasResults && !$hasGoals) {
|
if ($result->getGoals()->isEmpty()) {
|
||||||
foreach ($action->getResults() as $result) {
|
|
||||||
$csv->insertOne($this->formatRow($action, null, null, $result));
|
$csv->insertOne($this->formatRow($action, null, null, $result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process evaluations
|
foreach ($action->getEvaluations() as $evaluation) {
|
||||||
if ($hasEvaluations) {
|
$csv->insertOne($this->formatRow($action, evaluation: $evaluation));
|
||||||
foreach ($action->getEvaluations() as $evaluation) {
|
|
||||||
$csv->insertOne($this->formatRow($action, evaluation: $evaluation));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ Create a list of people according to various filters.: Crée une liste d'usagers
|
|||||||
Fields to include in export: Champs à inclure dans l'export
|
Fields to include in export: Champs à inclure dans l'export
|
||||||
Address valid at this date: Addresse valide à cette date
|
Address valid at this date: Addresse valide à cette date
|
||||||
Data valid at this date: Données valides à cette date
|
Data valid at this date: Données valides à cette date
|
||||||
Data regarding center, addresses, and so on will be computed at this date: Les données concernant le territoire, l'adresse, le ménage, sera calculé à cette date.
|
Data regarding center, addresses, and so on will be computed at this date: Les données concernant le centre, l'adresse, le ménage, sera calculé à cette date.
|
||||||
List duplicates: Liste des doublons
|
List duplicates: Liste des doublons
|
||||||
Create a list of duplicate people: Créer la liste des usagers détectés comme doublons.
|
Create a list of duplicate people: Créer la liste des usagers détectés comme doublons.
|
||||||
Count people participating in an accompanying course: Nombre d'usagers concernés par un parcours
|
Count people participating in an accompanying course: Nombre d'usagers concernés par un parcours
|
||||||
@@ -1110,9 +1110,9 @@ export:
|
|||||||
Group course by household composition: Grouper les usagers par composition familiale
|
Group course by household composition: Grouper les usagers par composition familiale
|
||||||
Calc date: Date de calcul de la composition du ménage
|
Calc date: Date de calcul de la composition du ménage
|
||||||
by_center:
|
by_center:
|
||||||
title: Grouper les usagers par territoire
|
title: Grouper les usagers par centre
|
||||||
at_date: Date de calcul du territoire
|
at_date: Date de calcul du centre
|
||||||
center: Territoire de l'usager
|
center: Centre de l'usager
|
||||||
by_postal_code:
|
by_postal_code:
|
||||||
title: Grouper les usagers par code postal de l'adresse
|
title: Grouper les usagers par code postal de l'adresse
|
||||||
at_date: Date de calcul de l'adresse
|
at_date: Date de calcul de l'adresse
|
||||||
@@ -1437,7 +1437,7 @@ export:
|
|||||||
acpParticipantPersons: Usagers concernés
|
acpParticipantPersons: Usagers concernés
|
||||||
acpParticipantPersonsIds: Usagers concernés (identifiants)
|
acpParticipantPersonsIds: Usagers concernés (identifiants)
|
||||||
duration: Durée du parcours (en jours)
|
duration: Durée du parcours (en jours)
|
||||||
centers: Territoires des usagers
|
centers: Centres des usagers
|
||||||
|
|
||||||
eval:
|
eval:
|
||||||
List of evaluations: Liste des évaluations
|
List of evaluations: Liste des évaluations
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ The gender must be set: Le genre doit être renseigné
|
|||||||
You are not allowed to perform this action: Vous n'avez pas le droit de changer cette valeur.
|
You are not allowed to perform this action: Vous n'avez pas le droit de changer cette valeur.
|
||||||
Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again: Désolé, mais quelqu'un d'autre a déjà modifié cette entité. Veuillez actualiser la page et appliquer à nouveau les modifications
|
Sorry, but someone else has already changed this entity. Please refresh the page and apply the changes again: Désolé, mais quelqu'un d'autre a déjà modifié cette entité. Veuillez actualiser la page et appliquer à nouveau les modifications
|
||||||
|
|
||||||
A center is required: Un territoire est requis
|
A center is required: Un centre est requis
|
||||||
|
|
||||||
#export list
|
#export list
|
||||||
You must select at least one element: Vous devez sélectionner au moins un élément
|
You must select at least one element: Vous devez sélectionner au moins un élément
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
'Report list': 'Liste des rapports'
|
'Report list': 'Liste des rapports'
|
||||||
Details: Détails
|
Details: Détails
|
||||||
Person: Usager
|
Person: Usager
|
||||||
Scope: Service
|
Scope: Cercle
|
||||||
Date: Date
|
Date: Date
|
||||||
User: Utilisateur
|
User: Utilisateur
|
||||||
'Report type': 'Type de rapport'
|
'Report type': 'Type de rapport'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Tasks: "Tâches"
|
|||||||
Title: Titre
|
Title: Titre
|
||||||
Description: Description
|
Description: Description
|
||||||
Assignee: "Personne assignée"
|
Assignee: "Personne assignée"
|
||||||
Scope: Service
|
Scope: Cercle
|
||||||
"Start date": "Date de début"
|
"Start date": "Date de début"
|
||||||
"End date": "Date d'échéance"
|
"End date": "Date d'échéance"
|
||||||
"Warning date": "Date d'avertissement"
|
"Warning date": "Date d'avertissement"
|
||||||
@@ -106,7 +106,7 @@ My tasks over deadline: Mes tâches à échéance dépassée
|
|||||||
#transition page
|
#transition page
|
||||||
Apply transition on task <em>%title%</em>: Appliquer la transition sur la tâche <em>%title%</em>
|
Apply transition on task <em>%title%</em>: Appliquer la transition sur la tâche <em>%title%</em>
|
||||||
|
|
||||||
All centers: Tous les territoires
|
All centers: Tous les centres
|
||||||
|
|
||||||
# ROLES
|
# ROLES
|
||||||
CHILL_TASK_TASK_CREATE: Ajouter une tâche
|
CHILL_TASK_TASK_CREATE: Ajouter une tâche
|
||||||
|
|||||||
@@ -69,11 +69,10 @@ readonly class ThirdpartyMergeService
|
|||||||
if (($assoc['type'] & ClassMetadata::TO_ONE) !== 0) {
|
if (($assoc['type'] & ClassMetadata::TO_ONE) !== 0) {
|
||||||
$joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']);
|
$joinColumn = $meta->getSingleAssociationJoinColumnName($assoc['fieldName']);
|
||||||
|
|
||||||
$schema = $meta->getSchemaName();
|
$suffix = (ThirdParty::class === $assoc['sourceEntity']) ? 'chill_3party.' : '';
|
||||||
$prefix = null !== $schema && '' !== $schema ? $schema.'.' : '';
|
|
||||||
|
|
||||||
$queries[] = [
|
$queries[] = [
|
||||||
'sql' => "UPDATE {$prefix}{$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete",
|
'sql' => "UPDATE {$suffix}{$tableName} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete",
|
||||||
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
||||||
];
|
];
|
||||||
} elseif (ClassMetadata::MANY_TO_MANY === $assoc['type'] && isset($assoc['joinTable'])) {
|
} elseif (ClassMetadata::MANY_TO_MANY === $assoc['type'] && isset($assoc['joinTable'])) {
|
||||||
@@ -86,36 +85,13 @@ readonly class ThirdpartyMergeService
|
|||||||
];
|
];
|
||||||
|
|
||||||
$queries[] = [
|
$queries[] = [
|
||||||
'sql' => "DELETE FROM {$prefix}{$joinTable} WHERE {$joinColumn} = :toDelete",
|
'sql' => "DELETE FROM {$joinTable} WHERE {$joinColumn} = :toDelete",
|
||||||
'params' => ['toDelete' => $toDelete->getId()],
|
'params' => ['toDelete' => $toDelete->getId()],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also handle many-to-many where ThirdParty is the source
|
|
||||||
$thirdPartyMeta = $this->em->getClassMetadata(ThirdParty::class);
|
|
||||||
foreach ($thirdPartyMeta->getAssociationMappings() as $assoc) {
|
|
||||||
if (ClassMetadata::MANY_TO_MANY === $assoc['type'] && isset($assoc['joinTable'])) {
|
|
||||||
$joinTable = $assoc['joinTable']['name'];
|
|
||||||
$prefix = null !== ($assoc['joinTable']['schema'] ?? null) ? $assoc['joinTable']['schema'].'.' : '';
|
|
||||||
$joinColumn = $assoc['joinTable']['joinColumns'][0]['name']; // Note: joinColumns, not inverseJoinColumns
|
|
||||||
|
|
||||||
// Get the other column name to build proper duplicate check
|
|
||||||
$otherColumn = $assoc['joinTable']['inverseJoinColumns'][0]['name'];
|
|
||||||
|
|
||||||
$queries[] = [
|
|
||||||
'sql' => "UPDATE {$prefix}{$joinTable} SET {$joinColumn} = :toKeep WHERE {$joinColumn} = :toDelete AND NOT EXISTS (SELECT 1 FROM {$prefix}{$joinTable} AS t2 WHERE t2.{$joinColumn} = :toKeep AND t2.{$otherColumn} = {$prefix}{$joinTable}.{$otherColumn})",
|
|
||||||
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
|
||||||
];
|
|
||||||
|
|
||||||
$queries[] = [
|
|
||||||
'sql' => "DELETE FROM {$prefix}{$joinTable} WHERE {$joinColumn} = :toDelete",
|
|
||||||
'params' => ['toDelete' => $toDelete->getId()],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $queries;
|
return $queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +102,10 @@ readonly class ThirdpartyMergeService
|
|||||||
'sql' => 'UPDATE chill_3party.third_party SET parent_id = :toKeep WHERE parent_id = :toDelete',
|
'sql' => 'UPDATE chill_3party.third_party SET parent_id = :toKeep WHERE parent_id = :toDelete',
|
||||||
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'sql' => 'UPDATE chill_3party.thirdparty_category SET thirdparty_id = :toKeep WHERE thirdparty_id = :toDelete AND NOT EXISTS (SELECT 1 FROM chill_3party.thirdparty_category WHERE thirdparty_id = :toKeep)',
|
||||||
|
'params' => ['toKeep' => $toKeep->getId(), 'toDelete' => $toDelete->getId()],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'sql' => 'DELETE FROM chill_3party.third_party WHERE id = :toDelete',
|
'sql' => 'DELETE FROM chill_3party.third_party WHERE id = :toDelete',
|
||||||
'params' => ['toDelete' => $toDelete->getId()],
|
'params' => ['toDelete' => $toDelete->getId()],
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ declare(strict_types=1);
|
|||||||
namespace Chill\ThirdPartyBundle\Tests\Service;
|
namespace Chill\ThirdPartyBundle\Tests\Service;
|
||||||
|
|
||||||
use Chill\ActivityBundle\Entity\Activity;
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
use Chill\MainBundle\Entity\Center;
|
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||||
use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory;
|
use Chill\ThirdPartyBundle\Entity\ThirdPartyCategory;
|
||||||
use Chill\ThirdPartyBundle\Service\ThirdpartyMergeService;
|
use Chill\ThirdPartyBundle\Service\ThirdpartyMergeService;
|
||||||
@@ -48,20 +47,19 @@ class ThirdpartyMergeServiceTest extends KernelTestCase
|
|||||||
$toDelete->setName('Thirdparty to delete');
|
$toDelete->setName('Thirdparty to delete');
|
||||||
$this->em->persist($toDelete);
|
$this->em->persist($toDelete);
|
||||||
|
|
||||||
// Create a related entity with TO_ONE relation (thirdparty parent) - tests schema handling for TO_ONE
|
// Create a related entity with TO_ONE relation (thirdparty parent)
|
||||||
$relatedToOneEntity = new ThirdParty();
|
$relatedToOneEntity = new ThirdParty();
|
||||||
$relatedToOneEntity->setName('RelatedToOne thirdparty');
|
$relatedToOneEntity->setName('RelatedToOne thirdparty');
|
||||||
$relatedToOneEntity->setParent($toDelete);
|
$relatedToOneEntity->setParent($toDelete);
|
||||||
$this->em->persist($relatedToOneEntity);
|
$this->em->persist($relatedToOneEntity);
|
||||||
|
|
||||||
// Create a related entity with MANY_TO_MANY relation (thirdparty category) - tests schema handling for MANY_TO_MANY where ThirdParty is target
|
// Create a related entity with TO_MANY relation (thirdparty category)
|
||||||
$thirdpartyCategory = new ThirdPartyCategory();
|
$thirdpartyCategory = new ThirdPartyCategory();
|
||||||
$thirdpartyCategory->setName(['fr' => 'Thirdparty category']);
|
$thirdpartyCategory->setName(['fr' => 'Thirdparty category']);
|
||||||
$this->em->persist($thirdpartyCategory);
|
$this->em->persist($thirdpartyCategory);
|
||||||
$toDelete->addCategory($thirdpartyCategory);
|
$toDelete->addCategory($thirdpartyCategory);
|
||||||
$this->em->persist($toDelete);
|
$this->em->persist($toDelete);
|
||||||
|
|
||||||
// Test MANY_TO_MANY relation from another bundle (Activity) - tests cross-bundle schema handling
|
|
||||||
$activity = new Activity();
|
$activity = new Activity();
|
||||||
$activity->setDate(new \DateTime());
|
$activity->setDate(new \DateTime());
|
||||||
$activity->addThirdParty($toDelete);
|
$activity->addThirdParty($toDelete);
|
||||||
@@ -75,55 +73,14 @@ class ThirdpartyMergeServiceTest extends KernelTestCase
|
|||||||
$this->em->refresh($relatedToOneEntity);
|
$this->em->refresh($relatedToOneEntity);
|
||||||
|
|
||||||
// Check that references were updated
|
// Check that references were updated
|
||||||
|
$this->assertEquals($toKeep->getId(), $relatedToOneEntity->getParent()->getId(), 'The parent thirdparty was succesfully merged');
|
||||||
|
|
||||||
// Test TO_ONE relation in chill_3party schema was properly handled
|
|
||||||
$this->assertEquals($toKeep->getId(), $relatedToOneEntity->getParent()->getId(), 'The parent thirdparty in chill_3party schema was successfully merged');
|
|
||||||
|
|
||||||
// Test MANY_TO_MANY relation in chill_3party schema was properly handled
|
|
||||||
$updatedRelatedManyEntity = $this->em->find(ThirdPartyCategory::class, $thirdpartyCategory->getId());
|
$updatedRelatedManyEntity = $this->em->find(ThirdPartyCategory::class, $thirdpartyCategory->getId());
|
||||||
$this->assertContains($updatedRelatedManyEntity, $toKeep->getCategories(), 'The thirdparty category in chill_3party schema was found in the toKeep entity');
|
$this->assertContains($updatedRelatedManyEntity, $toKeep->getCategories(), 'The thirdparty category was found in the toKeep entity');
|
||||||
|
|
||||||
// Test MANY_TO_MANY relation from different schema (Activity bundle) was properly handled
|
|
||||||
$this->em->refresh($activity);
|
|
||||||
$this->assertContains($toKeep, $activity->getThirdParties(), 'The activity relation from different schema was successfully merged');
|
|
||||||
$this->assertNotContains($toDelete, $activity->getThirdParties(), 'The toDelete thirdparty was removed from activity relation');
|
|
||||||
|
|
||||||
// Check that toDelete was removed
|
// Check that toDelete was removed
|
||||||
$this->em->clear();
|
$this->em->clear();
|
||||||
$deletedThirdParty = $this->em->find(ThirdParty::class, $toDelete->getId());
|
$deletedThirdParty = $this->em->find(ThirdParty::class, $toDelete->getId());
|
||||||
$this->assertNull($deletedThirdParty, 'The toDelete thirdparty was successfully removed');
|
$this->assertNull($deletedThirdParty);
|
||||||
}
|
|
||||||
|
|
||||||
public function testMergeWithSharedCenterDoesNotCauseUniqueConstraintViolation(): void
|
|
||||||
{
|
|
||||||
// Create a center that will be shared by both thirdparties
|
|
||||||
$sharedCenter = new Center();
|
|
||||||
$sharedCenter->setName('Shared Center');
|
|
||||||
$this->em->persist($sharedCenter);
|
|
||||||
|
|
||||||
// Create ThirdParty entities
|
|
||||||
$toKeep = new ThirdParty();
|
|
||||||
$toKeep->setName('Thirdparty to keep');
|
|
||||||
$toKeep->addCenter($sharedCenter); // Both thirdparties linked to same center
|
|
||||||
$this->em->persist($toKeep);
|
|
||||||
|
|
||||||
$toDelete = new ThirdParty();
|
|
||||||
$toDelete->setName('Thirdparty to delete');
|
|
||||||
$toDelete->addCenter($sharedCenter); // Both thirdparties linked to same center
|
|
||||||
$this->em->persist($toDelete);
|
|
||||||
|
|
||||||
$this->em->flush();
|
|
||||||
|
|
||||||
// This should not throw a unique constraint violation
|
|
||||||
$this->service->merge($toKeep, $toDelete);
|
|
||||||
|
|
||||||
// Verify that toKeep still has the shared center
|
|
||||||
$this->em->refresh($toKeep);
|
|
||||||
$this->assertContains($sharedCenter, $toKeep->getCenters(), 'The shared center is still linked to the kept thirdparty');
|
|
||||||
|
|
||||||
// Verify that toDelete was removed
|
|
||||||
$this->em->clear();
|
|
||||||
$deletedThirdParty = $this->em->find(ThirdParty::class, $toDelete->getId());
|
|
||||||
$this->assertNull($deletedThirdParty, 'The toDelete thirdparty was successfully removed');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ No acronym given: Aucun sigle renseigné
|
|||||||
No phone given: Aucun téléphone renseigné
|
No phone given: Aucun téléphone renseigné
|
||||||
No email given: Aucune adresse courriel renseignée
|
No email given: Aucune adresse courriel renseignée
|
||||||
|
|
||||||
The party is visible in those centers: Le tiers est visible dans ces territoires
|
The party is visible in those centers: Le tiers est visible dans ces centres
|
||||||
The party is not visible in any center: Le tiers n'est associé à aucun territoire
|
The party is not visible in any center: Le tiers n'est associé à aucun centre
|
||||||
No third parties: Aucun tiers
|
No third parties: Aucun tiers
|
||||||
Any third party selected: Aucun tiers sélectionné
|
Any third party selected: Aucun tiers sélectionné
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user