Compare commits

...

108 Commits

Author SHA1 Message Date
a46e987f81 Upgrade version of chill-bundles to v2.23.1 2024-07-25 16:56:43 +02:00
81220b5b22 Correct phpstan error: ParsingException is never thrown
The typing of the exception had to be changed, because \Datetime throws an \Exception
instead of a ParsingException.
2024-07-25 11:30:33 +02:00
5b0019cde7 Fix query in filter: period having activity between dates filter
Adding statement to ensure that subquery only considers records that are identical to the
ones being processed in the main query.
2024-07-25 11:23:36 +02:00
b42473b01d Upgrade bundles version with new features and fix 2024-07-23 16:54:08 +02:00
be19d09bad Fix typing error in ListActivityHelper: string expected got int 2024-07-23 16:43:54 +02:00
c82991674e Rector changes: repositories become final readonly classes 2024-07-18 09:49:05 +02:00
3fc3f32c5f Pipeline fixes 2024-07-17 16:21:19 +02:00
20af766cdf Fix the query to return the count of events
Left join was causing events to counted multiple times (once per participation). Using DISTINCT fixes this.
2024-07-17 15:38:55 +02:00
681f637d13 Merge branch 'add-module-emploi' into upgrade-sf5 2024-07-17 14:21:10 +02:00
fb8a6d960e Remove duplicate edit method in events controller 2024-07-17 14:20:15 +02:00
a2310a662f Fix delete request for event status controller 2024-07-17 14:17:19 +02:00
dd7d126bec Fix edit request for event bundle 2024-07-17 14:15:48 +02:00
29f6a43288 Fix translation in event bundle using new pluralization syntax 2024-07-17 13:35:47 +02:00
74be6460d4 Fix delete request for all entities 2024-07-17 13:33:05 +02:00
c8e87ced35 Remove duplicate edit method in events controller 2024-07-17 13:29:14 +02:00
b8002d56ec merge latest changes from upgrade-sf5 branch 2024-07-17 13:17:59 +02:00
a80b36bb31 Fix EntityPersonCRUDController.php add 'person' key 2024-07-17 13:15:22 +02:00
116fe35ad2 Fix delete request for event status controller 2024-07-17 13:11:09 +02:00
5b95336bac Fix event and participation voters 2024-07-17 13:10:51 +02:00
f9d5ba7778 Fix edit request for event bundle 2024-07-17 13:10:34 +02:00
f76379551c Change namespace event voters for consistency 2024-07-17 13:08:48 +02:00
15094d5a91 Fix delete request for all entities 2024-07-17 13:05:14 +02:00
1079c7e394 Fix translation in event bundle using new pluralization syntax 2024-07-16 07:07:08 +02:00
bc2dfd159c merge upgrade-sf5 branch 2024-07-15 15:16:27 +02:00
b100792a34 Fix request to France Travail Api to accomodate for new request limit
A new limit was set to allow a maximum amount of request in a certain timeframe. We need to wait 1 sec to make next request.
2024-07-15 15:11:34 +02:00
00ceee1fd5 Remove "document" from document name 2024-07-15 15:09:45 +02:00
724b98e8c5 Improve naming of downloaded documents in job bundle
When downloaded the title of the document was\ always set to 'Document', which gave little\ indication as to what the document was about.\
Now documents are titled with the name of the\ person and the type of document (CV, permis,...)
2024-07-04 13:28:54 +02:00
e38d47ec5e fix pipeline rector and cs 2024-06-26 09:38:13 +02:00
36f2275a56 Delete overriding of generateTemplateParameters method
This method was requiring a person_id to be set, which was\
not the case here so it threw an error. Using the method\
already available in CRUDController works fine, seems to be\
no need to override it.
2024-06-26 09:04:57 +02:00
9a34064b23 Implement chill_document_button_group to allow download of document
At first another download button was used, but not working.\ Elsewhere in Chill the chill_document_button_group seems to\
be used so more coherent to use it here too.
2024-06-26 09:04:41 +02:00
1812e84c92 Merge branch 'testing-2024-03' into add-module-emploi 2024-06-25 15:54:54 +02:00
dfa7de4f38 merge upgrade-sf5 into branch for latest fixes 2024-06-25 15:54:22 +02:00
635b1ee537 merge upgrade-sf5 branch to have latest fixes 2024-06-06 13:01:12 +02:00
3fd6e52e9d remove unnecessary constraints from 3party properties acronym and nameCompany 2024-05-29 11:37:42 +02:00
f60a595ab6 Update vue toast version and implementation 2024-05-29 11:37:04 +02:00
436ef33dbc final fix for appellation selector: define metier when appellation already exists 2024-05-23 14:05:14 +02:00
405aad7333 fix rome appellation selector and admin 2024-05-23 13:47:33 +02:00
a5c2576124 Fix the appellation selection for projet professional 2024-05-23 12:46:12 +02:00
33a6f9996e remove dumps 2024-05-22 21:02:17 +02:00
b96cbc5594 rector fixes 2024-05-22 17:13:27 +02:00
854d72fa42 php cs fixes 2024-05-22 17:12:49 +02:00
cd6fd091dc resolve merge conflicts 2024-05-22 16:57:28 +02:00
cb5ade3d14 add changie for module emploi 2024-05-22 16:49:45 +02:00
dddb6d66bc php cs fixes after merge 2024-05-22 16:44:02 +02:00
d34f9450b8 Merge branch 'add-module-emploi' of gitlab.com:Chill-Projet/chill-bundles into add-module-emploi 2024-05-22 16:43:02 +02:00
9ce1788a14 phpstan en rector fixes 2024-05-22 16:42:47 +02:00
2895638f3b php cs fixes 2024-05-22 15:26:23 +02:00
2708bafb1f Export for list person with cs_person columns fixed 2024-05-22 15:24:39 +02:00
7d309136b1 minor last fixes for immersion and remove of dumps 2024-05-22 08:53:56 +02:00
82d3ec4d6f Merge branch 'add-module-emploi' of gitlab.com:Chill-Projet/chill-bundles into add-module-emploi 2024-05-22 08:40:46 +02:00
cad2dea148 Wip: add jsonb fields to export 2024-05-16 09:26:40 +02:00
bff14aa700 minor last fixes for immersion and remove of dumps 2024-05-15 16:02:14 +02:00
66570cd430 merge add-module-emploi into testing 2024-05-15 15:19:24 +02:00
53df2ec9ba Merge branch 'master' into testing-2024-03 2024-05-15 15:17:49 +02:00
068503a830 Merge branch 'master' into add-module-emploi 2024-05-15 14:36:36 +02:00
c07a728f1d Wip: add jsonb fields to export 2024-05-15 14:35:51 +02:00
b7e61c6747 merge master into module emploi branch 2024-05-15 14:26:51 +02:00
97846a5877 add basic fields csperson to list person export 2024-05-14 11:01:45 +02:00
4ed9d3d8e2 Fix API call 2024-05-14 07:49:48 +02:00
d82d534a4c Try to fix API: adjust to new urls, but still receiving error code 400 2024-05-07 11:15:10 +02:00
684f28291a reinstate exports 2024-05-07 11:14:39 +02:00
526882a5b6 phpstan, rector and cs fixes 2024-04-29 15:39:05 +02:00
422b6b99eb Change translation for the group of voter rights 2024-04-29 15:28:13 +02:00
02b150b0a5 fix delete of reports in crud config + template 2024-04-29 15:25:15 +02:00
20c27c100c Name change from CSConnecte to Job 2024-04-29 15:20:23 +02:00
12a22bcc13 template + form + property fixes for emploi reports 2024-04-29 14:13:37 +02:00
cba8a342d5 more template fixes 2024-04-24 18:02:06 +02:00
6f55ba15d6 Split crud controllers for each report entity 2024-04-24 17:40:31 +02:00
454ab73303 WIP fix emploi reports 2024-04-24 17:11:26 +02:00
800942bc92 Add missing columns to report tables 2024-04-24 17:10:30 +02:00
c4e7683e48 fix templates for personal situation 2024-04-24 15:02:24 +02:00
28c986fddf controller with crud logic + templates fixed for dispositif 2024-04-24 14:52:19 +02:00
adca4f0d6a php cs fixes 2024-04-24 12:28:55 +02:00
7b25c8e390 New migration to take care of everything needed for ChillJobBundle 2024-04-24 12:27:49 +02:00
0b5be0419b Move old migrations to directory 'old' just in case 2024-04-24 12:27:23 +02:00
650e85c481 Fix crudconfig 2024-04-24 11:44:14 +02:00
56d5d08ed3 Run rector on ListCSPerson file 2024-04-24 11:34:06 +02:00
d3390ca334 Phpstan error for unused parameter fixed 2024-04-24 10:49:19 +02:00
511c0af5fa Last php cs fix 2024-04-24 10:40:09 +02:00
4c354c47c9 Fix construct method for ListCSPerson 2024-04-24 10:39:46 +02:00
d8b6cef7b4 rector fixes 2024-04-24 10:18:07 +02:00
e312929d86 php cs fixes 2024-04-24 10:16:54 +02:00
20b38af812 Create phpstan baseline for level 5 taking into account new bundles 2024-04-24 10:16:16 +02:00
2f07be0843 Revert "adjust phpstan baselines"
This reverts commit a71573136a.
2024-04-24 10:11:02 +02:00
11c069a2ff Revert "phpstan baseline 5 updated"
This reverts commit deaab80270.
2024-04-24 10:10:55 +02:00
3929602f59 Revert "php style fixes"
This reverts commit 38fcccfd83.
2024-04-24 10:09:25 +02:00
38fcccfd83 php style fixes 2024-04-23 21:22:29 +02:00
deaab80270 phpstan baseline 5 updated 2024-04-23 21:22:19 +02:00
a71573136a adjust phpstan baselines 2024-04-23 21:12:57 +02:00
b1082f6a55 fix phpstan errors level 3 2024-04-23 21:03:42 +02:00
dcc285e976 fix phpstan errors level 2 2024-04-23 20:52:22 +02:00
ed3e0f889e Rector changes + namespace changes 2024-04-23 17:43:23 +02:00
63fe8070c4 Rector passed again on JobBundle entities 2024-04-19 11:30:47 +02:00
8e3322f578 rector rules for upgrade to php 8.2 and symfony 5.4 applied + php cs fixer 2024-04-19 10:56:49 +02:00
00756a3bde Move migrations directory to src 2024-04-19 10:33:40 +02:00
2c68224e9c Add jobBundle and FranceTravailApiBundle 2024-04-19 10:21:17 +02:00
166d7fe0b0 Merge branch 'master' into testing-2024-03 2024-03-26 22:14:51 +01:00
ab9d5439c1 Release 2.18.1 2024-03-26 22:08:30 +01:00
0737838dd6 Fix layout in admin document generation
A layout issue in the admin document generation has been fixed, particularly in the ChillDocGeneratorBundle. Unnecessary elements such as table headers and multiple entity data rows in DocGeneratorTemplate have been removed, simplifying the view page and improving its performance.
2024-03-26 22:08:01 +01:00
88bac5b5d8 Merge remote-tracking branch 'origin/145-permettre-de-visualiser-les-documents-dans-libreoffice-en-utilisant-webdav' into testing-2024-03 2024-03-26 21:34:15 +01:00
cf1df462dc optional parameter after the required one 2024-01-15 21:18:51 +01:00
a0328b9d68 Apply new CS rules on the webdav feature 2024-01-15 20:38:03 +01:00
813a80d6f9 Dav: add UI to edit document 2024-01-15 20:22:14 +01:00
ab95bb157e Dav: add some documentation on classes 2024-01-15 20:19:03 +01:00
18fd1dbc4a Dav: Introduce access control inside de dav controller 2024-01-15 20:19:03 +01:00
a35f7656cb Dav: refactor WebdavController 2024-01-15 20:19:03 +01:00
ff05f9f48a Dav: implements JWT extraction from the URL, and add the access_token in dav urls 2024-01-15 20:19:02 +01:00
482c494034 Webdav: fully implements the controller and response
The controller is tested from real request scraped from apache mod_dav implementation. The requests were scraped using a wireshark-like tool. Those requests have been adapted to suit to our xml.
2024-01-15 20:19:02 +01:00
228 changed files with 12759 additions and 393 deletions

View File

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

View File

@@ -1,6 +0,0 @@
kind: Feature
body: |
Upgrade import of address list to the last version of compiled addresses of belgian-best-address
time: 2024-05-30T16:00:03.440767606+02:00
custom:
Issue: ""

View File

@@ -1,6 +0,0 @@
kind: Feature
body: |
Upgrade CKEditor and refactor configuration with use of typescript
time: 2024-05-31T19:02:42.776662753+02:00
custom:
Issue: ""

View File

@@ -1,6 +0,0 @@
kind: Fixed
body: Fix resolving of centers for an household, which will fix in turn the access
control
time: 2024-04-10T10:37:36.462484988+02:00
custom:
Issue: ""

11
.changes/v2.23.0.md Normal file
View File

@@ -0,0 +1,11 @@
## v2.23.0 - 2024-07-23
### Feature
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export

3
.changes/v2.23.1.md Normal file
View File

@@ -0,0 +1,3 @@
## v2.23.1 - 2024-07-25
### Fixed
* Fix export activities linked to accompanying period between two dates

View File

@@ -6,6 +6,18 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).
## v2.23.0 - 2024-07-23
### Feature
* ([#221](https://gitlab.com/Chill-Projet/chill-bundles/-/issues/221)) [DX] move async-upload-bundle features into chill-bundles
* Add job bundle (module emploi)
* Upgrade import of address list to the last version of compiled addresses of belgian-best-address
* Upgrade CKEditor and refactor configuration with use of typescript
### Fixed
* Fix resolving of centers for an household, which will fix in turn the access control
* Resolved type hinting error in activity list export
## v2.22.2 - 2024-07-03
### Fixed
* Remove scope required for event participation stats

View File

@@ -114,6 +114,8 @@
"Chill\\DocGeneratorBundle\\": "src/Bundle/ChillDocGeneratorBundle",
"Chill\\DocStoreBundle\\": "src/Bundle/ChillDocStoreBundle",
"Chill\\EventBundle\\": "src/Bundle/ChillEventBundle",
"Chill\\FranceTravailApiBundle\\": "src/Bundle/ChillFranceTravailApiBundle/src",
"Chill\\JobBundle\\": "src/Bundle/ChillJobBundle/src",
"Chill\\MainBundle\\": "src/Bundle/ChillMainBundle",
"Chill\\PersonBundle\\": "src/Bundle/ChillPersonBundle",
"Chill\\ReportBundle\\": "src/Bundle/ChillReportBundle",

View File

@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
class BirthdateFilter implements ExportElementValidatedInterface, FilterInterface
{
// add specific role for this filter
public function addRole()
public function addRole(): ?string
{
// we do not need any new role for this filter, so we return null
return null;

View File

@@ -27,7 +27,7 @@
"popper.js": "^1.16.1",
"postcss-loader": "^7.0.2",
"raw-loader": "^4.0.2",
"sass-loader": "^13.0.0",
"sass-loader": "^14.0.0",
"select2": "^4.0.13",
"select2-bootstrap-theme": "0.1.0-beta.10",
"style-loader": "^3.3.1",
@@ -58,7 +58,7 @@
"vue": "^3.2.37",
"vue-i18n": "^9.1.6",
"vue-multiselect": "3.0.0-alpha.2",
"vue-toast-notification": "^2.0",
"vue-toast-notification": "^3.1.2",
"vuex": "^4.0.0"
},
"browserslist": [

View File

@@ -1,34 +1,29 @@
parameters:
ignoreErrors:
-
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
message: "#^Property Chill\\\\CustomFieldsBundle\\\\Entity\\\\CustomField\\:\\:\\$required \\(false\\) does not accept bool\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/PersonType.php
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomField.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Entity/CustomFieldsGroup.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentAccompanyingCourseController.php
-
message: "#^Foreach overwrites \\$key with its key variable\\.$#"
count: 1
path: src/Bundle/ChillCustomFieldsBundle/Controller/CustomFieldsGroupController.php
message: "#^Parameter \\#1 \\$user of method Chill\\\\DocStoreBundle\\\\Entity\\\\Document\\:\\:setUser\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User\\|null, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#"
count: 2
path: src/Bundle/ChillDocStoreBundle/Controller/DocumentPersonController.php
-
message: "#^Variable \\$participation might not be defined\\.$#"
@@ -40,6 +35,106 @@ parameters:
count: 1
path: src/Bundle/ChillEventBundle/Form/ChoiceLoader/EventChoiceLoader.php
-
message: "#^Comparison operation \"\\>\" between \\(bool\\|int\\|Redis\\) and 0 results in an error\\.$#"
count: 1
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
-
message: "#^Variable \\$response might not be defined\\.$#"
count: 1
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/ApiWrapper.php
-
message: "#^Function GuzzleHttp\\\\Psr7\\\\get not found\\.$#"
count: 1
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
-
message: "#^Function GuzzleHttp\\\\Psr7\\\\str not found\\.$#"
count: 2
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
-
message: "#^Parameter \\#1 \\$seconds of function sleep expects int, string given\\.$#"
count: 1
path: src/Bundle/ChillFranceTravailApiBundle/src/ApiHelper/PartenaireRomeAppellation.php
-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Controller/CSPersonController.php
-
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
-
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Entity/Immersion.php
-
message: "#^Property Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Metier\\:\\:\\$appellations is never read, only written\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Entity/Rome/Metier.php
-
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListCSPerson\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
-
message: "#^Variable \\$f might not be defined\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Export/ListCSPerson.php
-
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListFrein\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Export/ListFrein.php
-
message: "#^Method Chill\\\\JobBundle\\\\Export\\\\ListProjetProfessionnel\\:\\:splitArrayToColumns\\(\\) never returns Closure so it can be removed from the return type\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Export/ListProjetProfessionnel.php
-
message: "#^Property Chill\\\\JobBundle\\\\Form\\\\ChoiceLoader\\\\RomeAppellationChoiceLoader\\:\\:\\$appellationRepository \\(Chill\\\\JobBundle\\\\Repository\\\\Rome\\\\AppellationRepository\\) does not accept Doctrine\\\\ORM\\\\EntityRepository\\<Chill\\\\JobBundle\\\\Entity\\\\Rome\\\\Appellation\\>\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
-
message: "#^Result of && is always false\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
-
message: "#^Strict comparison using \\=\\=\\= between array\\{\\} and Symfony\\\\Component\\\\Validator\\\\ConstraintViolationListInterface will always evaluate to false\\.$#"
count: 2
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
-
message: "#^Strict comparison using \\=\\=\\= between null and string will always evaluate to false\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
-
message: "#^Variable \\$metier might not be defined\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Form/ChoiceLoader/RomeAppellationChoiceLoader.php
-
message: "#^Parameter \\#1 \\$interval of method DateTimeImmutable\\:\\:add\\(\\) expects DateInterval, string\\|null given\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
-
message: "#^Parameter \\#1 \\$object of static method DateTimeImmutable\\:\\:createFromMutable\\(\\) expects DateTime, DateTimeInterface given\\.$#"
count: 1
path: src/Bundle/ChillJobBundle/src/Security/Authorization/CSConnectesVoter.php
-
message: "#^Cannot unset offset '_token' on array\\{formatter\\: mixed, export\\: mixed, centers\\: mixed, alias\\: string\\}\\.$#"
count: 1
@@ -65,11 +160,31 @@ parameters:
count: 1
path: src/Bundle/ChillMainBundle/Form/ChoiceLoader/PostalCodeChoiceLoader.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 2
path: src/Bundle/ChillMainBundle/Repository/NotificationRepository.php
-
message: "#^Parameter \\#1 \\$user of method Chill\\\\MainBundle\\\\Security\\\\Authorization\\\\AuthorizationHelper\\:\\:userHasAccessForCenter\\(\\) expects Chill\\\\MainBundle\\\\Entity\\\\User, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Security/Authorization/AuthorizationHelper.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillMainBundle/Templating/ChillTwigRoutingHelper.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/ChoiceLoader/PersonChoiceLoader.php
-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
path: src/Bundle/ChillPersonBundle/Form/PersonType.php
-
message: "#^Foreach overwrites \\$value with its value variable\\.$#"
count: 1

View File

@@ -28,6 +28,9 @@ return static function (RectorConfig $rectorConfig): void {
// register a single rule
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector::class);
$rectorConfig->rule(Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector::class);
$rectorConfig->rule(Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector::class);
// part of the symfony 54 rules
$rectorConfig->rule(\Rector\Symfony\Symfony53\Rector\StaticPropertyFetch\KernelTestCaseContainerPropertyDeprecationRector::class);
@@ -36,14 +39,14 @@ return static function (RectorConfig $rectorConfig): void {
//define sets of rules
$rectorConfig->sets([
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_50_TYPES,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_51,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_52,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_53,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_54,
LevelSetList::UP_TO_PHP_82,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_40,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_41,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_42,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_43,
\Rector\Symfony\Set\SymfonySetList::SYMFONY_44,
\Rector\Doctrine\Set\DoctrineSetList::DOCTRINE_CODE_QUALITY,
\Rector\Doctrine\Set\DoctrineSetList::ANNOTATIONS_TO_ATTRIBUTES,
\Rector\PHPUnit\Set\PHPUnitSetList::PHPUNIT_90,
]);
$rectorConfig->ruleWithConfiguration(\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class, [

View File

@@ -99,10 +99,10 @@ final class ActivityController extends AbstractController
$form = $this->createDeleteForm($activity->getId(), $person, $accompanyingPeriod);
if (Request::METHOD_DELETE === $request->getMethod()) {
if (Request::METHOD_POST === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
$this->logger->notice('An activity has been removed', [
'by_user' => $this->getUser()->getUsername(),
'activity_id' => $activity->getId(),
@@ -640,7 +640,6 @@ final class ActivityController extends AbstractController
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_activity_activity_delete', $params))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -79,11 +79,9 @@ class ActivityReason
/**
* Set active.
*
* @param bool $active
*
* @return ActivityReason
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;
@@ -110,11 +108,9 @@ class ActivityReason
/**
* Set name.
*
* @param array $name
*
* @return ActivityReason
*/
public function setName($name)
public function setName(array $name)
{
$this->name = $name;

View File

@@ -152,7 +152,7 @@ class ListActivityHelper
return '';
}
return $this->translator->trans($value);
return $this->translator->trans((string) $value);
},
};
}

View File

@@ -73,13 +73,15 @@ final readonly class PeriodHavingActivityBetweenDatesFilter implements FilterInt
$qb->andWhere(
$qb->expr()->exists(
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = acp"
'SELECT 1 FROM '.Activity::class." {$alias} WHERE {$alias}.date >= :{$from} AND {$alias}.date < :{$to} AND {$alias}.accompanyingPeriod = activity.accompanyingPeriod AND {$alias}.id = activity.id"
)
);
$qb
->setParameter($from, $this->rollingDateConverter->convert($data['start_date']))
->setParameter($to, $this->rollingDateConverter->convert($data['end_date']));
dump($qb->getQuery()->getResult());
}
public function applyOn()

View File

@@ -15,9 +15,9 @@ use Chill\ActivityBundle\Entity\ActivityType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ActivityTypeRepository implements ActivityTypeRepositoryInterface
final readonly class ActivityTypeRepository implements ActivityTypeRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $em)
{

View File

@@ -54,7 +54,7 @@ abstract class AbstractElementController extends AbstractController
$indexPage = 'chill_budget_elements_household_index';
}
if (Request::METHOD_DELETE === $request->getMethod()) {
if (Request::METHOD_POST === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
@@ -198,10 +198,9 @@ abstract class AbstractElementController extends AbstractController
/**
* Creates a form to delete a help request entity by id.
*/
private function createDeleteForm(): Form
private function createDeleteForm(): \Symfony\Component\Form\FormInterface
{
return $this->createFormBuilder()
->setMethod(Request::METHOD_DELETE)
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -100,7 +100,7 @@ class Charge extends AbstractElement implements HasCentersInterface
return $this;
}
public function setHelp($help)
public function setHelp(?string $help)
{
$this->help = $help;

View File

@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ChargeKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ChargeKindRepository implements ChargeKindRepositoryInterface
final readonly class ChargeKindRepository implements ChargeKindRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -15,9 +15,9 @@ use Chill\BudgetBundle\Entity\ResourceKind;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
final class ResourceKindRepository implements ResourceKindRepositoryInterface
final readonly class ResourceKindRepository implements ResourceKindRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{

View File

@@ -84,7 +84,7 @@ class CalendarController extends AbstractController
$form = $this->createDeleteForm($entity);
if (Request::METHOD_DELETE === $request->getMethod()) {
if (Request::METHOD_POST === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
@@ -512,7 +512,6 @@ class CalendarController extends AbstractController
{
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_calendar_calendar_delete', ['id' => $calendar->getId()]))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -172,11 +172,9 @@ class CustomField
/**
* Set active.
*
* @param bool $active
*
* @return CustomField
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;
@@ -224,18 +222,16 @@ class CustomField
/**
* Set order.
*
* @param float $order
*
* @return CustomField
*/
public function setOrdering($order)
public function setOrdering(?float $order)
{
$this->ordering = $order;
return $this;
}
public function setRequired($required)
public function setRequired(bool $required)
{
$this->required = $required;
@@ -245,7 +241,7 @@ class CustomField
/**
* @return $this
*/
public function setSlug($slug)
public function setSlug(?string $slug)
{
$this->slug = $slug;
@@ -255,11 +251,9 @@ class CustomField
/**
* Set type.
*
* @param string $type
*
* @return CustomField
*/
public function setType($type)
public function setType(?string $type)
{
$this->type = $type;

View File

@@ -129,7 +129,7 @@ class Option
/**
* @return $this
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;
@@ -139,7 +139,7 @@ class Option
/**
* @return $this
*/
public function setInternalKey($internal_key)
public function setInternalKey(string $internal_key)
{
$this->internalKey = $internal_key;
@@ -149,7 +149,7 @@ class Option
/**
* @return $this
*/
public function setKey($key)
public function setKey(?string $key)
{
$this->key = $key;

View File

@@ -69,7 +69,7 @@ class CustomFieldsDefaultGroup
*
* @return CustomFieldsDefaultGroup
*/
public function setCustomFieldsGroup($customFieldsGroup)
public function setCustomFieldsGroup(?CustomFieldsGroup $customFieldsGroup)
{
$this->customFieldsGroup = $customFieldsGroup;
@@ -79,11 +79,9 @@ class CustomFieldsDefaultGroup
/**
* Set entity.
*
* @param string $entity
*
* @return CustomFieldsDefaultGroup
*/
public function setEntity($entity)
public function setEntity(?string $entity)
{
$this->entity = $entity;

View File

@@ -165,11 +165,9 @@ class CustomFieldsGroup
/**
* Set entity.
*
* @param string $entity
*
* @return CustomFieldsGroup
*/
public function setEntity($entity)
public function setEntity(?string $entity)
{
$this->entity = $entity;

View File

@@ -21,14 +21,14 @@ use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final class RelatorioDriver implements DriverInterface
final readonly class RelatorioDriver implements DriverInterface
{
private readonly string $url;
private string $url;
public function __construct(
private readonly HttpClientInterface $client,
private HttpClientInterface $client,
ParameterBagInterface $parameterBag,
private readonly LoggerInterface $logger
private LoggerInterface $logger
) {
$this->url = $parameterBag->get('chill_doc_generator')['driver']['relatorio']['url'];
}

View File

@@ -16,11 +16,11 @@ use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\HttpFoundation\RequestStack;
final class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
final readonly class DocGeneratorTemplateRepository implements DocGeneratorTemplateRepositoryInterface
{
private readonly EntityRepository $repository;
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager, private readonly RequestStack $requestStack)
public function __construct(EntityManagerInterface $entityManager, private RequestStack $requestStack)
{
$this->repository = $entityManager->getRepository(DocGeneratorTemplate::class);
}

View File

@@ -129,7 +129,7 @@ class Document implements TrackCreationInterface, TrackUpdateInterface
return $this;
}
public function setUser($user): self
public function setUser(?\Chill\MainBundle\Entity\User $user): self
{
$this->user = $user;

View File

@@ -86,7 +86,7 @@ class DocumentCategory
return $this;
}
public function setDocumentClass($documentClass): self
public function setDocumentClass(?string $documentClass): self
{
$this->documentClass = $documentClass;

View File

@@ -55,14 +55,14 @@ class PersonDocument extends Document implements HasCenterInterface, HasScopeInt
return $this->scope;
}
public function setPerson($person): self
public function setPerson(Person $person): self
{
$this->person = $person;
return $this;
}
public function setScope($scope): self
public function setScope(?Scope $scope): self
{
$this->scope = $scope;

View File

@@ -61,7 +61,7 @@ final class EventController extends AbstractController
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'DELETE'])]
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/delete', name: 'chill_event__event_delete', requirements: ['event_id' => '\d+'], methods: ['GET', 'POST', 'DELETE'])]
public function deleteAction($event_id, Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|Response
{
$em = $this->managerRegistry->getManager();
@@ -78,10 +78,10 @@ final class EventController extends AbstractController
$form = $this->createDeleteForm($event_id);
if (Request::METHOD_DELETE === $request->getMethod()) {
if (Request::METHOD_POST === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
foreach ($participations as $participation) {
$em->remove($participation);
}
@@ -108,28 +108,6 @@ final class EventController extends AbstractController
]);
}
/**
* Displays a form to edit an existing Event entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit')]
public function editAction($event_id): Response
{
$em = $this->managerRegistry->getManager();
$entity = $em->getRepository(Event::class)->find($event_id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Event entity.');
}
$editForm = $this->createEditForm($entity);
return $this->render('@ChillEvent/Event/edit.html.twig', [
'entity' => $entity,
'edit_form' => $editForm->createView(),
]);
}
/**
* List events subscriptions for a person.
*
@@ -313,7 +291,7 @@ final class EventController extends AbstractController
/**
* Edits an existing Event entity.
*/
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/update', name: 'chill_event__event_update', methods: ['POST', 'PUT'])]
#[\Symfony\Component\Routing\Annotation\Route(path: '/{_locale}/event/event/{event_id}/edit', name: 'chill_event__event_edit', methods: ['GET', 'POST', 'PUT'])]
public function updateAction(Request $request, $event_id): \Symfony\Component\HttpFoundation\RedirectResponse|Response
{
$em = $this->managerRegistry->getManager();
@@ -324,14 +302,20 @@ final class EventController extends AbstractController
throw $this->createNotFoundException('Unable to find Event entity.');
}
$editForm = $this->createEditForm($entity);
$editForm = $this->createForm(EventType::class, $entity, [
'center' => $entity->getCenter(),
'role' => EventVoter::UPDATE,
]);
$editForm->add('submit', SubmitType::class, ['label' => 'Update']);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->persist($entity);
$em->flush();
$this->addFlash('success', $this->translator
->trans('The event was updated'));
$this->addFlash('success', $this->translator->trans('The event was updated'));
return $this->redirectToRoute('chill_event__event_show', ['event_id' => $event_id]);
}
@@ -599,29 +583,7 @@ final class EventController extends AbstractController
->setAction($this->generateUrl('chill_event__event_delete', [
'event_id' => $event_id,
]))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}
/**
* Creates a form to edit a Event entity.
*
* @return \Symfony\Component\Form\FormInterface
*/
private function createEditForm(Event $entity)
{
$form = $this->createForm(EventType::class, $entity, [
'action' => $this->generateUrl('chill_event__event_update', ['event_id' => $entity->getId()]),
'method' => 'PUT',
'center' => $entity->getCenter(),
'role' => 'CHILL_EVENT_CREATE',
]);
$form->remove('center');
$form->add('submit', SubmitType::class, ['label' => 'Update']);
return $form;
}
}

View File

@@ -201,7 +201,7 @@ class EventTypeController extends AbstractController
/**
* Creates a form to delete a EventType entity by id.
*
* @return \Symfony\Component\Form\Form The form
* @return \Symfony\Component\Form\FormInterface The form
*/
private function createDeleteForm(mixed $id)
{
@@ -210,7 +210,6 @@ class EventTypeController extends AbstractController
'chill_eventtype_admin_delete',
['id' => $id]
))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -259,10 +259,10 @@ final class ParticipationController extends AbstractController
$form = $this->createDeleteForm($participation_id);
if (Request::METHOD_DELETE === $request->getMethod()) {
if (Request::METHOD_POST === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
if ($form->isSubmitted() && $form->isValid()) {
$em->remove($participation);
$em->flush();
@@ -753,7 +753,6 @@ final class ParticipationController extends AbstractController
->setAction($this->generateUrl('chill_event_participation_delete', [
'participation_id' => $participation_id,
]))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -201,7 +201,7 @@ class RoleController extends AbstractController
/**
* Creates a form to delete a Role entity by id.
*
* @return \Symfony\Component\Form\Form The form
* @return \Symfony\Component\Form\FormInterface The form
*/
private function createDeleteForm(mixed $id)
{

View File

@@ -201,13 +201,12 @@ class StatusController extends AbstractController
/**
* Creates a form to delete a Status entity by id.
*
* @return \Symfony\Component\Form\Form The form
* @return \Symfony\Component\Form\FormInterface The form
*/
private function createDeleteForm(mixed $id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('chill_event_admin_status_delete', ['id' => $id]))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}

View File

@@ -47,7 +47,7 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
private ?Scope $circle = null;
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_MUTABLE)]
private ?\DateTime $date;
private ?\DateTime $date = null;
#[ORM\Id]
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
@@ -265,11 +265,9 @@ class Event implements HasCenterInterface, HasScopeInterface, TrackCreationInter
/**
* Set label.
*
* @param string $label
*
* @return Event
*/
public function setName($label)
public function setName(?string $label)
{
$this->name = $label;

View File

@@ -146,11 +146,9 @@ class EventType
/**
* Set active.
*
* @param bool $active
*
* @return EventType
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;

View File

@@ -81,11 +81,9 @@ class Role
/**
* Set active.
*
* @param bool $active
*
* @return Role
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;

View File

@@ -81,11 +81,9 @@ class Status
/**
* Set active.
*
* @param bool $active
*
* @return Status
*/
public function setActive($active)
public function setActive(bool $active)
{
$this->active = $active;

View File

@@ -94,7 +94,7 @@ readonly class CountEvents implements ExportInterface, GroupedExportInterface
->leftJoin('event.participations', 'epart')
->leftJoin('epart.person', 'person');
$qb->select('COUNT(event.id) as export_count_event');
$qb->select('COUNT(DISTINCT event.id) as export_count_event');
if ($this->filterStatsByCenters) {
$qb

View File

@@ -13,6 +13,7 @@ namespace Chill\EventBundle\Form;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\DocStoreBundle\Form\StoredObjectType;
use Chill\EventBundle\Entity\Event;
use Chill\EventBundle\Form\Type\PickEventTypeType;
use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Form\Type\ChillCollectionType;
@@ -23,6 +24,7 @@ use Chill\MainBundle\Form\Type\PickUserLocationType;
use Chill\MainBundle\Form\Type\ScopePickerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -31,7 +33,9 @@ class EventType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('name', TextType::class, [
'required' => true,
])
->add('date', ChillDateTimeType::class, [
'required' => true,
])
@@ -75,7 +79,7 @@ class EventType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => \Chill\EventBundle\Entity\Event::class,
'data_class' => Event::class,
]);
$resolver
->setRequired(['center', 'role'])

View File

@@ -1,7 +1,7 @@
{% import '@ChillPerson/Person/macro.html.twig' as person_macro %}
{% if ignored_participations|length > 0 %}
<p>{% transchoice ignored_participations|length %}The following people have been ignored because they are already participating on the event{% endtranschoice %}&nbsp;:</p>
<p>{{ 'ignored_participations'|trans({'count': ignored_participations|length}) }}:</p>
<ul>
{% for p in ignored_participations %}
<li>{{ person_macro.render(p.person) }}</li>

View File

@@ -19,3 +19,9 @@ events:
one {et un autre participant}
other {et # autres participants}
}
ignored_participations: >-
{ count, plural,
one {La personne suivante a été ignorée parce qu''elle participe déjà à l''événement}
other {Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l'événement}
}

View File

@@ -41,7 +41,6 @@ Back to the event: Retour à l'événement
The participation was created: La participation a été créée
The participation was updated: La participation a été mise à jour
'None of the requested people may participate the event: they are maybe already participating.': 'Aucune des personnes indiquées ne peut être ajoutée à l''événement: elles sont peut-être déjà inscrites comme participantes.'
'The following people have been ignored because they are already participating on the event': '{1} La personne suivante a été ignorée parce qu''elle participe déjà à l''événement | ]1,Inf] Les personnes suivantes ont été ignorées parce qu''elles participent déjà à l''événement'
There are no participation to edit for this event: Il n'y a pas de participation pour cet événement
The participations have been successfully updated.: Les participations ont été mises à jour.
The participation has been sucessfully removed: La participation a été correctement supprimée.

View File

@@ -0,0 +1,84 @@
<?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\FranceTravailApiBundle\ApiHelper;
use Chill\MainBundle\Redis\ChillRedis;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
/**
* Wraps the pole emploi api.
*/
class ApiWrapper
{
/**
* @var Client
*/
private $client;
/**
* key for the bearer for the api pole emploi.
*
* This bearer is shared across users
*/
public const UNPERSONAL_BEARER = 'api_pemploi_bear_';
public function __construct(private $clientId, private $clientSecret, private readonly ChillRedis $redis)
{
$this->client = new Client([
'base_uri' => 'https://entreprise.francetravail.fr/connexion/oauth2/access_token',
]);
}
public function getPublicBearer($scopes): string
{
$cacheKey = $this->getCacheKey($scopes);
if ($this->redis->exists($cacheKey) > 0) {
$data = \unserialize($this->redis->get($cacheKey));
return $data->access_token;
}
try {
$response = $this->client->post('', [
'query' => ['realm' => '/partenaire'],
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
],
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => \implode(' ', \array_merge($scopes, ['application_'.$this->clientId])),
],
]);
} catch (ClientException $e) {
dump($e->getResponse());
}
$data = \json_decode((string) $response->getBody());
// set the key with an expiry time
$this->redis->setex(
$cacheKey,
$data->expires_in - 2,
\serialize($data)
);
return $data->access_token;
}
protected function getCacheKey($scopes)
{
return self::UNPERSONAL_BEARER.implode('', $scopes);
}
}

View File

@@ -0,0 +1,113 @@
<?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\FranceTravailApiBundle\ApiHelper;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
/**
* Queries for ROME partenaires api.
*/
class PartenaireRomeAppellation
{
use ProcessRequestTrait;
/**
* @var ApiWrapper
*/
protected $wrapper;
/**
* @var Client
*/
protected $client;
/**
* @var LoggerInterface
*/
protected $logger;
private const BASE = 'https://api.pole-emploi.io/partenaire/rome-metiers/v1/metiers/';
public function __construct(
ApiWrapper $wrapper,
LoggerInterface $logger,
private \Symfony\Contracts\HttpClient\HttpClientInterface $httpClient,
) {
$this->wrapper = $wrapper;
$this->logger = $logger;
$this->client = new Client([
'base_uri' => 'https://api.pole-emploi.io/partenaire/rome-metiers/v1/metiers/',
]);
}
private function getBearer()
{
return $this->wrapper->getPublicBearer([
'api_rome-metiersv1',
'nomenclatureRome',
]);
}
public function getListeAppellation(string $search): array
{
$bearer = $this->getBearer();
try {
$response = $this->httpClient->request(
'GET',
self::BASE.'appellation/requete',
[
'headers' => [
'Authorization' => 'Bearer '.$bearer,
'Accept' => 'application/json',
],
'query' => [
'q' => $search,
],
]
);
return $response->toArray()['resultats'];
} catch (HttpExceptionInterface $exception) {
throw $exception;
}
}
public function getAppellation(string $code): array
{
$bearer = $this->getBearer();
while (true) {
try {
$response = $this->httpClient->request('GET', sprintf(self::BASE.'appellation/%s', $code), [
'headers' => [
'Authorization' => 'Bearer '.$bearer,
'Accept' => 'application/json',
],
'query' => [
'champs' => 'code,libelle,metier(code,libelle)',
],
]);
return $response->toArray();
} catch (HttpExceptionInterface $exception) {
if (429 === $exception->getResponse()->getStatusCode()) {
$retryAfter = $exception->getResponse()->getHeaders(false)['retry-after'][0] ?? 1;
sleep((int) $retryAfter);
} else {
throw $exception;
}
}
}
}
}

View File

@@ -0,0 +1,100 @@
<?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\FranceTravailApiBundle\ApiHelper;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Psr7;
use Psr\Log\LoggerInterface;
/**
* Methods to process request against the api, and handle the
* Exceptions.
*/
trait ProcessRequestTrait
{
/**
* Handle a request and 429 errors.
*
* @param Request $request the request
* @param array $parameters the requests parameters
*/
protected function handleRequest(
Request $request,
array $parameters,
Client $client,
LoggerInterface $logger
) {
return $this->handleRequestRecursive(
$request,
$parameters,
$client,
$logger
);
}
/**
* internal method to handle recursive requests.
*
* @throws BadResponseException
*/
private function handleRequestRecursive(
Request $request,
array $parameters,
Client $client,
LoggerInterface $logger,
$counter = 0
) {
try {
return $client->send($request, $parameters);
} catch (BadResponseException $e) {
if (
// get 429 exceptions
$e instanceof ClientException
&& 429 === $e->getResponse()->getStatusCode()
&& count($e->getResponse()->getHeader('Retry-After')) > 0) {
if ($counter > 5) {
$logger->error('too much 429 response', [
'request' => Psr7\get($e->getRequest()),
]);
throw $e;
}
$delays = $e->getResponse()->getHeader('Retry-After');
$delay = \end($delays);
sleep($delay);
return $this->handleRequestRecursive(
$request,
$parameters,
$client,
$logger,
$counter + 1
);
}
// handling other errors
$logger->error('Error while querying ROME api', [
'status_code' => $e->getResponse()->getStatusCode(),
'part' => 'appellation',
'request' => $e->getRequest()->getBody()->getContents(),
'response' => $e->getResponse()->getBody()->getContents(),
]);
throw $e;
}
}
}

View File

@@ -0,0 +1,16 @@
<?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\FranceTravailApiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillFranceTravailApiBundle extends Bundle {}

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\FranceTravailApiBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class RomeController extends AbstractController
{
/**
* @var PartenaireRomeAppellation
*/
protected $apiAppellation;
public function __construct(PartenaireRomeAppellation $apiAppellation)
{
$this->apiAppellation = $apiAppellation;
}
#[Route(path: '/{_locale}/france-travail/appellation/search.{_format}', name: 'chill_france_travail_api_appellation_search')]
public function appellationSearchAction(Request $request)
{
if (false === $request->query->has('q')) {
return new JsonResponse([]);
}
$appellations = $this->apiAppellation
->getListeAppellation($request->query->get('q'));
$results = [];
foreach ($appellations as $appellation) {
$appellation['id'] = 'original-'.$appellation['code'];
$appellation['text'] = $appellation['libelle'];
$results[] = $appellation;
}
$computed = new \stdClass();
$computed->pagination = (new \stdClass());
$computed->pagination->more = false;
$computed->results = $results;
return new JsonResponse($computed);
}
}

View File

@@ -0,0 +1,52 @@
<?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\FranceTravailApiBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration.
*
* @see http://symfony.com/doc/current/cookbook/bundles/extension.html
*/
class ChillFranceTravailApiExtension extends Extension implements PrependExtensionInterface
{
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
public function prepend(ContainerBuilder $container): void
{
$this->prependRoute($container);
}
protected function prependRoute(ContainerBuilder $container): void
{
// declare routes for france travail api bundle
$container->prependExtensionConfig('chill_main', [
'routing' => [
'resources' => [
'@ChillFranceTravailApiBundle/Resources/config/routing.yml',
],
],
]);
}
}

View File

@@ -0,0 +1,34 @@
<?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\FranceTravailApiBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
*/
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_france_travail_api');
$rootNode = $treeBuilder->getRootNode();
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}

View File

@@ -0,0 +1,3 @@
chill_france_travail_api_controllers:
resource: "@ChillFranceTravailApiBundle/Controller"
type: annotation

View File

@@ -0,0 +1,16 @@
services:
_defaults:
autowire: true
autoconfigure: true
Chill\FranceTravailApiBundle\ApiHelper\ApiWrapper:
$clientId: '%env(FRANCE_TRAVAIL_CLIENT_ID)%'
$clientSecret: '%env(FRANCE_TRAVAIL_CLIENT_SECRET)%'
$redis: '@Chill\MainBundle\Redis\ChillRedis'
Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation: ~
Chill\FranceTravailApiBundle\Controller\RomeController:
autowire: true
autoconfigure: true
tags: ['controller.service_arguments']

View File

@@ -0,0 +1,93 @@
<?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\FranceTravailApiBundle\Tests\ApiHelper;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
/**
* @author Julien Fastré <julien.fastre@champs-libres.coop>
*
* @internal
*
* @coversNothing
*/
class PartenaireRomeAppellationTest extends KernelTestCase
{
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
}
public function testGetListeMetiersSimple()
{
/** @var PartenaireRomeAppellation $appellations */
$appellations = self::$kernel
->getContainer()
->get(PartenaireRomeAppellation::class)
;
$data = $appellations->getListeAppellation('arb');
$this->assertTrue(\is_array($data));
$this->assertNotNull($data[0]->libelle);
$this->assertNotNull($data[0]->code);
}
public function testGetListeMetiersTooMuchRequests()
{
/** @var PartenaireRomeMetier $appellations */
$appellations = self::$kernel
->getContainer()
->get(PartenaireRomeAppellation::class)
;
$appellations->getListeAppellation('arb');
$appellations->getListeAppellation('ing');
$appellations->getListeAppellation('rob');
$appellations->getListeAppellation('chori');
$data = $appellations->getListeAppellation('camion');
$this->assertTrue(
$data[0] instanceof \stdClass,
'assert that first index of data is an instance of stdClass'
);
}
public function testGetAppellation()
{
/** @var PartenaireRomeMetier $appellations */
$appellations = self::$kernel
->getContainer()
->get(PartenaireRomeAppellation::class)
;
$a = $appellations->getListeAppellation('arb');
$full = $appellations->getAppellation($a[0]->code);
$this->assertNotNull(
$full->libelle,
'assert that libelle is not null'
);
$this->assertTrue(
$full->metier instanceof \stdClass,
'assert that metier is returned'
);
$this->assertNotNull(
$full->metier->libelle,
'assert that metier->libelle is not null'
);
}
}

View File

@@ -0,0 +1,29 @@
<?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\FranceTravailApiBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* @internal
*
* @coversNothing
*/
class RomeControllerTest extends WebTestCase
{
public function testAppellationsearch()
{
$client = static::createClient();
$crawler = $client->request('GET', '/{_locale}/pole-emploi/appellation/search');
}
}

View File

@@ -0,0 +1,16 @@
<?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\JobBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ChillJobBundle extends Bundle {}

View File

@@ -0,0 +1,123 @@
<?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\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\JobBundle\Entity\Immersion;
use Symfony\Component\HttpFoundation\Response;
/**
* CRUD Controller for reports (Frein, ...).
*/
class CSCrudReportController extends EntityPersonCRUDController
{
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
$next = $request->request->get('submit', 'save-and-close');
return match ($next) {
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
'person' => $entity->getPerson()->getId(),
]),
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
};
}
protected function duplicateEntity(string $action, Request $request)
{
if ('cscv' === $this->getCrudName()) {
$id = $request->query->get('duplicate_id', 0);
/** @var \Chill\JobBundle\Entity\CV $cv */
$cv = $this->getEntity($action, $id, $request);
$em = $this->managerRegistry->getManager();
$em->detach($cv);
foreach ($cv->getExperiences() as $experience) {
$cv->removeExperience($experience);
$em->detach($experience);
$cv->addExperience($experience);
}
foreach ($cv->getFormations() as $formation) {
$cv->removeFormation($formation);
$em->detach($formation);
$cv->addFormation($formation);
}
return $cv;
}
if ('projet_prof' === $this->getCrudName()) {
$id = $request->query->get('duplicate_id', 0);
/** @var \Chill\JobBundle\Entity\ProjetProfessionnel $original */
$original = $this->getEntity($action, $id, $request);
$new = parent::duplicateEntity($action, $request);
foreach ($original->getSouhait() as $s) {
$new->addSouhait($s);
}
foreach ($original->getValide() as $s) {
$new->addValide($s);
}
return $new;
}
return parent::duplicateEntity($action, $request);
}
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
{
if ($entity instanceof Immersion) {
if ('edit' === $action || 'new' === $action) {
return parent::createFormFor($action, $entity, $formClass, [
'center' => $entity->getPerson()->getCenter(),
]);
}
if ('bilan' === $action) {
return parent::createFormFor($action, $entity, $formClass, [
'center' => $entity->getPerson()->getCenter(),
'step' => 'bilan',
]);
}
if ('delete' === $action) {
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
throw new \LogicException("this step {$action} is not supported");
}
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
{
// for immersion / edit-bilan action
if ('bilan' === $action) {
/* @var $entity Immersion */
$entity->setIsBilanFullfilled(true);
}
parent::onPreFlush($action, $entity, $form, $request);
}
/**
* Edit immersion bilan.
*
* @param int $id
*/
public function editBilan(Request $request, $id): Response
{
return $this->formEditAction('bilan', $request, $id);
}
}

View File

@@ -0,0 +1,150 @@
<?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\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\OneToOneEntityPersonCRUDController;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\JobBundle\Form\CSPersonPersonalSituationType;
use Chill\JobBundle\Form\CSPersonDispositifsType;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CSPersonController extends OneToOneEntityPersonCRUDController
{
#[Route(path: '{_locale}/person/job/personal_situation/{id}/edit', name: 'chill_crud_job_personal_situation_edit')]
public function personalSituationEdit(Request $request, $id): Response
{
return $this->formEditAction(
'ps_situation_edit',
$request,
$id,
CSPersonPersonalSituationType::class
);
}
#[Route(path: '{_locale}/person/job/dispositifs/{id}/edit', name: 'chill_crud_job_dispositifs_edit')]
public function dispositifsEdit(Request $request, $id)
{
return $this->formEditAction(
'dispositifs_edit',
$request,
$id,
CSPersonDispositifsType::class
);
}
#[Route(path: '{_locale}/person/job/{person}/personal_situation', name: 'chill_crud_job_personal_situation_view')]
public function personalSituationView(Request $request, $person): Response
{
return $this->viewAction('ps_situation_view', $request, $person);
}
#[Route(path: '{_locale}/person/job/{person}/dispositifs', name: 'chill_crud_job_dispositifs_view')]
public function dispositifsView(Request $request, $person): Response
{
return $this->viewAction('dispositifs_view', $request, $person);
}
protected function generateRedirectOnCreateRoute($action, Request $request, $entity): string
{
$route = '';
switch ($action) {
case 'ps_situation_view':
$route = 'chill_crud_job_personal_situation_edit';
break;
case 'dispositifs_view':
$route = 'chill_crud_job_dispositifs_edit';
break;
default:
parent::generateRedirectOnCreateRoute($action, $request, $entity);
}
return $this->generateUrl($route, ['id' => $entity->getPerson()->getId()]);
}
protected function checkACL($action, $entity): void
{
match ($action) {
'ps_situation_edit', 'dispositifs_edit' => $this->denyAccessUnlessGranted(
PersonVoter::UPDATE,
$entity->getPerson()
),
'ps_situation_view', 'dispositifs_view' => $this->denyAccessUnlessGranted(
PersonVoter::SEE,
$entity->getPerson()
),
default => parent::checkACL($action, $entity),
};
}
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
return match ($action) {
'ps_situation_edit' => $this->redirectToRoute(
'chill_crud_'.$this->getCrudName().'_personal_situation_view',
['person' => $entity->getId()]
),
'dispositifs_edit' => $this->redirectToRoute(
'chill_crud_'.$this->getCrudName().'_dispositifs_view',
['person' => $entity->getId()]
),
default => null,
};
}
protected function getTemplateFor($action, $entity, Request $request): string
{
return match ($action) {
'ps_situation_edit' => '@ChillJob/CSPerson/personal_situation_edit.html.twig',
'dispositifs_edit' => '@ChillJob/CSPerson/dispositifs_edit.html.twig',
'ps_situation_view' => '@ChillJob/CSPerson/personal_situation_view.html.twig',
'dispositifs_view' => '@ChillJob/CSPerson/dispositifs_view.html.twig',
default => parent::getTemplateFor($action, $entity, $request),
};
}
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
{
switch ($action) {
case 'ps_situation_edit':
case 'dispositifs_edit':
$form = $this->createForm($formClass, $entity, \array_merge(
$formOptions,
['center' => $entity->getPerson()->getCenter()]
));
$this->customizeForm($action, $form);
return $form;
default:
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
}
protected function generateLabelForButton($action, $formName, $form): string
{
switch ($action) {
case 'ps_situation_edit':
case 'dispositifs_edit':
if ('submit' === $formName) {
return 'Enregistrer';
}
throw new \LogicException("this formName is not supported: {$formName}");
break;
default:
return 'Enregistrer';
}
}
}

View File

@@ -0,0 +1,73 @@
<?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\JobBundle\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\PersonBundle\Entity\Person;
use Symfony\Component\HttpFoundation\Response;
use Chill\JobBundle\Entity\Frein;
use Chill\JobBundle\Entity\CV;
use Chill\JobBundle\Entity\Immersion;
use Chill\JobBundle\Entity\ProjetProfessionnel;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\JobBundle\Security\Authorization\JobVoter;
class CSReportController extends AbstractController
{
public function __construct(private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {}
#[Route(path: '{_locale}/person/job/{person}/report', name: 'chill_job_report_index')]
public function index(Person $person): Response
{
$this->denyAccessUnlessGranted(PersonVoter::SEE, $person, 'The access to '
.'person is denied');
$reports = $this->getReports($person);
return $this->render('@ChillJob/Report/index.html.twig', \array_merge([
'person' => $person,
], $reports));
}
protected function getReports(Person $person): array
{
$results = [];
$kinds = [];
if ($this->isGranted(JobVoter::REPORT_CV, $person)) {
$kinds['cvs'] = CV::class;
}
if ($this->isGranted(JobVoter::REPORT_NEW, $person)) {
$kinds = \array_merge($kinds, [
'cvs' => CV::class,
'freins' => Frein::class,
'immersions' => Immersion::class,
'projet_professionnels' => ProjetProfessionnel::class,
]);
}
foreach ($kinds as $key => $className) {
$ordering = match ($key) {
'immersions' => ['debutDate' => 'DESC'],
default => ['reportDate' => 'DESC'],
};
$results[$key] = $this->managerRegistry->getManager()
->getRepository($className)
->findBy(['person' => $person], $ordering);
}
return $results;
}
}

View File

@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* CRUD Controller for reports (Frein, ...).
*/
class CVCrudController extends EntityPersonCRUDController
{
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
$next = $request->request->get('submit', 'save-and-close');
return match ($next) {
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
'person' => $entity->getPerson()->getId(),
]),
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
};
}
protected function duplicateEntity(string $action, Request $request)
{
if ('cv' === $this->getCrudName()) {
$id = $request->query->get('duplicate_id', 0);
/** @var \Chill\JobBundle\Entity\CV $cv */
$cv = $this->getEntity($action, $id, $request);
$em = $this->managerRegistry->getManager();
$em->detach($cv);
foreach ($cv->getExperiences() as $experience) {
$cv->removeExperience($experience);
$em->detach($experience);
$cv->addExperience($experience);
}
foreach ($cv->getFormations() as $formation) {
$cv->removeFormation($formation);
$em->detach($formation);
$cv->addFormation($formation);
}
return $cv;
}
return parent::duplicateEntity($action, $request);
}
}

View File

@@ -0,0 +1,35 @@
<?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\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* CRUD Controller for reports (Frein, ...).
*/
class FreinCrudController extends EntityPersonCRUDController
{
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
$next = $request->request->get('submit', 'save-and-close');
return match ($next) {
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
'person' => $entity->getPerson()->getId(),
]),
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
};
}
}

View File

@@ -0,0 +1,80 @@
<?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\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Chill\JobBundle\Entity\Immersion;
use Symfony\Component\HttpFoundation\Response;
/**
* CRUD Controller for reports (Frein, ...).
*/
class ImmersionCrudController extends EntityPersonCRUDController
{
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
$next = $request->request->get('submit', 'save-and-close');
return match ($next) {
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
'person' => $entity->getPerson()->getId(),
]),
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
};
}
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
{
if ($entity instanceof Immersion) {
if ('edit' === $action || 'new' === $action) {
return parent::createFormFor($action, $entity, $formClass, [
'center' => $entity->getPerson()->getCenter(),
]);
}
if ('bilan' === $action) {
return parent::createFormFor($action, $entity, $formClass, [
'center' => $entity->getPerson()->getCenter(),
'step' => 'bilan',
]);
}
if ('delete' === $action) {
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
throw new \LogicException("this step {$action} is not supported");
}
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
{
// for immersion / edit-bilan action
if ('bilan' === $action) {
/* @var $entity Immersion */
$entity->setIsBilanFullfilled(true);
}
parent::onPreFlush($action, $entity, $form, $request);
}
/**
* Edit immersion bilan.
*
* @param int $id
*/
public function bilan(Request $request, $id): Response
{
return $this->formEditAction('bilan', $request, $id);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Controller;
use Chill\PersonBundle\CRUD\Controller\EntityPersonCRUDController;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* CRUD Controller for reports (Frein, ...).
*/
class ProjetProfessionnelCrudController extends EntityPersonCRUDController
{
protected function onBeforeRedirectAfterSubmission(string $action, $entity, FormInterface $form, Request $request): ?Response
{
$next = $request->request->get('submit', 'save-and-close');
return match ($next) {
'save-and-close', 'delete-and-close' => $this->redirectToRoute('chill_job_report_index', [
'person' => $entity->getPerson()->getId(),
]),
default => parent::onBeforeRedirectAfterSubmission($action, $entity, $form, $request),
};
}
protected function duplicateEntity(string $action, Request $request)
{
if ('projet_prof' === $this->getCrudName()) {
$id = $request->query->get('duplicate_id', 0);
/** @var \Chill\JobBundle\Entity\ProjetProfessionnel $original */
$original = $this->getEntity($action, $id, $request);
$new = parent::duplicateEntity($action, $request);
foreach ($original->getSouhait() as $s) {
$new->addSouhait($s);
}
foreach ($original->getValide() as $s) {
$new->addValide($s);
}
return $new;
}
return parent::duplicateEntity($action, $request);
}
}

View File

@@ -0,0 +1,196 @@
<?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\JobBundle\DependencyInjection;
use Chill\JobBundle\Controller\CSPersonController;
use Chill\JobBundle\Controller\CVCrudController;
use Chill\JobBundle\Controller\FreinCrudController;
use Chill\JobBundle\Controller\ImmersionCrudController;
use Chill\JobBundle\Controller\ProjetProfessionnelCrudController;
use Chill\JobBundle\Entity\CSPerson;
use Chill\JobBundle\Entity\CV;
use Chill\JobBundle\Entity\Frein;
use Chill\JobBundle\Entity\Immersion;
use Chill\JobBundle\Entity\ProjetProfessionnel;
use Chill\JobBundle\Form\CVType;
use Chill\JobBundle\Form\FreinType;
use Chill\JobBundle\Form\ImmersionType;
use Chill\JobBundle\Form\ProjetProfessionnelType;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration.
*
* @see http://symfony.com/doc/current/cookbook/bundles/extension.html
*/
class ChillJobExtension extends Extension implements PrependExtensionInterface
{
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('services/3party_type.yml');
$loader->load('services/controller.yml');
$loader->load('services/form.yml');
$loader->load('services/export.yml');
$loader->load('services/menu.yml');
$loader->load('services/security.yml');
}
public function prepend(ContainerBuilder $container): void
{
$this->prependRoute($container);
$this->prependCruds($container);
}
protected function prependCruds(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'cruds' => [
[
'class' => CSPerson::class,
'controller' => CSPersonController::class,
'name' => 'job',
'base_role' => 'ROLE_USER',
'base_path' => '/person/job/',
],
[
'class' => CV::class,
'controller' => CVCrudController::class,
'name' => 'cscv',
'base_role' => 'ROLE_USER',
'base_path' => '/person/report/cv',
'form_class' => CVType::class,
'actions' => [
'view' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/CV/view.html.twig',
],
'new' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/CV/new.html.twig',
],
'edit' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/CV/edit.html.twig',
],
'delete' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Report/delete.html.twig',
],
],
],
[
'class' => ProjetProfessionnel::class,
'controller' => ProjetProfessionnelCrudController::class,
'name' => 'projet_prof',
'base_role' => 'ROLE_USER',
'base_path' => '/person/report/projet-professionnel',
'form_class' => ProjetProfessionnelType::class,
'actions' => [
'view' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/ProjetProfessionnel/view.html.twig',
],
'new' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/ProjetProfessionnel/new.html.twig',
],
'edit' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/ProjetProfessionnel/edit.html.twig',
],
'delete' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Report/delete.html.twig',
],
],
],
[
'class' => Frein::class,
'controller' => FreinCrudController::class,
'name' => 'csfrein',
'base_role' => 'ROLE_USER',
'base_path' => '/person/report/frein',
'form_class' => FreinType::class,
'actions' => [
'view' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Frein/view.html.twig',
],
'new' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Frein/new.html.twig',
],
'edit' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Frein/edit.html.twig',
],
'delete' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Report/delete.html.twig',
],
],
],
[
'class' => Immersion::class,
'controller' => ImmersionCrudController::class,
'name' => 'immersion',
'base_role' => 'ROLE_USER',
'base_path' => '/person/report/immersion',
'form_class' => ImmersionType::class,
'actions' => [
'view' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Immersion/view.html.twig',
],
'new' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Immersion/new.html.twig',
],
'bilan' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Immersion/edit-bilan.html.twig',
],
'edit' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Immersion/edit.html.twig',
],
'delete' => [
'role' => 'ROLE_USER',
'template' => '@ChillJob/Report/delete.html.twig',
],
],
],
],
]);
}
protected function prependRoute(ContainerBuilder $container): void
{
// declare routes for job bundle
$container->prependExtensionConfig('chill_main', [
'routing' => [
'resources' => [
'@ChillJobBundle/Resources/config/routing.yml',
],
],
]);
}
}

View File

@@ -0,0 +1,35 @@
<?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\JobBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files.
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
*/
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder('chill_job');
$rootNode = $treeBuilder->getRootNode();
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
<?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\JobBundle\Entity;
use Doctrine\Common\Collections\Order;
use Doctrine\ORM\Mapping as ORM;
use Chill\PersonBundle\Entity\Person;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* CV.
*/
#[ORM\Table(name: 'chill_job.cv')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\CVRepository::class)]
class CV implements \Stringable
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
/**
* @Assert\NotNull()
*/
#[ORM\Column(name: 'reportDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE)]
private ?\DateTimeInterface $reportDate = null;
public const FORMATION_LEVEL = [
'sans_diplome',
'BEP_CAP',
'BAC',
'BAC+2',
'BAC+3',
'BAC+4',
'BAC+5',
'BAC+8',
];
/**
* @Assert\NotBlank()
*/
#[ORM\Column(name: 'formationLevel', type: \Doctrine\DBAL\Types\Types::STRING, length: 255, nullable: true)]
private ?string $formationLevel = null;
public const FORMATION_TYPE = [
'formation_initiale',
'formation_continue',
];
#[ORM\Column(name: 'formationType', type: \Doctrine\DBAL\Types\Types::STRING, length: 255, nullable: true)]
private ?string $formationType = null;
/**
* @var string[]|null
*/
#[ORM\Column(name: 'spokenLanguages', type: \Doctrine\DBAL\Types\Types::JSON, nullable: true)]
private $spokenLanguages;
#[ORM\Column(name: 'notes', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
private ?string $notes = null;
/**
* @Assert\Valid(traverse=true)
*
* @var \Doctrine\Common\Collections\Collection<int, \Chill\JobBundle\Entity\CV\Formation>
*/
#[ORM\OneToMany(targetEntity: CV\Formation::class, mappedBy: 'CV', cascade: ['persist', 'remove', 'detach'], orphanRemoval: true)]
// #[ORM\OrderBy(['startDate' => Order::Descending, 'endDate' => 'DESC'])]
private Collection $formations;
/**
* @Assert\Valid(traverse=true)
*
* @var \Doctrine\Common\Collections\Collection<int, \Chill\JobBundle\Entity\CV\Experience>
*/
#[ORM\OneToMany(targetEntity: CV\Experience::class, mappedBy: 'CV', cascade: ['persist', 'remove', 'detach'], orphanRemoval: true)]
// #[ORM\OrderBy(['startDate' => Order::Descending, 'endDate' => 'DESC'])]
private Collection $experiences;
#[ORM\ManyToOne(targetEntity: Person::class)]
private ?Person $person = null;
public function __construct()
{
$this->formations = new ArrayCollection();
$this->experiences = new ArrayCollection();
$this->reportDate = new \DateTime('now');
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set reportDate.
*/
public function setReportDate(\DateTime $reportDate): self
{
$this->reportDate = $reportDate;
return $this;
}
/**
* Get reportDate.
*/
public function getReportDate(): ?\DateTimeInterface
{
return $this->reportDate;
}
/**
* Set formationLevel.
*/
public function setFormationLevel(?string $formationLevel = null): self
{
$this->formationLevel = $formationLevel;
return $this;
}
/**
* Get formationLevel.
*
* @return string|null
*/
public function getFormationLevel()
{
return $this->formationLevel;
}
/**
* Set formationType.
*/
public function setFormationType(string $formationType): self
{
$this->formationType = $formationType;
return $this;
}
/**
* Get formationType.
*/
public function getFormationType(): ?string
{
return $this->formationType;
}
/**
* Set spokenLanguages.
*
* @param mixed|null $spokenLanguages
*/
public function setSpokenLanguages($spokenLanguages = null): self
{
$this->spokenLanguages = $spokenLanguages;
return $this;
}
/**
* Get spokenLanguages.
*/
public function getSpokenLanguages(): array
{
return $this->spokenLanguages ?? [];
}
/**
* Set notes.
*/
public function setNotes(?string $notes = null): self
{
$this->notes = $notes;
return $this;
}
/**
* Get notes.
*/
public function getNotes(): ?string
{
return $this->notes;
}
public function getFormations(): Collection
{
return $this->formations;
}
public function getExperiences(): Collection
{
return $this->experiences;
}
public function setFormations(Collection $formations): self
{
foreach ($formations as $formation) {
/* @var CV\Formation $formation */
$formation->setCV($this);
}
$this->formations = $formations;
return $this;
}
public function addFormation(CV\Formation $formation): self
{
if ($this->formations->contains($formation)) {
return $this;
}
$this->formations->add($formation);
$formation->setCV($this);
return $this;
}
public function removeFormation(CV\Formation $formation): self
{
if (false === $this->formations->contains($formation)) {
return $this;
}
$formation->setCV(null);
$this->formations->removeElement($formation);
return $this;
}
public function setExperiences(Collection $experiences): self
{
foreach ($experiences as $experience) {
/* @var CV\Experience $experience */
$experience->setCV($this);
}
$this->experiences = $experiences;
return $this;
}
public function addExperience(CV\Experience $experience): self
{
if ($this->experiences->contains($experience)) {
return $this;
}
$experience->setCV($this);
$this->experiences->add($experience);
return $this;
}
public function removeExperience(CV\Experience $experience): self
{
if (false === $this->experiences->contains($experience)) {
return $this;
}
$this->experiences->removeElement($experience);
$experience->setCV(null);
return $this;
}
public function getPerson(): Person
{
return $this->person;
}
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function __toString(): string
{
return 'CV de '.$this->getPerson().' daté du '.
$this->getReportDate()->format('d-m-Y');
}
}

View File

@@ -0,0 +1,205 @@
<?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\JobBundle\Entity\CV;
use Doctrine\ORM\Mapping as ORM;
use Chill\JobBundle\Entity\CV;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Experience.
*/
#[ORM\Table(name: 'chill_job.cv_experience')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\CV\ExperienceRepository::class)]
class Experience
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
#[ORM\Column(name: 'poste', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
private ?string $poste = null;
#[ORM\Column(name: 'structure', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
private ?string $structure = null;
#[ORM\Column(name: 'startDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $startDate = null;
/**
* @Assert\GreaterThan(propertyPath="startDate", message="La date de fin doit être postérieure à la date de début")
*/
#[ORM\Column(name: 'endDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $endDate = null;
public const CONTRAT_TYPE = [
'cddi',
'cdd-6mois',
'cdd+6mois',
'interim',
'apprentissage',
'contrat_prof',
'cui',
'cae',
'cdi',
'stage',
'volontariat',
'benevolat',
'autres',
];
#[ORM\Column(name: 'contratType', type: \Doctrine\DBAL\Types\Types::STRING, length: 100)]
private ?string $contratType = null;
#[ORM\Column(name: 'notes', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
private ?string $notes = null;
#[ORM\ManyToOne(targetEntity: CV::class, inversedBy: 'experiences')]
private ?CV $CV = null;
/**
* Get id.
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Set poste.
*/
public function setPoste(?string $poste = null): self
{
$this->poste = $poste;
return $this;
}
/**
* Get poste.
*/
public function getPoste(): ?string
{
return $this->poste;
}
/**
* Set structure.
*/
public function setStructure(?string $structure = null): self
{
$this->structure = $structure;
return $this;
}
/**
* Get structure.
*/
public function getStructure(): ?string
{
return $this->structure;
}
/**
* Set startDate.
*
* @param \DateTime|null $startDate
*/
public function setStartDate(?\DateTimeInterface $startDate = null): self
{
$this->startDate = $startDate;
return $this;
}
/**
* Get startDate.
*
* @return \DateTimeInterface
*/
public function getStartDate()
{
return $this->startDate;
}
/**
* Set endDate.
*
* @param \DateTime|null $endDate
*/
public function setEndDate(?\DateTimeInterface $endDate = null): self
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endDate.
*
* @return \DateTimeInterface
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set contratType.
*/
public function setContratType(?string $contratType): self
{
$this->contratType = $contratType;
return $this;
}
/**
* Get contratType.
*/
public function getContratType(): ?string
{
return $this->contratType;
}
/**
* Set notes.
*/
public function setNotes(?string $notes): self
{
$this->notes = $notes;
return $this;
}
/**
* Get notes.
*/
public function getNotes(): ?string
{
return $this->notes;
}
public function getCV(): CV
{
return $this->CV;
}
public function setCV(?CV $CV = null): self
{
$this->CV = $CV;
return $this;
}
}

View File

@@ -0,0 +1,214 @@
<?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\JobBundle\Entity\CV;
use Doctrine\ORM\Mapping as ORM;
use Chill\JobBundle\Entity\CV;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Formation.
*/
#[ORM\Table(name: 'chill_job.cv_formation')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\CV\FormationRepository::class)]
class Formation
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
/**
* @Assert\Length(min=3)
*
* @Assert\NotNull()
*/
#[ORM\Column(name: 'title', type: \Doctrine\DBAL\Types\Types::TEXT)]
private ?string $title = null;
#[ORM\Column(name: 'startDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $startDate = null;
/**
* @Assert\GreaterThan(propertyPath="startDate", message="La date de fin doit être postérieure à la date de début")
*/
#[ORM\Column(name: 'endDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $endDate = null;
public const DIPLOMA_OBTAINED = [
'fr', 'non-fr', 'aucun',
];
#[ORM\Column(name: 'diplomaObtained', type: \Doctrine\DBAL\Types\Types::STRING, nullable: true)]
private ?string $diplomaObtained = null;
public const DIPLOMA_RECONNU = [
'oui', 'non', 'nsp',
];
#[ORM\Column(name: 'diplomaReconnue', type: \Doctrine\DBAL\Types\Types::STRING, length: 50, nullable: true)]
private ?string $diplomaReconnue = null;
#[ORM\Column(name: 'organisme', type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
private ?string $organisme = null;
#[ORM\ManyToOne(targetEntity: CV::class, inversedBy: 'formations')]
private ?CV $CV = null;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title.
*
* @return Formation
*/
public function setTitle(?string $title)
{
$this->title = $title;
return $this;
}
/**
* Get title.
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set startDate.
*
* @param \DateTime|null $startDate
*
* @return Formation
*/
public function setStartDate(?\DateTimeInterface $startDate = null)
{
$this->startDate = $startDate;
return $this;
}
/**
* Get startDate.
*
* @return \DateTimeInterface
*/
public function getStartDate()
{
return $this->startDate;
}
/**
* Set endDate.
*
* @param \DateTime|null $endDate
*
* @return Formation
*/
public function setEndDate(?\DateTimeInterface $endDate = null)
{
$this->endDate = $endDate;
return $this;
}
/**
* Get endDate.
*
* @return \DateTimeInterface
*/
public function getEndDate()
{
return $this->endDate;
}
/**
* Set diplomaObtained.
*
* @return Formation
*/
public function setDiplomaObtained(?string $diplomaObtained = null)
{
$this->diplomaObtained = $diplomaObtained;
return $this;
}
/**
* Get diplomaObtained.
*/
public function getDiplomaObtained()
{
return $this->diplomaObtained;
}
/**
* Set diplomaReconnue.
*/
public function setDiplomaReconnue(?string $diplomaReconnue = null): self
{
$this->diplomaReconnue = $diplomaReconnue;
return $this;
}
/**
* Get diplomaReconnue.
*/
public function getDiplomaReconnue(): ?string
{
return $this->diplomaReconnue;
}
/**
* Set organisme.
*/
public function setOrganisme(?string $organisme): self
{
$this->organisme = $organisme;
return $this;
}
/**
* Get organisme.
*/
public function getOrganisme(): ?string
{
return $this->organisme;
}
public function getCV(): ?CV
{
return $this->CV;
}
public function setCV(?CV $CV = null): self
{
$this->CV = $CV;
return $this;
}
}

View File

@@ -0,0 +1,192 @@
<?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\JobBundle\Entity;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
use Chill\PersonBundle\Entity\HasPerson;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Frein.
*/
#[ORM\Table(name: 'chill_job.frein')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\FreinRepository::class)]
class Frein implements HasPerson, \Stringable
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
/**
* @Assert\NotNull()
*
* @Assert\GreaterThan("5 years ago",
* message="La date du rapport ne peut pas être plus de cinq ans dans le passé"
* )
*/
#[ORM\Column(name: 'reportDate', type: \Doctrine\DBAL\Types\Types::DATE_MUTABLE)]
private ?\DateTimeInterface $reportDate = null;
public const FREINS_PERSO = [
'situation_administrative',
'situation_personnelle_et_familiale',
'comportement',
'etat_de_sante',
'precarite_situation_materielle',
'condition_ou_absence_logement',
'autres',
];
/**
* @var string[]
*/
#[ORM\Column(name: 'freinsPerso', type: \Doctrine\DBAL\Types\Types::JSON)]
private $freinsPerso = [];
#[ORM\Column(name: 'notesPerso', type: \Doctrine\DBAL\Types\Types::TEXT)]
private ?string $notesPerso = '';
public const FREINS_EMPLOI = [
'garde_d_enfants',
'sante',
'famille',
'finances',
'maitrise_de_la_langue',
'autres',
];
/**
* @var string[]
*/
#[ORM\Column(name: 'freinsEmploi', type: \Doctrine\DBAL\Types\Types::JSON)]
private $freinsEmploi = [];
#[ORM\Column(name: 'notesEmploi', type: \Doctrine\DBAL\Types\Types::TEXT)]
private ?string $notesEmploi = '';
/**
* @Assert\NotNull()
*/
#[ORM\ManyToOne(targetEntity: Person::class)]
private Person $person;
public function __construct()
{
$this->reportDate = new \DateTime('today');
}
public function getId(): ?int
{
return $this->id;
}
public function setReportDate(?\DateTimeInterface $reportDate): self
{
$this->reportDate = $reportDate;
return $this;
}
public function getReportDate(): ?\DateTimeInterface
{
return $this->reportDate;
}
public function setFreinsPerso(array $freinsPerso = []): self
{
$this->freinsPerso = $freinsPerso;
return $this;
}
public function getFreinsPerso(): array
{
return $this->freinsPerso;
}
public function setNotesPerso(?string $notesPerso = null): self
{
$this->notesPerso = (string) $notesPerso;
return $this;
}
public function getNotesPerso(): ?string
{
return $this->notesPerso;
}
public function setFreinsEmploi(array $freinsEmploi = []): self
{
$this->freinsEmploi = $freinsEmploi;
return $this;
}
public function getFreinsEmploi(): array
{
return $this->freinsEmploi;
}
public function setNotesEmploi(?string $notesEmploi = null): self
{
$this->notesEmploi = (string) $notesEmploi;
return $this;
}
public function getNotesEmploi(): ?string
{
return $this->notesEmploi;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person = null): HasPerson
{
$this->person = $person;
return $this;
}
/**
* @Assert\Callback()
*/
public function validateFreinsCount(ExecutionContextInterface $context, $payload): void
{
$nb = count($this->getFreinsEmploi()) + count($this->getFreinsPerso());
if (0 === $nb) {
$msg = 'Indiquez au moins un frein parmi les freins '
."liés à l'emploi et les freins liés à la situation personnelle.";
$context->buildViolation($msg)
->atPath('freinsEmploi')
->addViolation();
$context->buildViolation($msg)
->atPath('freinsPerso')
->addViolation();
}
}
public function __toString(): string
{
return 'Rapport "frein" de '.$this->getPerson().' daté du '.
$this->getReportDate()->format('d-m-Y');
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,407 @@
<?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\JobBundle\Entity;
use Chill\JobBundle\Repository\ProjetProfessionnelRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Chill\PersonBundle\Entity\Person;
use Chill\JobBundle\Entity\Rome\Appellation;
/**
* ProjetProfessionnel.
*/
#[ORM\Table(name: 'chill_job.projet_professionnel')]
#[ORM\Entity(repositoryClass: ProjetProfessionnelRepository::class)]
class ProjetProfessionnel implements \Stringable
{
#[ORM\Column(name: 'id', type: Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
/**
* @Assert\NotNull()
*/
#[ORM\ManyToOne(targetEntity: Person::class)]
private ?Person $person = null;
/**
* @Assert\NotNull()
*/
#[ORM\Column(name: 'reportDate', type: Types::DATE_MUTABLE)]
private ?\DateTimeInterface $reportDate = null;
/**
* @var \Doctrine\Common\Collections\Collection<int, \Chill\JobBundle\Entity\Rome\Appellation>
*/
#[ORM\JoinTable(name: 'chill_job.projetprofessionnel_souhait')]
#[ORM\ManyToMany(targetEntity: Appellation::class, cascade: ['persist'])]
private Collection $souhait;
#[ORM\Column(name: 'domaineActiviteSouhait', type: Types::TEXT, nullable: true)]
private ?string $domaineActiviteSouhait = null;
public const TYPE_CONTRAT = [
'cdd', 'cdi', 'contrat_insertion',
'interim', 'indifferent', 'apprentissage',
'personnalisation', 'creation_entreprise',
];
/**
* @var array|null
*
* @Assert\Count(min=1, minMessage="Indiquez au moins un type de contrat")
*/
#[ORM\Column(name: 'typeContrat', type: Types::JSON, nullable: true)]
private $typeContrat;
#[ORM\Column(name: 'typeContratNotes', type: Types::TEXT, nullable: true)]
private ?string $typeContratNotes = null;
public const VOLUME_HORAIRES = [
'temps_plein',
'temps_partiel',
];
/**
* @var array|null
*
* @Assert\Count(min=1, minMessage="Indiquez un volume horaire souhaité")
*/
#[ORM\Column(name: 'volumeHoraire', type: Types::JSON, nullable: true)]
private $volumeHoraire;
#[ORM\Column(name: 'volumeHoraireNotes', type: Types::TEXT, nullable: true)]
private ?string $volumeHoraireNotes = null;
#[ORM\Column(name: 'idee', type: Types::TEXT, nullable: true)]
private ?string $idee = null;
#[ORM\Column(name: 'enCoursConstruction', type: Types::TEXT, nullable: true)]
private ?string $enCoursConstruction = null;
/**
* @var \Doctrine\Common\Collections\Collection<int, \Chill\JobBundle\Entity\Rome\Appellation>
*/
#[ORM\JoinTable(name: 'chill_job.projetprofessionnel_valide')]
#[ORM\ManyToMany(targetEntity: Appellation::class)]
private Collection $valide;
#[ORM\Column(name: 'domaineActiviteValide', type: Types::TEXT, nullable: true)]
private ?string $domaineActiviteValide = null;
#[ORM\Column(name: 'valideNotes', type: Types::TEXT, nullable: true)]
private ?string $valideNotes = null;
#[ORM\Column(name: 'projetProfessionnelNote', type: Types::TEXT, nullable: true)]
private ?string $projetProfessionnelNote = null;
public function __construct()
{
$this->valide = new ArrayCollection();
$this->souhait = new ArrayCollection();
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set reportDate.
*
* @param \DateTime $reportDate
*
* @return ProjetProfessionnel
*/
public function setReportDate(?\DateTimeInterface $reportDate)
{
$this->reportDate = $reportDate;
return $this;
}
/**
* Get reportDate.
*
* @return \DateTimeInterface
*/
public function getReportDate()
{
return $this->reportDate;
}
/**
* Set typeContrat.
*
* @param mixed|null $typeContrat
*
* @return ProjetProfessionnel
*/
public function setTypeContrat($typeContrat = null)
{
$this->typeContrat = $typeContrat;
return $this;
}
/**
* Get typeContrat.
*/
public function getTypeContrat()
{
return $this->typeContrat;
}
/**
* Set typeContratNotes.
*
* @return ProjetProfessionnel
*/
public function setTypeContratNotes(?string $typeContratNotes = null)
{
$this->typeContratNotes = $typeContratNotes;
return $this;
}
/**
* Get typeContratNotes.
*
* @return string|null
*/
public function getTypeContratNotes()
{
return $this->typeContratNotes;
}
/**
* Set volumeHoraire.
*
* @param mixed|null $volumeHoraire
*
* @return ProjetProfessionnel
*/
public function setVolumeHoraire($volumeHoraire = null)
{
$this->volumeHoraire = $volumeHoraire;
return $this;
}
/**
* Get volumeHoraire.
*/
public function getVolumeHoraire()
{
return $this->volumeHoraire;
}
/**
* Set volumeHoraireNotes.
*
* @return ProjetProfessionnel
*/
public function setVolumeHoraireNotes(?string $volumeHoraireNotes = null)
{
$this->volumeHoraireNotes = $volumeHoraireNotes;
return $this;
}
/**
* Get volumeHoraireNotes.
*
* @return string|null
*/
public function getVolumeHoraireNotes()
{
return $this->volumeHoraireNotes;
}
/**
* Set idee.
*
* @return ProjetProfessionnel
*/
public function setIdee(?string $idee = null)
{
$this->idee = $idee;
return $this;
}
/**
* Get idee.
*
* @return string|null
*/
public function getIdee()
{
return $this->idee;
}
/**
* Set enCoursConstruction.
*
* @return ProjetProfessionnel
*/
public function setEnCoursConstruction(?string $enCoursConstruction = null)
{
$this->enCoursConstruction = $enCoursConstruction;
return $this;
}
/**
* Get enCoursConstruction.
*
* @return string|null
*/
public function getEnCoursConstruction()
{
return $this->enCoursConstruction;
}
/**
* Set valideNotes.
*
* @return ProjetProfessionnel
*/
public function setValideNotes(?string $valideNotes = null)
{
$this->valideNotes = $valideNotes;
return $this;
}
/**
* Get valideNotes.
*
* @return string|null
*/
public function getValideNotes()
{
return $this->valideNotes;
}
/**
* Set projetProfessionnelNote.
*
* @return ProjetProfessionnel
*/
public function setProjetProfessionnelNote(?string $projetProfessionnelNote = null)
{
$this->projetProfessionnelNote = $projetProfessionnelNote;
return $this;
}
/**
* Get projetProfessionnelNote.
*
* @return string|null
*/
public function getProjetProfessionnelNote()
{
return $this->projetProfessionnelNote;
}
public function getPerson(): Person
{
return $this->person;
}
public function getSouhait(): Collection
{
return $this->souhait;
}
public function getValide(): Collection
{
return $this->valide;
}
public function setPerson(Person $person)
{
$this->person = $person;
return $this;
}
public function setSouhait(Collection $souhait)
{
$this->souhait = $souhait;
return $this;
}
public function addSouhait(Appellation $souhait)
{
$this->souhait->add($souhait);
return $this;
}
public function setValide(Collection $valide)
{
$this->valide = $valide;
return $this;
}
public function addValide(Appellation $valide)
{
$this->valide->add($valide);
return $this;
}
public function getDomaineActiviteSouhait(): ?string
{
return $this->domaineActiviteSouhait;
}
public function getDomaineActiviteValide(): ?string
{
return $this->domaineActiviteValide;
}
public function setDomaineActiviteSouhait(?string $domaineActiviteSouhait = null)
{
$this->domaineActiviteSouhait = $domaineActiviteSouhait;
return $this;
}
public function setDomaineActiviteValide(?string $domaineActiviteValide = null)
{
$this->domaineActiviteValide = $domaineActiviteValide;
return $this;
}
public function __toString(): string
{
return 'Rapport "projet professionnel" de '.$this->getPerson().' daté du '.
$this->getReportDate()->format('d-m-Y');
}
}

View File

@@ -0,0 +1,107 @@
<?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\JobBundle\Entity\Rome;
use Doctrine\ORM\Mapping as ORM;
/**
* Appellation.
*/
#[ORM\Table(name: 'chill_job.rome_appellation')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\Rome\AppellationRepository::class)]
class Appellation implements \Stringable
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
#[ORM\Column(name: 'code', type: \Doctrine\DBAL\Types\Types::STRING, length: 40, unique: true)]
private ?string $code = '';
#[ORM\Column(name: 'libelle', type: \Doctrine\DBAL\Types\Types::TEXT)]
private ?string $libelle = '';
#[ORM\ManyToOne(targetEntity: Metier::class, inversedBy: 'appellations', cascade: ['persist'])]
private ?Metier $metier = null;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set code.
*
* @return Appellation
*/
public function setCode(?string $code)
{
$this->code = $code;
return $this;
}
/**
* Get code.
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set libelle.
*
* @return Appellation
*/
public function setLibelle(?string $libelle)
{
$this->libelle = $libelle;
return $this;
}
/**
* Get libelle.
*
* @return string
*/
public function getLibelle()
{
return $this->libelle;
}
public function getMetier(): Metier
{
return $this->metier;
}
public function setMetier(Metier $metier)
{
$this->metier = $metier;
return $this;
}
public function __toString(): string
{
return (string) $this->libelle;
}
}

View File

@@ -0,0 +1,100 @@
<?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\JobBundle\Entity\Rome;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Metier.
*/
#[ORM\Table(name: 'chill_job.rome_metier')]
#[ORM\Entity(repositoryClass: \Chill\JobBundle\Repository\Rome\MetierRepository::class)]
class Metier
{
#[ORM\Column(name: 'id', type: \Doctrine\DBAL\Types\Types::INTEGER)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
#[ORM\Column(name: 'libelle', type: \Doctrine\DBAL\Types\Types::TEXT)]
private ?string $libelle = '';
#[ORM\Column(name: 'code', type: \Doctrine\DBAL\Types\Types::STRING, length: 20, unique: true)]
private ?string $code = '';
/**
* @var \Doctrine\Common\Collections\Collection<int, \Chill\JobBundle\Entity\Rome\Appellation>
*/
#[ORM\OneToMany(targetEntity: Appellation::class, mappedBy: 'metier')]
private \Doctrine\Common\Collections\Collection $appellations;
public function __construct()
{
$this->appellations = new ArrayCollection();
// $this->appellation = new ArrayCollection();
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set libelle.
*
* @return Metier
*/
public function setLibelle(?string $libelle)
{
$this->libelle = $libelle;
return $this;
}
/**
* Get libelle.
*
* @return string
*/
public function getLibelle()
{
return $this->libelle;
}
/**
* Set code.
*
* @return Metier
*/
public function setCode(?string $code)
{
$this->code = $code;
return $this;
}
/**
* Get code.
*
* @return string
*/
public function getCode()
{
return $this->code;
}
}

View File

@@ -0,0 +1,188 @@
<?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\JobBundle\Export;
use Chill\JobBundle\Entity\CSPerson;
use Chill\PersonBundle\Export\Helper\CustomizeListPersonHelperInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Class ListCSPerson.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class AddCSPersonToPersonListHelper implements CustomizeListPersonHelperInterface
{
public function __construct(private readonly TranslatorInterface $translator) {}
private const CSPERSON_FIELDS = [
'dateFinDernierEmploi',
/* 'prescripteur__name',
'prescripteur__email',
'prescripteur__phone',*/
'poleEmploiId',
'cafId',
'cafInscriptionDate',
'dateContratIEJ',
'cERInscriptionDate',
'pPAEInscriptionDate',
'pPAESignataire',
'cERSignataire',
'nEETCommissionDate',
'fSEMaDemarcheCode',
'enfantACharge',
'nEETEligibilite',
'situationProfessionnelle',
];
public function alterKeys(array $existing): array
{
$ressources = array_map(static fn ($key) => 'ressources__'.$key, CSPerson::RESSOURCES);
$moyenTransport = array_map(static fn ($key) => 'moyen_transport__'.$key, CSPerson::MOBILITE_MOYEN_TRANSPORT);
$accompagnements = array_map(static fn ($key) => 'accompagnements__'.$key, CSPerson::ACCOMPAGNEMENTS);
$permisConduire = array_map(static fn ($key) => 'permis_conduire__'.$key, CSPerson::PERMIS_CONDUIRE);
$typeContrat = array_map(static fn ($key) => 'type_contrat__'.$key, CSPerson::TYPE_CONTRAT);
return [
...$existing,
...self::CSPERSON_FIELDS,
...$ressources,
...$moyenTransport,
...$accompagnements,
...$permisConduire,
...$typeContrat,
];
}
public function alterSelect(QueryBuilder $qb, \DateTimeImmutable $computedDate): void
{
$qb
->leftJoin(CSPerson::class, 'cs_person', Query\Expr\Join::WITH, 'cs_person.person = person');
foreach (self::CSPERSON_FIELDS as $f) {
$qb->addSelect(sprintf('cs_person.%s as %s', $f, $f));
}
/* $qb->addSelect('cs_person.situationProfessionnelle as situation_prof');
$qb->addSelect('cs_person.nEETEligibilite as nEETEligibilite');*/
$i = 0;
foreach (CSPerson::RESSOURCES as $key) {
$qb->addSelect("JSONB_EXISTS_IN_ARRAY(cs_person.ressources, :param_{$i}) AS ressources__".$key)
->setParameter('param_'.$i, $key);
++$i;
}
foreach (CSPerson::MOBILITE_MOYEN_TRANSPORT as $key) {
$qb->addSelect("JSONB_EXISTS_IN_ARRAY(cs_person.mobiliteMoyenDeTransport, :param_{$i}) AS moyen_transport__".$key)
->setParameter('param_'.$i, $key);
++$i;
}
foreach (CSPerson::TYPE_CONTRAT as $key) {
$qb->addSelect("JSONB_EXISTS_IN_ARRAY(cs_person.typeContrat, :param_{$i}) AS type_contrat__".$key)
->setParameter('param_'.$i, $key);
++$i;
}
foreach (CSPerson::ACCOMPAGNEMENTS as $key) {
$qb->addSelect("JSONB_EXISTS_IN_ARRAY(cs_person.accompagnement, :param_{$i}) AS accompagnements__".$key)
->setParameter('param_'.$i, $key);
++$i;
}
foreach (CSPerson::PERMIS_CONDUIRE as $key) {
$qb->addSelect("JSONB_EXISTS_IN_ARRAY(cs_person.permisConduire, :param_{$i}) AS permis_conduire__".$key)
->setParameter('param_'.$i, $key);
++$i;
}
}
public function getLabels(string $key, array $values, array $data): ?callable
{
if (str_contains($key, '__')) {
return function (string|bool|null $value) use ($key): string {
if ('_header' === $value) {
[$domain, $v] = explode('__', $key);
return 'export.list.cs_person.'.$domain.'.'.$v;
}
if ('1' === $value || true === $value || 't' === $value) {
return 'x';
}
return '';
};
}
return match ($key) {
'nEETEligibilite' => function (string|bool|null $value): string {
if ('_header' === $value) {
return 'export.list.cs_person.neet_eligibility';
}
if ('1' === $value || true === $value || 't' === $value) {
return 'x';
}
return '';
},
'situationProfessionnelle' => function ($value) {
if ('_header' === $value) {
return 'export.list.cs_person.situation_professionelle';
}
return $value;
},
'cerinscriptiondate', 'ppaeinscriptiondate', 'neetcommissiondate', 'findernieremploidate', 'cafinscriptiondate', 'contratiejdate' => function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
if (null === $value) {
return '';
}
// warning: won't work with DateTimeImmutable as we reset time a few lines later
$date = \DateTime::createFromFormat('Y-m-d', $value);
$hasTime = false;
if (false === $date) {
$date = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
$hasTime = true;
}
// check that the creation could occur.
if (false === $date) {
throw new \Exception(sprintf('The value %s could not be converted to %s', $value, \DateTime::class));
}
if (!$hasTime) {
$date->setTime(0, 0, 0);
}
return $date;
},
default => function ($value) use ($key) {
if ('_header' === $value) {
return $this->translator->trans($key);
}
return $value;
},
};
}
}

View File

@@ -0,0 +1,395 @@
<?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\JobBundle\Export;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\Person;
use Chill\JobBundle\Security\Authorization\ExportsJobVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ListCV.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class ListCV implements ListInterface, ExportElementValidatedInterface
{
/**
* @var array
*/
public const FIELDS = [
'id' => 'integer',
'firstname' => 'string',
'lastname' => 'string',
'gender' => 'string',
'birthdate' => 'date',
'placeofbirth' => 'string',
'countryofbirth' => 'json',
'formationlevel' => 'string',
];
/**
* @var array
*/
protected $personIds = [];
/**
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* ListAcquisition constructor.
*/
public function __construct(EntityManagerInterface $em)
{
$this->entityManager = $em;
}
/**
* validate the form's data and, if required, build a contraint
* violation on the data.
*
* @param mixed $data the data, as returned by the user
*/
public function validateForm($data, ExecutionContextInterface $context) {}
/**
* get a title, which will be used in UI (and translated).
*
* @return string
*/
public function getTitle()
{
return 'Liste des CVs par personne';
}
/**
* Add a form to collect data from the user.
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
// 'choices_as_values' => true,
'label' => 'Fields to include in export',
'choices' => array_combine($this->getFields(), $this->getFields()),
'data' => array_combine($this->getFields(), $this->getFields()),
'choice_attr' => [],
'attr' => ['class' => ''],
'constraints' => [new Callback([
'callback' => function ($selected, ExecutionContextInterface $context) {
if (0 === count($selected)) {
$context
->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
},
])],
])
->add('reportdate_min', ChillDateType::class, [
'label' => 'Date du rapport après le',
'required' => false,
'label_attr' => [
'class' => 'reportdate_range',
],
'attr' => [
'class' => 'reportdate_range',
],
])
->add('reportdate_max', ChillDateType::class, [
'label' => 'Date du rapport avant le',
'required' => false,
'label_attr' => [
'class' => 'report_date_range',
],
'attr' => [
'class' => 'report_date_range',
],
])
;
}
/**
* Return the Export's type. This will inform _on what_ export will apply.
* Most of the type, it will be a string which references an entity.
*
* Example of types : Chill\PersonBundle\Export\Declarations::PERSON_TYPE
*
* @return string
*/
public function getType()
{
return Person::class;
}
/**
* A description, which will be used in the UI to explain what the export does.
* This description will be translated.
*
* @return string
*/
public function getDescription()
{
return 'Crée une liste des CVs en fonction de différents paramètres.';
}
/**
* The initial query, which will be modified by ModifiersInterface
* (i.e. AggregatorInterface, FilterInterface).
*
* This query should take into account the `$acl` and restrict result only to
* what the user is allowed to see. (Do not show personal data the user
* is not allowed to see).
*
* The returned object should be an instance of QueryBuilder or NativeQuery.
*
* @param array $acl an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )`
* @param array $data the data from the form, if any
*
* @return QueryBuilder|\Doctrine\ORM\NativeQuery the query to execute
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
return $this->entityManager->createQueryBuilder()
->from('ChillPersonBundle:Person', 'person');
}
/**
* Inform which ModifiersInterface (i.e. AggregatorInterface, FilterInterface)
* are allowed. The modifiers should be an array of types the _modifier_ apply on
* (@see ModifiersInterface::applyOn()).
*
* @return string[]
*/
public function supportsModifiers()
{
return ['cv', 'person'];
}
/**
* Return the required Role to execute the Export.
*/
public function requiredRole(): string
{
return ExportsJobVoter::EXPORT;
}
/**
* Return which formatter type is allowed for this report.
*
* @return string[]
*/
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
/**
* give the list of keys the current export added to the queryBuilder in
* self::initiateQuery.
*
* Example: if your query builder will contains `SELECT count(id) AS count_id ...`,
* this function will return `array('count_id')`.
*
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*
* @return array
*/
public function getQueryKeys($data)
{
return array_keys($data['fields']);
}
/**
* Return array FIELDS keys only.
*
* @return array
*/
private function getFields()
{
return array_keys(self::FIELDS);
}
/**
* Return the results of the query builder.
*
* @param QueryBuilder|\Doctrine\ORM\NativeQuery $qb
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*/
public function getResult($qb, $data)
{
$qb->select('person.id');
$ids = $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
$this->personIds = array_map(fn ($e) => $e['id'], $ids);
$personIdsParameters = '?'.\str_repeat(', ?', count($this->personIds) - 1);
$query = \str_replace('%person_ids%', $personIdsParameters, self::QUERY);
$rsm = new Query\ResultSetMapping();
foreach (self::FIELDS as $name => $type) {
if (null !== $data['fields'][$name]) {
$rsm->addScalarResult($name, $name, $type);
}
}
$nq = $this->entityManager->createNativeQuery($query, $rsm);
$idx = 1;
for ($i = 1; $i <= count($this->personIds); ++$i) {
++$idx;
$nq->setParameter($i, $this->personIds[$i - 1]);
}
$nq->setParameter($idx++, $data['reportdate_min'], 'date');
$nq->setParameter($idx, $data['reportdate_max'], 'date');
return $nq->getResult();
}
/**
* transform the results to viewable and understable string.
*
* The callable will have only one argument: the `value` to translate.
*
* The callable should also be able to return a key `_header`, which
* will contains the header of the column.
*
* The string returned **must** be already translated if necessary,
* **with an exception** for the string returned for `_header`.
*
* Example :
*
* ```
* protected $translator;
*
* public function getLabels($key, array $values, $data)
* {
* return function($value) {
* case $value
* {
* case '_header' :
* return 'my header not translated';
* case true:
* return $this->translator->trans('true');
* case false:
* return $this->translator->trans('false');
* default:
* // this should not happens !
* throw new \LogicException();
* }
* }
* ```
*
* **Note:** Why each string must be translated with an exception for
* the `_header` ? For performance reasons: most of the value will be number
* which do not need to be translated, or value already translated in
* database. But the header must be, in every case, translated.
*
* @param string $key The column key, as added in the query
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
* @param mixed $data The data from the export's form (as defined in `buildForm`
*/
public function getLabels($key, array $values, $data)
{
return match ($key) {
'birthdate' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $value->format('d-m-Y');
},
'countryofbirth' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
return $value['fr'];
},
'gender' => function ($value) use ($key) {
$gend_array = ['man' => 'Homme', 'woman' => 'Femme', 'both' => 'Indéterminé'];
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $gend_array[$value];
},
default => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
return $value;
},
};
}
/**
* Native Query SQL.
*/
public const QUERY = <<<'SQL'
SELECT
p.id as id,
p.firstname as firstname,
p.lastname as lastname,
p.gender as gender,
p.birthdate as birthdate,
p.place_of_birth as placeofbirth,
cn.name as countryofbirth,
cv.formationlevel as formationlevel
FROM
public.chill_person_person AS p
LEFT JOIN chill_job.cv AS cv
ON p.id = cv.person_id
LEFT JOIN public.country AS cn
ON p.countryofbirth_id = cn.id
-- condition 1
WHERE p.id IN (%person_ids%)
-- condition 2
AND (
cv.reportdate
BETWEEN COALESCE(?::date, '1900-01-01')
AND COALESCE(?::date, '2100-01-01')
)
ORDER BY cv.reportdate DESC
SQL;
public function getFormDefaultData(): array
{
return [];
}
}

View File

@@ -0,0 +1,497 @@
<?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\JobBundle\Export;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\Person;
use Chill\JobBundle\Entity\Frein;
use Chill\JobBundle\Security\Authorization\ExportsJobVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ListFrein.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class ListFrein implements ListInterface, ExportElementValidatedInterface
{
/**
* @var array
*/
public const FREINS = [
'freinsperso' => Frein::FREINS_PERSO,
'freinsemploi' => Frein::FREINS_EMPLOI,
];
/**
* @var array
*/
public const FIELDS = [
'id' => 'integer',
'firstname' => 'string',
'lastname' => 'string',
'gender' => 'string',
'birthdate' => 'date',
'placeofbirth' => 'string',
'countryofbirth' => 'json',
'reportdate' => 'date',
'freinsperso' => 'json',
'notesperso' => 'string',
'freinsemploi' => 'json',
'notesemploi' => 'string',
];
/**
* @var array
*/
protected $personIds = [];
/**
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* ListAcquisition constructor.
*/
public function __construct(EntityManagerInterface $em)
{
$this->entityManager = $em;
}
/**
* validate the form's data and, if required, build a contraint
* violation on the data.
*
* @param mixed $data the data, as returned by the user
*/
public function validateForm($data, ExecutionContextInterface $context) {}
/**
* get a title, which will be used in UI (and translated).
*
* @return string
*/
public function getTitle()
{
return 'Liste des freins identifiés par personne';
}
/**
* Add a form to collect data from the user.
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
// 'choices_as_values' => true,
'label' => 'Fields to include in export',
'choices' => array_combine($this->getFields(), $this->getFields()),
'data' => array_combine($this->getFields(), $this->getFields()),
'choice_attr' => [],
'attr' => ['class' => ''],
'constraints' => [new Callback([
'callback' => function ($selected, ExecutionContextInterface $context) {
if (0 === count($selected)) {
$context
->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
},
])],
])
->add('reportdate_min', ChillDateType::class, [
'label' => 'Date du rapport après le',
'required' => false,
'label_attr' => [
'class' => 'reportdate_range',
],
'attr' => [
'class' => 'reportdate_range',
],
])
->add('reportdate_max', ChillDateType::class, [
'label' => 'Date du rapport avant le',
'required' => false,
'label_attr' => [
'class' => 'report_date_range',
],
'attr' => [
'class' => 'report_date_range',
],
])
;
}
/**
* Return the Export's type. This will inform _on what_ export will apply.
* Most of the type, it will be a string which references an entity.
*
* Example of types : Chill\PersonBundle\Export\Declarations::PERSON_TYPE
*
* @return string
*/
public function getType()
{
return Person::class;
}
/**
* A description, which will be used in the UI to explain what the export does.
* This description will be translated.
*
* @return string
*/
public function getDescription()
{
return 'Crée une liste des personnes et de leurs freins identifiés en fonction de différents paramètres.';
}
/**
* The initial query, which will be modified by ModifiersInterface
* (i.e. AggregatorInterface, FilterInterface).
*
* This query should take into account the `$acl` and restrict result only to
* what the user is allowed to see. (Do not show personal data the user
* is not allowed to see).
*
* The returned object should be an instance of QueryBuilder or NativeQuery.
*
* @param array $acl an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )`
* @param array $data the data from the form, if any
*
* @return QueryBuilder|\Doctrine\ORM\NativeQuery the query to execute
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
return $this->entityManager->createQueryBuilder()
->from(Person::class, 'person');
}
/**
* Inform which ModifiersInterface (i.e. AggregatorInterface, FilterInterface)
* are allowed. The modifiers should be an array of types the _modifier_ apply on
* (@see ModifiersInterface::applyOn()).
*
* @return string[]
*/
public function supportsModifiers()
{
return ['frein', 'person'];
}
/**
* Return the required Role to execute the Export.
*/
public function requiredRole(): string
{
return ExportsJobVoter::EXPORT;
}
/**
* Return which formatter type is allowed for this report.
*
* @return string[]
*/
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
/**
* Return array FIELDS keys only.
*
* @return array
*/
private function getFields()
{
return array_keys(self::FIELDS);
}
/**
* give the list of keys the current export added to the queryBuilder in
* self::initiateQuery.
*
* Example: if your query builder will contains `SELECT count(id) AS count_id ...`,
* this function will return `array('count_id')`.
*
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*
* @return array
*/
public function getQueryKeys($data)
{
$freins = self::FREINS;
$fields = [];
foreach ($data['fields'] as $key) {
switch ($key) {
case 'freinsperso':
case 'freinsemploi':
foreach ($freins[$key] as $item) {
$this->translationCompatKey($item, $key);
$fields[] = $item;
}
break;
default:
$fields[] = $key;
}
}
return $fields;
}
/**
* Make key compatible with YAML messages ids
* for fields that are splitted in columns.
*
* @param string $item (&) passed by reference
* @param string $key
*/
private function translationCompatKey(&$item, $key)
{
$prefix = substr_replace($key, 'freins_', 0, 6);
$item = $prefix.'.'.$item;
}
/**
* Some fields values are arrays that have to be splitted in columns.
* This function split theses fields.
*
* @param array $rows
*
* @return array|\Closure
*/
private function splitArrayToColumns($rows)
{
$freins = self::FREINS;
$results = [];
foreach ($rows as $row) {
/**
* @var string $key
*/
$res = [];
foreach ($row as $key => $value) {
switch ($key) {
case 'freinsperso':
case 'freinsemploi':
foreach ($freins[$key] as $item) {
$this->translationCompatKey($item, $key);
if (0 === count($value)) {
$res[$item] = '';
} else {
foreach ($value as $v) {
$this->translationCompatKey($v, $key);
if ($item === $v) {
$res[$item] = 'x';
break;
}
$res[$item] = '';
}
}
}
break;
default:
$res[$key] = $value;
}
}
$results[] = $res;
}
return $results;
}
/**
* Return the results of the query builder.
*
* @param QueryBuilder|\Doctrine\ORM\NativeQuery $qb
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*/
public function getResult($qb, $data)
{
$qb->select('person.id');
$ids = $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
$this->personIds = array_map(fn ($e) => $e['id'], $ids);
$personIdsParameters = '?'.\str_repeat(', ?', count($this->personIds) - 1);
$query = \str_replace('%person_ids%', $personIdsParameters, self::QUERY);
$rsm = new Query\ResultSetMapping();
foreach (self::FIELDS as $name => $type) {
if (null !== $data['fields'][$name]) {
$rsm->addScalarResult($name, $name, $type);
}
}
$nq = $this->entityManager->createNativeQuery($query, $rsm);
$idx = 1;
for ($i = 1; $i <= count($this->personIds); ++$i) {
++$idx;
$nq->setParameter($i, $this->personIds[$i - 1]);
}
$nq->setParameter($idx++, $data['reportdate_min'], 'date');
$nq->setParameter($idx, $data['reportdate_max'], 'date');
return $this->splitArrayToColumns(
$nq->getResult()
);
}
/**
* transform the results to viewable and understable string.
*
* The callable will have only one argument: the `value` to translate.
*
* The callable should also be able to return a key `_header`, which
* will contains the header of the column.
*
* The string returned **must** be already translated if necessary,
* **with an exception** for the string returned for `_header`.
*
* Example :
*
* ```
* protected $translator;
*
* public function getLabels($key, array $values, $data)
* {
* return function($value) {
* case $value
* {
* case '_header' :
* return 'my header not translated';
* case true:
* return $this->translator->trans('true');
* case false:
* return $this->translator->trans('false');
* default:
* // this should not happens !
* throw new \LogicException();
* }
* }
* ```
*
* **Note:** Why each string must be translated with an exception for
* the `_header` ? For performance reasons: most of the value will be number
* which do not need to be translated, or value already translated in
* database. But the header must be, in every case, translated.
*
* @param string $key The column key, as added in the query
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
* @param mixed $data The data from the export's form (as defined in `buildForm`
*/
public function getLabels($key, array $values, $data)
{
return match ($key) {
'reportdate', 'birthdate' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $value->format('d-m-Y');
},
'countryofbirth' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
return $value['fr'];
},
'gender' => function ($value) use ($key) {
$gend_array = ['man' => 'Homme', 'woman' => 'Femme', 'both' => 'Indéterminé'];
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $gend_array[$value];
},
default => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $value;
},
};
}
/**
* Native Query SQL.
*/
public const QUERY = <<<'SQL'
SELECT
p.id as id,
p.firstname as firstname,
p.lastname as lastname,
p.gender as gender,
p.birthdate as birthdate,
p.place_of_birth as placeofbirth,
cn.name as countryofbirth,
fr.reportdate as reportdate,
fr.freinsperso as freinsperso,
fr.notesperso as notesperso,
fr.freinsemploi as freinsemploi,
fr.notesemploi as notesemploi
FROM
public.chill_person_person AS p
LEFT JOIN chill_job.frein AS fr
ON p.id = fr.person_id
LEFT JOIN public.country AS cn
ON p.countryofbirth_id = cn.id
-- condition 1
WHERE p.id IN (%person_ids%)
-- condition 2
AND (
fr.reportdate
BETWEEN COALESCE(?::date, '1900-01-01')
AND COALESCE(?::date, '2100-01-01')
)
ORDER BY fr.reportdate DESC
SQL;
public function getFormDefaultData(): array
{
return [];
}
}

View File

@@ -0,0 +1,586 @@
<?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\JobBundle\Export;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\ChillDateType;
use Chill\PersonBundle\Entity\Person;
use Chill\JobBundle\Entity\ProjetProfessionnel;
use Chill\JobBundle\Security\Authorization\ExportsJobVoter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ListProjetProfessionnel.
*
* @author Mathieu Jaumotte mathieu.jaumotte@champs-libres.coop
*/
class ListProjetProfessionnel implements ListInterface, ExportElementValidatedInterface
{
/**
* @var array
*/
public const PPROF = [
'projet_prof__type_contrat__label' => ProjetProfessionnel::TYPE_CONTRAT,
'projet_prof__volume_horaire__label' => ProjetProfessionnel::VOLUME_HORAIRES,
];
/**
* @var array
*/
public const FIELDS = [
'id' => 'integer',
'firstname' => 'string',
'lastname' => 'string',
'gender' => 'string',
'birthdate' => 'date',
'placeofbirth' => 'string',
'countryofbirth' => 'json',
'projet_prof__souhait__code' => 'string',
'projet_prof__type_contrat__label' => 'json',
'projet_prof__type_contrat__note' => 'string',
'projet_prof__volume_horaire__label' => 'json',
'projet_prof__volume_horaire__note' => 'string',
'projet_prof__idee' => 'string',
'projet_prof__encoursdeconstruction' => 'string',
'projet_prof__valide__note' => 'string',
'projet_prof__valide__code' => 'string',
'projet_prof__note' => 'string',
];
/**
* @var array
*/
protected $personIds = [];
/**
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* ListAcquisition constructor.
*/
public function __construct(EntityManagerInterface $em)
{
$this->entityManager = $em;
}
/**
* validate the form's data and, if required, build a contraint
* violation on the data.
*
* @param mixed $data the data, as returned by the user
*/
public function validateForm($data, ExecutionContextInterface $context) {}
/**
* get a title, which will be used in UI (and translated).
*
* @return string
*/
public function getTitle()
{
return 'Liste des projets professionnels par personne';
}
/**
* Add a form to collect data from the user.
*/
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('fields', ChoiceType::class, [
'multiple' => true,
'expanded' => true,
// 'choices_as_values' => true,
'label' => 'Fields to include in export',
'choice_label' => fn ($key) => str_replace('__', '.', (string) $key),
'choices' => array_combine($this->getFields(), $this->getFields()),
'data' => array_combine($this->getFields(), $this->getFields()),
'choice_attr' => [],
'attr' => ['class' => ''],
'constraints' => [new Callback([
'callback' => function ($selected, ExecutionContextInterface $context) {
if (0 === count($selected)) {
$context
->buildViolation('You must select at least one element')
->atPath('fields')
->addViolation();
}
},
])],
])
->add('reportdate_min', ChillDateType::class, [
'label' => 'Date du rapport après le',
'required' => false,
'label_attr' => [
'class' => 'reportdate_range',
],
'attr' => [
'class' => 'reportdate_range',
],
])
->add('reportdate_max', ChillDateType::class, [
'label' => 'Date du rapport avant le',
'required' => false,
'label_attr' => [
'class' => 'report_date_range',
],
'attr' => [
'class' => 'report_date_range',
],
])
;
}
/**
* Return the Export's type. This will inform _on what_ export will apply.
* Most of the type, it will be a string which references an entity.
*
* Example of types : Chill\PersonBundle\Export\Declarations::PERSON_TYPE
*
* @return string
*/
public function getType()
{
return Person::class;
}
/**
* A description, which will be used in the UI to explain what the export does.
* This description will be translated.
*
* @return string
*/
public function getDescription()
{
return 'Crée une liste des personnes et de leur projet professionnel en fonction de différents paramètres.';
}
/**
* The initial query, which will be modified by ModifiersInterface
* (i.e. AggregatorInterface, FilterInterface).
*
* This query should take into account the `$acl` and restrict result only to
* what the user is allowed to see. (Do not show personal data the user
* is not allowed to see).
*
* The returned object should be an instance of QueryBuilder or NativeQuery.
*
* @param array $acl an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )`
* @param array $data the data from the form, if any
*
* @return QueryBuilder|\Doctrine\ORM\NativeQuery the query to execute
*/
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
{
return $this->entityManager->createQueryBuilder()
->from('ChillPersonBundle:Person', 'person');
}
/**
* Inform which ModifiersInterface (i.e. AggregatorInterface, FilterInterface)
* are allowed. The modifiers should be an array of types the _modifier_ apply on
* (@see ModifiersInterface::applyOn()).
*
* @return string[]
*/
public function supportsModifiers()
{
return ['projetprofessionnel', 'person'];
}
/**
* Return the required Role to execute the Export.
*/
public function requiredRole(): string
{
return ExportsJobVoter::EXPORT;
}
/**
* Return which formatter type is allowed for this report.
*
* @return string[]
*/
public function getAllowedFormattersTypes()
{
return [FormatterInterface::TYPE_LIST];
}
/**
* Return array FIELDS keys only.
*
* @return array
*/
private function getFields()
{
return array_keys(self::FIELDS);
}
/**
* give the list of keys the current export added to the queryBuilder in
* self::initiateQuery.
*
* Example: if your query builder will contains `SELECT count(id) AS count_id ...`,
* this function will return `array('count_id')`.
*
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*
* @return array
*/
public function getQueryKeys($data)
{
$projet_professionnel = self::PPROF;
$fields = [];
foreach ($data['fields'] as $key) {
switch ($key) {
case 'projet_prof__type_contrat__label':
case 'projet_prof__volume_horaire__label':
foreach ($projet_professionnel[$key] as $item) {
$this->translationCompatKey($item, $key);
$fields[] = $item;
}
break;
case 'projet_prof__souhait__code':
case 'projet_prof__type_contrat__note':
case 'projet_prof__volume_horaire__note':
case 'projet_prof__idee':
case 'projet_prof__encoursdeconstruction':
case 'projet_prof__valide__note':
case 'projet_prof__valide__code':
case 'projet_prof__note':
$key = str_replace('__', '.', (string) $key);
// no break
default:
$fields[] = $key;
}
}
return $fields;
}
/**
* Make item compatible with YAML messages ids
* for fields that are splitted in columns (with field key to replace).
*
* AVANT
* key: projet_prof__volume_horaire__label
* item: temps_plein
* APRES
* item: projet_prof.volume_horaire.temps_plein
*
* @param string $item (&) passed by reference
* @param string $key
*/
private function translationCompatKey(&$item, $key)
{
$key = str_replace('label', $item, $key);
$key = str_replace('__', '.', $key);
$item = $key;
}
/**
* Some fields values are arrays that have to be splitted in columns.
* This function split theses fields.
*
* @param array $rows
*
* @return array|\Closure
*/
private function splitArrayToColumns($rows)
{
$projet_professionnel = self::PPROF;
$results = [];
foreach ($rows as $row) {
/**
* @var string $key
*/
$res = [];
foreach ($row as $key => $value) {
switch ($key) {
case 'projet_prof__type_contrat__label':
case 'projet_prof__volume_horaire__label':
foreach ($projet_professionnel[$key] as $item) {
$this->translationCompatKey($item, $key);
if (0 === count($value)) {
$res[$item] = '';
} else {
foreach ($value as $v) {
$this->translationCompatKey($v, $key);
if ($item === $v) {
$res[$item] = 'x';
break;
}
$res[$item] = '';
}
}
}
break;
case 'projet_prof__souhait__code':
case 'projet_prof__type_contrat__note':
case 'projet_prof__volume_horaire__note':
case 'projet_prof__idee':
case 'projet_prof__encoursdeconstruction':
case 'projet_prof__valide__note':
case 'projet_prof__valide__code':
case 'projet_prof__note':
$key = str_replace('__', '.', (string) $key);
// no break
default:
$res[$key] = $value;
}
}
$results[] = $res;
}
return $results;
}
/**
* Return the results of the query builder.
*
* @param QueryBuilder|\Doctrine\ORM\NativeQuery $qb
* @param mixed[] $data the data from the export's form (added by self::buildForm)
*/
public function getResult($qb, $data)
{
$qb->select('person.id');
$ids = $qb->getQuery()->getResult(Query::HYDRATE_SCALAR);
$this->personIds = array_map(fn ($e) => $e['id'], $ids);
$personIdsParameters = '?'.\str_repeat(', ?', count($this->personIds) - 1);
$query = \str_replace('%person_ids%', $personIdsParameters, self::QUERY);
$rsm = new Query\ResultSetMapping();
foreach (self::FIELDS as $name => $type) {
if (null !== $data['fields'][$name]) {
$rsm->addScalarResult($name, $name, $type);
}
}
$nq = $this->entityManager->createNativeQuery($query, $rsm);
$idx = 1;
for ($i = 1; $i <= count($this->personIds); ++$i) {
++$idx;
$nq->setParameter($i, $this->personIds[$i - 1]);
}
$nq->setParameter($idx++, $data['reportdate_min'], 'date');
$nq->setParameter($idx, $data['reportdate_max'], 'date');
return $this->splitArrayToColumns(
$nq->getResult()
);
}
/**
* transform the results to viewable and understable string.
*
* The callable will have only one argument: the `value` to translate.
*
* The callable should also be able to return a key `_header`, which
* will contains the header of the column.
*
* The string returned **must** be already translated if necessary,
* **with an exception** for the string returned for `_header`.
*
* Example :
*
* ```
* protected $translator;
*
* public function getLabels($key, array $values, $data)
* {
* return function($value) {
* case $value
* {
* case '_header' :
* return 'my header not translated';
* case true:
* return $this->translator->trans('true');
* case false:
* return $this->translator->trans('false');
* default:
* // this should not happens !
* throw new \LogicException();
* }
* }
* ```
*
* **Note:** Why each string must be translated with an exception for
* the `_header` ? For performance reasons: most of the value will be number
* which do not need to be translated, or value already translated in
* database. But the header must be, in every case, translated.
*
* @param string $key The column key, as added in the query
* @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
* @param mixed $data The data from the export's form (as defined in `buildForm`
*/
public function getLabels($key, array $values, $data)
{
return match ($key) {
'birthdate' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $value->format('d-m-Y');
},
'countryofbirth' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
return $value['fr'];
},
'projet_prof.valide.code', 'projet_prof.souhait.code' => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if ('{NULL}' === $value) {
return '';
}
return str_replace(['{', '}'], '', $value);
},
'gender' => function ($value) use ($key) {
$gend_array = ['man' => 'Homme', 'woman' => 'Femme', 'both' => 'Indéterminé'];
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $gend_array[$value];
},
default => function ($value) use ($key) {
if ('_header' === $value) {
return $key;
}
if (null === $value || '' === $value) {
return '';
}
return $value;
},
};
}
/**
* Native Query SQL.
*/
public const QUERY = <<<'SQL'
WITH projet_professionnel AS (
SELECT
pp.id,
pp.reportdate,
pp.typecontrat,
pp.typecontratnotes,
pp.volumehoraire,
pp.volumehorairenotes,
pp.idee,
pp.encoursconstruction,
pp.validenotes,
pp.projetprofessionnelnote,
ARRAY_AGG (DISTINCT rms.code) as rms_codes,
ARRAY_AGG (DISTINCT rmv.code) as rmv_codes
FROM chill_job.projet_professionnel AS pp
LEFT OUTER JOIN chill_job.projetprofessionnel_souhait AS pps
ON pp.id = pps.projetprofessionnel_id
LEFT OUTER JOIN chill_job.rome_appellation AS ras
ON pps.appellation_id = ras.id
LEFT OUTER JOIN chill_job.rome_metier AS rms
ON ras.metier_id = rms.id
LEFT OUTER JOIN chill_job.projetprofessionnel_valide AS ppv
ON pp.id = ppv.projetprofessionnel_id
LEFT OUTER JOIN chill_job.rome_appellation AS rav
ON ppv.appellation_id = rav.id
LEFT OUTER JOIN chill_job.rome_metier AS rmv
ON rav.metier_id = rmv.id
GROUP BY pp.id
)
SELECT
p.id,
p.firstname,
p.lastname,
p.gender as gender,
p.birthdate as birthdate,
p.place_of_birth as placeofbirth,
cn.name as countryofbirth,
pp.rms_codes as projet_prof__souhait__code,
pp.typecontrat as projet_prof__type_contrat__label,
pp.typecontratnotes as projet_prof__type_contrat__note,
pp.volumehoraire as projet_prof__volume_horaire__label,
pp.volumehorairenotes as projet_prof__volume_horaire__note,
pp.idee as projet_prof__idee,
pp.encoursconstruction as projet_prof__encoursdeconstruction,
pp.validenotes as projet_prof__valide__note,
pp.rmv_codes as projet_prof__valide__code,
pp.projetprofessionnelnote as projet_prof__note
FROM public.chill_person_person AS p
LEFT JOIN projet_professionnel AS pp
ON p.id = pp.id
LEFT OUTER JOIN public.country AS cn
ON p.countryofbirth_id = cn.id
-- condition 1
WHERE p.id IN (%person_ids%)
-- condition 2
AND (
pp.reportdate
BETWEEN COALESCE(?::date, '1900-01-01')
AND COALESCE(?::date, '2100-01-01')
)
ORDER BY pp.reportdate DESC
SQL;
public function getFormDefaultData(): array
{
return [];
}
}

View File

@@ -0,0 +1,125 @@
<?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\JobBundle\Form;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\JobBundle\Entity\CSPerson;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\ChillDateType;
class CSPersonDispositifsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('accompagnement', ChoiceType::class, [
'choices' => \array_combine(CSPerson::ACCOMPAGNEMENTS, CSPerson::ACCOMPAGNEMENTS),
'required' => false,
'label' => 'Accompagnement',
'multiple' => true,
'expanded' => true,
'choice_label' => fn ($k) => 'accompagnement.'.$k,
])
->add('accompagnementRQTHDate', ChillDateType::class, [
'label' => "Date d'accompagnement RQTH",
'required' => false,
])
->add('accompagnementComment', TextareaType::class, [
'label' => 'Accompagnement autre: précisions',
'required' => false,
])
->add('poleEmploiId', TextType::class, [
'label' => 'Identifiant pôle emploi',
'required' => false,
])
->add('poleEmploiInscriptionDate', ChillDateType::class, [
'label' => "Date d'inscription Pôle emploi",
'required' => false,
])
->add('cafId', TextType::class, [
'label' => 'Numéro allocataire CAF',
'required' => false,
])
->add('cafInscriptionDate', ChillDateType::class, [
'label' => "Date d'inscription à la CAF",
'required' => false,
])
->add('cERInscriptionDate', ChillDateType::class, [
'label' => 'Date CER',
'required' => false,
])
->add('pPAEInscriptionDate', ChillDateType::class, [
'label' => 'Date PPAE',
'required' => false,
])
->add('nEETEligibilite', ChoiceType::class, [
'label' => 'Éligibilité NEET',
'choices' => \array_combine(CSPerson::NEET_ELIGIBILITY, CSPerson::NEET_ELIGIBILITY),
'choice_label' => fn ($k) => 'neet_eligibility.'.$k,
'multiple' => false,
'expanded' => true,
'required' => false,
])
->add('cERSignataire', TextType::class, [
'label' => 'Signataire CER',
'required' => false,
])
->add('pPAESignataire', TextType::class, [
'label' => 'Signataire PPAE',
'required' => false,
])
->add('nEETCommissionDate', ChillDateType::class, [
'label' => 'Date commission NEET',
'required' => false,
])
->add('fSEMaDemarcheCode', TextType::class, [
'label' => 'Code "Ma démarche FSE"',
'required' => false,
])
->add('prescripteur', PickThirdpartyDynamicType::class, [
'required' => false,
'label' => 'Prescripteur',
])
->add('dispositifsNotes', TextareaType::class, [
'required' => false,
'label' => 'Notes',
])
->add('dateContratIEJ', ChillDateType::class, [
'required' => false,
'label' => ' Date du contrat dengagement IEJ',
])
->add('dateAvenantIEJ', ChillDateType::class, [
'required' => false,
'label' => " Date de l'avenant IEJ",
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => CSPerson::class]);
$resolver
->setDefined('center')
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class])
;
}
public function getBlockPrefix()
{
return 'job_bundle_csperson';
}
}

View File

@@ -0,0 +1,256 @@
<?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\JobBundle\Form;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\JobBundle\Entity\CSPerson;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Chill\DocStoreBundle\Form\StoredObjectType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Chill\PersonBundle\Form\Type\Select2MaritalStatusType;
class CSPersonPersonalSituationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// quick and dirty way to handle marital status...
->add('personMaritalStatus', Select2MaritalStatusType::class, ['required' => false, 'label' => 'État civil'])
->add('situationLogement', ChoiceType::class, [
'choices' => \array_combine(
CSPerson::SITUATIONS_LOGEMENTS,
CSPerson::SITUATIONS_LOGEMENTS
),
'choice_label' => fn ($k) => 'situation_logement.'.$k,
'required' => false,
'label' => 'Situation de logement',
'multiple' => false,
'expanded' => true,
])
->add('situationLogementPrecision', TextareaType::class, [
'label' => 'Précisions',
'required' => false,
])
->add('enfantACharge', IntegerType::class, [
'label' => 'Enfants à charge',
'required' => false,
])
->add('niveauMaitriseLangue', ChoiceType::class, [
'choices' => \array_combine(
CSPerson::NIVEAU_MAITRISE_LANGUE,
CSPerson::NIVEAU_MAITRISE_LANGUE
),
'choice_label' => fn ($k) => 'niveau_maitrise_langue.'.$k,
'multiple' => true,
'required' => false,
'expanded' => true,
'label' => 'Maitrise de la langue française',
])
->add('vehiculePersonnel', ChoiceType::class, [
'choices' => [
'Oui' => true,
'Non' => false,
],
'required' => false,
'multiple' => false,
])
->add('permisConduire', ChoiceType::class, [
'choices' => [
\array_combine(CSPerson::PERMIS_CONDUIRE, CSPerson::PERMIS_CONDUIRE),
],
'label' => 'Permis de conduire',
'required' => false,
'multiple' => true,
'expanded' => true,
'choice_label' => fn ($k) => 'permis_conduire.'.$k,
])
->add('situationProfessionnelle', ChoiceType::class, [
'choices' => \array_combine(CSPerson::SITUATION_PROFESSIONNELLE, CSPerson::SITUATION_PROFESSIONNELLE),
'required' => false,
'multiple' => false,
'expanded' => true,
'choice_label' => fn ($k) => 'situation_professionnelle.'.$k,
])
->add('dateFinDernierEmploi', ChillDateType::class, [
'label' => 'Date de la fin du dernier emploi',
'required' => false,
])
->add('typeContrat', ChoiceType::class, [
'choices' => \array_combine(CSPerson::TYPE_CONTRAT, CSPerson::TYPE_CONTRAT),
'label' => 'Type de contrat',
'required' => false,
'multiple' => true,
'expanded' => true,
'choice_label' => fn ($k) => 'type_contrat.'.$k,
])
->add('typeContratAide', TextType::class, [
'label' => 'Type de contrat aidé',
'required' => false,
])
->add('ressources', ChoiceType::class, [
'choices' => \array_combine(CSPerson::RESSOURCES, CSPerson::RESSOURCES),
'choice_label' => fn ($k) => 'ressource.'.$k,
'required' => false,
'multiple' => true,
'expanded' => true,
])
->add('ressourcesComment', TextareaType::class, [
'label' => 'Information autre ressource',
'required' => false,
])
->add('ressourceDate1Versement', ChillDateType::class, [
'label' => "Date du premier versement (si bénéficiaire d'une aide)",
'required' => false,
])
->add('cPFMontant', MoneyType::class, [
'label' => 'Montant CPF',
'required' => false,
])
->add('acompteDIF', MoneyType::class, [
'label' => 'Compte DIF',
'required' => false,
])
->add('handicapIs', ChoiceType::class, [
'label' => 'Handicap',
'required' => false,
'choices' => [
'Oui' => true,
'Non' => false,
],
'multiple' => false,
'expanded' => true,
])
->add('handicapNotes', TextareaType::class, [
'label' => 'Type de handicap',
'required' => false,
])
->add('handicapRecommandation', ChoiceType::class, [
'label' => 'Recommandation',
'required' => false,
'multiple' => false,
'expanded' => true,
'choices' => \array_combine(CSPerson::HANDICAP_RECOMMANDATIONS, CSPerson::HANDICAP_RECOMMANDATIONS),
'choice_label' => fn ($k) => 'handicap_recommandation.'.$k,
])
->add('handicapAccompagnement', PickThirdpartyDynamicType::class, [
'required' => false,
'multiple' => false,
])
->add('mobiliteMoyenDeTransport', ChoiceType::class, [
'required' => false,
'multiple' => true,
'expanded' => true,
'label' => 'Moyens de transports accessibles',
'choices' => \array_combine(
CSPerson::MOBILITE_MOYEN_TRANSPORT,
CSPerson::MOBILITE_MOYEN_TRANSPORT
),
'choice_label' => fn ($k) => 'moyen_transport.'.$k,
])
->add('mobiliteNotes', TextareaType::class, [
'required' => false,
'label' => 'Notes concernant la mobilité',
])
->add('documentCV', StoredObjectType::class, [
'label' => 'CV',
'required' => false,
'error_bubbling' => false,
])
->add('documentAgrementIAE', StoredObjectType::class, [
'label' => 'Document Agrément IAE',
'required' => false,
'error_bubbling' => false,
])
->add('documentRQTH', StoredObjectType::class, [
'label' => 'Document RQTH',
'required' => false,
'error_bubbling' => false,
])
->add('documentAttestationNEET', StoredObjectType::class, [
'label' => 'Attestation NEET',
'required' => false,
'error_bubbling' => false,
])
->add('documentCI', StoredObjectType::class, [
'label' => 'Carte d\'identité',
'required' => false,
'error_bubbling' => false,
])
->add('documentTitreSejour', StoredObjectType::class, [
'label' => 'Titre de séjour',
'required' => false,
'error_bubbling' => false,
])
->add('documentAttestationFiscale', StoredObjectType::class, [
'label' => 'Attestation fiscale',
'required' => false,
'error_bubbling' => false,
])
->add('documentPermis', StoredObjectType::class, [
'label' => 'Permis',
'required' => false,
'error_bubbling' => false,
])
->add('documentAttestationCAAF', StoredObjectType::class, [
'label' => 'Attestation CAF',
'required' => false,
'error_bubbling' => false,
])
->add('documentContraTravail', StoredObjectType::class, [
'label' => 'Contrat de travail',
'required' => false,
'error_bubbling' => false,
])
->add('documentAttestationFormation', StoredObjectType::class, [
'label' => 'Attestation formation',
'required' => false,
'error_bubbling' => false,
])
->add('documentQuittanceLoyer', StoredObjectType::class, [
'label' => 'Quittance de loyer',
'required' => false,
'error_bubbling' => false,
])
->add('documentFactureElectricite', StoredObjectType::class, [
'label' => 'Facture d\'électricité',
'required' => false,
'error_bubbling' => false,
])
->add('documentAttestationSecuriteSociale', StoredObjectType::class, [
'label' => 'Attestation de sécurité sociale',
'required' => false,
'error_bubbling' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => CSPerson::class]);
$resolver->setRequired('center')
->setAllowedTypes('center', [\Chill\MainBundle\Entity\Center::class])
;
}
public function getBlockPrefix()
{
return 'job_bundle_csperson';
}
}

View File

@@ -0,0 +1,70 @@
<?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\JobBundle\Form\CV;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Chill\JobBundle\Entity\CV\Experience;
class ExperienceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('poste', TextType::class, [
'required' => true,
'label' => 'Poste',
'label_attr' => [
'class' => 'required ',
],
])
->add('structure', TextType::class, [
'required' => false,
'label' => 'Nom de la structure',
])
->add('startDate', ChillDateType::class, [
'required' => false,
'label' => 'Date de début',
])
->add('endDate', ChillDateType::class, [
'required' => false,
'label' => 'Date de fin',
])
->add('contratType', ChoiceType::class, [
'required' => false,
'expanded' => true,
'multiple' => false,
'choices' => \array_combine(Experience::CONTRAT_TYPE, Experience::CONTRAT_TYPE),
'choice_label' => fn ($k) => 'xp_contrat_type.'.$k,
])
->add('notes', TextareaType::class, [
'label' => 'Notes',
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => Experience::class]);
}
public function getBlockPrefix()
{
return 'job_bundle_cv_experience';
}
}

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Form\CV;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\JobBundle\Entity\CV\Formation as F;
class FormationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'required' => true,
'label' => 'Nom de la formation',
'label_attr' => [
'class' => 'required ',
],
])
->add('organisme', TextType::class, [
'label' => 'Organisme',
'required' => false,
])
->add('startDate', ChillDateType::class, [
'required' => false,
'label' => 'Date de début',
])
->add('endDate', ChillDateType::class, [
'required' => false,
'label' => 'Date de fin',
])
->add('diplomaObtained', ChoiceType::class, [
'label' => 'Diplôme obtenu ?',
'required' => false,
'multiple' => false,
'expanded' => true,
'choices' => \array_combine(F::DIPLOMA_OBTAINED, F::DIPLOMA_OBTAINED),
'choice_label' => fn ($k) => 'diploma_obtained.'.$k,
])
->add('diplomaReconnue', ChoiceType::class, [
'label' => 'Diplôme reconnu en France ?',
'required' => false,
'multiple' => false,
'expanded' => true,
'choices' => \array_combine(F::DIPLOMA_RECONNU, F::DIPLOMA_RECONNU),
'choice_label' => fn ($k) => 'diploma_reconnu.'.$k,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => F::class]);
}
public function getBlockPrefix()
{
return 'job_bundle_cv_formation';
}
}

View File

@@ -0,0 +1,91 @@
<?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\JobBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Chill\JobBundle\Entity\CV;
use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\JobBundle\Form\CV\FormationType;
use Chill\JobBundle\Form\CV\ExperienceType;
use Chill\MainBundle\Form\Type\Select2LanguageType;
class CVType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('reportDate', ChillDateType::class, [
'label' => 'Date de rédaction du CV',
])
->add('formationLevel', ChoiceType::class, [
'label' => 'Niveau de formation',
'required' => true,
'multiple' => false,
'expanded' => true,
'choices' => \array_combine(CV::FORMATION_LEVEL, CV::FORMATION_LEVEL),
'choice_label' => fn ($k) => 'formation_level.'.$k,
])
->add('formationType', ChoiceType::class, [
'label' => 'Type de formation',
'required' => false,
'multiple' => false,
'expanded' => true,
'choices' => \array_combine(CV::FORMATION_TYPE, CV::FORMATION_TYPE),
'choice_label' => fn ($k) => 'formation_type.'.$k,
])
->add('spokenLanguages', Select2LanguageType::class, [
'required' => false,
'multiple' => true,
])
->add('notes', TextareaType::class, [
'label' => 'Note',
'required' => false,
])
->add('formations', ChillCollectionType::class, [
'label' => 'Formations',
'entry_type' => FormationType::class,
'allow_add' => true,
'allow_delete' => true,
'button_add_label' => 'Ajouter une formation',
'button_remove_label' => 'Retirer cette formation',
'required' => false,
'by_reference' => false,
'block_name' => 'formation_list',
])
->add('experiences', ChillCollectionType::class, [
'label' => 'Expériences',
'entry_type' => ExperienceType::class,
'allow_add' => true,
'allow_delete' => true,
'button_add_label' => 'Ajouter une expérience',
'button_remove_label' => 'Retirer cette expérience',
'required' => false,
'by_reference' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => CV::class]);
}
public function getBlockPrefix()
{
return 'job_bundle_cv';
}
}

View File

@@ -0,0 +1,155 @@
<?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\JobBundle\Form\ChoiceLoader;
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Chill\JobBundle\Entity\Rome\Appellation;
use Doctrine\ORM\EntityManagerInterface;
use Chill\JobBundle\Entity\Rome\Metier;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Lazy load third parties.
*/
class RomeAppellationChoiceLoader implements ChoiceLoaderInterface
{
/**
* @var Appellation[]
*/
protected $lazyLoadedAppellations = [];
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var \Chill\JobBundle\Repository\Rome\AppellationRepository
*/
protected $appellationRepository;
/**
* @var PartenaireRomeAppellation;
*/
protected $apiAppellation;
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* @var array<string, array{0: Appellation, 1: Metier}>
*/
private $toBeCreated = [];
public function __construct(
EntityManagerInterface $em,
PartenaireRomeAppellation $apiAppellation,
ValidatorInterface $validator
) {
$this->em = $em;
$this->apiAppellation = $apiAppellation;
$this->appellationRepository = $em->getRepository(Appellation::class);
$this->validator = $validator;
}
public function loadChoiceList($value = null): ChoiceListInterface
{
return new ArrayChoiceList($this->lazyLoadedAppellations, $value);
}
public function loadChoicesForValues($values, $value = null)
{
$choices = [];
$code = '';
foreach ($values as $v) {
if (null === $v || '' === $v) {
continue;
}
// start with "original-" ? then we load from api
if (str_starts_with($v, 'original-')) {
$code = \substr($v, \strlen('original-'));
$appellation = $this->appellationRepository->findOneBy(['code' => $code]);
if ($appellation) {
$metier = $appellation->getMetier();
}
if (null === $appellation) {
if (array_key_exists($v, $this->toBeCreated)) {
[$appellation, $metier] = $this->toBeCreated[$v];
}
}
} else {
$id = $v;
$appellation = $this->appellationRepository->find($id);
$metier = $appellation->getMetier();
}
if (null === $appellation && '' !== $code) {
$def = $this->apiAppellation->getAppellation($code);
$metier ??= $this->em->getRepository(Metier::class)
->findOneBy(['code' => $def['metier']['code']])
?? (new Metier())
->setCode($def['metier']['code'])
->setLibelle($def['metier']['libelle']);
$appellation = new Appellation();
$appellation
->setCode($def['code'])
->setLibelle($def['libelle'])
->setMetier($metier)
;
$errorsAppellation = $this->validator->validate($appellation);
$errorsMetier = $this->validator->validate($metier);
if (0 === $errorsAppellation->count() && 0 === $errorsMetier->count()) {
$this->toBeCreated[$v] = [$appellation, $metier];
$this->em->persist($appellation);
}
}
if ($this->em->contains($metier) and $this->em->contains($appellation)) {
$choices[] = $appellation;
}
}
return $choices;
}
public function loadValuesForChoices(array $choices, $value = null)
{
$values = [];
foreach ($choices as $choice) {
if (null === $choice) {
$values[] = null;
continue;
}
$id = \call_user_func($value, $choice);
$this->lazyLoadedAppellations[$id] = $choice;
}
return $this->loadChoiceList($value)->getValuesForChoices($choices);
}
}

View File

@@ -0,0 +1,66 @@
<?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\JobBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\JobBundle\Entity\Frein;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class FreinType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('reportDate', ChillDateType::class, [
'label' => 'Date du rapport',
])
->add('freinsPerso', ChoiceType::class, [
'label' => 'Freins identifiés liés à la situation personnelle',
'choices' => \array_combine(Frein::FREINS_PERSO, Frein::FREINS_PERSO),
'choice_label' => fn ($k) => 'freins_perso.'.$k,
'required' => false,
'expanded' => true,
'multiple' => true,
])
->add('freinsEmploi', ChoiceType::class, [
'label' => 'Freins identifiés liés à la situation professionnelle',
'choices' => \array_combine(Frein::FREINS_EMPLOI, Frein::FREINS_EMPLOI),
'choice_label' => fn ($k) => 'freins_emploi.'.$k,
'required' => false,
'expanded' => true,
'multiple' => true,
])
->add('notesPerso', TextareaType::class, [
'label' => 'Notes concernant la situation personnelle',
'required' => false,
])
->add('notesEmploi', TextareaType::class, [
'label' => 'Notes concernant l\'accès à l\'emploi',
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => Frein::class]);
}
public function getBlockPrefix()
{
return 'job_bundle_frein';
}
}

View File

@@ -0,0 +1,197 @@
<?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\JobBundle\Form;
use Chill\MainBundle\Form\Type\ChillPhoneNumberType;
use Chill\MainBundle\Form\Type\PickAddressType;
use Chill\ThirdPartyBundle\Form\Type\PickThirdpartyDynamicType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Chill\MainBundle\Form\Type\DateIntervalType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\JobBundle\Entity\Immersion;
class ImmersionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ('immersion' === $options['step']) {
$builder
->add('entreprise', PickThirdpartyDynamicType::class, [
'label' => "Identité de l'entreprise",
'required' => true,
'multiple' => false,
])
->add('domaineActivite', TextType::class, [
'label' => "Domaine d'activité",
'required' => true,
])
->add('tuteurName', TextType::class, [
'label' => 'Nom du tuteur',
'required' => true,
])
->add('tuteurFonction', TextType::class, [
'label' => 'Fonction du tuteur',
'required' => true,
])
->add('tuteurPhoneNumber', ChillPhoneNumberType::class, [
'label' => 'Téléphone du tuteur',
'required' => true,
])
->add('structureAccName', TextType::class, [
'label' => 'Nom de la structure',
'required' => false,
])
->add('structureAccPhonenumber', ChillPhoneNumberType::class, [
'label' => 'Téléphone de la structure',
'required' => false,
])
->add('structureAccEmail', EmailType::class, [
'label' => 'Email de la structure',
'required' => false,
])
->add('structureAccAddress', PickAddressType::class, [
'label' => 'Addresse de la structure d\'accompagnement',
'required' => false,
])
->add('posteTitle', TextType::class, [
'label' => 'Intitulé du poste',
'required' => true,
])
->add('posteLieu', TextType::class, [
'label' => "Lieu d'exercice",
'required' => true,
])
->add('debutDate', ChillDateType::class, [
'label' => "Date de début de l'immersion",
'required' => true,
])
->add('duration', DateIntervalType::class, [
'unit_choices' => [
'Weeks' => 'W',
'Months' => 'M',
'Days' => 'D',
],
'label' => "Durée de l'immersion",
'required' => true,
])
->add('horaire', TextareaType::class, [
'label' => 'Horaire du stagiaire',
'required' => true,
])
->add('objectifs', ChoiceType::class, [
'label' => 'Objectifs',
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => \array_combine(Immersion::OBJECTIFS, Immersion::OBJECTIFS),
'choice_label' => fn ($k) => 'immersion_objectif.'.$k,
])
->add('objectifsAutre', TextareaType::class, [
'label' => 'Précision sur les objectifs',
'required' => false,
])
->add('noteImmersion', TextareaType::class, [
'label' => 'Note',
'required' => false,
])
;
} elseif ('bilan' === $options['step']) {
$builder
->add('savoirEtre', ChoiceType::class, [
'label' => 'Savoir-être du jeune',
'required' => false,
'multiple' => true,
'expanded' => true,
'choices' => \array_combine(Immersion::SAVOIR_ETRE, Immersion::SAVOIR_ETRE),
'choice_label' => fn ($k) => 'immersion_savoir_etre.'.$k,
])
->add('savoirEtreNote', TextareaType::class, [
'label' => 'Note',
'required' => false,
])
->add('principalesActivites', TextareaType::class, [
'label' => 'Principales activités',
'required' => false,
])
->add('competencesAcquises', TextareaType::class, [
'label' => 'Compétences acquises',
'required' => false,
])
->add('competencesADevelopper', TextareaType::class, [
'label' => 'Compétences à développer',
'required' => false,
])
->add('noteBilan', TextareaType::class, [
'label' => 'Notes sur le bilan',
'required' => false,
])
;
foreach ([
['ponctualiteSalarie', Immersion::PONCTUALITE_SALARIE, 'Ponctualité du salarié'],
['assiduite', Immersion::ASSIDUITE, 'Assiduité'],
['interetActivite', Immersion::YES_NO_NSP, 'La personne sintéresse à lensemble des activités et membres de lentreprise'],
['integreRegle', Immersion::INTEGRE_REGLE, 'La personne a intégré les règles (les principes) de lentreprise'],
['espritInitiative', Immersion::YES_NO_NSP, 'La personne fait preuve desprit dinitiative'],
['organisation', Immersion::YES_NO_NSP, 'La personne a fait preuve dorganisation'],
['capaciteTravailEquipe', Immersion::YES_NO_NSP, 'Sa capacité à travailler en équipe'],
['styleVestimentaire', Immersion::YES_NO_NSP, 'Style vestimentaire adapté'],
['langageProf', Immersion::YES_NO_NSP, 'Langage professionnel'],
['appliqueConsigne', Immersion::YES_NO_NSP, 'Applique les consignes'],
['respectHierarchie', Immersion::YES_NO_NSP, 'Respecte les niveaux hiérarchiques'],
] as [$name, $choices, $label]) {
$builder
->add($name, ChoiceType::class, [
'label' => $label,
'multiple' => false,
'required' => false,
'expanded' => true,
'choices' => $choices,
'choice_label' => function ($el) use ($choices, $name) {
if (Immersion::YES_NO_NSP === $choices) {
return 'immersion_nsp.'.$el;
}
return 'immersion_'.$name.'.'.$el;
},
])
->add($name.'Note', TextareaType::class, [
'label' => 'Notes',
'required' => false,
]);
}
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => Immersion::class, 'step' => 'immersion']);
$resolver
->setAllowedValues('step', ['immersion', 'bilan'])
->setRequired('center')
->setAllowedTypes('center', \Chill\MainBundle\Entity\Center::class)
;
}
public function getBlockPrefix()
{
return 'job_bundle_immersion';
}
}

View File

@@ -0,0 +1,108 @@
<?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\JobBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Chill\MainBundle\Form\Type\ChillDateType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Chill\JobBundle\Entity\ProjetProfessionnel;
use Chill\JobBundle\Form\Type\PickRomeAppellationType;
class ProjetProfessionnelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('souhait', PickRomeAppellationType::class, [
'label' => 'Souhait',
'multiple' => true,
'required' => false,
])
->add('domaineActiviteSouhait', TextareaType::class, [
'label' => "Domaine d'activité souhaité",
'required' => false,
])
->add('reportDate', ChillDateType::class, [
'label' => 'Date',
'required' => true,
])
->add('typeContrat', ChoiceType::class, [
'label' => 'Type de contrat recherché',
'multiple' => true,
'expanded' => true,
'choices' => \array_combine(
ProjetProfessionnel::TYPE_CONTRAT,
ProjetProfessionnel::TYPE_CONTRAT
),
'choice_label' => fn ($k) => 'projet_prof.type_contrat.'.$k,
])
->add('typeContratNotes', TextareaType::class, [
'label' => 'Notes concernant le contrat recherché',
'required' => false,
])
->add('volumeHoraire', ChoiceType::class, [
'label' => 'Volume horaire',
'multiple' => true,
'expanded' => true,
'required' => true,
'choices' => \array_combine(
ProjetProfessionnel::VOLUME_HORAIRES,
ProjetProfessionnel::VOLUME_HORAIRES
),
'choice_label' => fn ($k) => 'projet_prof.volume_horaire.'.$k,
])
->add('volumeHoraireNotes', TextareaType::class, [
'label' => 'Notes concernant le volume horaire',
'required' => false,
])
->add('idee', TextareaType::class, [
'label' => 'Idée',
'required' => false,
])
->add('enCoursConstruction', TextareaType::class, [
'label' => 'En cours de construction',
'required' => false,
])
->add('valide', PickRomeAppellationType::class, [
'label' => 'Validé',
'multiple' => true,
'by_reference' => false,
'required' => false,
])
->add('domaineActiviteValide', TextareaType::class, [
'label' => "Domaine d'activité validé",
'required' => false,
])
->add('valideNotes', TextareaType::class, [
'label' => 'Validé (notes)',
'required' => false,
])
->add('projetProfessionnelNote', TextareaType::class, [
'label' => 'Notes concernant le projet professionnel',
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => ProjetProfessionnel::class]);
}
public function getBlockPrefix()
{
return 'job_bundle_projetprofessionnel';
}
}

View File

@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Form\Type;
use Chill\FranceTravailApiBundle\ApiHelper\PartenaireRomeAppellation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface;
use Chill\JobBundle\Entity\Rome\Appellation;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\OptionsResolver\Options;
use Chill\JobBundle\Form\ChoiceLoader\RomeAppellationChoiceLoader;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* Allow to grab an appellation.
*/
class PickRomeAppellationType extends AbstractType
{
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* @var UrlGeneratorInterface
*/
protected $urlGenerator;
// /**
// *
// * @var \Chill\JobBundle\Form\DataTransformer\RomeAppellationTransformer
// */
// protected $romeAppellationTransformer;
/**
* @var EntityManagerInterface
*/
protected $em;
/**
* @var PartenaireRomeAppellation
*/
protected $apiPartenaire;
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* PickRomeAppellationType constructor.
*/
public function __construct(
TranslatorInterface $translator,
UrlGeneratorInterface $urlGenerator,
EntityManagerInterface $em,
PartenaireRomeAppellation $apiPartenaire,
ValidatorInterface $validator
) {
$this->translator = $translator;
$this->urlGenerator = $urlGenerator;
$this->em = $em;
$this->apiPartenaire = $apiPartenaire;
$this->validator = $validator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ->addModelTransformer($this->romeAppellationTransformer)
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefault('class', Appellation::class)
->setDefault('choice_label', fn (Appellation $a) => $a->getLibelle())
->setDefault('placeholder', 'Choisir une appellation')
->setDefault('attr', ['class' => 'select2 '])
->setDefault('choice_loader', fn (Options $o) => new RomeAppellationChoiceLoader(
$this->em,
$this->apiPartenaire,
$this->validator
))
;
}
public function getParent()
{
return EntityType::class;
}
public function buildView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options)
{
$view->vars['attr']['data-rome-appellation-picker'] = true;
$view->vars['attr']['data-select-interactive-loading'] = true;
$view->vars['attr']['data-search-url'] = $this->urlGenerator
->generate('chill_france_travail_api_appellation_search', ['_format' => 'json']);
$view->vars['attr']['data-placeholder'] = 'Choisir une appellation';
$view->vars['attr']['data-no-results-label'] = $this->translator->trans('select2.no_results');
$view->vars['attr']['data-error-load-label'] = $this->translator->trans('select2.error_loading');
$view->vars['attr']['data-searching-label'] = $this->translator->trans('select2.searching');
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\JobBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
use Knp\Menu\MenuItem;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Chill\JobBundle\Security\Authorization\JobVoter;
class MenuBuilder implements LocalMenuBuilderInterface
{
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
public function buildMenu($menuId, MenuItem $menu, array $parameters)
{
/** @var \Chill\PersonBundle\Entity\Person $person */
$person = $parameters['person'];
if ($this->authorizationChecker->isGranted(JobVoter::REPORT_NEW, $person)) {
$menu->addChild('Situation personnelle', [
'route' => 'chill_crud_job_personal_situation_view',
'routeParameters' => [
'person' => $person->getId(),
],
])
->setExtras([
'order' => 50,
]);
$menu->addChild('Dispositifs', [
'route' => 'chill_crud_job_dispositifs_view',
'routeParameters' => [
'person' => $person->getId(),
],
])
->setExtras([
'order' => 51,
]);
}
$menu->addChild('Emploi', [
'route' => 'chill_job_report_index',
'routeParameters' => [
'person' => $person->getId(),
],
])
->setExtras([
'order' => 52,
]);
}
public static function getMenuIds(): array
{
return ['person'];
}
}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository;
/**
* CSPersonRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class CSPersonRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository\CV;
/**
* ExperienceRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ExperienceRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository\CV;
/**
* FormationRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class FormationRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository;
/**
* CVRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class CVRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository;
/**
* FreinRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class FreinRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository;
/**
* ImmersionRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ImmersionRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository;
/**
* ProjetProfessionnelRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ProjetProfessionnelRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository\Rome;
/**
* AppellationRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class AppellationRepository extends \Doctrine\ORM\EntityRepository {}

View File

@@ -0,0 +1,20 @@
<?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\JobBundle\Repository\Rome;
/**
* MetierRepository.
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class MetierRepository extends \Doctrine\ORM\EntityRepository {}

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