Compare commits

...

231 Commits

Author SHA1 Message Date
ed556d9ee8 FIX [review] process review and make minor visual changes 2023-05-31 09:47:48 +02:00
ff03299f80 FEATURE [workflow][doc] scroll immediately to document in workflow and let background flash 2023-05-25 17:06:10 +02:00
5749660760 Merge branch '108-fix-acc-period-transition-notification' into 'master'
Fixed: Do not send a confirmation message when the accompanying period is back to CONFIRM state

Closes #108

See merge request Chill-Projet/chill-bundles!549
2023-05-25 13:30:15 +00:00
b679dbe26c Fixed: Do not send a confirmation message when period is mark_active back 2023-05-25 15:24:14 +02:00
6c3fa5cb98 Fixed: [UI] in designation and redispatch list, the period's statuses
were'nt shown correctly
2023-05-25 15:22:52 +02:00
6bdd1f31d3 Merge branch '98-rewrite-download-button' into 'master'
Fixed: vue downloadButton: add more log and various improvements

See merge request Chill-Projet/chill-bundles!547
2023-05-25 10:42:10 +00:00
ff3dab0934 Fixed: vue downloadButton: add more log and various improvements
- create a dedicated button for opening
- use nextTick before clicking on the "opening" button
2023-05-25 12:36:47 +02:00
977299192f Merge branch 'deselect-checkbox-exports' into 'master'
Add button to deselect all centers in export

See merge request Chill-Projet/chill-bundles!544
2023-05-24 14:34:57 +00:00
77997e2b6f FIX [review] fix review comments 2023-05-24 16:24:58 +02:00
6e618e688b DX [php-cs-fixer] 2023-05-24 15:50:25 +02:00
dad36927f3 FEATURE [export] uncheck all centers button 2023-05-24 15:34:39 +02:00
f5448f9d95 DX [phpstan] fixes 2023-05-24 14:02:49 +02:00
a31c4063a1 DX [cs-fixer] fixes 2023-05-24 14:02:05 +02:00
c8f95528c0 Merge branch '98-fix-download-document' into 'master'
Resolve "Dans certaines situations, le téléchargement et déchiffrement des documents ne fonctionne pas"

See merge request Chill-Projet/chill-bundles!541
2023-05-24 11:40:03 +00:00
20023dff67 DX: fix cs 2023-05-24 13:35:35 +02:00
d82a3e0ff6 Fixed: [download document] add a target when downloading document 2023-05-24 13:13:57 +02:00
04359f27c6 Revert "Revert "FIX [duplicates] reinstate gestion des doublons""
This reverts commit 5109490aad.
2023-05-24 13:11:40 +02:00
5109490aad Revert "FIX [duplicates] reinstate gestion des doublons"
This reverts commit 5351223d44.
2023-05-24 13:09:04 +02:00
8b82e0c535 FIX [rights] user shouldn't be allowed to see accompanyingperiods from within household 2023-05-23 18:19:31 +02:00
5351223d44 FIX [duplicates] reinstate gestion des doublons 2023-05-23 18:18:44 +02:00
674e057f67 fixes for user permission list 2023-05-20 00:54:35 +02:00
b2e79b677b Feature: [admin] add an export list of all permissions associated to
each user
2023-05-20 00:34:53 +02:00
748e566c7e Feature: [admin] list of users on csv format 2023-05-20 00:03:25 +02:00
1f4c51f3bc Merge branch '96-worflow-regression' into 'master'
Workflow regression in accompanying period work

See merge request Chill-Projet/chill-bundles!538
2023-05-19 10:17:58 +00:00
dc6eeccaab Merge branch 'permission-page/modernize' into 'master'
DX et UX: permissions page

See merge request Chill-Projet/chill-bundles!530
2023-05-19 10:03:08 +00:00
0083842509 DX: modernize controller for permissions groups and order them alphabetically 2023-05-19 11:58:11 +02:00
ca7be4ecd0 Merge branch 'export/allow-check-multiple-geographical-zones' into 'master'
Feature: [export] allow to check multiple geographical layers in geographical aggregators

See merge request Chill-Projet/chill-bundles!532
2023-05-19 09:54:25 +00:00
785c70fe92 Merge branch 'exports/filters-on-work-date' into 'master'
Feature: [exports] add filters for start and end date for accompanying period works

See merge request Chill-Projet/chill-bundles!533
2023-05-19 09:51:06 +00:00
8bbca7e61a Merge branch 'master' into export/allow-check-multiple-geographical-zones 2023-05-19 11:50:12 +02:00
5d21612c2e Merge branch '98-fix-download-document' into 'master'
Fix: [document download] better memory management and introduce delay

See merge request Chill-Projet/chill-bundles!539
2023-05-19 09:45:57 +00:00
fb9f182edd Merge branch 'docgen-add-household-composition' into 'master'
Feature: add Household composition on household in docgen

See merge request Chill-Projet/chill-bundles!537
2023-05-19 09:39:56 +00:00
bbd3d2a83f Fix: [document download] better memory management and introduce delay
before opening

Related to https://gitlab.com/Chill-Projet/chill-bundles/-/issues/98
2023-05-19 11:34:25 +02:00
e87420dc57 Merge branch '20-finalisation-cire' into 'master'
Finalisation cire

See merge request Chill-Projet/chill-bundles!529
2023-05-19 08:26:39 +00:00
f1bf02d2b4 css classes 2023-05-17 19:38:48 +02:00
8a35c2e2ee Fix workflow regression with accompanying period work (introduced by commit 6b90a7d2a7 24/01/2023) 2023-05-17 16:33:07 +02:00
fbd555e89a Feature: add Household composition on household in docgen 2023-05-17 16:05:53 +02:00
66dc027354 Fixed: fix first execution of accompanying period step change cronjob 2023-05-17 13:27:20 +02:00
8863e0a92e Fixed: force string on username 2023-05-17 13:24:44 +02:00
db9fef095a Fixed: force default values for cc users in workflow 2023-05-17 13:24:15 +02:00
1df2342c49 Merge branch 'master' into 20-finalisation-cire 2023-05-17 10:40:01 +02:00
addbdacee8 Merge branch 'feature/change-parcours-status' into 'master'
Feature/change parcours status

See merge request Chill-Projet/chill-bundles!527
2023-05-17 08:13:18 +00:00
8a684734e7 Fixed: fix docgen normalization on household with "old" members
When a household had old members, the indexes of each "current" members
should be numerical and contiguous, to be transformed in a list. If this
is not the case, the members are mapped to an associative array.

This commit alter the generic DocGenObjectNormalizer to ensure that
the ReadableCollection are normalized using the
CollectionDocGenNormalizer as default, which do not preserve keys.
2023-05-16 23:30:02 +02:00
1abaf2acb0 DX: add help and description to use ImportSocialWorkMetadata command 2023-05-10 10:28:36 +02:00
a0ae1f0d0f FIX [budget] display budget element comment if it is of kind 'autre' 2023-05-03 12:11:00 +02:00
2554da9dd8 Fix: urgent fix for EntityToJsonTransformer
The throw on error flag imposes us to propose a valid json string for decoding
2023-04-28 23:19:23 +02:00
3e3f20993d fix migration after test on real data 2023-04-28 15:55:41 +02:00
997f3cdb09 Feature: [exports] add filters for start and end date for accompanying period works 2023-04-28 12:37:28 +02:00
ab5ad7ae14 fix cs 2023-04-28 12:02:31 +02:00
36413f16c3 DX: convert closure to arrow function 2023-04-28 11:51:16 +02:00
f75b90cb26 Feature: [export] add filters regarding to accompanying period infos 2023-04-28 11:49:37 +02:00
1956836f88 add migration to fix existing period steps 2023-04-28 11:49:37 +02:00
ea4294d12d add start date's accompanying period in accompanyingperiod info 2023-04-28 11:49:36 +02:00
c73e57ad73 doc: add info about syncrhonizing views 2023-04-28 11:49:36 +02:00
5b729e1cb1 add dev docs 2023-04-28 11:49:36 +02:00
229af2e4f9 Feature: Adapt filters and aggregators with new steps 2023-04-28 11:49:36 +02:00
e80a6e417b Feature: Track update of entities if no user is associated to the request
This happens in scripts/cli (messenger, ...)
2023-04-28 11:49:35 +02:00
c5989de120 Feature: Adapt UI to show new steps 2023-04-28 11:49:35 +02:00
fcbc00d0f1 Feature: force to add updatedAt and createdAt even if no user iss associated 2023-04-28 11:49:35 +02:00
722f053f06 Feature: Change accompanying period info step in a cronjob 2023-04-28 11:49:35 +02:00
97b7ff2e43 Feature: takes activity into account for AccompanyingPeriodInfo 2023-04-28 11:49:34 +02:00
f3e0302f3f Feature: takes document and evaluation update into account for AccompanyingPeriodInfo 2023-04-28 11:49:34 +02:00
4974995ea2 Feature: add evaluation info to accompangyin preiod info 2023-04-28 11:49:34 +02:00
f2e1c73f37 Build parts to track info on accompanying period 2023-04-28 11:49:33 +02:00
7e3295c71f Merge branch 'rector/rules-up-to-php74' into 'master'
Rector/rules up to php74

See merge request Chill-Projet/chill-bundles!522
2023-04-27 21:42:31 +00:00
b3d881c675 Merge branch 'refactor-using-rector-202303' into 'master'
DX: rector rules up to PHP7.3

See merge request Chill-Projet/chill-bundles!513
2023-04-27 21:35:30 +00:00
47d0334b9e Merge remote-tracking branch 'origin/master' into rector/rules-up-to-php74 2023-04-27 23:32:31 +02:00
c94653a388 Merge remote-tracking branch 'origin/master' into refactor-using-rector-202303 2023-04-27 23:23:49 +02:00
cdaca533a0 Feature: [export] allow to check multiple geographical layers in
geographical aggregators

BC: saved exports which activated this layers won't work any more. This
is the query to find them:

```sql
SELECT * FROM chill_main_saved_export
WHERE
    options->'export'->'export'->'aggregators'->'accompanyingcourse_geographicalunitstat_aggregator'->>'enabled' = '1'
```
2023-04-25 17:37:27 +02:00
00e62442cc Fix: fix typo in translation 2023-04-25 17:10:07 +02:00
a539febcfe Merge branch 'geographical-units/force-make-valid-during-import' into 'master'
Fix: make geographical units valid during imports

See merge request Chill-Projet/chill-bundles!524
2023-04-25 12:34:21 +00:00
1eb78d7273 Merge branch 'draft-periods-without-creator' into 'master'
Display too draft periods without creator in periods of a person

See merge request Chill-Projet/chill-bundles!526
2023-04-25 12:30:22 +00:00
1e2ca764c1 UX: more precision for the explanation of course draft pages 2023-04-25 14:20:24 +02:00
a859b7ea93 Merge branch '92-detail-address-button' into 'master'
Intégration bouton address details

See merge request Chill-Projet/chill-bundles!528
2023-04-25 10:38:18 +00:00
49094ff54a Merge branch 'improve-budget-template' into 'master'
improve Budget template

See merge request Chill-Projet/chill-bundles!514
2023-04-25 10:37:42 +00:00
1d2b6e167f improve Budget template 2023-04-25 10:37:41 +00:00
73c0dd0e9e Merge branch 'fix-event-bundle' into 20-finalisation-cire 2023-04-24 17:24:44 +02:00
8fd9010ea5 fix event bundle stuffs
- adapt event templates
- event bundle: fix deprecated deps injections
- fix error with n=0 not iterated into querybuilder with centers loop
2023-04-24 17:22:01 +02:00
488a0e5f0c Merge branch 'improve-budget-template' into 20-finalisation-cire 2023-04-24 16:22:30 +02:00
9918cf2d58 Fix: authorization helper won't send a list of center if an admin is authenticated 2023-04-24 15:50:27 +02:00
0c5a06c678 Fix: correct usage of badRequestHttpException signature 2023-04-24 15:50:08 +02:00
cb0ff88318 Fix action column width 2023-04-24 13:29:45 +02:00
be965e8698 - improve title hierarchy and ergonomie 2023-04-24 13:01:58 +02:00
241e605ea6 - style of h3 subtitle 2023-04-24 12:22:41 +02:00
fe3d437096 improve title hierarchy coherence 2023-04-24 12:12:10 +02:00
c1f5f02c41 Merge branch 'master' into improve-budget-template 2023-04-24 11:51:58 +02:00
13feb3b212 [twig][templates] introduce an option in adress_entity_render to display or not the address details button 2023-04-21 21:15:35 +02:00
dc22c94b95 Fix person banner contact details responsive placement 2023-04-21 21:10:51 +02:00
7cb7897e12 Align address detail button style with banner context buttons 2023-04-21 20:20:55 +02:00
01f2697b8d Merge branch 'admin-permissionsgroup-templates' into 'master'
Improve permissionsgroup page in admin templates

See merge request Chill-Projet/chill-bundles!504
2023-04-21 15:36:00 +00:00
025ee2ea95 UX: [Admin][persmissionsgroup] complete in template: content wrapper with bootstrap classes 2023-04-20 20:24:23 +02:00
631d65a57f improve indentation 2023-04-20 18:09:25 +02:00
dde35be9c3 hide pagination if no periods 2023-04-20 18:05:39 +02:00
a59e84b029 Display too draft periods without creator in periods of a person 2023-04-20 16:37:10 +02:00
60ada2edce Merge branch 'maintenance/deprecate-swiftmailer' into 'master'
DX: [mailer] deprecate the chill mailer based on swift mailer + fix RoleScopePickerType

Closes #90

See merge request Chill-Projet/chill-bundles!525
2023-04-19 11:09:09 +00:00
e6163b2bc3 DX: fix cs 2023-04-19 13:05:21 +02:00
906d1fdab5 DX: remove usage of deprecated Role class 2023-04-19 13:01:48 +02:00
a1421ea99f Fix: fix usage of scope picker with unactives scopes 2023-04-19 13:00:31 +02:00
cb1ea8c622 DX: [mailer] deprecate the chill mailer based on swift mailer 2023-04-19 12:23:15 +02:00
b985e1c93e Merge branch 'fixes-for-imports' into 'master'
Fix: fix import of address and problematiques

See merge request Chill-Projet/chill-bundles!518
2023-04-18 19:17:58 +00:00
929c9f4f72 Merge branch 'calendar/fix-loading-msgraph-events' into 'master'
Fix: small fixes for remote calendars

See merge request Chill-Projet/chill-bundles!521
2023-04-18 19:17:44 +00:00
eaeab4ed2b Merge branch 'exports/fixes-202304' into 'master'
Fix: fix exports

See merge request Chill-Projet/chill-bundles!520
2023-04-18 19:17:32 +00:00
034e2553bc Fix option with RoleWithoutScope distinction. Remove flag. 2023-04-18 21:13:24 +02:00
a0d0c3840f Fix deprecated Role call 2023-04-18 19:04:30 +02:00
b69fb740b1 Fix: make geographical units valid during imports 2023-04-18 11:36:46 +02:00
d31021247b Merge branch '73-user-manual-link' into 'master'
Feature [manual] update user manual link in footer

See merge request Chill-Projet/chill-bundles!523
2023-04-18 09:18:55 +00:00
d898f3ffce Feature [manual] update user manual link in footer 2023-04-17 10:44:43 +02:00
25c033be61 DX: apply rector rules 2023-04-15 00:47:06 +02:00
746ed4f5e5 DX: fix cs 2023-04-15 00:43:55 +02:00
80647147ee Merge branch 'refactor-using-rector-202303' into rector/rules-up-to-php74 2023-04-15 00:43:39 +02:00
a9db133a7b DX: fix typing in SearchProvider 2023-04-15 00:33:31 +02:00
858ade467c DX: rector rules upt to PHP 74 2023-04-15 00:20:19 +02:00
a68190f0c6 DX: set rector config: rules up to PHP74 2023-04-15 00:19:57 +02:00
459b91001f fix rector issues 2023-04-15 00:08:04 +02:00
19034ac1d8 Merge remote-tracking branch 'origin/master' into refactor-using-rector-202303 2023-04-15 00:07:09 +02:00
4c2e78ada7 DX: set rector back to php73 2023-04-15 00:02:39 +02:00
bef1d6b4a3 Merge branch '88-replace-activity-translations' into 'master'
Replace 'activité' by 'échange'

See merge request Chill-Projet/chill-bundles!519
2023-04-14 21:51:27 +00:00
c48c5875c6 DX: fix phpstan issues 2023-04-14 23:47:33 +02:00
ffa94dbe40 Fix: [msgraph calendar subscription] fix infinite iterating on calendar subscription
When the total of user to subscribe is a multiple of offset, the subscription was an infinite loop
2023-04-14 23:41:37 +02:00
33051366d4 Fix: [remote calendar ms graph] set another date format
The previous date format was compliant with the spec described here:

https://learn.microsoft.com/en-us/graph/api/user-list-calendarview?view=graph-rest-1.0&tabs=http

However, the date format was not accepted. This should be fix the date format.
2023-04-14 23:20:59 +02:00
2b42db87dc fix: [referrer scope aggregator] fix period overlapping 2023-04-14 21:59:25 +02:00
ffc3a97651 Permissions more readables with scope before role 2023-04-14 17:17:41 +02:00
499417aea5 permissiongroup: complete translation, remove itself role in 'which implies' 2023-04-14 17:04:04 +02:00
91d40d9153 remove (⚑) mark in added roles table (use flag only in form select) 2023-04-14 16:44:38 +02:00
ec7f59fc03 UX [translations] batch replace 'activité' by 'échange' + check !
note: fix some others mistakes
2023-04-14 15:58:27 +02:00
83ce3b3e92 Use ⚑ char to mark roles without scopes 2023-04-14 15:28:43 +02:00
324a350bf1 UX: [permissiongroup] improve transition between form and behind content 2023-04-14 15:14:08 +02:00
27415ce868 UX: [permissiongroup] sticky form more compact, and with help text 2023-04-14 14:38:23 +02:00
8042edc13c UX: [permissiongroup] fix form with new sticky-form class 2023-04-14 13:55:54 +02:00
318a84e04d permissiongroup: complete translation, remove itself role in 'which implies' 2023-04-14 13:22:31 +02:00
d759b9c727 Fixed: [import social work metadata] make association from parent/child SocialIssue and SocialAction on the owning side 2023-04-14 13:14:41 +02:00
0af13b028e UX: improve admin templates 2023-04-14 13:02:36 +02:00
91ba0c983d UX: improve admin > permissiongroup templates 2023-04-14 12:25:09 +02:00
78d3c6f1be Merge branch 'master' into admin-permissionsgroup-templates 2023-04-14 12:22:32 +02:00
19e51cc74c Fix: new column name in hexasmal data for geographical coordinates 2023-04-14 12:11:04 +02:00
48f5e7d21f Merge branch 'issue706_cc_in_workflow-2' into 'master'
Feature: [workflow] add users in cc

Closes champs-libres/departement-de-la-vendee/chill#706

See merge request Chill-Projet/chill-bundles!517
2023-04-13 14:51:32 +00:00
a9fe834978 Fixed: [homepage] count also workflows in cc 2023-04-13 12:25:06 +02:00
a21637331f Fixed: [workflow] validation of users in cc and dest
The validation is processed at the form step, so before the transition is processed
2023-04-13 12:24:44 +02:00
63759a940f Feature: [homepage] group the counter of tasks in warning and alert state into one counter 2023-04-13 12:21:48 +02:00
1cd153fb78 DX: fix CS 2023-04-13 09:58:22 +02:00
0335986326 Merge remote-tracking branch 'origin/master' into issue706_cc_in_workflow-2 2023-04-13 09:53:23 +02:00
1ecb285687 Merge branch 'issue707_docgen_filter_activity_by_user' into 'master'
Feature: allow filtering activities in docgen for acc course by logged user

See merge request Chill-Projet/chill-bundles!507
2023-04-12 16:25:58 +00:00
a553dc9aab UI: fix replacement Activité => échange in french translations 2023-04-12 18:18:08 +02:00
d0830079da DX: fix generation in test mode 2023-04-12 18:17:48 +02:00
2a4b73457b DX: fix phpstan issues 2023-04-12 17:48:02 +02:00
d04011ca07 Merge remote-tracking branch 'origin/master' into issue707_docgen_filter_activity_by_user 2023-04-12 13:49:37 +02:00
796608b399 Merge branch 'track-address-reference-update' into 'master'
Feature: Track address reference update

See merge request Chill-Projet/chill-bundles!500
2023-04-12 07:54:06 +00:00
b0ac8ce5af Merge remote-tracking branch 'origin/master' into track-address-reference-update 2023-04-12 09:45:19 +02:00
d70a0fa08b Merge branch 'fix-phpstan-issues-202303' into 'master'
DX: fix phpstan issues (2023-03)

See merge request Chill-Projet/chill-bundles!510
2023-04-11 20:11:18 +00:00
8bec6feb96 DX: fix ci and phpstan issues 2023-04-11 22:01:47 +02:00
4db1ff405e fix new phpstan issues 2023-04-11 13:04:08 +02:00
841bdb0ebf fix issues from master 2023-04-11 11:43:01 +02:00
d9dd8d7317 Merge remote-tracking branch 'origin/master' into fix-phpstan-issues-202303 2023-04-11 11:36:11 +02:00
98aad8c4b6 Fixed: [accompanying period work / edit] allow endDate to be equal to startDate, and show validation errors to users
Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/79
2023-04-11 10:45:10 +02:00
ef13833966 Fixed: [create person] handle case when the user has two differents groups in the same center
Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/72
2023-04-11 10:16:09 +02:00
9853845c9c Fixed: [workflow] show error message when applying a transition and no dest users are chosen
fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/80
2023-04-07 17:38:57 +02:00
a42a4ab9bd Fixed: fix empty result in household api search
fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/85
2023-04-07 17:34:12 +02:00
839b0fc826 Fixed: [section] remove hardcoded link to phonecall aside activity 2023-04-07 16:24:28 +02:00
cc69a3e86b Fixed: [Activity] allow to set a user null
Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/74
2023-04-06 17:29:05 +02:00
4d734714a8 Fixed: [person search] forward missing parameter city
Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/84
2023-04-05 20:31:19 +02:00
1c19d01b60 Fixed: [calendar doc] Add a return path to the calendar edit's form when the document has been generated
Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/81
2023-04-05 20:23:43 +02:00
3df8ee6dc4 fix cs 2023-04-05 19:31:27 +02:00
386d1e44d0 Fixed: [accompanying course document] fix access to accompanying course document
The entity AccompanyingCourseDocument didn't had any center associated with this entity. Implementing the interface `HasCenterInterface` fixed the problem.

Fix https://gitlab.com/Chill-Projet/chill-bundles/-/issues/83
2023-04-05 19:29:33 +02:00
907c724047 Feature: [calendar doc] re-introduce button to edit document title and remove document 2023-04-03 21:30:50 +02:00
4cf6721e35 Feature: [calendar] convert calendar to activity in the context of a person 2023-04-03 19:59:31 +02:00
46e1891386 Fixed: fix error when displaying calendar list with invite in person context 2023-04-03 19:17:30 +02:00
380ad2e2e6 UI: replace "période" by "parcours" in french 2023-04-03 18:54:11 +02:00
cba2bd8260 Merge branch 'renaming-work' into 'master'
Rename "Personne" to "Usager" in French translations

See merge request Chill-Projet/chill-bundles!516
2023-03-31 12:58:57 +00:00
ee61d131ec UI: Replace "personne" by usager in french translation 2023-03-31 14:58:07 +02:00
70141fd0e3 UI: rename 'liste des échanges' to "échanges" in person menu 2023-03-31 14:36:55 +02:00
94c8b571f0 Merge branch 'change_activity_translation' into 'master'
Change activity translation

See merge request Chill-Projet/chill-bundles!515
2023-03-31 12:29:20 +00:00
94a099c30c fix typo in échange gender 2023-03-31 12:29:00 +00:00
3ce6af4c36 FIX [translations] last fixes for translations créé instead of créée 2023-03-31 12:01:35 +02:00
c8e33daf4b change all translations using activité to using échange to make all chill versions coherent 2023-03-30 20:25:59 +02:00
0f635847f2 Merge branch 'master' of gitlab.com:Chill-Projet/chill-bundles 2023-03-30 19:43:01 +02:00
d290572ca8 FIX [translation] add hardcoded text into a translation 2023-03-30 19:41:55 +02:00
3c88630edc fix: [calendar list on person page] add scripts to download documents 2023-03-30 17:13:25 +02:00
866b92f7e5 Fixed: loading of main user through pick entity in calendar form 2023-03-30 16:29:02 +02:00
e84a93bd0c wip prepare rector config for up to PHP74 ruleset 2023-03-30 11:49:04 +02:00
087ada2250 UX: Better use of flex-table and tables in budget twig templates 2023-03-30 00:07:38 +02:00
4c5dae60a8 DX: apply rector rulesets up to PHP 73 2023-03-29 22:39:11 +02:00
b9a7530f7a DX: apply rector rulesset up to PHP72 2023-03-29 22:32:52 +02:00
64b8ae3df1 DX: temporarily allow failure on unit tests 2023-03-29 22:15:20 +02:00
nobohan
c9f26f3635 Feature: filter activities by user: add denormalize method 2023-03-29 22:12:59 +02:00
a943639cf1 Feature: [reassign] by default, see and reassign 50 courses at once 2023-03-29 22:11:45 +02:00
40e751c783 DX: clean classe 2023-03-29 22:08:41 +02:00
nobohan
6d6aa689d0 Feature: filter activities by user in docgen: use creator as user 2023-03-29 22:05:36 +02:00
nobohan
03fe9210a9 upd CHANGELOG 2023-03-29 21:23:18 +02:00
nobohan
f3ede8122d remove commented lines 2023-03-29 21:23:18 +02:00
nobohan
801853e60a Feature: translation for docgen + php cs fix 2023-03-29 21:23:18 +02:00
nobohan
3927a7f62b Feature: allow filtering for users in the public form for docgen acc course - php code fix 2023-03-29 21:19:53 +02:00
nobohan
1167ff25b1 Feature: allow filtering for users in the public form for docgen acc course 2023-03-29 21:19:53 +02:00
nobohan
5bba1aca93 Feature: allow filtering works in docgen for acc course by logged user - php cs fixer 2023-03-29 21:19:53 +02:00
nobohan
9ded3eb631 Feature: allow filtering works in docgen for acc course by logged user 2023-03-29 21:19:53 +02:00
nobohan
bf79e0afdb Feature: allow filtering activities in docgen for acc course by logged user 2023-03-29 21:19:53 +02:00
nobohan
6bf8789f85 Feature: add cc users in workflow: correct constraint validation 2023-03-29 21:07:25 +02:00
nobohan
8c37afa3a9 Feature: add cc users in workflow: add constraint validation on EntityWorkflowStep 2023-03-29 12:09:12 +02:00
nobohan
a8c2750ac8 Feature: add cc users in workflow: move isNotificationCc to WorkflowNotificationHandler 2023-03-29 11:19:52 +02:00
e45952f28c Feature: add cc users in workflow: add suggested users in form type 2023-03-29 11:19:52 +02:00
nobohan
3de5d29fe8 Feature: show 'Cc' for notications as Cc 2023-03-29 11:19:52 +02:00
nobohan
94046aab81 Feature: add cc users in workflow: show in workflow history and breadcrumb 2023-03-29 11:19:52 +02:00
nobohan
a3db67fd1e Add users in cc in workflow: fix code style 2023-03-29 11:19:52 +02:00
nobohan
295406d9b7 Feature: add cc users in workflow form: not required 2023-03-29 11:19:52 +02:00
nobohan
35174e4241 Feature: show my workflows in Cc in my workflow page 2023-03-29 11:19:52 +02:00
nobohan
7580565e08 Feature: show my workflows in Cc in homepage vue widget 2023-03-29 11:19:51 +02:00
nobohan
a992c45720 Feature: create a new API endpoint for my workflows in Cc 2023-03-29 11:19:51 +02:00
nobohan
1789a75216 Feature: send notification on ccuser for workflows 2023-03-29 11:19:51 +02:00
nobohan
2b3d7f34fc Feature: Add new field ccuser in workflow step form 2023-03-29 11:19:51 +02:00
nobohan
cbc3ee68b5 Feature: Add new field ccuser in workflow step entity 2023-03-29 11:19:51 +02:00
58faac5bca DX: fix cs 2023-03-28 23:27:25 +02:00
ea10565e17 DX: [rector] apply rector rule InlineConstructorDefaultToPropertyRector 2023-03-28 23:20:37 +02:00
c5ec0e77ff DX: bootstrap rector 2023-03-28 23:20:10 +02:00
2a303c7ba4 DX: fix contravariant type-hinting on ThirdPartyNormalizer 2023-03-28 23:01:00 +02:00
e935bc20aa DX: fix baseline from removed classes 2023-03-28 22:58:30 +02:00
4f2355b313 DX: increase memory in phpstan execution 2023-03-28 22:58:13 +02:00
48772efd54 DX: fix phpstan issues 2023-03-28 22:32:08 +02:00
331443ae12 fix cs 2023-03-28 18:12:47 +02:00
561d069a3e DX: [budget calculator] restore previous budget's element type to Charge/Resource kind
+ improve typing
2023-03-28 18:11:41 +02:00
0133e202d2 More doc for CalculatorInterface 2023-03-28 16:59:54 +02:00
59147b2217 Fix: more consistency in Resource and Charge repositories 2023-03-28 16:59:08 +02:00
99cdf0ebaf Feature: [update accompanying course] do not show "locate to person" button if the person is already locating the course 2023-03-22 15:32:43 +01:00
204c28d373 Feature: [address reference] adapt vue app AccompanyingCourse to allow to open address details modal 2023-03-22 15:32:43 +01:00
adc80d5080 Feature: [address reference] add a filter to filter person by ref status 2023-03-22 15:32:42 +01:00
f256dda6fe Feature: [address] apply details button on address-render-box 2023-03-22 15:32:42 +01:00
8177a0fcce Feature: Provide api endpoint for reviewing addresses
Feature: show warning when address does not match the reference

Feature: [address] do update the address from address reference when clicked inside address details
2023-03-22 15:32:42 +01:00
21e24c60c7 Feature: show list of geographical units which covers address in address details modal 2023-03-22 15:32:41 +01:00
71d0785ab4 Feature: add api endpoint for listing all geographical units covering an address 2023-03-22 15:32:40 +01:00
b740a88ae3 Feature: bootstrapping an app to show a modal for address details and showing it inside twig address's render box
Feature: showing map and link to external map, and address details inside address details modal

Feature: AddressDetailsMap show a warning if the address is incomplete
2023-03-22 15:32:40 +01:00
c56ae08fae Feature: [address reference] write logic to match existing addresses with updates
Feature: [address reference] match addresses with reference after import

fix sql query for address reference matcher
2023-03-22 15:32:37 +01:00
c9fe5a393f DX: [Address] fix inconsistencies between doctrine mapping and sql schema
DX: [Address] fix inconsistencies between doctrine mapping and sql schema

Fixed: Address vue module do now set empty value to empty string instead of null

DX: fixed AddressReferenceBaseImporterTest
2023-03-22 11:38:48 +01:00
8e0d144dd1 Feature: add columns into Address to track the matching with AddressReference 2023-03-22 11:36:03 +01:00
2fe77f2610 UX [admin][templates] fix styles on permissionsgroup pages for standard acl model (not vendee) 2023-03-08 20:05:51 +01:00
612 changed files with 8256 additions and 5229 deletions

View File

@@ -69,7 +69,7 @@ phpstan_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
script:
- bin/phpstan
- bin/phpstan analyze --memory-limit=2G
cache:
paths:
- .cache/
@@ -79,21 +79,37 @@ phpstan_tests:
- bin
- tests/app/vendor/
psalm_tests:
rector_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
script:
- bin/psalm
allow_failure: true
- bin/rector --dry-run
cache:
paths:
- .cache/
artifacts:
expire_in: 30 min
paths:
- bin
- tests/app/vendor/
# psalm_tests:
# stage: Tests
# image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
# script:
# - bin/psalm
# allow_failure: true
# artifacts:
# expire_in: 30 min
# paths:
# - bin
# - tests/app/vendor/
unit_tests:
stage: Tests
image: gitea.champs-libres.be/chill-project/chill-skeleton-basic/base-image:php82
# until we fix testes
allow_failure: true
script:
- php tests/app/bin/console doctrine:migrations:migrate -n
- php -d memory_limit=2G tests/app/bin/console cache:clear --env=dev

View File

@@ -34,6 +34,7 @@
"sensio/framework-extra-bundle": "^5.5",
"spomky-labs/base64url": "^2.0",
"symfony/browser-kit": "^4.4",
"symfony/clock": "^6.2",
"symfony/css-selector": "^4.4",
"symfony/expression-language": "^4.4",
"symfony/form": "^4.4",
@@ -47,7 +48,6 @@
"symfony/monolog-bundle": "^3.5",
"symfony/security-bundle": "^4.4",
"symfony/serializer": "^5.3",
"symfony/swiftmailer-bundle": "^3.5",
"symfony/templating": "^4.4",
"symfony/translation": "^4.4",
"symfony/twig-bundle": "^4.4",
@@ -75,6 +75,7 @@
"phpunit/phpunit": ">= 7.5",
"psalm/plugin-phpunit": "^0.18.4",
"psalm/plugin-symfony": "^4.0.2",
"rector/rector": "^0.15.23",
"symfony/debug-bundle": "^5.1",
"symfony/dotenv": "^4.4",
"symfony/maker-bundle": "^1.20",

View File

@@ -90,9 +90,7 @@ class CountPerson implements ExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
// we gather all center the user choose.
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();

View File

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

View File

@@ -35,6 +35,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
manual/index.rst
Assets <assets.rst>
Cron Jobs <cronjob.rst>
Info about entities <entity-info.rst>
Layout and UI
**************

View File

@@ -6,6 +6,8 @@
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _timelines:
Timelines
*********
@@ -18,24 +20,24 @@ Concept
From an user point of view
--------------------------
Chill has two objectives :
Chill has two objectives :
* make the administrative tasks more lightweight ;
* help social workers to have all information they need to work
To reach this second objective, Chill provides a special view: **timeline**. On a timeline view, information is gathered and shown on a single page, from the most recent event to the oldest one.
The information gathered is linked to a *context*. This *context* may be, for instance :
The information gathered is linked to a *context*. This *context* may be, for instance :
* a person : events linked to this person are shown on the page ;
* a center: events linked to a center are shown. They may concern different peoples ;
* ...
* ...
In other word, the *context* is the kind of argument that will be used in the event's query.
Let us recall that only the data the user has allowed to see should be shown.
.. seealso::
.. seealso::
`The issue where the subject was first discussed <https://redmine.champs-libres.coop/issues/224>`_
@@ -43,30 +45,30 @@ Let us recall that only the data the user has allowed to see should be shown.
For developers
--------------
The `Main` bundle provides interfaces and services to help to build timelines.
The `Main` bundle provides interfaces and services to help to build timelines.
If a bundle wants to *push* information in a timeline, it should be create a service which implements `Chill\MainBundle\Timeline\TimelineProviderInterface`, and tag is with `chill.timeline` and arguments defining the supported context (you may use multiple `chill.timeline` tags in order to support multiple context with a single service/class).
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
.. _understanding-queries :
Understanding queries
^^^^^^^^^^^^^^^^^^^^^
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
To avoid such load on database, the objects are queried in two steps :
To avoid such load on database, the objects are queried in two steps :
1. An UNION request which gather the last X events, ordered by date. The data retrieved are the ID, the date, and a string key: a type. This type discriminates the data type.
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
Those methods should ensure that only X PHP objects will be gathered and build by the ORM.
What does the master timeline builder service ?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
1. The service build an UNION query by assembling column and tables names provided by the `fetchQuery` result ;
2. The UNION query is run, the result contains an id and a type for each row (see :ref:`above <understanding-queries>`)
@@ -84,7 +86,7 @@ To push events on a timeline :
Implementing the TimelineProviderInterface
------------------------------------------
The has the following signature :
The has the following signature :
.. code-block:: php
@@ -92,19 +94,19 @@ The has the following signature :
interface TimelineProviderInterface
{
/**
*
/**
*
* @param string $context
* @param mixed[] $args the argument to the context.
* @return TimelineSingleQuery
* @throw \LogicException if the context is not supported
*/
public function fetchQuery($context, array $args);
/**
* Indicate if the result type may be handled by the service
*
*
* @param string $type the key present in the SELECT query
* @return boolean
*/
@@ -113,42 +115,42 @@ The has the following signature :
/**
* fetch entities from db into an associative array. The keys **MUST BE**
* the id
*
* All ids returned by all SELECT queries
*
* All ids returned by all SELECT queries
* (@see TimeLineProviderInterface::fetchQuery) and with the type
* supported by the provider (@see TimelineProviderInterface::supportsType)
* will be passed as argument.
*
*
* @param array $ids an array of id
* @return mixed[] an associative array of entities, with id as key
*/
public function getEntities(array $ids);
/**
* return an associative array with argument to render the entity
* in an html template, which will be included in the timeline page
*
*
* The result must have the following key :
*
*
* - `template` : the template FQDN
* - `template_data`: the data required by the template
*
*
*
*
* Example:
*
*
* ```
* array(
* array(
* 'template' => 'ChillMyBundle:timeline:template.html.twig',
* 'template_data' => array(
* 'accompanyingPeriod' => $entity,
* 'person' => $args['person']
* 'accompanyingPeriod' => $entity,
* 'person' => $args['person']
* )
* );
* ```
*
*
* `$context` and `$args` are defined by the bundle which will call the timeline
* rendering.
*
* rendering.
*
* @param type $entity
* @param type $context
* @param array $args
@@ -156,7 +158,7 @@ The has the following signature :
* @throws \LogicException if the context is not supported
*/
public function getEntityTemplate($entity, $context, array $args);
}
@@ -176,7 +178,7 @@ The parameters should be replaced into the query by :code:`?`. They will be repl
`$context` and `$args` are defined by the bundle which will call the timeline rendering. You may use them to build a different query depending on this context.
For instance, if the context is `'person'`, the args will be this array :
For instance, if the context is `'person'`, the args will be this array :
.. code-block:: php
@@ -197,7 +199,7 @@ You should find in the bundle documentation which contexts are arguments the bun
.. note::
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
Example of an implementation :
@@ -215,13 +217,13 @@ Example of an implementation :
*/
class TimelineReportProvider implements TimelineProviderInterface
{
/**
*
* @var EntityManager
*/
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
@@ -230,9 +232,9 @@ Example of an implementation :
public function fetchQuery($context, array $args)
{
$this->checkContext($context);
$metadata = $this->em->getClassMetadata('ChillReportBundle:Report');
return TimelineSingleQuery::fromArray([
'id' => $metadata->getColumnName('id'),
'type' => 'report',
@@ -254,11 +256,11 @@ Example of an implementation :
The `supportsType` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
The implementation of our previous example will be :
The implementation of our previous example will be :
.. code-block:: php
.. code-block:: php
namespace Chill\ReportBundle\Timeline;
@@ -272,7 +274,7 @@ The implementation of our previous example will be :
//...
/**
*
*
* {@inheritDoc}
*/
public function supportsType($type)
@@ -304,12 +306,12 @@ The results **must be** an array where the id given by the UNION query (remember
{
$reports = $this->em->getRepository('ChillReportBundle:Report')
->findBy(array('id' => $ids));
$result = array();
foreach($reports as $report) {
$result[$report->getId()] = $report;
}
return $result;
}
@@ -318,9 +320,9 @@ The results **must be** an array where the id given by the UNION query (remember
The `getEntityTemplate` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is where the master service will collect information to render the entity.
This is where the master service will collect information to render the entity.
The result must be an associative array with :
The result must be an associative array with :
- **template** is the FQDN of the template ;
- **template_data** is an associative array where keys are the variables'names for this template, and values are the values.
@@ -332,8 +334,8 @@ Example :
array(
'template' => 'ChillMyBundle:timeline:template.html.twig',
'template_data' => array(
'period' => $entity,
'person' => $args['person']
'period' => $entity,
'person' => $args['person']
)
);
@@ -349,7 +351,7 @@ Create a timeline with his own context
You have to create a Controller which will execute the service `chill.main.timeline_builder`. Using the `Chill\MainBundle\Timeline\TimelineBuilder::getTimelineHTML` function, you will get an HTML representation of the timeline, which you may include with twig `raw` filter.
Example :
Example :
.. code-block:: php

View File

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

View File

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

View File

@@ -41,6 +41,7 @@
"@fullcalendar/timegrid": "^6.1.4",
"@fullcalendar/vue3": "^6.1.4",
"@popperjs/core": "^2.9.2",
"@types/leaflet": "^1.9.3",
"dropzone": "^5.7.6",
"es6-promise": "^4.2.8",
"leaflet": "^1.7.1",

View File

@@ -442,11 +442,6 @@ parameters:
count: 2
path: src/Bundle/ChillMainBundle/Workflow/Validator/StepDestValidValidator.php
-
message: "#^Return type \\(int\\|void\\|null\\) of method Chill\\\\PersonBundle\\\\Command\\\\ImportPeopleFromCSVCommand\\:\\:execute\\(\\) should be covariant with return type \\(int\\) of method Symfony\\\\Component\\\\Console\\\\Command\\\\Command\\:\\:execute\\(\\)$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#3 \\$query \\(Doctrine\\\\ORM\\\\QueryBuilder\\) of method Chill\\\\PersonBundle\\\\Controller\\\\HouseholdCompositionTypeApiController\\:\\:customizeQuery\\(\\) should be contravariant with parameter \\$query \\(mixed\\) of method Chill\\\\MainBundle\\\\CRUD\\\\Controller\\\\AbstractCRUDController\\:\\:customizeQuery\\(\\)$#"
count: 1
@@ -788,16 +783,6 @@ parameters:
count: 1
path: src/Bundle/ChillThirdPartyBundle/Security/Voter/ThirdPartyVoter.php
-
message: "#^Parameter \\#1 \\$thirdParty \\(Chill\\\\ThirdPartyBundle\\\\Entity\\\\ThirdParty\\) of method Chill\\\\ThirdPartyBundle\\\\Serializer\\\\Normalizer\\\\ThirdPartyNormalizer\\:\\:normalize\\(\\) should be contravariant with parameter \\$object \\(mixed\\) of method Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\NormalizerInterface\\:\\:normalize\\(\\)$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php
-
message: "#^Parameter \\#2 \\$format \\(string\\) of method Chill\\\\ThirdPartyBundle\\\\Serializer\\\\Normalizer\\\\ThirdPartyNormalizer\\:\\:supportsNormalization\\(\\) should be contravariant with parameter \\$format \\(string\\|null\\) of method Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\NormalizerInterface\\:\\:supportsNormalization\\(\\)$#"
count: 1
path: src/Bundle/ChillThirdPartyBundle/Serializer/Normalizer/ThirdPartyNormalizer.php
-
message: "#^Parameter \\#1 \\$document \\(Chill\\\\DocStoreBundle\\\\Entity\\\\StoredObject\\) of method Chill\\\\WopiBundle\\\\Service\\\\Wopi\\\\ChillDocumentManager\\:\\:getBasename\\(\\) should be contravariant with parameter \\$document \\(ChampsLibres\\\\WopiLib\\\\Contract\\\\Entity\\\\Document\\) of method ChampsLibres\\\\WopiLib\\\\Contract\\\\Service\\\\DocumentManagerInterface\\:\\:getBasename\\(\\)$#"
count: 1

View File

@@ -2631,11 +2631,6 @@ parameters:
count: 2
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Call to method getRoleScopes\\(\\) on an unknown class Chill\\\\MainBundle\\\\Entity\\\\PermissionGroup\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Empty array passed to foreach\\.$#"
count: 1
@@ -3026,111 +3021,6 @@ parameters:
count: 2
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$force\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$headers\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$helperSet\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$input\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$output\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$person\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$rawHeaders\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$row\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Access to an undefined property Symfony\\\\Component\\\\EventDispatcher\\\\Event\\:\\:\\$skipPerson\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to an undefined method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:createQuery\\(\\)\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Call to method buildForm\\(\\) on an unknown class Chill\\\\CustomFieldsBundle\\\\Service\\\\CustomFieldInterface\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Elseif branch is unreachable because previous condition is always true\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Instanceof between \\*NEVER\\* and Symfony\\\\Component\\\\Form\\\\ChoiceList\\\\View\\\\ChoiceGroupView will always evaluate to false\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Instanceof between Symfony\\\\Component\\\\Form\\\\ChoiceList\\\\View\\\\ChoiceView and Symfony\\\\Component\\\\Form\\\\ChoiceList\\\\View\\\\ChoiceView will always evaluate to true\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Method Chill\\\\PersonBundle\\\\Command\\\\ImportPeopleFromCSVCommand\\:\\:processTextType\\(\\) has invalid return type Chill\\\\PersonBundle\\\\Command\\\\type\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Method Symfony\\\\Component\\\\Console\\\\Helper\\\\Table\\:\\:render\\(\\) invoked with 1 parameter, 0 required\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) invoked with 2 parameters, 1 required\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^PHPDoc tag @var above foreach loop does not specify variable name\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\$value of method Chill\\\\PersonBundle\\\\Command\\\\ImportPeopleFromCSVCommand\\:\\:processTextType\\(\\) has invalid type Chill\\\\PersonBundle\\\\Command\\\\type\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Strict comparison using \\=\\=\\= between false and false will always evaluate to true\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^PHPDoc tag @var for property Chill\\\\PersonBundle\\\\Command\\\\ImportSocialWorkMetadata\\:\\:\\$importer with type Psr\\\\Log\\\\LoggerInterface is not subtype of native type Chill\\\\PersonBundle\\\\Service\\\\Import\\\\ChillImporter\\.$#"
count: 1

View File

@@ -425,21 +425,6 @@ parameters:
count: 2
path: src/Bundle/ChillPersonBundle/Actions/Remove/PersonMove.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$mobilenumber of method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:setMobilenumber\\(\\) expects libphonenumber\\\\PhoneNumber\\|null, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$phonenumber of method Chill\\\\PersonBundle\\\\Entity\\\\Person\\:\\:setPhonenumber\\(\\) expects libphonenumber\\\\PhoneNumber\\|null, string given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
count: 1

View File

@@ -310,11 +310,6 @@ parameters:
count: 1
path: src/Bundle/ChillPersonBundle/Command/ChillPersonMoveCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 6
path: src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php
-
message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#"
count: 1

View File

@@ -3,7 +3,7 @@ parameters:
paths:
- src/
tmpDir: .cache/
reportUnmatchedIgnoredErrors: true
reportUnmatchedIgnoredErrors: false
excludePaths:
- .php_cs*
- docs/

60
rector.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/docs',
__DIR__ . '/src',
]);
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
$rectorConfig->disableParallel();
//define sets of rules
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_74
]);
// skip some path...
$rectorConfig->skip([
// make rector stuck for some files
\Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector::class,
// we need to discuss this: are we going to have FALSE in tests instead of an error ?
\Rector\Php71\Rector\FuncCall\CountOnNullRector::class,
// must merge MR500 and review a typing of "ArrayCollection" in entities
\Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class,
// remove all PHP80 rules, in order to activate them one by one
\Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector::class,
\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class,
\Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class,
\Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class,
\Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class,
\Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class,
\Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector::class,
\Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector::class,
\Rector\Php80\Rector\Ternary\GetDebugTypeRector::class,
\Rector\Php80\Rector\FunctionLike\MixedTypeRector::class,
\Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector::class,
\Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector::class,
\Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class,
\Rector\Php80\Rector\ClassMethod\SetStateToStaticRector::class,
\Rector\Php80\Rector\NotIdentical\StrContainsRector::class,
\Rector\Php80\Rector\Identical\StrEndsWithRector::class,
\Rector\Php80\Rector\Identical\StrStartsWithRector::class,
\Rector\Php80\Rector\Class_\StringableForToStringRector::class,
\Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class,
\Rector\Php80\Rector\FunctionLike\UnionTypesRector::class
]);
};

View File

@@ -41,7 +41,6 @@ use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function array_key_exists;
@@ -213,7 +212,7 @@ final class ActivityController extends AbstractController
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'role' => new Role('CHILL_ACTIVITY_UPDATE'),
'role' => 'CHILL_ACTIVITY_UPDATE',
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);
@@ -442,7 +441,7 @@ final class ActivityController extends AbstractController
$form = $this->createForm(ActivityType::class, $entity, [
'center' => $this->centerResolver->resolveCenters($entity)[0] ?? null,
'role' => new Role('CHILL_ACTIVITY_CREATE'),
'role' => 'CHILL_ACTIVITY_CREATE',
'activityType' => $entity->getActivityType(),
'accompanyingPeriod' => $accompanyingPeriod,
]);
@@ -651,8 +650,8 @@ final class ActivityController extends AbstractController
throw $this->createNotFoundException('Accompanying Period not found');
}
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
// TODO Add permission
// $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person);
} else {
throw $this->createNotFoundException('Person or Accompanying Period not found');
}

View File

@@ -50,7 +50,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
->findAll();
foreach ($persons as $person) {
$activityNbr = mt_rand(0, 3);
$activityNbr = random_int(0, 3);
for ($i = 0; $i < $activityNbr; ++$i) {
$activity = $this->newRandomActivity($person);
@@ -75,7 +75,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
// ->setAttendee($this->faker->boolean())
for ($i = 0; mt_rand(0, 4) > $i; ++$i) {
for ($i = 0; random_int(0, 4) > $i; ++$i) {
$reason = $this->getRandomActivityReason();
if (null !== $reason) {

View File

@@ -26,7 +26,7 @@ class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_activity');
$rootNode = $treeBuilder->getRootNode('chill_activity');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()
@@ -59,9 +59,7 @@ class Configuration implements ConfigurationInterface
->info('The number of seconds of this duration. Must be an integer.')
->cannotBeEmpty()
->validate()
->ifTrue(static function ($data) {
return !is_int($data);
})->thenInvalid('The value %s is not a valid integer')
->ifTrue(static fn ($data) => !is_int($data))->thenInvalid('The value %s is not a valid integer')
->end()
->end()
->scalarNode('label')

View File

@@ -195,7 +195,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
* @Groups({"docgen:read"})
*/
private User $user;
private ?User $user = null;
/**
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
@@ -494,7 +494,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this->activityType;
}
public function getUser(): User
public function getUser(): ?User
{
return $this->user;
}
@@ -681,14 +681,14 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
return $this;
}
public function setUser(UserInterface $user): self
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function setUsers(?Collection $users): self
public function setUsers(Collection $users): self
{
$this->users = $users;

View File

@@ -34,7 +34,7 @@ class ActivityPresence
* @ORM\GeneratedValue(strategy="AUTO")
* @Serializer\Groups({"docgen:read"})
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="json")

View File

@@ -122,7 +122,7 @@ class ActivityType
* @ORM\GeneratedValue(strategy="AUTO")
* @Groups({"docgen:read"})
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="string", nullable=false, options={"default": ""})

View File

@@ -86,9 +86,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository->createQueryBuilder('activity');

View File

@@ -87,9 +87,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository->createQueryBuilder('activity');

View File

@@ -86,9 +86,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')

View File

@@ -109,9 +109,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->entityManager->createQueryBuilder();

View File

@@ -87,9 +87,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')

View File

@@ -87,9 +87,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->repository
->createQueryBuilder('activity')

View File

@@ -137,13 +137,11 @@ class ListActivity implements ListInterface, GroupedExportInterface
$activity = $activityRepository->find($value);
return implode(', ', array_map(function (ActivityReason $r) {
return '"' .
$this->translatableStringHelper->localize($r->getCategory()->getName())
. ' > ' .
$this->translatableStringHelper->localize($r->getName())
. '"';
}, $activity->getReasons()->toArray()));
return implode(', ', array_map(fn (ActivityReason $r) => '"' .
$this->translatableStringHelper->localize($r->getCategory()->getName())
. ' > ' .
$this->translatableStringHelper->localize($r->getName())
. '"', $activity->getReasons()->toArray()));
};
case 'circle_name':
@@ -152,7 +150,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'circle';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
};
case 'type_name':
@@ -161,7 +159,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
return 'activity type';
}
return $this->translatableStringHelper->localize(json_decode($value, true));
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
};
default:
@@ -197,9 +195,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
// throw an error if any fields are present
if (!array_key_exists('fields', $data)) {

View File

@@ -179,7 +179,7 @@ class ListActivityHelper
}
}
$decoded = json_decode($value);
$decoded = json_decode($value, null, 512, JSON_THROW_ON_ERROR);
return implode(
'|',

View File

@@ -61,12 +61,9 @@ class ActivityTypeFilter implements FilterInterface
$builder->add('accepted_activitytypes', EntityType::class, [
'class' => ActivityType::class,
'choices' => $this->activityTypeRepository->findAllActive(),
'choice_label' => function (ActivityType $aty) {
return
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName());
},
'choice_label' => fn (ActivityType $aty) => ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName()),
'multiple' => true,
'expanded' => true,
]);

View File

@@ -20,13 +20,6 @@ use Symfony\Component\Form\FormBuilderInterface;
class LocationFilter implements FilterInterface
{
private TranslatableStringHelper $translatableStringHelper;
public function __construct(TranslatableStringHelper $translatableStringHelper)
{
$this->translatableStringHelper = $translatableStringHelper;
}
public function addRole(): ?string
{
return null;

View File

@@ -64,11 +64,9 @@ class UserScopeFilter implements FilterInterface
{
$builder->add('accepted_userscope', EntityType::class, [
'class' => Scope::class,
'choice_label' => function (Scope $s) {
return $this->translatableStringHelper->localize(
$s->getName()
);
},
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
$s->getName()
),
'multiple' => true,
'expanded' => true,
]);

View File

@@ -61,12 +61,9 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
$builder->add('types', EntityType::class, [
'choices' => $this->activityTypeRepository->findAllActive(),
'class' => ActivityType::class,
'choice_label' => function (ActivityType $aty) {
return
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName());
},
'choice_label' => fn (ActivityType $aty) => ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
.
$this->translatableStringHelper->localize($aty->getName()),
'group_by' => function (ActivityType $type) {
if (!$type->hasCategory()) {
return null;

View File

@@ -32,7 +32,7 @@ class ActivityReasonCategoryType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Chill\ActivityBundle\Entity\ActivityReasonCategory',
'data_class' => \Chill\ActivityBundle\Entity\ActivityReasonCategory::class,
]);
}

View File

@@ -211,13 +211,9 @@ class ActivityType extends AbstractType
'required' => $activityType->isRequired('attendee'),
'expanded' => true,
'class' => ActivityPresence::class,
'choice_label' => function (ActivityPresence $activityPresence) {
return $this->translatableStringHelper->localize($activityPresence->getName());
},
'query_builder' => static function (EntityRepository $er) {
return $er->createQueryBuilder('a')
->where('a.active = true');
},
'choice_label' => fn (ActivityPresence $activityPresence) => $this->translatableStringHelper->localize($activityPresence->getName()),
'query_builder' => static fn (EntityRepository $er) => $er->createQueryBuilder('a')
->where('a.active = true'),
]);
}
@@ -225,6 +221,7 @@ class ActivityType extends AbstractType
$builder->add('user', PickUserDynamicType::class, [
'label' => $activityType->getLabel('user'),
'required' => $activityType->isRequired('user'),
'multiple' => false,
]);
}
@@ -356,9 +353,7 @@ class ActivityType extends AbstractType
return (string) $location->getId();
},
function (?string $id): ?Location {
return $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id]);
}
fn (?string $id): ?Location => $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id])
));
}
@@ -400,9 +395,7 @@ class ActivityType extends AbstractType
// the datetimetransformer will then handle timezone as GMT
$timezoneUTC = new DateTimeZone('GMT');
/** @var DateTime $data */
$data = $formEvent->getData() === null ?
DateTime::createFromFormat('U', '300') :
$formEvent->getData();
$data = $formEvent->getData() ?? DateTime::createFromFormat('U', '300');
$seconds = $data->getTimezone()->getOffset($data);
$data->setTimeZone($timezoneUTC);
$data->add(new DateInterval('PT' . $seconds . 'S'));

View File

@@ -45,9 +45,7 @@ class ActivityTypeType extends AbstractType
])
->add('category', EntityType::class, [
'class' => ActivityTypeCategory::class,
'choice_label' => function (ActivityTypeCategory $activityTypeCategory) {
return $this->translatableStringHelper->localize($activityTypeCategory->getName());
},
'choice_label' => fn (ActivityTypeCategory $activityTypeCategory) => $this->translatableStringHelper->localize($activityTypeCategory->getName()),
])
->add('ordering', NumberType::class, [
'required' => true,

View File

@@ -45,9 +45,7 @@ class PickActivityReasonType extends AbstractType
$resolver->setDefaults(
[
'class' => ActivityReason::class,
'choice_label' => function (ActivityReason $choice) {
return $this->reasonRender->renderString($choice, []);
},
'choice_label' => fn (ActivityReason $choice) => $this->reasonRender->renderString($choice, []),
'group_by' => function (ActivityReason $choice): ?string {
if (null !== $category = $choice->getCategory()) {
return $this->translatableStringHelper->localize($category->getName());

View File

@@ -38,10 +38,8 @@ class TranslatableActivityReasonCategoryType extends AbstractType
$resolver->setDefaults(
[
'class' => ActivityReasonCategory::class,
'choice_label' => function (ActivityReasonCategory $category) {
return $this->translatableStringHelper->localize($category->getName())
. (!$category->getActive() ? ' (' . $this->translator->trans('inactive') . ')' : '');
},
'choice_label' => fn (ActivityReasonCategory $category) => $this->translatableStringHelper->localize($category->getName())
. (!$category->getActive() ? ' (' . $this->translator->trans('inactive') . ')' : ''),
]
);
}

View File

@@ -39,9 +39,7 @@ class TranslatableActivityType extends AbstractType
'class' => ActivityType::class,
'active_only' => true,
'choices' => $this->activityTypeRepository->findAllActive(),
'choice_label' => function (ActivityType $type) {
return $this->translatableStringHelper->localize($type->getName());
},
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
]
);
}

View File

@@ -26,12 +26,12 @@ final class PersonMenuBuilder implements LocalMenuBuilderInterface
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
private $authorizationChecker;
/**
* @var TranslatorInterface
*/
protected $translator;
private $translator;
public function __construct(
AuthorizationCheckerInterface $authorizationChecker,
@@ -48,7 +48,7 @@ final class PersonMenuBuilder implements LocalMenuBuilderInterface
if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) {
$menu->addChild(
$this->translator->trans('Activity list'),
$this->translator->trans('Activities'),
[
'route' => 'chill_activity_activity_list',
'routeParameters' => ['person_id' => $person->getId()],

View File

@@ -254,9 +254,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), ActivityVoter::SEE, $center);
// we get the ids for those scopes
$reachablesScopesId = array_map(
static function (Scope $scope) {
return $scope->getId();
},
static fn (Scope $scope) => $scope->getId(),
$reachableScopes
);

View File

@@ -63,7 +63,7 @@
</div>
{% endif %}
{% if activity.user and t.userVisible %}
{% if activity.user is not null and t.userVisible %}
<div class="wl-row">
<div class="wl-col title"><h3>{{ 'Referrer'|trans }}</h3></div>
<div class="wl-col list">

View File

@@ -5,7 +5,7 @@
Maybe should we think about abstracting this file a bit more ? Moving it to PersonBundle ?
#}
{% if context == 'calendar_accompanyingCourse' %}
{% if context == 'calendar_accompanyingCourse' or context == 'calendar_person' %}
{% import "@ChillCalendar/_invite.html.twig" as invite %}
{% endif %}

View File

@@ -34,10 +34,12 @@
<div class="item-row separator">
<dl class="chill_view_data">
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
<dd>
<span class="badge-user">{{ entity.user|chill_entity_render_box }}</span>
</dd>
{%- if entity.user is not null %}
<dt class="inline">{{ 'Referrer'|trans|capitalize }}</dt>
<dd>
<span class="badge-user">{{ entity.user|chill_entity_render_box }}</span>
</dd>
{% endif %}
{%- if entity.scope -%}
<dt class="inline">{{ 'Scope'|trans }}</dt>

View File

@@ -134,9 +134,7 @@ class ActivityContext implements
$builder->add($key, EntityType::class, [
'class' => Person::class,
'choices' => $persons,
'choice_label' => function (Person $p) {
return $this->personRender->renderString($p, []);
},
'choice_label' => fn (Person $p) => $this->personRender->renderString($p, []),
'multiple' => false,
'required' => false,
'expanded' => true,
@@ -147,6 +145,32 @@ class ActivityContext implements
}
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
if (null !== ($id = ($data[$k] ?? null))) {
$denormalized[$k] = $this->personRepository->find($id);
} else {
$denormalized[$k] = null;
}
}
return $denormalized;
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$normalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
$normalized[$k] = null === $data[$k] ? null : $data[$k]->getId();
}
return $normalized;
}
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
{
if (!$entity instanceof Activity) {
@@ -214,32 +238,6 @@ class ActivityContext implements
return $options['mainPerson'] || $options['person1'] || $options['person2'];
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$normalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
$normalized[$k] = null === $data[$k] ? null : $data[$k]->getId();
}
return $normalized;
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = [];
foreach (['mainPerson', 'person1', 'person2'] as $k) {
if (null !== ($id = ($data[$k] ?? null))) {
$denormalized[$k] = $this->personRepository->find($id);
} else {
$denormalized[$k] = null;
}
}
return $denormalized;
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void
{
$storedObject->setTitle($this->translatableStringHelper->localize($template->getName()));

View File

@@ -34,10 +34,16 @@ use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Chill\ThirdPartyBundle\Repository\ThirdPartyRepository;
use DateTime;
use libphonenumber\PhoneNumber;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use function in_array;
/**
* @implements DocGeneratorContextWithPublicFormInterface<AccompanyingPeriod>
* @implements DocGeneratorContextWithAdminFormInterface<AccompanyingPeriod>
*/
class ListActivitiesByAccompanyingPeriodContext implements
DocGeneratorContextWithAdminFormInterface,
DocGeneratorContextWithPublicFormInterface
@@ -69,7 +75,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
SocialIssueRepository $socialIssueRepository,
ThirdPartyRepository $thirdPartyRepository,
TranslatableStringHelperInterface $translatableStringHelper,
UserRepository $userRepository
UserRepository $userRepository,
) {
$this->accompanyingPeriodContext = $accompanyingPeriodContext;
$this->activityACLAwareRepository = $activityACLAwareRepository;
@@ -100,14 +106,81 @@ class ListActivitiesByAccompanyingPeriodContext implements
public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, $entity): void
{
$this->accompanyingPeriodContext->buildPublicForm($builder, $template, $entity);
$builder
->add('myActivitiesOnly', CheckboxType::class, [
'required' => false,
'label' => 'docgen.myActivitiesOnly',
])
->add('myWorksOnly', CheckboxType::class, [
'required' => false,
'label' => 'docgen.myWorksOnly',
]);
}
public function getData(DocGeneratorTemplate $template, $entity, array $contextGenerationData = []): array
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$denormalized = $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data);
foreach (['myActivitiesOnly', 'myWorksOnly'] as $k) {
$denormalized[$k] = $data[$k];
}
return $denormalized;
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
$normalized = $this->accompanyingPeriodContext->contextGenerationDataNormalize($template, $entity, $data);
foreach (['myActivitiesOnly', 'myWorksOnly'] as $k) {
$normalized[$k] = $data[$k] ?? false;
}
return $normalized;
}
private function filterActivitiesByUser(array $activities, User $user): array
{
return array_filter(
$activities,
function ($activity) use ($user) {
$activityUsernames = array_map(static fn ($user) => $user['username'], $activity['users'] ?? []);
return in_array($user->getUsername(), $activityUsernames, true);
}
);
}
private function filterWorksByUser(array $works, User $user): array
{
return array_filter(
$works,
function ($work) use ($user) {
$workUsernames = array_map(static fn ($user) => $user['username'], $work['referrers'] ?? []);
return in_array($user->getUsername(), $workUsernames, true);
}
);
}
public function getData(DocGeneratorTemplate $template, object $entity, array $contextGenerationData = []): array
{
$data = $this->accompanyingPeriodContext->getData($template, $entity, $contextGenerationData);
$data['activities'] = $this->getActivitiesSimplified($entity);
$activities = $this->getActivitiesSimplified($entity);
$myActivitiesOnly = $contextGenerationData['myActivitiesOnly'];
if ($myActivitiesOnly && isset($contextGenerationData['creator'])) {
$activities = $this->filterActivitiesByUser($activities, $contextGenerationData['creator']);
}
$data['activities'] = $activities;
$myWorksOnly = $contextGenerationData['myWorksOnly'];
if ($myWorksOnly && isset($contextGenerationData['creator'])) {
$data['course']['works'] = $this->filterWorksByUser($data['course']['works'], $contextGenerationData['creator']);
}
return $data;
}
@@ -143,17 +216,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
public function hasPublicForm(DocGeneratorTemplate $template, $entity): bool
{
return $this->accompanyingPeriodContext->hasPublicForm($template, $entity);
}
public function contextGenerationDataNormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
return $this->accompanyingPeriodContext->contextGenerationDataNormalize($template, $entity, $data);
}
public function contextGenerationDataDenormalize(DocGeneratorTemplate $template, $entity, array $data): array
{
return $this->accompanyingPeriodContext->contextGenerationDataDenormalize($template, $entity, $data);
return true;
}
public function storeGenerated(DocGeneratorTemplate $template, StoredObject $storedObject, object $entity, array $contextGenerationData): void

View File

@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\ActivityBundle\Service\EntityInfo\AccompanyingPeriodInfoQueryPart;
use Chill\ActivityBundle\Entity\Activity;
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
class ActivityUsersDateQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
{
public function getAccompanyingPeriodIdColumn(): string
{
return 'activity.accompanyingperiod_id';
}
public function getRelatedEntityColumn(): string
{
return Activity::class;
}
public function getRelatedEntityIdColumn(): string
{
return 'activity.id';
}
public function getUserIdColumn(): string
{
return 'au.user_id';
}
public function getDateTimeColumn(): string
{
return 'activity.date';
}
public function getDiscriminator(): string
{
return 'activity_date';
}
public function getMetadataColumn(): string
{
return '\'{}\'::jsonb';
}
public function getFromStatement(): string
{
return 'activity
LEFT JOIN activity_user au on activity.id = au.activity_id';
}
public function getWhereClause(): string
{
return 'activity.accompanyingperiod_id IS NOT NULL';
}
}

View File

@@ -121,14 +121,14 @@ final class ActivityControllerTest extends WebTestCase
$client->getResponse()->getStatusCode(),
'Unexpected HTTP status code for GET /activity/'
);
$crawler = $client->click($crawler->selectLink('Ajouter une nouvelle activité')
$crawler = $client->click($crawler->selectLink('Ajouter un nouvel échange')
->link());
$reason1 = $this->getRandomActivityReason();
$reason2 = $this->getRandomActivityReason([$reason1->getId()]);
// Fill in the form and submit it
$form = $crawler->selectButton('Ajouter une nouvelle activité')->form([
$form = $crawler->selectButton('Ajouter un nouvel échange')->form([
'chill_activitybundle_activity' => [
'date' => '15-01-2015',
'durationTime' => 600,
@@ -152,9 +152,9 @@ final class ActivityControllerTest extends WebTestCase
);
// Edit the entity
$crawler = $client->click($crawler->selectLink("Modifier l'activité")->link());
$crawler = $client->click($crawler->selectLink("Modifier l'échange")->link());
$form = $crawler->selectButton("Sauver l'activité")->form([
$form = $crawler->selectButton("Sauver l'échange")->form([
'chill_activitybundle_activity' => [
'date' => '25-01-2015',
// 'remark' => 'Foo'
@@ -369,12 +369,8 @@ final class ActivityControllerTest extends WebTestCase
$center
);
$reachableScopesId = array_intersect(
array_map(static function ($s) {
return $s->getId();
}, $reachableScopesDelete),
array_map(static function ($s) {
return $s->getId();
}, $reachableScopesUpdate)
array_map(static fn ($s) => $s->getId(), $reachableScopesDelete),
array_map(static fn ($s) => $s->getId(), $reachableScopesUpdate)
);
if (count($reachableScopesId) === 0) {

View File

@@ -112,9 +112,9 @@ final class ActivityTypeTest extends KernelTestCase
'attendee' => true,
]]);
// var_dump($form->getErrors()->count()); var_dump($form->isValid());
// foreach($form->getErrors() as $e) { fwrite(STDOUT, var_dump($e->getMessage())); }
// var_dump($form->getErrors());
// var_dump($form->getErrors()->count()); var_dump($form->isValid());
// foreach($form->getErrors() as $e) { fwrite(STDOUT, var_dump($e->getMessage())); }
// var_dump($form->getErrors());
$this->assertTrue($form->isSynchronized(), 'Test the form is synchronized');
$this->assertTrue($form->isValid(), 'test the form is valid');
@@ -188,9 +188,7 @@ final class ActivityTypeTest extends KernelTestCase
// map all the values in an array
$values = array_map(
static function ($choice) {
return $choice->value;
},
static fn ($choice) => $choice->value,
$view['activity']['durationTime']->vars['choices']
);

View File

@@ -79,15 +79,13 @@ final class TranslatableActivityReasonTest extends TypeTestCase
$request = $prophet->prophesize();
$translator = $prophet->prophesize();
$request->willExtend('Symfony\Component\HttpFoundation\Request');
$request->willExtend(\Symfony\Component\HttpFoundation\Request::class);
$request->getLocale()->willReturn($fallbackLocale);
$requestStack->willExtend('Symfony\Component\HttpFoundation\RequestStack');
$requestStack->getCurrentRequest()->will(static function () use ($request) {
return $request;
});
$requestStack->willExtend(\Symfony\Component\HttpFoundation\RequestStack::class);
$requestStack->getCurrentRequest()->will(static fn () => $request);
$translator->willExtend('Symfony\Component\Translation\Translator');
$translator->willExtend(\Symfony\Component\Translation\Translator::class);
$translator->getFallbackLocales()->willReturn($locale);
return new TranslatableStringHelper(

View File

@@ -160,7 +160,7 @@ final class ActivityVoterTest extends KernelTestCase
{
$token = $this->prophet->prophesize();
$token
->willImplement('\Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
->willImplement('\\' . \Symfony\Component\Security\Core\Authentication\Token\TokenInterface::class);
if (null === $user) {
$token->getUser()->willReturn(null);

View File

@@ -34,6 +34,7 @@ services:
resource: '../Validator/Constraints/'
Chill\ActivityBundle\Service\DocGenerator\:
autowire: true
autoconfigure: true
resource: '../Service/DocGenerator/'
Chill\ActivityBundle\Service\EntityInfo\:
resource: '../Service/EntityInfo/'

View File

@@ -1,41 +1,41 @@
#general
Show the activity: Voir l'activité
Edit the activity: Modifier l'activité
Activity: Activité
Show the activity: Voir l'échange
Edit the activity: Modifier l'échange
Activity: Échange
Duration time: Durée
Duration Time: Durée
durationTime: durée
Travel time: Durée de déplacement
Attendee: Présence de la personne
attendee: présence de la personne
Attendee: Présence de l'usager
attendee: présence de l'usager
list_reasons: liste des sujets
user_username: nom de l'utilisateur
circle_name: nom du cercle
Remark: Commentaire
No comments: Aucun commentaire
Add a new activity: Ajouter une nouvelle activité
Activity list: Liste des activités
Add a new activity: Ajouter une nouvel échange
Activity list: Liste des échanges
present: présent
not present: absent
Delete: Supprimer
Update: Mettre à jour
Update activity: Modifier l'activité
Update activity: Modifier l'échange
Scope: Cercle
Activity data: Données de l'activité
Activity location: Localisation de l'activité
Activity data: Données de l'échange
Activity location: Localisation de l'échange
No reason associated: Aucun sujet
No social issues associated: Aucune problématique sociale
No social actions associated: Aucune action d'accompagnement
There isn't any activities.: Aucune activité enregistrée.
type_name: type de l'activité
There isn't any activities.: Aucun échange enregistré.
type_name: type de l'échange
person_firstname: prénom
person_lastname: nom de famille
person_id: identifiant de la personne
person_id: identifiant de l'usager
Type: Type
Invisible: Invisible
Optional: Optionnel
Required: Obligatoire
Persons: Personnes
Persons: Usagers
Users: Utilisateurs
Emergency: Urgent
Sent received: Entrant / Sortant
@@ -50,10 +50,10 @@ received: Reçu
#forms
Activity creation: Nouvelle activité
Activity creation: Nouvel échange
Create: Créer
Back to the list: Retour à la liste
Save activity: Sauver l'activité
Save activity: Sauver l'échange
Reset form: Remise à zéro du formulaire
Choose the duration: Choisir la durée
Choose a type: Choisir un type
@@ -90,47 +90,50 @@ activity:
No documents: Aucun document
#timeline
'%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"'
'%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"'
#controller
'Success : activity created!': L'activité a été créée.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'activité n'a pas été créée.
'Success : activity updated!': L'activité a été mise à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'activité n'a pas été mise à jour.
'Success : activity created!': L'échange a été créé.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'échange n'a pas été créé.
'Success : activity updated!': L'échange a été mis à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'échange n'a pas été mise à jour.
# ROLES
CHILL_ACTIVITY_CREATE: Créer une activité
CHILL_ACTIVITY_UPDATE: Modifier une activité
CHILL_ACTIVITY_SEE: Voir une activité
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des activités
CHILL_ACTIVITY_DELETE: Supprimer une activité
CHILL_ACTIVITY_STATS: Statistique des activités
CHILL_ACTIVITY_LIST: Liste des activités
CHILL_ACTIVITY_CREATE: Créer un échange
CHILL_ACTIVITY_UPDATE: Modifier un échange
CHILL_ACTIVITY_SEE: Voir un échange
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des échanges
CHILL_ACTIVITY_DELETE: Supprimer un échange
CHILL_ACTIVITY_STATS: Statistique des échanges
CHILL_ACTIVITY_LIST: Liste des échanges
CHILL_ACTIVITY_CREATE_PERSON: Créer un échange lié à un usager
CHILL_ACTIVITY_CREATE_ACCOMPANYING_COURSE: Créer un échange lié à un parcours
CHILL_ACTIVITY_FULL: Voir les détails, créer, supprimer et mettre à jour un échange
# admin
Activities: Activités
Activity configuration: Configuration des activités
Activity configuration menu: Configuration des activités
Activity types: Types d'activité
Activity type configuration: Configuration des categories d'activités
Activity Reasons: Sujets d'une activité
Activity Reasons Category: Catégories de sujet d'activités
Activity Types Categories: Catégories des types d'activité
Activity Presences: Presences aux activités
Activities: Échanges
Activity configuration: Configuration des échanges
Activity configuration menu: Configuration des échanges
Activity types: Types d'échange
Activity type configuration: Configuration des catégories d'échanges
Activity Reasons: Sujets d'un échange
Activity Reasons Category: Catégories de sujet d'échanges
Activity Types Categories: Catégories des types d'échange
Activity Presences: Présences aux échanges
Associated activity reason category is inactive: La catégorie de sujet attachée est inactive
# Crud
crud:
activity_type:
title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité
title_new: Nouveau type d'échange
title_edit: Édition d'un type d'échange
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
title_new: Nouvelle catégorie de type d'échange
title_edit: Édition d'une catégorie de type d'échange
activity_presence:
title_new: Nouvelle Présence aux activités
title_edit: Edition d'une Présence aux activités
title_new: Nouvelle présence aux échanges
title_edit: Édition d'une présence aux échanges
# activity reason admin
ActivityReason list: Liste des sujets
@@ -139,7 +142,7 @@ Active: Actif
Category: Catégorie
ActivityReason creation: Nouveau sujet
ActivityReason edit: Modification d'un sujet
ActivityReason: Sujet d'activité
ActivityReason: Sujet d'échange
The entity is inactive and won't be proposed: Le sujet est inactif et ne sera pas proposé
The entity is active and will be proposed: Le sujet est actif et sera proposé
@@ -148,19 +151,19 @@ ActivityReasonCategory list: Catégories de sujets
Create a new activity category reason: Créer une nouvelle catégorie
ActivityReasonCategory creation: Nouvelle catégorie de sujet
ActivityReasonCategory edit: Modification d'une catégorie de sujet
ActivityReasonCategory: Catégorie de sujet d'activité
ActivityReasonCategory: Catégorie de sujet d'échange
ActivityReasonCategory is active and will be proposed: La catégorie est active et sera proposée
ActivityReasonCategory is inactive and won't be proposed: La catégorie est inactive et ne sera pas proposée
#activity presence admin
ActivityPresence list: Liste des Présences aux activités
Create a new activity presence: Créer une nouvelle "Présence aux activités"
ActivityPresence list: Liste des présences aux échanges
Create a new activity presence: Créer une nouvelle "Présence aux échanges"
# activity type type admin
ActivityType list: Types d'activités
Create a new activity type: Créer un nouveau type d'activité
Persons visible: Visibilité du champ Personnes
Persons label: Libellé du champ Personnes
ActivityType list: Types d'échanges
Create a new activity type: Créer un nouveau type d'échange
Persons visible: Visibilité du champ Usagers
Persons label: Libellé du champ Usagers
User visible: Visibilité du champ Utilisateur
User label: Libellé du champ Utilisateur
Date visible: Visibilité du champ Date
@@ -183,8 +186,8 @@ Private comment visible: Visibilité du champ Commentaire Privé
Private comment label: Libellé du champ Commentaire Privé
Emergency visible: Visibilité du champ Urgent
Emergency label: Libellé du champ Urgent
Accompanying period visible: Visibilité du champ Période d'accompagnement
Accompanying period label: Libellé du champ Période d'accompagnement
Accompanying period visible: Visibilité du champ parcours d'accompagnement
Accompanying period label: Libellé du champ parcours d'accompagnement
Social issues visible: Visibilité du champ Problématiques sociales
Social issues label: Libellé du champ Problématiques sociales
Social actions visible: Visibilité du champ Action sociale
@@ -197,138 +200,140 @@ Documents visible: Visibilité du champ Documents
Documents label: Libellé du champ Documents
# activity type category admin
ActivityTypeCategory list: Liste des catégories des types d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'activité
ActivityTypeCategory list: Liste des catégories des types d'échange
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
# activity delete
Remove activity: Supprimer une activité
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité qui concerne "%name%" ?
The activity has been successfully removed.: L'activité a été supprimée.
Remove activity: Supprimer un échange
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer un échange qui concerne "%name%" ?
The activity has been successfully removed.: L'échange a été supprimé.
# exports
Exports of activities linked to a person: Exports des activités liées à une personne
Number of activities linked to a person: Nombre d'activités liées à une personne
Count activities linked to a person: Nombre d'activités
Count activities linked to a person by various parameters.: Compte le nombre d'activités enregistrées et liées à une personne en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des activités
Sum activities linked to a person duration: Durée des activités liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activity linked to a person: Liste les activités
List activities linked to a person: Liste des activités liés à un usager
List activities linked to a person description: Crée la liste des activités en fonction de différents paramètres.
Exports of activities linked to a person: Exports des échanges liés à un usager
Number of activities linked to a person: Nombre d'échanges liés à un usager
Count activities linked to a person: Nombre d'échanges
Count activities linked to a person by various parameters.: Compte le nombre d'échanges enregistrés et liés à un usager en fonction de différents paramètres.
Sum activity linked to a person duration: Durée des échanges
Sum activities linked to a person duration: Durée des échanges liés à un usager
Sum activities linked to a person duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
List activity linked to a person: Liste les échanges
List activities linked to a person: Liste des échanges liés à un usager
List activities linked to a person description: Crée la liste des échanges en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des activités liées à un parcours
Number of activities linked to an accompanying period: Nombre d'activités liées à un parcours
Count activities linked to an accompanying period: Nombre d'activités
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'activités enregistrées et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des activités
Sum activities linked to an accompanying period duration: Somme de la durée des activités liées à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des activités
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des activités liées à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des activités en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des activités
Average activities linked to an accompanying period duration: Moyenne de la durée des activités liées à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des activités en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des activités liées à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des activités en fonction de différents paramètres.
Exports of activities linked to an accompanying period: Exports des échanges liés à un parcours
Number of activities linked to an accompanying period: Nombre d'échanges liés à un parcours
Count activities linked to an accompanying period: Nombre d'échanges
Count activities linked to an accompanying period by various parameters.: Compte le nombre d'échanges enregistrés et liées à un parcours en fonction de différents paramètres.
Sum activity linked to an accompanying period duration: Somme de la durée des échanges
Sum activities linked to an accompanying period duration: Somme de la durée des échanges liés à un parcours
Sum activities linked to an accompanying period duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
Sum activity linked to an accompanying period visit duration: Somme de la durée de déplacement des échanges
Sum activities linked to an accompanying period visit duration: Somme de la durée de déplacement des échanges liés à un parcours
Sum activities linked to an accompanying period visit duration by various parameters.: Additionne la durée de déplacement des échanges en fonction de différents paramètres.
Average activity linked to an accompanying period duration: Moyenne de la durée des échanges
Average activities linked to an accompanying period duration: Moyenne de la durée des échanges liés à un parcours
Average activities linked to an accompanying period duration by various parameters.: Moyenne de la durée des échanges en fonction de différents paramètres.
Average activity linked to an accompanying period visit duration: Moyenne de la durée de déplacement des échanges
Average activities linked to an accompanying period visit duration: Moyenne de la durée de déplacement des échanges liés à un parcours
Average activities linked to an accompanying period visit duration by various parameters.: Moyenne de la durée de déplacement des échanges en fonction de différents paramètres.
#filters
Filter by reason: Filtrer les activités par sujet
Filter by reason: Filtrer les échanges par sujet
'Filtered by reasons: only %list%': 'Filtré par sujet: seulement %list%'
'Filtered by activity type: only %list%': "Filtré par type d'activité: uniquement %list%"
Filtered by date activity: Filtrer les activités par date
Activities after this date: Activités après cette date
Activities before this date: Activités avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'activité: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités après cette date"
'Filtered by activity type: only %list%': "Filtré par type d'échange: uniquement %list%"
Filtered by date activity: Filtrer les échanges par date
Activities after this date: Échanges après cette date
Activities before this date: Échanges avant cette date
"Filtered by date of activity: only between %date_from% and %date_to%": "Filtré par date de l'échange: uniquement entre %date_from% et %date_to%"
This date should be after the date given in "Implied in an activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "échanges après cette date"
Filtered by person having an activity in a period: Uniquement les personnes ayant eu une activité dans la période donnée
Implied in an activity after this date: Impliqué dans une activité après cette date
Implied in an activity before this date: Impliqué dans une activité avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par personnes associées à une activité entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces activités
Filtered by person having an activity in a period: Uniquement les usagers ayant eu un échange dans la période donnée
Implied in an activity after this date: Impliqué dans un échange après cette date
Implied in an activity before this date: Impliqué dans un échange avant cette date
Filtered by person having an activity between %date_from% and %date_to% with reasons %reasons_name%: Filtré par usager associées à un échange entre %date_from% et %date_to% avec les sujets %reasons_name%
Activity reasons for those activities: Sujets de ces échanges
Filter by activity type: Filtrer les activités par type
Filter by activity type: Filtrer les échanges par type
Filter activity by location: Filtrer les activités par localisation
Filter activity by location: Filtrer les échanges par localisation
'Filtered activity by location: only %locations%': "Filtré par localisation: uniquement %locations%"
Filter activity by locationtype: Filtrer les activités par type de localisation
Filter activity by locationtype: Filtrer les échanges par type de localisation
'Filtered activity by locationtype: only %types%': "Filtré par type de localisation: uniquement %types%"
Accepted locationtype: Types de localisation
Accepted users: TMS(s)
Filter activity by emergency: Filtrer les activités par urgence
Filter activity by emergency: Filtrer les échanges par urgence
'Filtered activity by emergency: only %emergency%': "Filtré par urgence: uniquement si %emergency%"
activity is emergency: l'activité est urgente
activity is not emergency: l'activité n'est pas urgente
Filter activity by sentreceived: Filtrer les activités par envoyé/reçu
activity is emergency: l'échange est urgent
activity is not emergency: l'échange n'est pas urgent
Filter activity by sentreceived: Filtrer les échanges par envoyé/reçu
'Filtered activity by sentreceived: only %sentreceived%': "Filtré par envoyé/reçu: uniquement %sentreceived%"
Accepted sentreceived: ''
Filter activity by linked socialaction: Filtrer les activités par action liée
Filter activity by linked socialaction: Filtrer les échanges par action liée
'Filtered activity by linked socialaction: only %actions%': "Filtré par action liée: uniquement %actions%"
Filter activity by linked socialissue: Filtrer les activités par problématique liée
Filter activity by linked socialissue: Filtrer les échanges par problématique liée
'Filtered activity by linked socialissue: only %issues%': "Filtré par problématique liée: uniquement %issues%"
Filter activity by user: Filtrer les activités par créateur
Filter activity by users: Filtrer les activités par utilisateur participant
Filter activity by creator: Filtrer les activités par créateur de l'échange
Filter activity by user: Filtrer les échanges par créateur
Filter activity by users: Filtrer les échanges par utilisateur participant
Filter activity by creator: Filtrer les échanges par créateur de l'échange
'Filtered activity by user: only %users%': "Filtré par référent: uniquement %users%"
'Filtered activity by users: only %users%': "Filtré par utilisateurs participants: uniquement %users%"
'Filtered activity by creator: only %users%': "Filtré par créateur: uniquement %users%"
Creators: Créateurs
Filter activity by userscope: Filtrer les activités par service du créateur
Filter activity by userscope: Filtrer les échanges par service du créateur
'Filtered activity by userscope: only %scopes%': "Filtré par service du créateur: uniquement %scopes%"
Accepted userscope: Services
Filter acp which has no activity: Filtrer les parcours qui nont pas dactivité
Filtered acp which has no activities: Filtrer les parcours sans activité associée
Group acp by activity number: Grouper les parcours par nombre dactivité
Filter acp which has no activity: Filtrer les parcours qui nont pas déchange
Filtered acp which has no activities: Filtrer les parcours sans échange associé
Group acp by activity number: Grouper les parcours par nombre déchange
#aggregators
Activity type: Type d'activité
Activity user: Utilisateur lié à l'activité
Activity type: Type d'échange
Activity user: Utilisateur lié à l'échange
By reason: Par sujet
By category of reason: Par catégorie de sujet
Reason's level: Niveau du sujet
Group by reasons: Sujet d'activité
Aggregate by activity user: Grouper les activités par référent
Aggregate by activity users: Grouper les activités par utilisateurs participants
Aggregate by activity type: Grouper les activités par type
Aggregate by activity reason: Grouper les activités par sujet
Aggregate by users scope: Grouper les activités par service principal de l'utilisateur
Users 's scope: Service principal des utilisateurs participants à l'activité
Aggregate by users job: Grouper les activités par métier des utilisateurs participants
Users 's job: Métier des utilisateurs participants à l'activité
Group by reasons: Sujet d'échange
Aggregate by activity user: Grouper les échanges par référent
Aggregate by activity users: Grouper les échanges par utilisateurs participants
Aggregate by activity type: Grouper les échanges par type
Aggregate by activity reason: Grouper les échanges par sujet
Aggregate by users scope: Grouper les échanges par service principal de l'utilisateur
Users 's scope: Service principal des utilisateurs participants à l'échange
Aggregate by users job: Grouper les échanges par métier des utilisateurs participants
Users 's job: Métier des utilisateurs participants à l'échange
Group activity by locationtype: Grouper les activités par type de localisation
Group activity by date: Grouper les activités par date
Group activity by locationtype: Grouper les échanges par type de localisation
Group activity by date: Grouper les échanges par date
Frequency: Fréquence
by month: Par mois
by week: Par semaine
for week: Semaine
by year: Par année
in year: En
Group activity by creator: Grouper les activités par créateur de l'échange
Group activity by creator scope: Grouper les activités par service du créateur de l'échange
Group activity by linked thirdparties: Grouper les activités par tiers impliqué
Group activity by creator: Grouper les échanges par créateur de l'échange
Group activity by creator scope: Grouper les échanges par service du créateur de l'échange
Group activity by linked thirdparties: Grouper les échanges par tiers impliqué
Accepted thirdparty: Tiers impliqué
Group activity by linked socialaction: Grouper les activités par action liée
Group activity by linked socialissue: Grouper les activités par problématique liée
Group activity by userscope: Grouper les activités par service du créateur
Group activity by linked socialaction: Grouper les échanges par action liée
Group activity by linked socialissue: Grouper les échanges par problématique liée
Group activity by userscope: Grouper les échanges par service du créateur
Last activities: Les dernières activités
Last activities: Les derniers échanges
See activity in accompanying course context: Voir l'activité dans le contexte du parcours d'accompagnement
See activity in accompanying course context: Voir l'échange dans le contexte du parcours d'accompagnement
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à une activité valide.
you are not allowed to see it details: La notification fait référence à une activité à laquelle vous n'avez pas accès.
This is the minimal activity data: Activité
You get notified of an activity which does not exists any more: Cette notification ne correspond pas à un échange valide.
you are not allowed to see it details: La notification fait référence à un échange auquel vous n'avez pas accès.
This is the minimal activity data: Échange
docgen:
Activity basic: Echange
A basic context for activity: Contexte pour les activités
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des activités
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les activités pour un parcours. Les activités ne sont pas filtrés.
Activity basic: Échange
A basic context for activity: Contexte pour les échanges
Accompanying period with a list of activities: Parcours d'accompagnement avec liste des échanges
Accompanying period with a list of activities description: Ce contexte reprend les informations du parcours, et tous les échanges pour un parcours. Les échanges ne sont pas filtrés.
myActivitiesOnly: Prendre en compte uniquement les échanges dans lesquels je suis intervenu
myWorksOnly: Prendre en compte uniquement les actions d'accompagnement dont je suis référent
export:
list:
@@ -336,10 +341,10 @@ export:
users name: Nom des utilisateurs
users ids: Identifiant des utilisateurs
third parties ids: Identifiant des tiers
persons ids: Identifiant des personnes
persons name: Nom des personnes
persons ids: Identifiant des usagers
persons name: Nom des usagers
thirds parties: Tiers
date: Date de l'activité
date: Date de l'échange
locationName: Localisation
sent received: Envoyé ou reçu
emergency: Urgence
@@ -348,17 +353,17 @@ export:
travelTime: Durée de déplacement
durationTime: Durée
id: Identifiant
List activities linked to an accompanying course: Liste les activités liées à un parcours en fonction de différents filtres.
List activity linked to a course: Liste des activités liées à un parcours
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
filter:
activity:
by_usersjob:
Filter by users job: Filtrer les activités par métier d'au moins un utilisateur participant
Filter by users job: Filtrer les échanges par métier d'au moins un utilisateur participant
'Filtered activity by users job: only %jobs%': 'Filtré par métier d''au moins un utilisateur participant: seulement %jobs%'
by_usersscope:
Filter by users scope: Filtrer les activités par services d'au moins un utilisateur participant
Filter by users scope: Filtrer les échanges par services d'au moins un utilisateur participant
'Filtered activity by users scope: only %scopes%': 'Filtré par service d''au moins un utilisateur participant: seulement %scopes%'
aggregator:
activity:
@@ -366,4 +371,4 @@ export:
Sent or received: Envoyé ou reçu
is sent: envoyé
is received: reçu
Group activity by sentreceived: Grouper les activités par envoyé / reçu
Group activity by sentreceived: Grouper les échanges par envoyé / reçu

View File

@@ -47,10 +47,10 @@ Reasons: Onderwerpen
#forms
Activity creation: Nouvelle activité
Activity creation: Nouvel échange
Create: Créer
Back to the list: Retour à la liste
Save activity: Sauver l'activité
Save activity: Sauver l'échange
Reset form: Remise à zéro du formulaire
Choose the duration: Choisir la durée
Choose a type: Choisir un type
@@ -79,43 +79,43 @@ activity:
No documents: Aucun document
#timeline
'%user% has done an %activity_type%': '%user% a effectué une activité de type "%activity_type%"'
'%user% has done an %activity_type%': '%user% a effectué un échange de type "%activity_type%"'
#controller
'Success : activity created!': L'activité a été créée.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'activité n'a pas été créée.
'Success : activity updated!': L'activité a été mise à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'activité n'a pas été mise à jour.
'Success : activity created!': L'échange a été créé.
'The form is not valid. The activity has not been created !': Le formulaire est invalide. L'échange n'a pas été créé.
'Success : activity updated!': L'échange a été mis à jour.
'The form is not valid. The activity has not been updated !': Le formulaire est invalide. L'échange n'a pas été mis à jour.
# ROLES
CHILL_ACTIVITY_CREATE: Créer une activité
CHILL_ACTIVITY_UPDATE: Modifier une activité
CHILL_ACTIVITY_SEE: Voir une activité
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des activités
CHILL_ACTIVITY_DELETE: Supprimer une activité
CHILL_ACTIVITY_STATS: Statistique des activités
CHILL_ACTIVITY_LIST: Liste des activités
CHILL_ACTIVITY_CREATE: Créer un échange
CHILL_ACTIVITY_UPDATE: Modifier un échange
CHILL_ACTIVITY_SEE: Voir un échange
CHILL_ACTIVITY_SEE_DETAILS: Voir le détail des échanges
CHILL_ACTIVITY_DELETE: Supprimer un échange
CHILL_ACTIVITY_STATS: Statistique des échanges
CHILL_ACTIVITY_LIST: Liste des échanges
# admin
Activities: Activités
Activity configuration: Configuration des activités
Activity configuration menu: Configuration des activités
Activity types: Types d'activité
Activity type configuration: Configuration des categories d'activités
Activity Reasons: Sujets d'une activité
Activity Reasons Category: Catégories de sujet d'activités
Activity Types Categories: Catégories des types d'activité
Activity Presences: Presences des activités
Activities: Échanges
Activity configuration: Configuration des échanges
Activity configuration menu: Configuration des échanges
Activity types: Types d'échange
Activity type configuration: Configuration des categories d'échanges
Activity Reasons: Sujets d'un échange
Activity Reasons Category: Catégories de sujet d'échanges
Activity Types Categories: Catégories des types d'échanges
Activity Presences: Presences des échanges
# Crud
crud:
activity_type:
title_new: Nouveau type d'activité
title_edit: Edition d'un type d'activité
title_new: Nouveau type d'échange
title_edit: Edition d'un type d'échange
activity_type_category:
title_new: Nouvelle catégorie de type d'activité
title_edit: Edition d'une catégorie de type d'activité
title_new: Nouvelle catégorie de type d'échange
title_edit: Edition d'une catégorie de type d'échange
# activity reason admin
ActivityReason list: Liste des sujets
@@ -124,7 +124,7 @@ Active: Actif
Category: Catégorie
ActivityReason creation: Nouveau sujet
ActivityReason edit: Modification d'un sujet
ActivityReason: Sujet d'activité
ActivityReason: Sujet d'échange
The entity is inactive and won't be proposed: Le sujet est inactif et ne sera pas proposé
The entity is active and will be proposed: Le sujet est actif et sera proposé
@@ -133,13 +133,13 @@ ActivityReasonCategory list: Catégories de sujets
Create a new activity category reason: Créer une nouvelle catégorie
ActivityReasonCategory creation: Nouvelle catégorie de sujet
ActivityReasonCategory edit: Modification d'une catégorie de sujet
ActivityReasonCategory: Catégorie de sujet d'activité
ActivityReasonCategory: Catégorie de sujet d'échange
ActivityReasonCategory is active and will be proposed: La catégorie est active et sera proposée
ActivityReasonCategory is inactive and won't be proposed: La catégorie est inactive et ne sera pas proposée
# activity type type admin
ActivityType list: Types d'activités
Create a new activity type: Créer un nouveau type d'activité
ActivityType list: Types d'échanges
Create a new activity type: Créer un nouveau type d'échange
Persons visible: Visibilité du champ Personnes
Persons label: Libellé du champ Personnes
User visible: Visibilité du champ Utilisateur
@@ -177,20 +177,20 @@ Documents label: Libellé du champ Documents
# activity type category admin
ActivityTypeCategory list: Liste des catégories des types d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'activité
Create a new activity type category: Créer une nouvelle catégorie de type d'échange
# activity delete
Remove activity: Supprimer une activité
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer une activité qui concerne "%name%" ?
The activity has been successfully removed.: L'activité a été supprimée.
Remove activity: Supprimer un échange
Are you sure you want to remove the activity about "%name%" ?: Êtes-vous sûr de vouloir supprimer un échange qui concerne "%name%" ?
The activity has been successfully removed.: L'échange a été supprimée.
# exports
Count activities: Nombre d'activités
Count activities by various parameters.: Compte le nombre d'activités enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des activités
Sum activities duration by various parameters.: Additionne la durée des activités en fonction de différents paramètres.
List activities: Liste les activités
Number of activities: Nombre d'activités
Count activities: Nombre d'échanges
Count activities by various parameters.: Compte le nombre d'échanges enregistrées en fonction de différents paramètres.
Sum activity duration: Total de la durée des échanges
Sum activities duration by various parameters.: Additionne la durée des échanges en fonction de différents paramètres.
List activities: Liste les échanges
Number of activities: Nombre d'échanges
#filters
Filter by reason: Filtrer par sujet d'activité

View File

@@ -1,22 +1,22 @@
The reasons's level should not be empty: Le niveau du sujet ne peut pas être vide
At least one reason must be choosen: Au moins un sujet doit être choisi
For this type of activity, you must add at least one person: Pour ce type d'activité, vous devez ajouter au moins un usager
For this type of activity, you must add at least one user: Pour ce type d'activité, vous devez ajouter au moins un utilisateur
For this type of activity, you must add at least one third party: Pour ce type d'activité, vous devez ajouter au moins un tiers
For this type of activity, user is required: Pour ce type d'activité, l'utilisateur est requis
For this type of activity, date is required: Pour ce type d'activité, la date est requise
For this type of activity, location is required: Pour ce type d'activité, la localisation est requise
For this type of activity, attendee is required: Pour ce type d'activité, le champ "Présence de la personne" est requis
For this type of activity, duration time is required: Pour ce type d'activité, la durée est requise
For this type of activity, travel time is required: Pour ce type d'activité, la durée du trajet est requise
For this type of activity, reasons is required: Pour ce type d'activité, le champ "sujet" est requis
For this type of activity, comment is required: Pour ce type d'activité, un commentaire est requis
For this type of activity, sent/received is required: Pour ce type d'activité, le champ Entrant/Sortant est requis
For this type of activity, document is required: Pour ce type d'activité, un document est requis
For this type of activity, emergency is required: Pour ce type d'activité, le champ "Urgent" est requis
For this type of activity, accompanying period is required: Pour ce type d'activité, le parcours d'accompagnement est requis
For this type of activity, you must add at least one social issue: Pour ce type d'activité, vous devez ajouter au moins une problématique sociale
For this type of activity, you must add at least one social action: Pour ce type d'activité, vous devez indiquer au moins une action sociale
For this type of activity, you must add at least one person: Pour ce type d'échange, vous devez ajouter au moins un usager
For this type of activity, you must add at least one user: Pour ce type d'échange, vous devez ajouter au moins un utilisateur
For this type of activity, you must add at least one third party: Pour ce type d'échange, vous devez ajouter au moins un tiers
For this type of activity, user is required: Pour ce type d'échange, l'utilisateur est requis
For this type of activity, date is required: Pour ce type d'échange, la date est requise
For this type of activity, location is required: Pour ce type d'échange, la localisation est requise
For this type of activity, attendee is required: Pour ce type d'échange, le champ "Présence de l'usager" est requis
For this type of activity, duration time is required: Pour ce type d'échange, la durée est requise
For this type of activity, travel time is required: Pour ce type d'échange, la durée du trajet est requise
For this type of activity, reasons is required: Pour ce type d'échange, le champ "sujet" est requis
For this type of activity, comment is required: Pour ce type d'échange, un commentaire est requis
For this type of activity, sent/received is required: Pour ce type d'échange, le champ Entrant/Sortant est requis
For this type of activity, document is required: Pour ce type d'échange, un document est requis
For this type of activity, emergency is required: Pour ce type d'échange, le champ "Urgent" est requis
For this type of activity, accompanying period is required: Pour ce type d'échange, le parcours d'accompagnement est requis
For this type of activity, you must add at least one social issue: Pour ce type d'échange, vous devez ajouter au moins une problématique sociale
For this type of activity, you must add at least one social action: Pour ce type d'échange, vous devez indiquer au moins une action sociale
# admin
This parameter must be equal to social issue parameter: Ce paramètre doit être égal au paramètre "Visibilité du champs Problématiques sociales"

View File

@@ -22,7 +22,7 @@ class Configuration implements ConfigurationInterface
{
$treeBuilder = new TreeBuilder('chill_aside_activity');
$treeBuilder->getRootNode('chill_aside_activity')
$treeBuilder->getRootNode()
->children()
->arrayNode('form')
->canBeEnabled()
@@ -132,9 +132,7 @@ class Configuration implements ConfigurationInterface
->info('The number of seconds of this duration. Must be an integer.')
->cannotBeEmpty()
->validate()
->ifTrue(static function ($data) {
return !is_int($data);
})->thenInvalid('The value %s is not a valid integer')
->ifTrue(static fn ($data) => !is_int($data))->thenInvalid('The value %s is not a valid integer')
->end()
->end()
->scalarNode('label')

View File

@@ -57,7 +57,7 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private ?int $id;
private ?int $id = null;
/**
* @ORM\Column(type="string", length=100, nullable=true)

View File

@@ -59,9 +59,7 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
return static fn ($value) => $labels[$value];
}
public function getQueryKeys($data): array

View File

@@ -160,8 +160,8 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
return 'export.aside_activity.main_center';
}
/** @var Center $c */
if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) {
/** @var Center $c */
return '';
}
@@ -190,10 +190,6 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface
];
}
/**
* @param QueryBuilder $query
* @param array $data
*/
public function getResult($query, $data): array
{
return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY);

View File

@@ -28,22 +28,17 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use function in_array;
final class AsideActivityFormType extends AbstractType
{
private TokenStorageInterface $storage;
private array $timeChoices;
public function __construct(
ParameterBagInterface $parameterBag,
TokenStorageInterface $storage
) {
$this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration');
$this->storage = $storage;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -100,9 +95,7 @@ final class AsideActivityFormType extends AbstractType
// the datetimetransformer will then handle timezone as GMT
$timezoneUTC = new DateTimeZone('GMT');
/** @var DateTimeImmutable $data */
$data = $formEvent->getData() === null ?
DateTime::createFromFormat('U', '300') :
$formEvent->getData();
$data = $formEvent->getData() ?? DateTime::createFromFormat('U', '300');
$seconds = $data->getTimezone()->getOffset($data);
$data->setTimeZone($timezoneUTC);
$data->add(new DateInterval('PT' . $seconds . 'S'));

View File

@@ -44,17 +44,6 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
'order' => 11,
'icons' => ['plus'],
]);
$menu->addChild($this->translator->trans('Phonecall'), [
'route' => 'chill_crud_aside_activity_new',
'routeParameters' => [
'type' => 1,
'duration' => 900,
],
])
->setExtras([
'order' => 12,
'icons' => ['plus'],
]);
}
}

View File

@@ -21,7 +21,7 @@ Type: Type
Invisible: Invisible
Optional: Optionnel
Required: Obligatoire
Persons: Personnes
Persons: Usagers
Users: Utilisateurs
Emergency: Urgent
by: "Par "
@@ -50,7 +50,7 @@ For agent: Pour l'utilisateur
date: Date
Duration: Durée
Note: Note
Choose the agent for whom this activity is created: Choisissez l'agent pour qui l'activité est créée
Choose the agent for whom this activity is created: Choisissez l'agent pour qui l'échange est créé
Choose the activity category: Choisir la catégorie
#Duration

View File

@@ -11,12 +11,13 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Calculator;
use Chill\BudgetBundle\Entity\AbstractElement;
use Chill\BudgetBundle\Entity\Charge;
use Chill\BudgetBundle\Entity\Resource;
interface CalculatorInterface
{
/**
* @param AbstractElement[] $elements
* @param array<Charge|Resource> $elements
*/
public function calculate(array $elements): ?CalculatorResult;

View File

@@ -12,6 +12,8 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Calculator;
use Chill\BudgetBundle\Entity\AbstractElement;
use Chill\BudgetBundle\Entity\Charge;
use Chill\BudgetBundle\Entity\Resource;
use OutOfBoundsException;
use function array_key_exists;
@@ -21,11 +23,14 @@ use function implode;
class CalculatorManager
{
/**
* @var CalculatorInterface[]
* @var array<string, CalculatorInterface>
*/
protected $calculators = [];
private array $calculators = [];
protected $defaultCalculator = [];
/**
* @var string[]
*/
private array $defaultCalculator = [];
public function addCalculator(CalculatorInterface $calculator, bool $default)
{
@@ -37,7 +42,7 @@ class CalculatorManager
}
/**
* @param AbstractElement[] $elements
* @param array<Resource|Charge> $elements
*
* @return CalculatorResult[]
*/
@@ -46,23 +51,17 @@ class CalculatorManager
$results = [];
foreach ($this->defaultCalculator as $alias) {
$calculator = $this->calculators[$alias];
$result = $calculator->calculate($elements);
$result = $this->getCalculator($alias)->calculate($elements);
if (null !== $result) {
$results[$calculator->getAlias()] = $result;
$results[$alias] = $result;
}
}
return $results;
}
/**
* @param string $alias
*
* @return CalculatorInterface
*/
public function getCalculator($alias)
public function getCalculator(string $alias): CalculatorInterface
{
if (false === array_key_exists($alias, $this->calculators)) {
throw new OutOfBoundsException("The calculator with alias '{$alias}' does "

View File

@@ -116,7 +116,7 @@ abstract class AbstractElementController extends AbstractController
$indexPage = 'chill_budget_elements_household_index';
}
$entity = null !== $element->getPerson() ? $element->getPerson() : $element->getHousehold();
$entity = $element->getPerson() ?? $element->getHousehold();
$form = $this->createForm($this->getType(), $element);
$form->add('submit', SubmitType::class);

View File

@@ -14,6 +14,8 @@ namespace Chill\BudgetBundle\Controller;
use Chill\BudgetBundle\Calculator\CalculatorManager;
use Chill\BudgetBundle\Entity\Charge;
use Chill\BudgetBundle\Entity\Resource;
use Chill\BudgetBundle\Repository\ChargeRepository;
use Chill\BudgetBundle\Repository\ResourceRepository;
use Chill\BudgetBundle\Security\Authorization\BudgetElementVoter;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
@@ -29,24 +31,20 @@ use function count;
class ElementController extends AbstractController
{
protected CalculatorManager $calculator;
private CalculatorManager $calculator;
protected LoggerInterface $chillMainLogger;
private ResourceRepository $resourceRepository;
protected EntityManagerInterface $em;
protected TranslatorInterface $translator;
private ChargeRepository $chargeRepository;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator,
LoggerInterface $chillMainLogger,
CalculatorManager $calculator
CalculatorManager $calculator,
ResourceRepository $resourceRepository,
ChargeRepository $chargeRepository,
) {
$this->em = $em;
$this->translator = $translator;
$this->chillMainLogger = $chillMainLogger;
$this->calculator = $calculator;
$this->resourceRepository = $resourceRepository;
$this->chargeRepository = $chargeRepository;
}
/**
@@ -59,24 +57,10 @@ class ElementController extends AbstractController
{
$this->denyAccessUnlessGranted(BudgetElementVoter::SEE, $person);
$charges = $this->em
->getRepository(Charge::class)
->findByPerson($person);
$charges = $this->chargeRepository->findAllByEntity($person);
$resources = $this->resourceRepository->findAllByEntity($person);
$ressources = $this->em
->getRepository(Resource::class)
->findByPerson($person);
$now = new DateTime('now');
$actualCharges = $this->em
->getRepository(Charge::class)
->findByEntityAndDate($person, $now);
$actualResources = $this->em
->getRepository(Resource::class)
->findByEntityAndDate($person, $now);
$elements = array_merge($actualCharges, $actualResources);
$elements = array_merge($charges, $resources);
if (count($elements) > 0) {
$results = $this->calculator->calculateDefault($elements);
@@ -85,7 +69,7 @@ class ElementController extends AbstractController
return $this->render('ChillBudgetBundle:Person:index.html.twig', [
'person' => $person,
'charges' => $charges,
'resources' => $ressources,
'resources' => $resources,
'results' => $results ?? [],
]);
}
@@ -100,60 +84,19 @@ class ElementController extends AbstractController
{
$this->denyAccessUnlessGranted(BudgetElementVoter::SEE, $household);
$charges = $this->em
->getRepository(Charge::class)
->findByHousehold($household);
$charges = $this->chargeRepository->findAllByEntity($household);
$resources = $this->resourceRepository->findAllByEntity($household);
$ressources = $this->em
->getRepository(Resource::class)
->findByHousehold($household);
$now = new DateTime('now');
$actualCharges = $this->em
->getRepository(Charge::class)
->findByEntityAndDate($household, $now);
$actualResources = $this->em
->getRepository(Resource::class)
->findByEntityAndDate($household, $now);
$elements = array_merge($actualCharges, $actualResources);
$elements = array_merge($charges, $resources);
if (count($elements) > 0) {
$results = $this->calculator->calculateDefault($elements);
}
// quick solution to calculate the sum, difference and amount from
// controller. This should be done from the calculators
// TODO replace this by calculators
$wholeCharges = $actualCharges;
$wholeResources = $actualResources;
foreach ($household->getCurrentPersons() as $person) {
$wholeCharges = array_merge(
$wholeCharges,
$this->em
->getRepository(Charge::class)
->findByEntityAndDate($person, $now)
);
$wholeResources = array_merge(
$wholeResources,
$this->em
->getRepository(Resource::class)
->findByEntityAndDate($person, $now)
);
}
return $this->render('ChillBudgetBundle:Household:index.html.twig', [
'household' => $household,
'charges' => $charges,
'resources' => $ressources,
'wholeResources' => array_filter($wholeResources, static function (Resource $r) use ($now) {
return $r->getStartDate() <= $now && ($r->getEndDate() === null || $r->getEndDate() >= $now);
}),
'wholeCharges' => array_filter($wholeCharges, static function (Charge $c) use ($now) {
return $c->getStartDate() <= $now && ($c->getEndDate() === null || $c->getEndDate() >= $now);
}),
'resources' => $resources,
'results' => $results ?? [],
]);
}

View File

@@ -19,7 +19,7 @@ class CalculatorCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$manager = $container->getDefinition('Chill\BudgetBundle\Calculator\CalculatorManager');
$manager = $container->getDefinition(\Chill\BudgetBundle\Calculator\CalculatorManager::class);
foreach ($container->findTaggedServiceIds('chill_budget.calculator') as $id => $tags) {
foreach ($tags as $tag) {

View File

@@ -19,7 +19,7 @@ class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_budget');
$rootNode = $treeBuilder->getRootNode('chill_budget');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()

View File

@@ -41,7 +41,7 @@ abstract class AbstractElement
/**
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private ?string $comment;
private ?string $comment = null;
/**
* @ORM\Column(name="endDate", type="datetime_immutable", nullable=true)
@@ -50,7 +50,7 @@ abstract class AbstractElement
* message="The budget element's end date must be after the start date"
* )
*/
private ?DateTimeImmutable $endDate;
private ?DateTimeImmutable $endDate = null;
/**
* @ORM\ManyToOne(

View File

@@ -52,9 +52,7 @@ class ChargeType extends AbstractType
'label' => 'Charge type',
'required' => true,
'placeholder' => $this->translator->trans('admin.form.Choose the type of charge'),
'choice_label' => function (ChargeKind $resource) {
return $this->translatableStringHelper->localize($resource->getName());
},
'choice_label' => fn (ChargeKind $resource) => $this->translatableStringHelper->localize($resource->getName()),
'attr' => ['class' => 'select2'],
])
->add('amount', MoneyType::class)

View File

@@ -51,9 +51,7 @@ class ResourceType extends AbstractType
'label' => 'Resource type',
'required' => true,
'placeholder' => $this->translator->trans('admin.form.Choose the type of resource'),
'choice_label' => function (ResourceKind $resource) {
return $this->translatableStringHelper->localize($resource->getName());
},
'choice_label' => fn (ResourceKind $resource) => $this->translatableStringHelper->localize($resource->getName()),
'attr' => ['class' => 'select2'],
])
->add('amount', MoneyType::class)

View File

@@ -30,7 +30,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface
}
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAll(): array
{
@@ -38,7 +38,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface
}
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAllActive(): array
{
@@ -53,7 +53,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface
}
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAllByType(string $type): array
{
@@ -64,7 +64,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{

View File

@@ -19,25 +19,25 @@ interface ChargeKindRepositoryInterface extends ObjectRepository
public function find($id): ?ChargeKind;
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAll(): array;
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAllActive(): array;
/**
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findAllByType(string $type): array;
/**
* @param mixed|null $limit
* @param mixed|null $offset
* @param int|null $limit
* @param int|null $offset
*
* @return ChargeType[]
* @return array<ChargeKind>
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;

View File

@@ -11,9 +11,12 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\Charge;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\ORM\EntityRepository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* ChargeRepository.
@@ -21,24 +24,44 @@ use Doctrine\ORM\EntityRepository;
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ChargeRepository extends EntityRepository
class ChargeRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Charge::class);
}
/**
* @return Charge[]
*/
public function findAllByEntity(Person|Household $entity): array
{
$qb = $this->createQueryBuilder('c');
$property = $entity instanceof Person ? 'person' : 'household';
$qb->where("c.{$property} = :entity")
->setParameter('entity', $entity);
return $qb->getQuery()->getResult();
}
public function findByEntityAndDate($entity, DateTime $date, $sort = null)
{
$qb = $this->createQueryBuilder('c');
$entityStr = $entity instanceof Person ? 'person' : 'household';
$qb->where("c.{$entityStr} = :{$entityStr}")
->andWhere('c.startDate < :date')
->andWhere('c.startDate < :date OR c.startDate IS NULL');
$qb->where("c.{$entityStr} = :entity")
->andWhere('c.startDate <= :date')
->andWhere('c.endDate > :date OR c.endDate IS NULL');
if (null !== $sort) {
$qb->orderBy($sort);
}
$qb->setParameters([
$entityStr => $entity,
'entity' => $entity,
'date' => $date,
]);

View File

@@ -30,7 +30,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface
}
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAll(): array
{
@@ -38,7 +38,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface
}
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAllActive(): array
{
@@ -58,7 +58,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface
}
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAllByType(string $type): array
{
@@ -69,7 +69,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface
* @param mixed|null $limit
* @param mixed|null $offset
*
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
{

View File

@@ -19,25 +19,25 @@ interface ResourceKindRepositoryInterface extends ObjectRepository
public function find($id): ?ResourceKind;
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAll(): array;
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAllActive(): array;
/**
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findAllByType(string $type): array;
/**
* @param mixed|null $limit
* @param mixed|null $offset
* @param int|null $limit
* @param int|null $offset
*
* @return ResourceType[]
* @return list<ResourceKind>
*/
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array;

View File

@@ -11,9 +11,13 @@ declare(strict_types=1);
namespace Chill\BudgetBundle\Repository;
use Chill\BudgetBundle\Entity\Resource;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Person;
use DateTime;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* ResourceRepository.
@@ -21,28 +25,45 @@ use Doctrine\ORM\EntityRepository;
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ResourceRepository extends EntityRepository
class ResourceRepository extends ServiceEntityRepository
{
public function findByEntityAndDate($entity, DateTime $date, $sort = null)
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Resource::class);
}
/**
* @return Resource[]
*/
public function findAllByEntity(Person|Household $entity): array
{
$qb = $this->createQueryBuilder('r');
$property = $entity instanceof Person ? 'person' : 'household';
$qb->where("r.{$property} = :entity")
->setParameter('entity', $entity);
return $qb->getQuery()->getResult();
}
public function findByEntityAndDate(Person|Household $entity, DateTime $date, $sort = null)
{
$qb = $this->createQueryBuilder('c');
$entityStr = $entity instanceof Person ? 'person' : 'household';
$qb->where("c.{$entityStr} = :{$entityStr}")
// TODO: in controller, the budget and charges asked are also for future and actual
//->andWhere('c.startDate < :date')
// TODO: there is a misconception here, the end date must be lower or null. startDate are never null
//->andWhere('c.startDate < :date OR c.startDate IS NULL');
;
$qb->where("c.{$entityStr} = :entity")
->andWhere('c.startDate <= :date')
->andWhere('c.endDate > :date OR c.endDate IS NULL');
if (null !== $sort) {
$qb->orderBy($sort);
}
$qb->setParameters([
$entityStr => $entity,
//'date' => $date,
'entity' => $entity,
'date' => $date,
]);
return $qb->getQuery()->getResult();

View File

@@ -1,39 +1,42 @@
.subtitle {
h3.subtitle {
margin-top: 1rem;
margin-bottom: 1rem;
padding: 1rem;
&::before {
font: normal normal normal 20px/1 ForkAwesome;
margin-right: 0.5em;
content: "\f061";
}
}
.family-title {
$col_charge: #e03851d7;
$col_resource: #6d9e63d8;
h4.family-title {
margin-top: 1.5rem;
margin-bottom: 1rem !important;
padding-left: 0.7em;
i {
margin-right: 0.4em;
}
&.charge i { color: $col_charge; }
&.resource i { color: $col_resource; }
}
.budget-table th {
th {
color: white;
}
}
.budget-table {
th.charge {
background-color: #e03851d7;
}
}
.budget-table {
th.resource {
background-color: #6d9e63d8;
}
}
.budget-table {
table.budget-table {
th, td {
padding: 10px;
text-align: right;
}
td.column-wide {
width: 20%;
}
td.column-small {
width: 15%;
&.right {
align-items: right;
}
th.charge { background-color: $col_charge; }
th.resource { background-color: $col_resource; }
td.column-fixed {
width: 9.5em;
}
}
@@ -58,4 +61,4 @@
button[aria-expanded="true"] > span.folded,
button[aria-expanded="false"] > span.unfolded { display: none; }
button[aria-expanded="false"] > span.folded,
button[aria-expanded="true"] > span.unfolded { display: inline; }
button[aria-expanded="true"] > span.unfolded { display: inline; }

View File

@@ -32,28 +32,21 @@
{% endif %}
{% endfor %}
<h3 class="subtitle">{{ 'Actual budget'|trans }}</h3>
{% if actualCharges|length > 0 or actualResources|length > 0 %}
{% include 'ChillBudgetBundle:Budget:_current_budget.html.twig' with {
{% include '@ChillBudget/Budget/_current_budget.html.twig' with {
'actualResources': actualResources,
'actualCharges': actualCharges,
'results': results,
'entity': entity
} %}
{% else %}
<div class="flex-table">
<div class="item-bloc">
<p><span class="chill-no-data-statement">{{ "There isn't any element recorded"|trans }}</span></p>
</div>
</div>
<p><span class="chill-no-data-statement">{{ "There isn't any element recorded"|trans }}</span></p>
{% endif %}
{% if pastCharges|length > 0 or pastResources|length > 0 %}
<h2 class="subtitle">{{ 'Past budget'|trans }}</h2>
{% include 'ChillBudgetBundle:Budget:_past_budget.html.twig' with {
<h3 class="subtitle">{{ 'Past budget'|trans }}</h3>
{% include '@ChillBudget/Budget/_past_budget.html.twig' with {
'pastCharges': pastCharges,
'pastResources': pastResources,
'entity': entity
@@ -61,9 +54,8 @@
{% endif %}
{% if futureCharges|length > 0 or futureResources|length > 0 %}
<h2 class="subtitle">{{ 'Future budget'|trans }}</h2>
{% include 'ChillBudgetBundle:Budget:_future_budget.html.twig' with {
<h3 class="subtitle">{{ 'Future budget'|trans }}</h3>
{% include '@ChillBudget/Budget/_future_budget.html.twig' with {
'futureResources': futureResources,
'futureCharges': futureCharges,
'entity': entity

View File

@@ -1,30 +1,17 @@
{% from 'ChillBudgetBundle:Budget:_macros.html.twig' import table_elements, table_results %}
{# <h2 class="subtitle">{{ 'Actual budget'|trans }}</h2> #}
<div class="flex-table">
<h4 class="family-title">{{ 'Actual resources'|trans }}</h4>
{% from '@ChillBudget/Budget/_macros.html.twig' import table_elements, table_results %}
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Actual resources'|trans }}</h4>
{% if actualResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(actualResources, 'resource') }}
</div>
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No resources registered'|trans }}</span>
</div>
{% endif %}
</div>
<div class="flex-table">
<h4 class="family-title">{{ 'Actual charges'|trans }}</h4>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Actual charges'|trans }}</h4>
{% if actualCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(actualCharges, 'charge') }}
</div>
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No charges registered'|trans }}</span>
</div>
{% endif %}
</div>

View File

@@ -20,32 +20,23 @@
aria-labelledby="heading_future_{{ entity.id }}"
data-bs-parent="#future_{{ entity.id }}">
<div class="flex-table">
<h3 class="family-title">{{ 'Future resources'|trans }}</h3>
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Future resources'|trans }}</h4>
{% if futureResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(futureResources, 'resource') }}
</div>
{{ table_elements(futureResources, 'resource') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No future resources registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No future resources registered'|trans }}</span>
{% endif %}
</div>
<div class="flex-table">
<h3 class="family-title">{{ 'Future charges'|trans }}</h3>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Future charges'|trans }}</h4>
{% if futureCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(futureCharges, 'charge') }}
</div>
{{ table_elements(futureCharges, 'charge') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No future charges registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No future charges registered'|trans }}</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
{% macro table_elements(elements, family) %}
<table class="budget-table">
<table class="table table-bordered border-dark budget-table">
<thead>
<tr>
<th class="{{ family }} el-type">{{ 'Budget element type'|trans }}</th>
@@ -13,25 +13,28 @@
{% for f in elements %}
{% set total = total + f.amount %}
<tr>
<td class="column-wide el-type">
<span class="badge-title">
<span class="title_label title_label_{{ family }}"></span>
{% if f.isResource %}
<span class="title_action">{{ f.resource.name|localize_translatable_string }}<span>
{% else %}
<span class="title_action">{{ f.charge.name|localize_translatable_string }}<span>
<td class="el-type">
{% if f.isResource %}
{{ f.resource.name|localize_translatable_string }}
{% if f.resource.getKind is same as 'other' %}
: {{ f.getComment }}
{% endif %}
</span>
{% else %}
{{ f.charge.name|localize_translatable_string }}
{% if f.charge.getKind is same as 'other' %}
: {{ f.getComment }}
{% endif %}
{% endif %}
</td>
<td class="column-small">{{ f.amount|format_currency('EUR') }}</td>
<td class="column-wide">
<td>{{ f.amount|format_currency('EUR') }}</td>
<td>
{% if f.endDate is not null %}
{{ f.startDate|format_date('short') ~ ' - ' ~ f.endDate|format_date('short') }}
{% else %}
{{ f.startDate|format_date('short') ~ ' - ...' }}
{{ 'depuis le ' ~ f.startDate|format_date('short') }}
{% endif %}
</td>
<td class="column-small">
<td class="column-fixed">
<ul class="record_actions">
{% if is_granted('CHILL_BUDGET_ELEMENT_SEE', f) %}
<li>
@@ -80,10 +83,9 @@
{% set result = (totalResources - totalCharges) %}
<table>
<table class="table table-bordered border-dark">
<thead>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>{{ 'Budget calculator result'|trans }}</th>
</tr>
@@ -91,7 +93,6 @@
<tbody>
<tr>
<td>{{ 'The balance'|trans }}</td>
<td>&nbsp;</td>
<td>
{{ result|format_currency('EUR') }}
</td>

View File

@@ -20,34 +20,24 @@
aria-labelledby="heading_past_{{ entity.id }}"
data-bs-parent="#past_{{ entity.id }}">
<div class="flex-table">
<h3 class="family-title">{{ 'Past resources'|trans }}</h3>
<div class="my-4">
<h4 class="family-title resource"><i class="fa fa-fw fa-plus-square"></i>{{ 'Past resources'|trans }}</h4>
{% if pastResources|length > 0 %}
<div class="item-bloc">
{{ table_elements(pastResources, 'resource') }}
</div>
{{ table_elements(pastResources, 'resource') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No past resources registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No past resources registered'|trans }}</span>
{% endif %}
</div>
<div class="flex-table">
<h3 class="family-title">{{ 'Past charges'|trans }}</h3>
<h4 class="family-title charge"><i class="fa fa-fw fa-minus-square"></i>{{ 'Past charges'|trans }}</h4>
{% if pastCharges|length > 0 %}
<div class="item-bloc">
{{ table_elements(pastCharges, 'charge') }}
</div>
{{ table_elements(pastCharges, 'charge') }}
{% else %}
<div class="item-bloc">
<span class="chill-no-data-statement">{{ 'No past charges registered'|trans }}</span>
</div>
<span class="chill-no-data-statement">{{ 'No past charges registered'|trans }}</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>

View File

@@ -24,16 +24,14 @@
} %}
{#
<div class="flex-table">
<h3 class="family-title">{{ 'Budget calculator'|trans }}</h3>
<div class="item-bloc">
{{ table_results(wholeCharges, wholeResources) }}
</div>
<div class="my-4">
<h4 class="family-title">{{ 'Budget calculator'|trans }}</h4>
{{ table_results(wholeCharges, wholeResources) }}
</div>
#}
{% if household.getCurrentMembers|length > 0 %}
<h2 class="subtitle">{{ 'Current budget household members'|trans }}</h2>
<h1 class="my-5">{{ 'Budget household members'|trans }}</h1>
{% for hm in household.getCurrentMembers %}
{% set member = hm.person %}
@@ -57,6 +55,8 @@
aria-labelledby="heading_{{ member.id }}"
data-bs-parent="#nonCurrent">
<h2 class="mt-4">{{ 'Budget for %name%'|trans({'%name%': member.firstName ~ " " ~ member.lastName }) }}</h2>
{% include 'ChillBudgetBundle:Budget:_budget.html.twig' with {
'resources': member.getBudgetResources,
'charges': member.getBudgetCharges,

View File

@@ -17,17 +17,15 @@
{% block content %}
<h1>{{ title }}</h1>
{% include 'ChillBudgetBundle:Budget:_budget.html.twig' with {
{% include '@ChillBudget/Budget/_budget.html.twig' with {
'resources': resources,
'charges': charges,
'person': person
} %}
<div class="flex-table">
<h3 class="family-title">{{ 'Budget calculator'|trans }}</h2>
<div class="item-bloc">
{{ table_results(charges, resources) }}
</div>
<div class="mt-5">
<h3 class="subtitle">{{ 'Budget calculator'|trans }}</h3>
{{ table_results(charges, resources) }}
</div>
{% if is_granted('CHILL_BUDGET_ELEMENT_CREATE', person) %}

View File

@@ -9,7 +9,7 @@
{% set indexPage = 'chill_budget_elements_household_index' %}
{% set activeRouteKey = '' %}
{% set household = element.household %}
{% set confirm_question = 'Are you sure you want to remove the ressource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.resource.getName | localize_translatable_string} ) %}
{% set confirm_question = 'Are you sure you want to remove the resource "%type%" associated to household "%household%" ?'|trans({ '%household%' : household.id, '%type%': element.resource.getName | localize_translatable_string} ) %}
{% endif %}
{% extends template %}

View File

@@ -66,9 +66,7 @@ final class SummaryBudget implements SummaryBudgetInterface
];
}
$personIds = $household->getCurrentPersons()->map(static function (Person $p) {
return $p->getId();
});
$personIds = $household->getCurrentPersons()->map(static fn (Person $p) => $p->getId());
$ids = implode(', ', array_fill(0, count($personIds), '?'));
$parameters = [...$personIds, $household->getId()];
@@ -127,18 +125,14 @@ final class SummaryBudget implements SummaryBudgetInterface
{
$keys = array_map(static fn (ChargeKind $kind) => $kind->getKind(), $this->chargeKindRepository->findAll());
return array_combine($keys, array_map(function ($kind) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
}, $keys));
return array_combine($keys, array_map(fn ($kind) => ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''], $keys));
}
private function getEmptyResourceArray(): array
{
$keys = array_map(static fn (ResourceKind $kind) => $kind->getKind(), $this->resourceKindRepository->findAll());
return array_combine($keys, array_map(function ($kind) {
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
}, $keys));
return array_combine($keys, array_map(fn ($kind) => ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''], $keys));
}
private function rowToArray(array $rows, string $kind): array

View File

@@ -50,9 +50,7 @@ final class SummaryBudgetTest extends TestCase
],
]);
$queryCharges->setParameters(Argument::type('array'))
->will(static function ($args, $query) {
return $query;
});
->will(static fn ($args, $query) => $query);
$queryResources = $this->prophesize(AbstractQuery::class);
$queryResources->getResult()->willReturn([
@@ -63,9 +61,7 @@ final class SummaryBudgetTest extends TestCase
],
]);
$queryResources->setParameters(Argument::type('array'))
->will(static function ($args, $query) {
return $query;
});
->will(static fn ($args, $query) => $query);
$em = $this->prophesize(EntityManagerInterface::class);
$em->createNativeQuery(Argument::type('string'), Argument::type(Query\ResultSetMapping::class))
@@ -100,9 +96,7 @@ final class SummaryBudgetTest extends TestCase
$resourceRepository->findOneByKind('misc')->willReturn($misc);
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($arg) {
return $arg[0]['fr'];
});
$translatableStringHelper->localize(Argument::type('array'))->will(static fn ($arg) => $arg[0]['fr']);
$person = new Person();
$personReflection = new ReflectionClass($person);

View File

@@ -0,0 +1,56 @@
<?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\Budget;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20230328155010 extends AbstractMigration
{
public function getDescription(): string
{
return 'budget elements: restore the previous type to resource/charge kind if applicable';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
WITH type_to_id AS (
SELECT DISTINCT charge.charge_id AS id, charge.type
FROM chill_budget.charge
WHERE type <> ''
)
UPDATE chill_budget.charge_type
SET kind = type_to_id.type
FROM type_to_id
WHERE type_to_id.type <> '' AND type_to_id.id = charge_type.id
SQL);
$this->addSql(<<<'SQL'
WITH type_to_id AS (
SELECT DISTINCT resource.resource_id AS id, resource.type
FROM chill_budget. resource
WHERE type <> ''
)
UPDATE chill_budget.resource_type
SET kind = type_to_id.type
FROM type_to_id
WHERE type_to_id.type <> '' AND type_to_id.id = resource_type.id
SQL);
}
public function down(Schema $schema): void
{
$this->addSql("UPDATE chill_budget.resource_type SET kind=md5(random()::text) WHERE kind = ''");
$this->addSql("UPDATE chill_budget.charge_type SET kind=md5(random()::text) WHERE kind = ''");
}
}

View File

@@ -3,23 +3,23 @@ Resource: Ressource
Charge: Charge
Budget for %name%: Budget de %name%
Budget for household %household%: Budget du ménage
Current budget household members: Budget actuel des membres du ménage
Budget household members: Budget des membres du ménage
Show budget of %name%: Montrer budget de %name%
See complete budget: Voir budget complet
Hide budget: Masquer
Hide budget of %name%: Masquer budget de %name%
Resource element type: Nature de la ressource
Actual budget: Éléments actuels du budget
Actual budget: Éléments actuels
Actual resources: Ressources actuelles
Actual resources for %name%: Ressources actuelles de %name%
Actual charges for %name%: Charges actuelles de %name%
Actual charges: Charges actuelles
Past budget: Éléments du budget passé
Past budget: Éléments passés
Show past budget: Montrer budget passé
Show future budget: Montrer budget future
Past resources: Ressources passées
Past charges: Charges passées
Future budget: Futurs éléments du budget
Future budget: Éléments futurs
Future resources: Ressources futures
Future charges: Charges futures
Budget element type: Nature
@@ -29,7 +29,7 @@ End of validity period: Fin de la période de validité
Total: Total
Create new resource: Créer une nouvelle ressource
Create new charge: Créer une nouvelle charge
See person: Voir personne
See person: Voir usagers
There isn't any element recorded: Aucun élément enregistré
No resources registered: Aucune ressource enregistrée
@@ -49,6 +49,8 @@ Remove resource: Supprimer la ressource
Remove charge: Supprimer la charge
Are you sure you want to remove the ressource "%type%" associated to "%name%" ?: Êtes-vous sûr·e de vouloir supprimer la ressource de nature "%type%" associée à %name% ?
Are you sure you want to remove the charge "%type%" associated to "%name%" ?: Êtes-vous sûr·e de vouloir supprimer la charge de nature "%type%" associée à %name% ?
Are you sure you want to remove the charge "%type%" associated to household "%household%" ?: Êtes-vous sur·e de vouloir supprimer la charge "%type%" associée au ménage ?
Are you sure you want to remove the resource "%type%" associated to household "%household%" ?: Êtes-vous sur·e de vouloir supprimer la ressource "%type%" associée au ménage ?
Resource deleted: Ressource supprimée
Charge deleted: Charge supprimée
Charge created: Charge créée

View File

@@ -60,7 +60,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->logger->info(__CLASS__ . ' execute command');
$this->logger->info(self::class . ' execute command');
$limit = 50;
$offset = 0;
@@ -71,12 +71,12 @@ class MapAndSubscribeUserCalendarCommand extends Command
$created = 0;
$renewed = 0;
$this->logger->info(__CLASS__ . ' the number of user to get - renew', [
$this->logger->info(self::class . ' the number of user to get - renew', [
'total' => $total,
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
]);
while ($offset < ($total - 1)) {
while ($offset < $total) {
$users = $this->userRepository->findByMostOldSubscriptionOrWithoutSubscriptionOrData(
$interval,
$limit,
@@ -92,7 +92,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
// we first try to renew an existing subscription, if any.
// if not, or if it fails, we try to create a new one
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(__CLASS__ . ' renew a subscription for', [
$this->logger->debug(self::class . ' renew a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -104,7 +104,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
if (0 !== $expirationTs) {
++$renewed;
} else {
$this->logger->warning(__CLASS__ . ' could not renew subscription for a user', [
$this->logger->warning(self::class . ' could not renew subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -112,7 +112,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
}
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
$this->logger->debug(__CLASS__ . ' create a subscription for', [
$this->logger->debug(self::class . ' create a subscription for', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -124,7 +124,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
if (0 !== $expirationTs) {
++$created;
} else {
$this->logger->warning(__CLASS__ . ' could not create subscription for a user', [
$this->logger->warning(self::class . ' could not create subscription for a user', [
'userId' => $user->getId(),
'username' => $user->getUsernameCanonical(),
]);
@@ -139,7 +139,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
$this->em->clear();
}
$this->logger->warning(__CLASS__ . ' process executed', [
$this->logger->warning(self::class . ' process executed', [
'created' => $created,
'renewed' => $renewed,
]);

View File

@@ -207,6 +207,7 @@ class CalendarController extends AbstractController
'entityClassName' => Calendar::class,
'entityId' => $entity->getId(),
'template' => $template->getId(),
'returnPath' => $request->getRequestUri(),
]);
}
}
@@ -377,7 +378,9 @@ class CalendarController extends AbstractController
$this->addFlash('success', $this->translator->trans('Success : calendar item created!'));
if ($form->get('save_and_upload_doc')->isClicked()) {
return $this->redirectToRoute('chill_calendar_calendardoc_new', ['id' => $entity->getId()]);
return $this->redirectToRoute('chill_calendar_calendardoc_new', [
'id' => $entity->getId()
]);
}
foreach ($templates as $template) {
@@ -386,6 +389,7 @@ class CalendarController extends AbstractController
'entityClassName' => Calendar::class,
'entityId' => $entity->getId(),
'template' => $template->getId(),
'returnPath' => $this->generateUrl('chill_calendar_calendar_edit', ['id' => $entity->getId()]),
]);
}
}
@@ -526,14 +530,20 @@ class CalendarController extends AbstractController
'comment' => $calendar->getComment()->getComment(),
];
return $this->redirectToRoute(
'chill_activity_activity_new',
[
'accompanying_period_id' => $calendar->getAccompanyingPeriod()->getId(),
'activityData' => $activityData,
'returnPath' => $request->query->get('returnPath', null),
]
);
$routeParams = [
'activityData' => $activityData,
'returnPath' => $request->query->get('returnPath', null),
];
if ($calendar->getContext() === 'accompanying_period') {
$routeParams['accompanying_period_id'] = $calendar->getAccompanyingPeriod()->getId();
} elseif ($calendar->getContext() === 'person') {
$routeParams['person_id'] = $calendar->getPerson()->getId();
} else {
throw new RuntimeException('context not found for this calendar');
}
return $this->redirectToRoute('chill_activity_activity_new', $routeParams);
}
private function buildListFilterOrder(): FilterOrderHelper
@@ -544,21 +554,6 @@ class CalendarController extends AbstractController
return $filterOrder->build();
}
private function buildParamsToUrl(?User $user, ?AccompanyingPeriod $accompanyingPeriod): array
{
$params = [];
if (null !== $user) {
$params['user_id'] = $user->getId();
}
if (null !== $accompanyingPeriod) {
$params['id'] = $accompanyingPeriod->getId();
}
return $params;
}
/**
* Creates a form to delete a Calendar entity by id.
*/

View File

@@ -24,7 +24,7 @@ class Configuration implements ConfigurationInterface
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_calendar');
$rootNode = $treeBuilder->getRootNode('chill_calendar');
$rootNode = $treeBuilder->getRootNode();
$rootNode
->children()

View File

@@ -29,6 +29,7 @@ use DateTimeImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\ORM\Mapping as ORM;
use LogicException;
use Symfony\Component\Serializer\Annotation as Serializer;
@@ -92,6 +93,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
/**
* @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars")
* @Serializer\Groups({"calendar:read", "read"})
*/
private ?AccompanyingPeriod $accompanyingPeriod = null;
@@ -507,14 +509,12 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
}
/**
* @return Collection|User[]
* @return ReadableCollection<(int|string), User>
* @Serializer\Groups({"calendar:read", "read"})
*/
public function getUsers(): Collection
public function getUsers(): ReadableCollection
{
return $this->getInvites()->map(static function (Invite $i) {
return $i->getUser();
});
return $this->getInvites()->map(static fn (Invite $i) => $i->getUser());
}
public function hasCalendarRange(): bool
@@ -599,9 +599,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
}
$invite = $this->invites
->filter(static function (Invite $invite) use ($user) {
return $invite->getUser() === $user;
})
->filter(static fn (Invite $invite) => $invite->getUser() === $user)
->first();
$this->removeInvite($invite);

View File

@@ -63,7 +63,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
* @Groups({"read", "write", "calendar:read"})
* @Assert\NotNull
*/
private ?Location $location;
private ?Location $location = null;
/**
* @ORM\Column(type="datetime_immutable", nullable=false)

View File

@@ -52,7 +52,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
return 'Exports of calendar';
}
public function getLabels($key, array $values, $data): Closure
public function getLabels($key, array $values, $data)
{
if ('export_result' !== $key) {
throw new LogicException("the key {$key} is not used by this export");
@@ -61,9 +61,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
return static fn ($value) => $labels[$value];
}
public function getQueryKeys($data): array
@@ -91,9 +89,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
{
$centers = array_map(static function ($el) {
return $el['center'];
}, $acl);
$centers = array_map(static fn ($el) => $el['center'], $acl);
$qb = $this->calendarRepository->createQueryBuilder('cal');

View File

@@ -61,9 +61,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
return static fn ($value) => $labels[$value];
}
public function getQueryKeys($data): array

View File

@@ -61,9 +61,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
$labels = array_combine($values, $values);
$labels['_header'] = $this->getTitle();
return static function ($value) use ($labels) {
return $labels[$value];
};
return static fn ($value) => $labels[$value];
}
public function getQueryKeys($data): array

View File

@@ -58,9 +58,7 @@ class AgentFilter implements FilterInterface
{
$builder->add('accepted_agents', EntityType::class, [
'class' => User::class,
'choice_label' => function (User $u) {
return $this->userRender->renderString($u, []);
},
'choice_label' => fn (User $u) => $this->userRender->renderString($u, []),
'multiple' => true,
'expanded' => true,
]);

View File

@@ -69,11 +69,9 @@ class JobFilter implements FilterInterface
{
$builder->add('job', EntityType::class, [
'class' => UserJob::class,
'choice_label' => function (UserJob $j) {
return $this->translatableStringHelper->localize(
$j->getLabel()
);
},
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
$j->getLabel()
),
'multiple' => true,
'expanded' => true,
]);

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