mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 09:18:24 +00:00 
			
		
		
		
	Compare commits
	
		
			105 Commits
		
	
	
		
			issue186_A
			...
			features/p
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 865cedb6ab | ||
| ca9ae3874c | |||
| 7fb4616aa6 | |||
| c04ccef4cd | |||
|  | d8afbc4a40 | ||
| 95edbc673c | |||
| 50be29308d | |||
| d1e0d997ac | |||
| 40fcb09082 | |||
| aa4a9e874a | |||
| 3abfdbf6fd | |||
| 07cc394abd | |||
| 99337c4aa6 | |||
| 04ccca092b | |||
| e1f01a47f8 | |||
| dc79e1a02d | |||
| 7a6117a264 | |||
| 1b1a25edc4 | |||
| 5ab7f7a37b | |||
| b174397823 | |||
| 9d35e4efbb | |||
| 0e9933ca41 | |||
| 0add757230 | |||
| 2bf65dfadb | |||
| a7ecd08d78 | |||
| b801b75eb9 | |||
| f1120af59e | |||
| 9d58356b90 | |||
| a7131653c9 | |||
| 9f3cd943cb | |||
|  | 970635dfad | ||
|  | c8297b2230 | ||
| 994cce41e5 | |||
| 86e41fb877 | |||
| f8b97b96f4 | |||
| 27907e7558 | |||
|  | 38712de462 | ||
| fd7c7388d9 | |||
| ef55d2cf7f | |||
|  | e2633a2a79 | ||
|  | 1cfc29caf7 | ||
|  | 5b72eeb147 | ||
|  | 06daf35e96 | ||
| 38bff2e42f | |||
| e68a5405a1 | |||
| 8bd20c9c78 | |||
| 54997e5893 | |||
|  | 35d35c9f40 | ||
| 78ca61c82e | |||
| b68de1f2e9 | |||
| 68ecff253a | |||
| e95d8fbc7a | |||
| efdfd10e49 | |||
|  | 000ae6c2cb | ||
|  | 7c26f0a56c | ||
| f827e50431 | |||
| 41617295c1 | |||
| b2c1d75fc5 | |||
| 17c3ecbabe | |||
| 51399b21b9 | |||
| c40019da8f | |||
| 1df759e970 | |||
| 807d3674fc | |||
| af740fd87d | |||
| 45dc8ed661 | |||
| ecc8b929ca | |||
|  | c378f59f5a | ||
| 4fd6d38187 | |||
|  | f3a7556bca | ||
|  | 14db7265fe | ||
|  | f22461af1b | ||
|  | 1b709d39a4 | ||
|  | 5daf5cbe84 | ||
|  | 331cdf13ca | ||
| cbadcb4980 | |||
|  | 1551ea796d | ||
|  | 4c025184b4 | ||
|  | 6f2c219e3b | ||
|  | 550aaacaab | ||
|  | dd78e9759c | ||
|  | 1b36d9b1ab | ||
|  | e14a125bf3 | ||
|  | 9375c2cf86 | ||
|  | db77224b9f | ||
| 0e1bbbfee9 | |||
|  | 6a42edd460 | ||
|  | 76d945f2a7 | ||
|  | 7d13bc7cfe | ||
|  | 66b54bad31 | ||
|  | 9d32ccbcad | ||
|  | 63c3b5a970 | ||
|  | 03a7ec389b | ||
|  | f39fe05dd5 | ||
|  | 6c126b0581 | ||
|  | 5ee824d1c2 | ||
|  | 6d20476b81 | ||
|  | 2b5ed5e5a3 | ||
|  | 6f68349f57 | ||
|  | d513be2fce | ||
|  | aff140230c | ||
|  | 7e1b7b7e9f | ||
|  | 878ee5d9c7 | ||
|  | 7b67ef5db2 | ||
|  | d806a074a7 | ||
|  | 14a9d9c739 | 
| @@ -3,24 +3,18 @@ | |||||||
| namespace Chill\ActivityBundle\DataFixtures\ORM; | namespace Chill\ActivityBundle\DataFixtures\ORM; | ||||||
|  |  | ||||||
| use Doctrine\Common\DataFixtures\AbstractFixture; | use Doctrine\Common\DataFixtures\AbstractFixture; | ||||||
| use Doctrine\Common\DataFixtures\OrderedFixtureInterface; | use Doctrine\Common\DataFixtures\DependentFixtureInterface; | ||||||
| use Doctrine\Persistence\ObjectManager; |  | ||||||
| use Symfony\Component\Intl\Intl; |  | ||||||
| use Chill\MainBundle\Entity\Notification; |  | ||||||
| use Chill\ActivityBundle\Entity\Activity; | use Chill\ActivityBundle\Entity\Activity; | ||||||
| use Chill\MainBundle\DataFixtures\ORM\LoadAbstractNotificationsTrait; | use Chill\MainBundle\DataFixtures\ORM\LoadAbstractNotificationsTrait; | ||||||
|  | use Chill\ActivityBundle\DataFixtures\ORM\LoadActivity; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Load notififications into database |  * Load notififications into database | ||||||
|  */ |  */ | ||||||
| class LoadActivityNotifications extends AbstractFixture implements OrderedFixtureInterface | class LoadActivityNotifications extends AbstractFixture implements DependentFixtureInterface | ||||||
| { | { | ||||||
|     use LoadAbstractNotificationsTrait; |     use LoadAbstractNotificationsTrait; | ||||||
|  |  | ||||||
|     public function getOrder() { |  | ||||||
|         return 16500; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public $notifs = [ |     public $notifs = [ | ||||||
|         [ |         [ | ||||||
|             'message' => 'Hello !', |             'message' => 'Hello !', | ||||||
| @@ -35,5 +29,10 @@ class LoadActivityNotifications extends AbstractFixture implements OrderedFixtur | |||||||
|         ] |         ] | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|  |     public function getDependencies() | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             LoadActivity::class, | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | |||||||
| use Symfony\Component\DependencyInjection\Reference; | use Symfony\Component\DependencyInjection\Reference; | ||||||
| use Chill\MainBundle\Routing\MenuComposer; | use Chill\MainBundle\Routing\MenuComposer; | ||||||
| use Symfony\Component\DependencyInjection\Definition; | use Symfony\Component\DependencyInjection\Definition; | ||||||
|  | use Symfony\Component\DependencyInjection\Alias; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
| @@ -49,29 +50,32 @@ class CRUDControllerCompilerPass implements CompilerPassInterface | |||||||
|     private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void |     private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void | ||||||
|     { |     { | ||||||
|         $controllerClass = $crudEntry['controller']; |         $controllerClass = $crudEntry['controller']; | ||||||
|  |  | ||||||
|         $controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller'; |         $controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller'; | ||||||
|  |  | ||||||
|         if ($container->hasDefinition($controllerClass)) { |         // create config parameter in container | ||||||
|             $controller = $container->getDefinition($controllerClass); |  | ||||||
|             $container->removeDefinition($controllerClass); |  | ||||||
|             $alreadyDefined = true; |  | ||||||
|         } else { |  | ||||||
|             $controller = new Definition($controllerClass); |  | ||||||
|             $alreadyDefined = false; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $controller->addTag('controller.service_arguments'); |  | ||||||
|         if (FALSE === $alreadyDefined) { |  | ||||||
|             $controller->setAutoconfigured(true); |  | ||||||
|             $controller->setPublic(true); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; |         $param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name']; | ||||||
|         $container->setParameter($param, $crudEntry); |         $container->setParameter($param, $crudEntry); | ||||||
|         $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); |  | ||||||
|  |  | ||||||
|         $container->setDefinition($controllerServiceName, $controller); |         if ($container->hasDefinition($controllerClass)) { | ||||||
|  |             // create an alias to not to re-create the service | ||||||
|  |             $alias = new Alias($controllerClass, true); | ||||||
|  |             $container->setAlias($controllerServiceName, $alias); | ||||||
|  |  | ||||||
|  |             // add the "addMethodCall" | ||||||
|  |             $container->getDefinition($controllerClass) | ||||||
|  |                 ->addMethodCall('setCrudConfig', ['%'.$param.'%']); | ||||||
|  |  | ||||||
|  |         } else { | ||||||
|  |             $controller = new Definition($controllerClass); | ||||||
|  |  | ||||||
|  |             $controller->addTag('controller.service_arguments'); | ||||||
|  |             $controller->setAutoconfigured(true); | ||||||
|  |             $controller->setPublic(true); | ||||||
|  |  | ||||||
|  |             $controller->addMethodCall('setCrudConfig', ['%'.$param.'%']); | ||||||
|  |  | ||||||
|  |             $container->setDefinition($controllerServiceName, $controller); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
| } | } | ||||||
|   | |||||||
| @@ -359,9 +359,10 @@ class ApiController extends AbstractCRUDController | |||||||
|      * 6. validate the base entity (not the deserialized one). Groups are fetched from getValidationGroups, validation is perform by `validate` |      * 6. validate the base entity (not the deserialized one). Groups are fetched from getValidationGroups, validation is perform by `validate` | ||||||
|      * 7. run onAfterValidation |      * 7. run onAfterValidation | ||||||
|      * 8. if errors, return a 422 response with errors |      * 8. if errors, return a 422 response with errors | ||||||
|      * 9. flush the data  |      * 9. if $forcePersist === true, persist the entity | ||||||
|      * 10. run onAfterFlush |      * 10. flush the data  | ||||||
|      * 11. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity |      * 11. run onAfterFlush | ||||||
|  |      * 12. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity | ||||||
|      * |      * | ||||||
|      * @param string action |      * @param string action | ||||||
|      * @param mixed id |      * @param mixed id | ||||||
| @@ -370,11 +371,12 @@ class ApiController extends AbstractCRUDController | |||||||
|      * @param string $property the name of the property. This will be used to make a `add+$property` and `remove+$property` method |      * @param string $property the name of the property. This will be used to make a `add+$property` and `remove+$property` method | ||||||
|      * @param string $postedDataType the type of the posted data (the content) |      * @param string $postedDataType the type of the posted data (the content) | ||||||
|      * @param string $postedDataContext a context to deserialize posted data (the content) |      * @param string $postedDataContext a context to deserialize posted data (the content) | ||||||
|  |      * @param bool $forcePersist force to persist the created element (only for POST request) | ||||||
|      * @throw BadRequestException if unable to deserialize the posted data |      * @throw BadRequestException if unable to deserialize the posted data | ||||||
|      * @throw BadRequestException if the method is not POST or DELETE |      * @throw BadRequestException if the method is not POST or DELETE | ||||||
|      * |      * | ||||||
|      */  |      */  | ||||||
|     protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, $postedDataContext = []): Response |     protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, array $postedDataContext = [], bool $forcePersist = false): Response | ||||||
|     { |     { | ||||||
|         $entity = $this->getEntity($action, $id, $request); |         $entity = $this->getEntity($action, $id, $request); | ||||||
|  |  | ||||||
| @@ -429,6 +431,10 @@ class ApiController extends AbstractCRUDController | |||||||
|             return $this->json($errors, 422); |             return $this->json($errors, 422); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ($forcePersist && $request->getMethod() === Request::METHOD_POST) { | ||||||
|  |             $this->getDoctrine()->getManager()->persist($postedData); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $this->getDoctrine()->getManager()->flush(); |         $this->getDoctrine()->getManager()->flush(); | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -142,11 +142,11 @@ class CRUDRoutesLoader extends Loader | |||||||
|     protected function loadApi(array $crudConfig): RouteCollection |     protected function loadApi(array $crudConfig): RouteCollection | ||||||
|     { |     { | ||||||
|         $collection = new RouteCollection(); |         $collection = new RouteCollection(); | ||||||
|         $controller ='csapi_'.$crudConfig['name'].'_controller'; |         $controller = 'csapi_'.$crudConfig['name'].'_controller'; | ||||||
|  |  | ||||||
|         foreach ($crudConfig['actions'] as $name => $action) { |         foreach ($crudConfig['actions'] as $name => $action) { | ||||||
|             // filter only on single actions |             // filter only on single actions | ||||||
|             $singleCollection = $action['single-collection'] ?? $name === '_entity' ? 'single' : NULL; |             $singleCollection = $action['single_collection'] ?? $name === '_entity' ? 'single' : NULL; | ||||||
|             if ('collection' === $singleCollection) { |             if ('collection' === $singleCollection) { | ||||||
| //                continue; | //                continue; | ||||||
|             } |             } | ||||||
| @@ -171,7 +171,7 @@ class CRUDRoutesLoader extends Loader | |||||||
|             // path are rewritten |             // path are rewritten | ||||||
|             // if name === 'default', we rewrite it to nothing :-) |             // if name === 'default', we rewrite it to nothing :-) | ||||||
|             $localName = \in_array($name, [ '_entity', '_index' ]) ? '' : '/'.$name; |             $localName = \in_array($name, [ '_entity', '_index' ]) ? '' : '/'.$name; | ||||||
|             if ('collection' === $action['single-collection'] || '_index' === $name) { |             if ('collection' === $action['single_collection'] || '_index' === $name) { | ||||||
|                 $localPath = $action['path'] ?? $localName.'.{_format}'; |                 $localPath = $action['path'] ?? $localName.'.{_format}'; | ||||||
|             } else { |             } else { | ||||||
|                 $localPath = $action['path'] ?? '/{id}'.$localName.'.{_format}'; |                 $localPath = $action['path'] ?? '/{id}'.$localName.'.{_format}'; | ||||||
|   | |||||||
| @@ -195,7 +195,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, | |||||||
|             ->prependExtensionConfig( |             ->prependExtensionConfig( | ||||||
|                 'doctrine', |                 'doctrine', | ||||||
|                 [ |                 [ | ||||||
|                    'dbal' => [ |                     'dbal' => [ | ||||||
|  |                         // ignore views: | ||||||
|  |                         'schema_filter' => '~^(?!view_)~', | ||||||
|                         // This is mandatory since we are using postgis as database. |                         // This is mandatory since we are using postgis as database. | ||||||
|                         'mapping_types' => [ |                         'mapping_types' => [ | ||||||
|                             'geometry' => 'string', |                             'geometry' => 'string', | ||||||
| @@ -277,7 +279,8 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, | |||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_GET => true, |                                 Request::METHOD_GET => true, | ||||||
|                                 Request::METHOD_POST => true, |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_HEAD => true |                                 Request::METHOD_HEAD => true, | ||||||
|  |                                 Request::METHOD_PATCH => true | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                     ] |                     ] | ||||||
| @@ -319,7 +322,8 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, | |||||||
|                         '_entity' => [ |                         '_entity' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_GET => true, |                                 Request::METHOD_GET => true, | ||||||
|                                 Request::METHOD_HEAD => true |                                 Request::METHOD_HEAD => true, | ||||||
|  |                                 Request::METHOD_POST => true, | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                     ] |                     ] | ||||||
|   | |||||||
| @@ -205,7 +205,7 @@ class Configuration implements ConfigurationInterface | |||||||
|                                             ->ignoreExtraKeys(false) |                                             ->ignoreExtraKeys(false) | ||||||
|                                             ->info('the requirements for the route. Will be set to `[ \'id\' => \'\d+\' ]` if left empty.') |                                             ->info('the requirements for the route. Will be set to `[ \'id\' => \'\d+\' ]` if left empty.') | ||||||
|                                         ->end() |                                         ->end() | ||||||
|                                         ->enumNode('single-collection') |                                         ->enumNode('single_collection') | ||||||
|                                             ->values(['single', 'collection']) |                                             ->values(['single', 'collection']) | ||||||
|                                             ->defaultValue('single') |                                             ->defaultValue('single') | ||||||
|                                             ->info('indicates if the returned object is a single element or a collection. '. |                                             ->info('indicates if the returned object is a single element or a collection. '. | ||||||
|   | |||||||
| @@ -116,7 +116,7 @@ class Address | |||||||
|      * @ORM\Column(type="date") |      * @ORM\Column(type="date") | ||||||
|      * @groups({"write"}) |      * @groups({"write"}) | ||||||
|      */ |      */ | ||||||
|     private $validFrom; |     private \DateTime $validFrom; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Indicates when the address ends. Used to build an history |      * Indicates when the address ends. Used to build an history | ||||||
| @@ -127,7 +127,7 @@ class Address | |||||||
|      * @ORM\Column(type="date", nullable=true) |      * @ORM\Column(type="date", nullable=true) | ||||||
|      * @groups({"write"}) |      * @groups({"write"}) | ||||||
|      */ |      */ | ||||||
|     private $validTo; |     private ?\DateTime $validTo = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * True if the address is a "no address", aka homeless person, ... |      * True if the address is a "no address", aka homeless person, ... | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ class PostalCode | |||||||
|      * @ORM\Id |      * @ORM\Id | ||||||
|      * @ORM\Column(name="id", type="integer") |      * @ORM\Column(name="id", type="integer") | ||||||
|      * @ORM\GeneratedValue(strategy="AUTO") |      * @ORM\GeneratedValue(strategy="AUTO") | ||||||
|      * @groups({"read"}) |      * @groups({"write", "read"}) | ||||||
|      */ |      */ | ||||||
|     private $id; |     private $id; | ||||||
|  |  | ||||||
| @@ -34,7 +34,7 @@ class PostalCode | |||||||
|      * @var string |      * @var string | ||||||
|      * |      * | ||||||
|      * @ORM\Column(type="string", length=255, name="label") |      * @ORM\Column(type="string", length=255, name="label") | ||||||
|      * @groups({"read"}) |      * @groups({"write", "read"}) | ||||||
|      */ |      */ | ||||||
|     private $name; |     private $name; | ||||||
|  |  | ||||||
| @@ -42,7 +42,7 @@ class PostalCode | |||||||
|      * @var string |      * @var string | ||||||
|      * |      * | ||||||
|      * @ORM\Column(type="string", length=100) |      * @ORM\Column(type="string", length=100) | ||||||
|      * @groups({"read"}) |      * @groups({"write", "read"}) | ||||||
|      */ |      */ | ||||||
|     private $code; |     private $code; | ||||||
|  |  | ||||||
| @@ -50,10 +50,17 @@ class PostalCode | |||||||
|      * @var Country |      * @var Country | ||||||
|      * |      * | ||||||
|      * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Country") |      * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Country") | ||||||
|      * @groups({"read"}) |      * @groups({"write", "read"}) | ||||||
|      */ |      */ | ||||||
|     private $country; |     private $country; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @var integer | ||||||
|  |      * | ||||||
|  |      * @ORM\Column(name="origin", type="integer", nullable=true) | ||||||
|  |      * @groups({"write", "read"}) | ||||||
|  |      */ | ||||||
|  |     private $origin = 0; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Get id |      * Get id | ||||||
| @@ -65,6 +72,32 @@ class PostalCode | |||||||
|         return $this->id; |         return $this->id; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set origin | ||||||
|  |      * | ||||||
|  |      * @param int $origin | ||||||
|  |      * | ||||||
|  |      * @return PostalCode | ||||||
|  |      */ | ||||||
|  |     public function setOrigin($origin) | ||||||
|  |     { | ||||||
|  |         $this->origin = $origin; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get origin | ||||||
|  |      * | ||||||
|  |      * @return int | ||||||
|  |      */ | ||||||
|  |     public function getOrigin() | ||||||
|  |     { | ||||||
|  |         return $this->origin; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Set name |      * Set name | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -11,13 +11,16 @@ | |||||||
|  * |  * | ||||||
|  * Do not take time into account |  * Do not take time into account | ||||||
|  * |  * | ||||||
|  * **Experimental** |  | ||||||
|  */ |  */ | ||||||
| const dateToISO = (date) => { | const dateToISO = (date) => { | ||||||
|  |   if (null === date) { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return [ |   return [ | ||||||
|     this.$store.state.startDate.getFullYear(), |     date.getFullYear(), | ||||||
|     (this.$store.state.startDate.getMonth() + 1).toString().padStart(2, '0'), |     (date.getMonth() + 1).toString().padStart(2, '0'), | ||||||
|     this.$store.state.startDate.getDate().toString().padStart(2, '0') |     date.getDate().toString().padStart(2, '0') | ||||||
|   ].join('-'); |   ].join('-'); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -36,10 +39,12 @@ const ISOToDate = (str) => { | |||||||
| /** | /** | ||||||
|  * Return a date object from iso string formatted as YYYY-mm-dd:HH:MM:ss+01:00 |  * Return a date object from iso string formatted as YYYY-mm-dd:HH:MM:ss+01:00 | ||||||
|  * |  * | ||||||
|  * **Experimental** |  | ||||||
|  */ |  */ | ||||||
| const ISOToDatetime = (str) => { | const ISOToDatetime = (str) => { | ||||||
|   console.log(str); |   if (null === str) { | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   let  |   let  | ||||||
|     [cal, times] = str.split('T'), |     [cal, times] = str.split('T'), | ||||||
|     [year, month, date] = cal.split('-'), |     [year, month, date] = cal.split('-'), | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ | |||||||
|         &.bt-cancel::before, |         &.bt-cancel::before, | ||||||
|         &.bt-view::before, |         &.bt-view::before, | ||||||
|         &.bt-show::before { |         &.bt-show::before { | ||||||
|             font: normal normal normal 14px/1 FontAwesome; |             font: normal normal normal 14px/1 ForkAwesome; | ||||||
|             margin-right: 0.5em; |             margin-right: 0.5em; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|   | |||||||
| @@ -0,0 +1,10 @@ | |||||||
|  | /*  | ||||||
|  |  * when an alert is the first child of the page, with a banner, we do not want the alert to be merged with the banner | ||||||
|  |  */ | ||||||
|  | div.container.content { | ||||||
|  |   & > div { | ||||||
|  |     div.alert:nth-child(2) { | ||||||
|  |       margin-top: 1rem; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  |  | ||||||
|  | div.alert.alert-with-actions { | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: row; | ||||||
|  |  | ||||||
|  |   ul.record_actions { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: column; | ||||||
|  |     padding: 0; | ||||||
|  |     margin: 0; | ||||||
|  |  | ||||||
|  |     li:nth-child(1n+2) { | ||||||
|  |       margin-top: 0.5rem;  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     li { | ||||||
|  |       margin-right: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @media screen and (max-width: 1050px) { | ||||||
|  |       flex-direction: column; | ||||||
|  |  | ||||||
|  |       ul.record_actions { | ||||||
|  |         margin-top: 1rem; | ||||||
|  |         text-align: center; | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -7,6 +7,10 @@ | |||||||
| */ | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @import 'alert-first-child'; | ||||||
|  | @import 'alert-with-actions'; | ||||||
|  |  | ||||||
|  |  | ||||||
| /*  [hack]  /!\ Contourne le positionnement problématique du div#content_conainter suivant, | /*  [hack]  /!\ Contourne le positionnement problématique du div#content_conainter suivant, | ||||||
|  *  car sa position: relative le place au-dessus du bandeau et les liens sont incliquables  */ |  *  car sa position: relative le place au-dessus du bandeau et les liens sont incliquables  */ | ||||||
| div.subheader { | div.subheader { | ||||||
| @@ -248,6 +252,9 @@ div.address_form { | |||||||
|          display: flex; |          display: flex; | ||||||
|          flex-direction: column; |          flex-direction: column; | ||||||
|          flex-grow: 1; |          flex-grow: 1; | ||||||
|  |          div.custom-address, div.custom-postcode { | ||||||
|  |              padding: 12px; | ||||||
|  |          } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       div.address_form__select__map { |       div.address_form__select__map { | ||||||
| @@ -255,13 +262,20 @@ div.address_form { | |||||||
|          div#address_map { |          div#address_map { | ||||||
|             height:400px; |             height:400px; | ||||||
|             width:400px; |             width:400px; | ||||||
|  |             input { | ||||||
|  |                border: 1px solid #999; | ||||||
|  |             } | ||||||
|          } |          } | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|    div.address_form__more { |    div.address_form__more { | ||||||
|  |       & > div { | ||||||
|  |          display: flex; | ||||||
|  |          & > label { | ||||||
|  |             width: 30%; | ||||||
|  |          } | ||||||
|  |       } | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,33 +1,73 @@ | |||||||
| <template> | <template> | ||||||
|  |  | ||||||
|    <div v-if="address.address"> |    <div class='person__address__create'> | ||||||
|       {{ address.address.street }}, {{ address.address.streetNumber }} |       <div> | ||||||
|  |          <h2 v-if="!edit">{{ $t('create_a_new_address') }}</h2> | ||||||
|  |          <h2 v-else>{{ $t('edit_a_new_address') }}</h2> | ||||||
|  |          <add-address | ||||||
|  |             @addNewAddress="addNewAddress"> | ||||||
|  |          </add-address> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div> | ||||||
|  |          <show-address | ||||||
|  |             v-if="address" | ||||||
|  |             v-bind:address="address"> | ||||||
|  |          </show-address> | ||||||
|  |       </div> | ||||||
|    </div> |    </div> | ||||||
|    <div v-if="address.city"> |    <div v-if="!edit" class='person__address__valid'> | ||||||
|       {{ address.city.code }} {{ address.city.name }} |       <h2>{{ $t('date') }}</h2> | ||||||
|    </div> |       <input | ||||||
|    <div v-if="address.country"> |          type="date" | ||||||
|       {{ address.country.name }} |          name="validFrom" | ||||||
|  |          :placeholder="$t('validFrom')" | ||||||
|  |          v-model="validFrom"/> | ||||||
|  |       <div v-if="errors.length > 0"> | ||||||
|  |          {{ errors }} | ||||||
|  |       </div> | ||||||
|  |    </div> | ||||||
|  |  | ||||||
|  |    <div> | ||||||
|  |       <ul class="record_actions sticky-form-buttons"> | ||||||
|  |          <li class="cancel"> | ||||||
|  |             <a :href=backUrl class="sc-button bt-cancel">{{ $t('back_to_the_list') }}</a> | ||||||
|  |          </li> | ||||||
|  |          <li v-if="!edit"> | ||||||
|  |             <button type="submit" class="sc-button bt-update centered" @click="addToPerson"> | ||||||
|  |                {{ $t('add_an_address_to_person') }} | ||||||
|  |             </button> | ||||||
|  |          </li> | ||||||
|  |       </ul> | ||||||
|    </div> |    </div> | ||||||
|  |  | ||||||
|    <add-address |  | ||||||
|       @addNewAddress="addNewAddress"> |  | ||||||
|    </add-address> |  | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| import { mapState } from 'vuex'; |  | ||||||
|  |  | ||||||
| import AddAddress from '../_components/AddAddress.vue'; | import AddAddress from '../_components/AddAddress.vue'; | ||||||
|  | import ShowAddress from '../_components/ShowAddress.vue'; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|    name: 'App', |    name: 'App', | ||||||
|    components: { |    components: { | ||||||
|       AddAddress |       AddAddress, | ||||||
|  |       ShowAddress | ||||||
|  |    }, | ||||||
|  |    data() { | ||||||
|  |       return { | ||||||
|  |          edit: window.mode === 'edit', | ||||||
|  |          personId: window.personId, | ||||||
|  |          addressId: window.addressId, | ||||||
|  |          backUrl: `/fr/person/${window.personId}/address/list`, //TODO better way to pass this | ||||||
|  |          validFrom: new Date().toISOString().split('T')[0] | ||||||
|  |       } | ||||||
|    }, |    }, | ||||||
|    computed: { |    computed: { | ||||||
|       address() { |       address() { | ||||||
|          return this.$store.state.address; |          return this.$store.state.address; | ||||||
|  |       }, | ||||||
|  |       errors() { | ||||||
|  |          return this.$store.state.errorMsg; | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|    methods: { |    methods: { | ||||||
| @@ -36,9 +76,9 @@ export default { | |||||||
|  |  | ||||||
|          let newAddress = { |          let newAddress = { | ||||||
|             'isNoAddress': address.isNoAddress, |             'isNoAddress': address.isNoAddress, | ||||||
|             'street': address.selected.address.street, |             'street': address.isNoAddress ? '' : address.street, | ||||||
|             'streetNumber': address.selected.address.streetNumber, |             'streetNumber': address.isNoAddress ? '' : address.streetNumber, | ||||||
|             'postcode': {'id': address.selected.city.id }, |             'postcode': {'id': address.selected.city.id}, | ||||||
|             'floor': address.floor, |             'floor': address.floor, | ||||||
|             'corridor': address.corridor, |             'corridor': address.corridor, | ||||||
|             'steps': address.steps, |             'steps': address.steps, | ||||||
| @@ -49,12 +89,47 @@ export default { | |||||||
|          }; |          }; | ||||||
|  |  | ||||||
|          if (address.selected.address.point !== undefined){ |          if (address.selected.address.point !== undefined){ | ||||||
|             newAddress = Object.assign(newAddress, {'point': address.selected.address.point.coordinates}); |             newAddress = Object.assign(newAddress, { | ||||||
|  |                'point': address.selected.address.point.coordinates | ||||||
|  |             }); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (address.writeNewPostalCode){ | ||||||
|  |             let newPostalCode = address.newPostalCode; | ||||||
|  |             newPostalCode = Object.assign(newPostalCode, { | ||||||
|  |                 'country': {'id': address.selected.country.id }, | ||||||
|  |             }); | ||||||
|  |             newAddress = Object.assign(newAddress, { | ||||||
|  |                'newPostalCode': newPostalCode | ||||||
|  |             }); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (this.edit){ | ||||||
|  |             this.$store.dispatch('updateAddress', { | ||||||
|  |                addressId: this.addressId, | ||||||
|  |                newAddress: newAddress | ||||||
|  |             }); | ||||||
|  |          } else { | ||||||
|  |             this.$store.dispatch('addAddress', newAddress); | ||||||
|          } |          } | ||||||
|  |  | ||||||
|          this.$store.dispatch('addAddress', newAddress); |  | ||||||
|          modal.showModal = false; |          modal.showModal = false; | ||||||
|  |       }, | ||||||
|  |       addToPerson() { | ||||||
|  |          this.$store.dispatch('addDateToAddressAndAddressToPerson', { | ||||||
|  |             personId: this.personId, | ||||||
|  |             addressId: this.$store.state.address.address_id, | ||||||
|  |             body: { validFrom: {datetime: `${this.validFrom}T00:00:00+0100`}} | ||||||
|  |          }) | ||||||
|  |       }, | ||||||
|  |       getEditAddress() { | ||||||
|  |          this.$store.dispatch('getEditAddress', this.addressId); | ||||||
|       } |       } | ||||||
|    } |    }, | ||||||
|  |    mounted() { | ||||||
|  |       if (this.edit) { | ||||||
|  |          this.getEditAddress(); | ||||||
|  |       } | ||||||
|  |    }, | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -1,20 +1,32 @@ | |||||||
| const addressMessages = { | const addressMessages = { | ||||||
|    fr: { |    fr: { | ||||||
|       add_an_address_title: 'Ajouter une adresse', |       add_an_address_title: 'Créer une adresse', | ||||||
|  |       edit_an_address_title: 'Modifier une adresse', | ||||||
|  |       create_a_new_address: 'Créer une nouvelle adresse', | ||||||
|  |       edit_a_new_address: 'Modifier l\'adresse', | ||||||
|       select_an_address_title: 'Sélectionner une adresse', |       select_an_address_title: 'Sélectionner une adresse', | ||||||
|       fill_an_address: 'Compléter l\'adresse', |       fill_an_address: 'Compléter l\'adresse', | ||||||
|       select_country: 'Choisir le pays', |       select_country: 'Choisir le pays', | ||||||
|       select_city: 'Choisir une localité', |       select_city: 'Choisir une localité', | ||||||
|       select_address: 'Choisir une adresse', |       select_address: 'Choisir une adresse', | ||||||
|       create_address: 'Appuyer sur "Entrée" pour créer une nouvelle adresse', |       create_address: 'Adresse inconnue. Cliquez ici pour créer une nouvelle adresse', | ||||||
|       isNoAddress: 'Pas d\'adresse complète', |       isNoAddress: 'Pas d\'adresse complète', | ||||||
|  |       street: 'Nom de rue', | ||||||
|  |       streetNumber: 'Numéro', | ||||||
|       floor: 'Étage', |       floor: 'Étage', | ||||||
|       corridor: 'Couloir', |       corridor: 'Couloir', | ||||||
|       steps: 'Escalier', |       steps: 'Escalier', | ||||||
|       flat: 'Appartement', |       flat: 'Appartement', | ||||||
|       buildingName: 'Nom du batiment', |       buildingName: 'Nom du batiment', | ||||||
|       extra: 'Complément d\'adresse', |       extra: 'Complément d\'adresse', | ||||||
|       distribution: 'Service particulier de distribution' |       distribution: 'Service particulier de distribution', | ||||||
|  |       create_postal_code: 'Localité inconnue. Cliquez ici pour créer une nouvelle localité', | ||||||
|  |       postalCode_name: 'Nom de la localité', | ||||||
|  |       postalCode_code: 'Code postal de la localité', | ||||||
|  |       date: 'Date de la nouvelle adresse', | ||||||
|  |       add_an_address_to_person: 'Ajouter l\'adresse à la personne', | ||||||
|  |       validFrom: 'Date de la nouvelle adresse', | ||||||
|  |       back_to_the_list: 'Retour à la liste' | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| import 'es6-promise/auto'; | import 'es6-promise/auto'; | ||||||
| import { createStore } from 'vuex'; | import { createStore } from 'vuex'; | ||||||
|  |  | ||||||
| import { postAddress } from '../../_api/AddAddress' | import { patchAddress, postAddress, postPostalCode, postAddressToPerson, getAddress } from '../../_api/AddAddress' | ||||||
|  |  | ||||||
| const debug = process.env.NODE_ENV !== 'production'; | const debug = process.env.NODE_ENV !== 'production'; | ||||||
|  |  | ||||||
| @@ -9,6 +9,8 @@ const store = createStore({ | |||||||
|    strict: debug, |    strict: debug, | ||||||
|    state: { |    state: { | ||||||
|       address: {}, |       address: {}, | ||||||
|  |       editAddress: {}, //TODO or should be address? | ||||||
|  |       person: {}, | ||||||
|       errorMsg: [] |       errorMsg: [] | ||||||
|    }, |    }, | ||||||
|    getters: { |    getters: { | ||||||
| @@ -20,25 +22,119 @@ const store = createStore({ | |||||||
|       addAddress(state, address) { |       addAddress(state, address) { | ||||||
|          console.log('@M addAddress address', address); |          console.log('@M addAddress address', address); | ||||||
|          state.address = address; |          state.address = address; | ||||||
|       } |       }, | ||||||
|  |       updateAddress(state, address) { | ||||||
|  |          console.log('@M updateAddress address', address); | ||||||
|  |          state.address = address; | ||||||
|  |       }, | ||||||
|  |       addAddressToPerson(state, person) { | ||||||
|  |          console.log('@M addAddressToPerson person', person); | ||||||
|  |          state.person = person; | ||||||
|  |       }, | ||||||
|  |       addDateToAddress(state, validFrom) { | ||||||
|  |          console.log('@M addDateToAddress address.validFrom', validFrom); | ||||||
|  |          state.validFrom = validFrom; | ||||||
|  |       }, | ||||||
|  |       getEditAddress(state, address) { | ||||||
|  |          console.log('@M getEditAddress address', address); | ||||||
|  |          state.editAddress = address; | ||||||
|  |       }, | ||||||
|    }, |    }, | ||||||
|    actions: { |    actions: { | ||||||
|       addAddress({ commit }, payload) { |       addAddress({ commit }, payload) { | ||||||
|          console.log('@A addAddress payload', payload); |          console.log('@A addAddress payload', payload); | ||||||
|          //commit('addAddress', payload); // à remplacer par la suite |  | ||||||
|  |  | ||||||
|          //fetch POST qui envoie l'adresse, et récupère la confirmation que c'est ok. |          if('newPostalCode' in payload){ | ||||||
|          //La confirmation est l'adresse elle-même. |             let postalCodeBody = payload.newPostalCode; | ||||||
|  |             postalCodeBody = Object.assign(postalCodeBody, {'origin': 3}); | ||||||
|  |             postPostalCode(postalCodeBody) | ||||||
|  |                .then(postalCode => { | ||||||
|  |                   let body = payload; | ||||||
|  |                   body.postcode = {'id': postalCode.id}, | ||||||
|  |                   postAddress(body) | ||||||
|  |                      .then(address => new Promise((resolve, reject) => { | ||||||
|  |                         commit('addAddress', address); | ||||||
|  |                         resolve(); | ||||||
|  |                      })) | ||||||
|  |                      .catch((error) => { | ||||||
|  |                         commit('catchError', error); | ||||||
|  |                      }); | ||||||
|  |                }) | ||||||
|  |  | ||||||
|          postAddress(payload) |          } else { | ||||||
|  |             postAddress(payload) | ||||||
|  |                .then(address => new Promise((resolve, reject) => { | ||||||
|  |                   commit('addAddress', address); | ||||||
|  |                   resolve(); | ||||||
|  |                })) | ||||||
|  |                .catch((error) => { | ||||||
|  |                   commit('catchError', error); | ||||||
|  |                }); | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |       addDateToAddressAndAddressToPerson({ commit }, payload) { | ||||||
|  |          console.log('@A addDateToAddressAndAddressToPerson payload', payload); | ||||||
|  |  | ||||||
|  |          patchAddress(payload.addressId, payload.body) | ||||||
|             .then(address => new Promise((resolve, reject) => { |             .then(address => new Promise((resolve, reject) => { | ||||||
|                commit('addAddress', address); |                commit('addDateToAddress', address.validFrom); | ||||||
|                resolve(); |                resolve(); | ||||||
|             })) |             }).then( | ||||||
|  |                postAddressToPerson(payload.personId, payload.addressId) | ||||||
|  |                   .then(person => new Promise((resolve, reject) => { | ||||||
|  |                      commit('addAddressToPerson', person); | ||||||
|  |                      resolve(); | ||||||
|  |                })) | ||||||
|  |                .catch((error) => { | ||||||
|  |                   commit('catchError', error); | ||||||
|  |                }) | ||||||
|  |             )) | ||||||
|             .catch((error) => { |             .catch((error) => { | ||||||
|                commit('catchError', error); |                commit('catchError', error); | ||||||
|             }); |             }); | ||||||
|       } |       }, | ||||||
|  |       updateAddress({ commit }, payload) { | ||||||
|  |          console.log('@A updateAddress payload', payload); | ||||||
|  |  | ||||||
|  |          if('newPostalCode' in payload.newAddress){ // TODO change the condition because it writes new postal code in edit mode now: !writeNewPostalCode | ||||||
|  |             let postalCodeBody = payload.newAddress.newPostalCode; | ||||||
|  |             postalCodeBody = Object.assign(postalCodeBody, {'origin': 3}); | ||||||
|  |             postPostalCode(postalCodeBody) | ||||||
|  |                .then(postalCode => { | ||||||
|  |                   let body = payload.newAddress; | ||||||
|  |                   body.postcode = {'id': postalCode.id }, | ||||||
|  |                   patchAddress(payload.addressId, body) | ||||||
|  |                      .then(address => new Promise((resolve, reject) => { | ||||||
|  |                         commit('updateAddress', address); | ||||||
|  |                         resolve(); | ||||||
|  |                      })) | ||||||
|  |                      .catch((error) => { | ||||||
|  |                         commit('catchError', error); | ||||||
|  |                      }); | ||||||
|  |                }) | ||||||
|  |  | ||||||
|  |          } else { | ||||||
|  |             patchAddress(payload.addressId, payload.newAddress) | ||||||
|  |                .then(address => new Promise((resolve, reject) => { | ||||||
|  |                   commit('updateAddress', address); | ||||||
|  |                   resolve(); | ||||||
|  |                })) | ||||||
|  |                .catch((error) => { | ||||||
|  |                   commit('catchError', error); | ||||||
|  |                }); | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |       getEditAddress({ commit }, payload) { | ||||||
|  |          console.log('@A getEditAddress payload', payload); | ||||||
|  |  | ||||||
|  |          getAddress(payload).then(address => new Promise((resolve, reject) => { | ||||||
|  |             commit('getEditAddress', address); | ||||||
|  |             resolve(); | ||||||
|  |          })) | ||||||
|  |          .catch((error) => { | ||||||
|  |             commit('catchError', error); | ||||||
|  |          }); | ||||||
|  |       }, | ||||||
|    } |    } | ||||||
| }); | }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ const fetchCities = (country) => { | |||||||
| */ | */ | ||||||
| const fetchReferenceAddresses = (postalCode) => { | const fetchReferenceAddresses = (postalCode) => { | ||||||
|     console.log('<<< fetching references addresses for', postalCode); |     console.log('<<< fetching references addresses for', postalCode); | ||||||
|    //TODO deal with huge number of addresses... we should do suggestion... |  | ||||||
|     const url = `/api/1.0/main/address-reference.json?item_per_page=1000&postal_code=${postalCode.id}`; |     const url = `/api/1.0/main/address-reference.json?item_per_page=1000&postal_code=${postalCode.id}`; | ||||||
|     return fetch(url) |     return fetch(url) | ||||||
|        .then(response => { |        .then(response => { | ||||||
| @@ -45,16 +44,75 @@ const fetchReferenceAddresses = (postalCode) => { | |||||||
|        }); |        }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * Endpoint chill_api_single_address_reference__index | ||||||
|  | * method GET, get AddressReference Object | ||||||
|  | * @returns {Promise} a promise containing all AddressReference objects filtered with postal code | ||||||
|  | */ | ||||||
|  | const fetchAddresses = () => { | ||||||
|  |    console.log('<<< fetching addresses'); | ||||||
|  |   //TODO deal with huge number of addresses... we should do suggestion... | ||||||
|  |    const url = `/api/1.0/main/address.json?item_per_page=1000`; | ||||||
|  |    return fetch(url) | ||||||
|  |       .then(response => { | ||||||
|  |          if (response.ok) { return response.json(); } | ||||||
|  |          throw Error('Error with request resource response'); | ||||||
|  |       }); | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| * Endpoint chill_api_single_address__entity__create | * Endpoint chill_api_single_address__entity__create | ||||||
| * method POST, post Address Object | * method POST, post Address Object | ||||||
| * @returns {Promise} | * @returns {Promise} | ||||||
| */ | */ | ||||||
| const postAddress = (address) => { | const postAddress = (address) => { | ||||||
|    console.log(address); |  | ||||||
|    const url = `/api/1.0/main/address.json?`; |    const url = `/api/1.0/main/address.json?`; | ||||||
|    const body = address; |    const body = address; | ||||||
|  |  | ||||||
|  |    return fetch(url, { | ||||||
|  |       method: 'POST', | ||||||
|  |       headers: { | ||||||
|  |          'Content-Type': 'application/json;charset=utf-8' | ||||||
|  |       }, | ||||||
|  |       body: JSON.stringify(body) | ||||||
|  |    }).then(response => { | ||||||
|  |       if (response.ok) { return response.json(); } | ||||||
|  |       throw Error('Error with request resource response'); | ||||||
|  |    }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * Endpoint chill_api_single_address__entity__create | ||||||
|  | * method PATCH, patch Address Instance | ||||||
|  | * | ||||||
|  | *  @id     integer - id of address | ||||||
|  | *  @body   Object - dictionary with changes to post | ||||||
|  | */ | ||||||
|  | const patchAddress = (id, body) => { | ||||||
|  |    const url = `/api/1.0/main/address/${id}.json`; | ||||||
|  |    return fetch(url, { | ||||||
|  |          method: 'PATCH', | ||||||
|  |          headers: { | ||||||
|  |            'Content-Type': 'application/json;charset=utf-8' | ||||||
|  |          }, | ||||||
|  |          body: JSON.stringify(body) | ||||||
|  |       }) | ||||||
|  |       .then(response => { | ||||||
|  |          if (response.ok) { return response.json(); } | ||||||
|  |          throw Error('Error with request resource response'); | ||||||
|  |       }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * Endpoint chill_api_single_postal_code__entity_create | ||||||
|  | * method POST, post Postal Code Object | ||||||
|  | * @returns {Promise} | ||||||
|  | */ | ||||||
|  | const postPostalCode = (postalCode) => { | ||||||
|  |    const url = `/api/1.0/main/postal-code.json?`; | ||||||
|  |    const body = postalCode; | ||||||
|  |  | ||||||
|    return fetch(url, { |    return fetch(url, { | ||||||
|       method: 'POST', |       method: 'POST', | ||||||
|       headers: { |       headers: { | ||||||
| @@ -67,9 +125,55 @@ const postAddress = (address) => { | |||||||
|    }); |    }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * Endpoint chill_api_single_person_address | ||||||
|  | * method POST, post Person instance | ||||||
|  | * | ||||||
|  | *  @id     integer - id of Person | ||||||
|  | *  @body   Object - dictionary with changes to post | ||||||
|  | */ | ||||||
|  | const postAddressToPerson = (personId, addressId) => { | ||||||
|  |    console.log(personId); | ||||||
|  |    console.log(addressId); | ||||||
|  |    const body = { | ||||||
|  |       'id': addressId | ||||||
|  |    }; | ||||||
|  |    const url = `/api/1.0/person/person/${personId}/address.json` | ||||||
|  |    return fetch(url, { | ||||||
|  |          method: 'POST', | ||||||
|  |          headers: {'Content-Type': 'application/json;charset=utf-8'}, | ||||||
|  |          body: JSON.stringify(body) | ||||||
|  |       }) | ||||||
|  |       .then(response => { | ||||||
|  |          if (response.ok) { return response.json(); } | ||||||
|  |          throw Error('Error with request resource response'); | ||||||
|  |       }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * Endpoint chill_api_single_address__index | ||||||
|  | * method GET, get Address Object | ||||||
|  | * @params {id} the address id | ||||||
|  | * @returns {Promise} a promise containing a Address object | ||||||
|  | */ | ||||||
|  | const getAddress = (id) => { | ||||||
|  |    console.log('<<< get address'); | ||||||
|  |    const url = `/api/1.0/main/address/${id}.json`; | ||||||
|  |    return fetch(url) | ||||||
|  |       .then(response => { | ||||||
|  |          if (response.ok) { return response.json(); } | ||||||
|  |          throw Error('Error with request resource response'); | ||||||
|  |       }); | ||||||
|  | }; | ||||||
|  |  | ||||||
| export { | export { | ||||||
|    fetchCountries, |    fetchCountries, | ||||||
|    fetchCities, |    fetchCities, | ||||||
|    fetchReferenceAddresses, |    fetchReferenceAddresses, | ||||||
|    postAddress |    fetchAddresses, | ||||||
|  |    postAddress, | ||||||
|  |    patchAddress, | ||||||
|  |    postPostalCode, | ||||||
|  |    postAddressToPerson, | ||||||
|  |    getAddress | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| <template> | <template> | ||||||
|    <button class="sc-button bt-create centered mt-4" @click="openModal"> |    <button v-if="!edit" class="sc-button bt-create mt-4" @click="openModal"> | ||||||
|       {{ $t('add_an_address_title') }} |       {{ $t('add_an_address_title') }} | ||||||
|    </button> |    </button> | ||||||
|  |    <button v-else class="sc-button bt-create mt-4" @click="openModal"> | ||||||
|  |       {{ $t('edit_an_address_title') }} | ||||||
|  |    </button> | ||||||
|  |  | ||||||
|    <teleport to="body"> |    <teleport to="body"> | ||||||
|       <modal v-if="modal.showModal" |       <modal v-if="modal.showModal" | ||||||
| @@ -9,7 +12,8 @@ | |||||||
|          @close="modal.showModal = false"> |          @close="modal.showModal = false"> | ||||||
|  |  | ||||||
|          <template v-slot:header> |          <template v-slot:header> | ||||||
|             <h3 class="modal-title">{{ $t('add_an_address_title') }}</h3> |             <h3 v-if="!edit" class="modal-title">{{ $t('add_an_address_title') }}</h3> | ||||||
|  |             <h3 v-if="edit" class="modal-title">{{ $t('edit_an_address_title') }}</h3> | ||||||
|          </template> |          </template> | ||||||
|  |  | ||||||
|          <template v-slot:body> |          <template v-slot:body> | ||||||
| @@ -42,6 +46,7 @@ | |||||||
|                      </city-selection> |                      </city-selection> | ||||||
|  |  | ||||||
|                      <address-selection |                      <address-selection | ||||||
|  |                         v-if="!isNoAddress" | ||||||
|                         v-bind:address="address" |                         v-bind:address="address" | ||||||
|                         v-bind:updateMapCenter="updateMapCenter"> |                         v-bind:updateMapCenter="updateMapCenter"> | ||||||
|                      </address-selection> |                      </address-selection> | ||||||
| @@ -100,11 +105,14 @@ export default { | |||||||
|    emits: ['addNewAddress'], |    emits: ['addNewAddress'], | ||||||
|    data() { |    data() { | ||||||
|       return { |       return { | ||||||
|  |          edit: window.mode === 'edit', | ||||||
|          modal: { |          modal: { | ||||||
|             showModal: false, |             showModal: false, | ||||||
|             modalDialogClass: "modal-dialog-scrollable modal-xl" |             modalDialogClass: "modal-dialog-scrollable modal-xl" | ||||||
|          }, |          }, | ||||||
|          address: { |          address: { | ||||||
|  |             writeNewAddress: false, | ||||||
|  |             writeNewPostalCode: false, | ||||||
|             loaded: { |             loaded: { | ||||||
|                countries: [], |                countries: [], | ||||||
|                cities: [], |                cities: [], | ||||||
| @@ -115,11 +123,17 @@ export default { | |||||||
|                city: {}, |                city: {}, | ||||||
|                address: {}, |                address: {}, | ||||||
|             }, |             }, | ||||||
|  |             newPostalCode: { | ||||||
|  |                code: null, | ||||||
|  |                name: null | ||||||
|  |             }, | ||||||
|             addressMap: { |             addressMap: { | ||||||
|                center : [48.8589, 2.3469], // Note: LeafletJs demands [lat, lon] cfr https://macwright.com/lonlat/ |                center : [48.8589, 2.3469], // Note: LeafletJs demands [lat, lon] cfr https://macwright.com/lonlat/ | ||||||
|                zoom: 12 |                zoom: 12 | ||||||
|             }, |             }, | ||||||
|             isNoAddress: false, |             isNoAddress: false, | ||||||
|  |             street: null, | ||||||
|  |             streetNumber: null, | ||||||
|             floor: null, |             floor: null, | ||||||
|             corridor: null, |             corridor: null, | ||||||
|             steps: null, |             steps: null, | ||||||
| @@ -167,8 +181,8 @@ export default { | |||||||
|       getCities(country) { |       getCities(country) { | ||||||
|          console.log('getCities for', country.name); |          console.log('getCities for', country.name); | ||||||
|          fetchCities(country).then(cities => new Promise((resolve, reject) => { |          fetchCities(country).then(cities => new Promise((resolve, reject) => { | ||||||
|             this.address.loaded.cities = cities.results; |             this.address.loaded.cities = cities.results.filter(c => c.origin !== 3); // filter out user-defined cities | ||||||
|             resolve() |             resolve(); | ||||||
|          })) |          })) | ||||||
|          .catch((error) => { |          .catch((error) => { | ||||||
|             this.errorMsg.push(error.message); |             this.errorMsg.push(error.message); | ||||||
| @@ -198,6 +212,23 @@ export default { | |||||||
|          this.address.loaded.cities = []; |          this.address.loaded.cities = []; | ||||||
|          this.address.selected.city = {}; |          this.address.selected.city = {}; | ||||||
|          this.address.selected.country = {}; |          this.address.selected.country = {}; | ||||||
|  |          this.address.isNoAddress = this.edit ? this.$store.state.editAddress.isNoAddress: false;; | ||||||
|  |          this.address.street = this.edit ? this.$store.state.editAddress.street: null; | ||||||
|  |          this.address.streetNumber = this.edit ? this.$store.state.editAddress.streetNumber: null; | ||||||
|  |          this.address.floor = this.edit ? this.$store.state.editAddress.floor: null; | ||||||
|  |          this.address.corridor = this.edit ? this.$store.state.editAddress.corridor: null; | ||||||
|  |          this.address.steps = this.edit ? this.$store.state.editAddress.steps: null; | ||||||
|  |          this.address.flat = this.edit ? this.$store.state.editAddress.flat: null; | ||||||
|  |          this.address.buildingName = this.edit ? this.$store.state.editAddress.buildingName: null; | ||||||
|  |          this.address.distribution = this.edit ? this.$store.state.editAddress.distribution: null; | ||||||
|  |          this.address.extra = this.edit ? this.$store.state.editAddress.extra: null; | ||||||
|  |          this.address.writeNewAddress = this.edit; | ||||||
|  |          this.address.writeNewPostalCode = this.edit; | ||||||
|  |          this.address.newPostalCode = this.edit ? | ||||||
|  |              { | ||||||
|  |                  code: this.$store.state.editAddress.postcode !== undefined ? this.$store.state.editAddress.postcode.code : null, | ||||||
|  |                  name: this.$store.state.editAddress.postcode !== undefined ? this.$store.state.editAddress.postcode.name : null | ||||||
|  |              } : {}; | ||||||
|          console.log('cities and addresses', this.address.loaded.cities, this.address.loaded.addresses); |          console.log('cities and addresses', this.address.loaded.cities, this.address.loaded.addresses); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ export default { | |||||||
|       update() { |       update() { | ||||||
|          console.log('update map with : ', this.address.addressMap.center) |          console.log('update map with : ', this.address.addressMap.center) | ||||||
|          marker.setLatLng(this.address.addressMap.center); |          marker.setLatLng(this.address.addressMap.center); | ||||||
|          map.setView(this.address.addressMap.center, 12); |          map.setView(this.address.addressMap.center, 15); | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|    mounted(){ |    mounted(){ | ||||||
|   | |||||||
| @@ -1,37 +1,56 @@ | |||||||
| <template> | <template> | ||||||
|  |    <h4>{{ $t('fill_an_address') }}</h4> | ||||||
|    <div> |    <div> | ||||||
|       <h4>{{ $t('fill_an_address') }}</h4> |       <label for="floor">{{ $t('floor') }}</label> | ||||||
|       <input  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="floor" |          name="floor" | ||||||
|          :placeholder="$t('floor')" |          :placeholder="$t('floor')" | ||||||
|          v-model="floor"/> |          v-model="floor"/> | ||||||
|       <input  |       </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="corridor">{{ $t('corridor') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="corridor" |          name="corridor" | ||||||
|          :placeholder="$t('corridor')" |          :placeholder="$t('corridor')" | ||||||
|          v-model="corridor"/> |          v-model="corridor"/> | ||||||
|       <input  |    </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="steps">{{ $t('steps') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="steps" |          name="steps" | ||||||
|          :placeholder="$t('steps')" |          :placeholder="$t('steps')" | ||||||
|          v-model="steps"/> |          v-model="steps"/> | ||||||
|       <input  |    </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="flat">{{ $t('flat') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="flat" |          name="flat" | ||||||
|          :placeholder="$t('flat')" |          :placeholder="$t('flat')" | ||||||
|          v-model="flat"/> |          v-model="flat"/> | ||||||
|       <input  |    </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="buildingName">{{ $t('buildingName') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="buildingName" |          name="buildingName" | ||||||
|          :placeholder="$t('buildingName')" |          :placeholder="$t('buildingName')" | ||||||
|          v-model="buildingName"/> |          v-model="buildingName"/> | ||||||
|       <input  |    </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="extra">{{ $t('extra') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="extra" |          name="extra" | ||||||
|          :placeholder="$t('extra')" |          :placeholder="$t('extra')" | ||||||
|          v-model="extra"/> |          v-model="extra"/> | ||||||
|       <input  |    </div> | ||||||
|  |    <div> | ||||||
|  |       <label for="distribution">{{ $t('distribution') }}</label> | ||||||
|  |       <input | ||||||
|          type="text" |          type="text" | ||||||
|          name="distribution" |          name="distribution" | ||||||
|          :placeholder="$t('distribution')" |          :placeholder="$t('distribution')" | ||||||
|   | |||||||
| @@ -15,6 +15,18 @@ | |||||||
|          :options="addresses"> |          :options="addresses"> | ||||||
|       </VueMultiselect> |       </VueMultiselect> | ||||||
|    </div> |    </div> | ||||||
|  |    <div class="custom-address" v-if="writeNewAddress || writeNewPostalCode"> | ||||||
|  |       <input | ||||||
|  |          type="text" | ||||||
|  |          name="street" | ||||||
|  |          :placeholder="$t('street')" | ||||||
|  |          v-model="street"/> | ||||||
|  |       <input | ||||||
|  |          type="text" | ||||||
|  |          name="streetNumber" | ||||||
|  |          :placeholder="$t('streetNumber')" | ||||||
|  |          v-model="streetNumber"/> | ||||||
|  |    </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| @@ -30,9 +42,31 @@ export default { | |||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|    computed: { |    computed: { | ||||||
|  |       writeNewAddress() { | ||||||
|  |          return this.address.writeNewAddress; | ||||||
|  |       }, | ||||||
|  |       writeNewPostalCode() { | ||||||
|  |          return this.address.writeNewPostalCode; | ||||||
|  |       }, | ||||||
|       addresses() { |       addresses() { | ||||||
|          return this.address.loaded.addresses; |          return this.address.loaded.addresses; | ||||||
|       } |       }, | ||||||
|  |       street: { | ||||||
|  |          set(value) { | ||||||
|  |             this.address.street = value; | ||||||
|  |          }, | ||||||
|  |          get() { | ||||||
|  |             return this.address.street; | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |       streetNumber: { | ||||||
|  |          set(value) { | ||||||
|  |             this.address.streetNumber = value; | ||||||
|  |          }, | ||||||
|  |          get() { | ||||||
|  |             return this.address.streetNumber; | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|    }, |    }, | ||||||
|    methods: { |    methods: { | ||||||
|       transName(value) { |       transName(value) { | ||||||
| @@ -40,14 +74,12 @@ export default { | |||||||
|       }, |       }, | ||||||
|       selectAddress(value) { |       selectAddress(value) { | ||||||
|          this.address.selected.address = value; |          this.address.selected.address = value; | ||||||
|  |          this.address.street = value.street; | ||||||
|  |          this.address.streetNumber = value.streetNumber; | ||||||
|          this.updateMapCenter(value.point); |          this.updateMapCenter(value.point); | ||||||
|       }, |       }, | ||||||
|       addAddress (newAddress) { |       addAddress() { | ||||||
|          const address = { |          this.address.writeNewAddress = true; | ||||||
|             street: newAddress |  | ||||||
|          }; |  | ||||||
|          this.value = address; |  | ||||||
|          this.address.selected.address = address; |  | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -8,9 +8,25 @@ | |||||||
|          label="value" |          label="value" | ||||||
|          :custom-label="transName" |          :custom-label="transName" | ||||||
|          :placeholder="$t('select_city')" |          :placeholder="$t('select_city')" | ||||||
|  |          :taggable="true" | ||||||
|  |          :multiple="false" | ||||||
|  |          @tag="addPostalCode" | ||||||
|  |          :tagPlaceholder="$t('create_postal_code')" | ||||||
|          :options="cities"> |          :options="cities"> | ||||||
|       </VueMultiselect> |       </VueMultiselect> | ||||||
|    </div> |    </div> | ||||||
|  |    <div class="custom-postcode" v-if="writeNewPostalCode"> | ||||||
|  |       <input | ||||||
|  |          type="text" | ||||||
|  |          name="name" | ||||||
|  |          :placeholder="$t('postalCode_name')" | ||||||
|  |          v-model="name"/> | ||||||
|  |       <input | ||||||
|  |          type="text" | ||||||
|  |          name="code" | ||||||
|  |          :placeholder="$t('postalCode_code')" | ||||||
|  |          v-model="code"/> | ||||||
|  |    </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
| @@ -25,18 +41,42 @@ export default { | |||||||
|          value: null |          value: null | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|  |    computed: { | ||||||
|  |       writeNewPostalCode() { | ||||||
|  |          return this.address.writeNewPostalCode; | ||||||
|  |       }, | ||||||
|  |       cities() { | ||||||
|  |          return this.address.loaded.cities; | ||||||
|  |       }, | ||||||
|  |       name: { | ||||||
|  |          set(value) { | ||||||
|  |             this.address.newPostalCode.name = value; | ||||||
|  |          }, | ||||||
|  |          get() { | ||||||
|  |             return this.address.newPostalCode.name; | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |       code: { | ||||||
|  |          set(value) { | ||||||
|  |             this.address.newPostalCode.code= value; | ||||||
|  |          }, | ||||||
|  |          get() { | ||||||
|  |             return this.address.newPostalCode.code; | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |    }, | ||||||
|    methods: { |    methods: { | ||||||
|       transName(value) { |       transName(value) { | ||||||
|          return `${value.code}-${value.name}` |          return `${value.code}-${value.name}` | ||||||
|       }, |       }, | ||||||
|       selectCity(value) { |       selectCity(value) { | ||||||
|          this.address.selected.city = value; |          this.address.selected.city = value; | ||||||
|  |          this.address.newPostalCode.name = value.name; | ||||||
|  |          this.address.newPostalCode.code = value.code; | ||||||
|          this.getReferenceAddresses(value); |          this.getReferenceAddresses(value); | ||||||
|       }, |       }, | ||||||
|    }, |       addPostalCode() { | ||||||
|    computed: { |          this.address.writeNewPostalCode = true; | ||||||
|       cities() { |  | ||||||
|          return this.address.loaded.cities; |  | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -22,13 +22,18 @@ export default { | |||||||
|    props: ['address', 'getCities'], |    props: ['address', 'getCities'], | ||||||
|    data() { |    data() { | ||||||
|       return { |       return { | ||||||
|          value: this.address.loaded.countries.filter(c => c.countryCode === 'FR')[0] |          edit: window.mode === 'edit', | ||||||
|  |          defaultCountry: this.edit ? this.$store.state.editAddress.country.code : 'FR', | ||||||
|  |          value: this.address.loaded.countries.filter(c => c.countryCode === this.defaultCountry)[0] | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|    methods: { |    methods: { | ||||||
|       init() { |       init() { | ||||||
|  |          this.value = this.edit ? | ||||||
|  |             this.address.loaded.countries.filter(c => c.countryCode === this.$store.state.editAddress.country.code)[0]: | ||||||
|  |             this.address.loaded.countries.filter(c => c.countryCode === 'FR')[0] | ||||||
|          if (this.value !== undefined) { |          if (this.value !== undefined) { | ||||||
|             this.getCities(this.value); |             this.selectCountry(this.value); | ||||||
|          } |          } | ||||||
|       }, |       }, | ||||||
|       transName ({ name }) { |       transName ({ name }) { | ||||||
|   | |||||||
| @@ -0,0 +1,46 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="chill_address_address chill_address_address--multiline"> | ||||||
|  |    <div v-if="address.text"> | ||||||
|  |       {{ address.text }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.postcode"> | ||||||
|  |       {{ address.postcode.name }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.country"> | ||||||
|  |       {{ address.country.name.fr }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.floor"> | ||||||
|  |       <span>{{ $t('floor') }}</span>: {{ address.floor }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.corridor"> | ||||||
|  |       <span>{{ $t('corridor') }}</span>: {{ address.corridor }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.steps"> | ||||||
|  |       <span>{{ $t('steps') }}</span>: {{ address.steps }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.flat"> | ||||||
|  |       <span>{{ $t('flat') }}</span>: {{ address.flat }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.buildingName"> | ||||||
|  |       <span>{{ $t('buildingName') }}</span>: {{ address.buildingName }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.extra"> | ||||||
|  |       <span>{{ $t('extra') }}</span>: {{ address.extra }} | ||||||
|  |    </div> | ||||||
|  |    <div v-if="address.distribution"> | ||||||
|  |       <span>{{ $t('distribution') }}</span>: {{ address.distribution }} | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |    name: 'ShowAddress', | ||||||
|  |    props: ['address'], | ||||||
|  |    data() { | ||||||
|  |       return { | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
| @@ -164,10 +164,12 @@ | |||||||
|       {{ encore_entry_script_tags('ckeditor5') }} |       {{ encore_entry_script_tags('ckeditor5') }} | ||||||
|     {% endif %} |     {% endif %} | ||||||
|     <script type="text/javascript"> |     <script type="text/javascript"> | ||||||
|  |       window.addEventListener('DOMContentLoaded', function(e) { | ||||||
|         chill.checkOtherValueOnChange(); |         chill.checkOtherValueOnChange(); | ||||||
|         $('.select2').select2({allowClear: true}); |         $('.select2').select2({allowClear: true}); | ||||||
|         chill.categoryLinkParentChildSelect(); |         chill.categoryLinkParentChildSelect(); | ||||||
|     </script> |       }); | ||||||
|     {% block js%}<!-- nothing added to js -->{% endblock %} |       </script> | ||||||
| </body> |       {% block js%}<!-- nothing added to js -->{% endblock %} | ||||||
| </html> |   </body> | ||||||
|  |   </html> | ||||||
|   | |||||||
| @@ -14,8 +14,20 @@ class AddressNormalizer implements NormalizerAwareInterface, NormalizerInterface | |||||||
|     public function normalize($address, string $format = null, array $context = []) |     public function normalize($address, string $format = null, array $context = []) | ||||||
|     { |     { | ||||||
|         $data['address_id'] = $address->getId(); |         $data['address_id'] = $address->getId(); | ||||||
|         $data['text'] = $address->getStreet().', '.$address->getBuildingName(); |         $data['text'] = $address->getStreet().', '.$address->getStreetNumber(); | ||||||
|  |         $data['street'] = $address->getStreet(); | ||||||
|  |         $data['streetNumber'] = $address->getStreetNumber(); | ||||||
|         $data['postcode']['name'] = $address->getPostCode()->getName(); |         $data['postcode']['name'] = $address->getPostCode()->getName(); | ||||||
|  |         $data['postcode']['code'] = $address->getPostCode()->getCode(); | ||||||
|  |         $data['country']['name'] = $address->getPostCode()->getCountry()->getName(); | ||||||
|  |         $data['country']['code'] = $address->getPostCode()->getCountry()->getCountryCode(); | ||||||
|  |         $data['floor'] = $address->getFloor(); | ||||||
|  |         $data['corridor'] = $address->getCorridor(); | ||||||
|  |         $data['steps'] = $address->getSteps(); | ||||||
|  |         $data['flat'] = $address->getBuildingName(); | ||||||
|  |         $data['buildingName'] = $address->getFlat(); | ||||||
|  |         $data['distribution'] = $address->getDistribution(); | ||||||
|  |         $data['extra'] = $address->getExtra(); | ||||||
|  |  | ||||||
|         return $data; |         return $data; | ||||||
|     } |     } | ||||||
| @@ -25,5 +37,5 @@ class AddressNormalizer implements NormalizerAwareInterface, NormalizerInterface | |||||||
|         return $data instanceof Address; |         return $data instanceof Address; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|      |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ class CollectionNormalizer implements NormalizerInterface, NormalizerAwareInterf | |||||||
|     public function normalize($collection, string $format = null, array $context = []) |     public function normalize($collection, string $format = null, array $context = []) | ||||||
|     { |     { | ||||||
|         /** @var $collection Collection */ |         /** @var $collection Collection */ | ||||||
|         /** @var $collection Chill\MainBundle\Pagination\PaginatorInterface */  |  | ||||||
|         $paginator = $collection->getPaginator(); |         $paginator = $collection->getPaginator(); | ||||||
|  |  | ||||||
|         $data['count'] = $paginator->getTotalItems(); |         $data['count'] = $paginator->getTotalItems(); | ||||||
|   | |||||||
							
								
								
									
										216
									
								
								src/Bundle/ChillMainBundle/Tests/Util/DateRangeCoveringTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/Bundle/ChillMainBundle/Tests/Util/DateRangeCoveringTest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\MainBundle\Tests\Util; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\Util\DateRangeCovering; | ||||||
|  | use PHPUnit\Framework\TestCase; | ||||||
|  |  | ||||||
|  | class DateRangeCoveringTest extends TestCase | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover1() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(1, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-12-01'), 1) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2011-06-01'), 2) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2019-06-01'), 3) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         $this->assertTrue($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(1, $cover->getIntersections()); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-06-01'), | ||||||
|  |             $cover->getIntersections()[0][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-12-01'), | ||||||
|  |             $cover->getIntersections()[0][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(1, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(2, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertNotContains(3, $cover->getIntersections()[0][2]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover1WithTwoIntersections() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(1, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-12-01'), 1) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2011-06-01'), 2) | ||||||
|  |             ->add(new \DateTime('2019-01-01'), new \DateTime('2019-12-01'), 3) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2020-06-01'), 4) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         $this->assertTrue($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(2, $cover->getIntersections()); | ||||||
|  |  | ||||||
|  |         $intersections = $cover->getIntersections(); | ||||||
|  |  | ||||||
|  |         // sort the intersections to compare them in expected order | ||||||
|  |         \usort($intersections, function($a, $b) { | ||||||
|  |             if ($a[0] === $b[0]) { | ||||||
|  |                 return $a[1] <=> $b[1]; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return $a[0] <=> $b[0]; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // first intersection | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-06-01'), | ||||||
|  |             $intersections[0][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-12-01'), | ||||||
|  |             $intersections[0][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($intersections[0][2]); | ||||||
|  |         $this->assertContains(1, $intersections[0][2]); | ||||||
|  |         $this->assertContains(2, $intersections[0][2]); | ||||||
|  |         $this->assertNotContains(3, $intersections[0][2]); | ||||||
|  |         $this->assertNotContains(4, $intersections[0][2]); | ||||||
|  |  | ||||||
|  |         // second intersection | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2019-06-01'), | ||||||
|  |             $intersections[1][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2019-12-01'), | ||||||
|  |             $intersections[1][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($intersections[1][2]); | ||||||
|  |         $this->assertContains(3, $intersections[1][2]); | ||||||
|  |         $this->assertContains(4, $intersections[1][2]); | ||||||
|  |         $this->assertNotContains(1, $intersections[1][2]); | ||||||
|  |         $this->assertNotContains(2, $intersections[1][2]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover2() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(2, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-10-01'), 1) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2010-09-01'), 2) | ||||||
|  |             ->add(new \DateTime('2010-04-01'), new \DateTime('2010-12-01'), 3) | ||||||
|  |             ->add(new \DateTime('2019-01-01'), new \DateTime('2019-10-01'), 4) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2019-09-01'), 5) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |         $this->assertTrue($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(1, $cover->getIntersections()); | ||||||
|  |  | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-06-01'), | ||||||
|  |             $cover->getIntersections()[0][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-09-01'), | ||||||
|  |             $cover->getIntersections()[0][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(1, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(2, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(3, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertNotContains(4, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertNotContains(5, $cover->getIntersections()[0][2]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover2AndThreePeriodsCovering() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(2, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-10-01'), 1) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2010-09-01'), 2) | ||||||
|  |             ->add(new \DateTime('2010-04-01'), new \DateTime('2010-12-01'), 3) | ||||||
|  |             ->add(new \DateTime('2009-01-01'), new \DateTime('2010-09-15'), 4) | ||||||
|  |             ->add(new \DateTime('2019-01-01'), new \DateTime('2019-10-01'), 5) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2019-09-01'), 6) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         $this->assertTrue($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(1, $cover->getIntersections()); | ||||||
|  |  | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-04-01'), | ||||||
|  |             $cover->getIntersections()[0][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-09-15'), | ||||||
|  |             $cover->getIntersections()[0][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(1, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(2, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(3, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertContains(4, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertNotContains(5, $cover->getIntersections()[0][2]); | ||||||
|  |         $this->assertNotContains(6, $cover->getIntersections()[0][2]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover2AndThreePeriodsCoveringWithNullMetadata() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(2, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-10-01'), null) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2010-09-01'), null) | ||||||
|  |             ->add(new \DateTime('2010-04-01'), new \DateTime('2010-12-01'), null) | ||||||
|  |             ->add(new \DateTime('2009-01-01'), new \DateTime('2010-09-15'), null) | ||||||
|  |             ->add(new \DateTime('2019-01-01'), new \DateTime('2019-10-01'), null) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2019-09-01'), null) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         $this->assertTrue($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(1, $cover->getIntersections()); | ||||||
|  |  | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-04-01'), | ||||||
|  |             $cover->getIntersections()[0][0], | ||||||
|  |             "assert date start are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertEquals( | ||||||
|  |             new \DateTime('2010-09-15'), | ||||||
|  |             $cover->getIntersections()[0][1], | ||||||
|  |             "assert date end are the intersection" | ||||||
|  |         ); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()[0][2]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public function testCoveringWithMinCover3Absent() | ||||||
|  |     { | ||||||
|  |         $cover = new DateRangeCovering(3, new \DateTimeZone('Europe/Brussels'));  | ||||||
|  |         $cover | ||||||
|  |             ->add(new \DateTime('2010-01-01'), new \DateTime('2010-10-01'), 1) | ||||||
|  |             ->add(new \DateTime('2010-06-01'), new \DateTime('2010-09-01'), 2) | ||||||
|  |             ->add(new \DateTime('2010-04-01'), new \DateTime('2010-12-01'), 3) | ||||||
|  |             ->add(new \DateTime('2019-01-01'), new \DateTime('2019-10-01'), 4) | ||||||
|  |             ->add(new \DateTime('2019-06-01'), new \DateTime('2019-09-01'), 5) | ||||||
|  |             ->compute() | ||||||
|  |             ; | ||||||
|  |         $this->assertFalse($cover->hasIntersections()); | ||||||
|  |         $this->assertIsArray($cover->getIntersections()); | ||||||
|  |         $this->assertCount(0, $cover->getIntersections()); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										224
									
								
								src/Bundle/ChillMainBundle/Util/DateRangeCovering.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/Bundle/ChillMainBundle/Util/DateRangeCovering.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\MainBundle\Util; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Utilities to compare date periods | ||||||
|  |  * | ||||||
|  |  * This class allow to compare periods when there are period covering. The  | ||||||
|  |  * argument `minCovers` allow to find also when there are more than 2 period | ||||||
|  |  * which intersects.  | ||||||
|  |  * | ||||||
|  |  * Example: a team may have maximum 2 leaders on a same period: you will  | ||||||
|  |  * find here all periods where there are more than 2 leaders. | ||||||
|  |  * | ||||||
|  |  * Usage: | ||||||
|  |  *  | ||||||
|  |  * ```php | ||||||
|  |  * $cover = new DateRangeCovering(2); // 2 means we will have periods | ||||||
|  |  *    // when there are 2+ periods intersecting | ||||||
|  |  * $cover | ||||||
|  |  *     ->add(new \DateTime('2010-01-01'), new \DateTime('2010-10-01'), null) | ||||||
|  |  *     ->add(new \DateTime('2010-06-01'), new \DateTime('2010-09-01'), null) | ||||||
|  |  *     ->add(new \DateTime('2010-04-01'), new \DateTime('2010-12-01'), null) | ||||||
|  |  *     ->add(new \DateTime('2009-01-01'), new \DateTime('2010-09-15'), null) | ||||||
|  |  *     ->add(new \DateTime('2019-01-01'), new \DateTime('2019-10-01'), null) | ||||||
|  |  *     ->add(new \DateTime('2019-06-01'), new \DateTime('2019-09-01'), null) | ||||||
|  |  *     ->compute() | ||||||
|  |  *     ; | ||||||
|  |  * $cover->getIntersections(); | ||||||
|  |  * ``` | ||||||
|  |  */ | ||||||
|  | class DateRangeCovering | ||||||
|  | { | ||||||
|  |     private bool $computed = false; | ||||||
|  |  | ||||||
|  |     private array $intersections = []; | ||||||
|  |  | ||||||
|  |     private array $intervals = []; | ||||||
|  |  | ||||||
|  |     private int $minCover; | ||||||
|  |  | ||||||
|  |     private int $uniqueKeyCounter = 0; | ||||||
|  |  | ||||||
|  |     private array $metadatas = []; | ||||||
|  |  | ||||||
|  |     private array $sequence = []; | ||||||
|  |  | ||||||
|  |     private \DateTimeZone $tz; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param int $minCover the minimum of covering required | ||||||
|  |      */ | ||||||
|  |     public function __construct(int $minCover, \DateTimeZone $tz) | ||||||
|  |     { | ||||||
|  |         if ($minCover < 0) { | ||||||
|  |             throw new \LogicException("argument minCover cannot be lower than 0"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->minCover = $minCover; | ||||||
|  |         $this->tz = $tz; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function add(\DateTimeInterface $start, \DateTimeInterface $end = null, $metadata = null): self | ||||||
|  |     { | ||||||
|  |         if ($this->computed) { | ||||||
|  |             throw new \LogicException("You cannot add intervals to a computed instance"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $k = $this->uniqueKeyCounter++; | ||||||
|  |         $this->intervals[$k] = [$start, $end]; | ||||||
|  |         $this->metadatas[$k] = $metadata; | ||||||
|  |  | ||||||
|  |         $this->addToSequence($start->getTimestamp(), $k, null); | ||||||
|  |         $this->addToSequence( | ||||||
|  |             NULL === $end ? PHP_INT_MAX : $end->getTimestamp(), null, $k | ||||||
|  |         );  | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function addToSequence($timestamp, int $start = null, int $end = null) | ||||||
|  |     { | ||||||
|  |         if (!\array_key_exists($timestamp, $this->sequence)) { | ||||||
|  |             $this->sequence[$timestamp] = [ 's' => [], 'e' => [] ]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (NULL !== $start) { | ||||||
|  |             $this->sequence[$timestamp]['s'][] = $start; | ||||||
|  |         } | ||||||
|  |         if (NULL !== $end) { | ||||||
|  |             $this->sequence[$timestamp]['e'][] = $end; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function compute(): self | ||||||
|  |     { | ||||||
|  |         \ksort($this->sequence); | ||||||
|  |  | ||||||
|  |         $currentPeriod = []; | ||||||
|  |         $currents = []; | ||||||
|  |         $isOpen = false; | ||||||
|  |         $overs = []; | ||||||
|  |  | ||||||
|  |         foreach ($this->sequence as $ts => $moves) { | ||||||
|  |             $currents = \array_merge($currents, $moves['s']); | ||||||
|  |             $currents = \array_diff($currents, $moves['e']); | ||||||
|  |  | ||||||
|  |             if (count($currents) > $this->minCover && !$isOpen) { | ||||||
|  |                 $currentPeriod[0] = $ts; | ||||||
|  |                 $currentPeriod[2] = $currents; | ||||||
|  |                 $isOpen = true; | ||||||
|  |             } elseif ($isOpen && count($currents) <= $this->minCover) { | ||||||
|  |                 $currentPeriod[1] = $ts; | ||||||
|  |                 $overs[] = $currentPeriod; | ||||||
|  |                 $currentPeriod = []; | ||||||
|  |                 $isOpen = false; | ||||||
|  |             } elseif ($isOpen) { | ||||||
|  |                 $currentPeriod[2] = \array_merge($currentPeriod[2], $currents); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // process metadata | ||||||
|  |         foreach ($overs as list($start, $end, $metadata)) { | ||||||
|  |             $this->intersections[] = [ | ||||||
|  |                 (new \DateTimeImmutable('@'.$start)) | ||||||
|  |                     ->setTimezone($this->tz), | ||||||
|  |                 $end === PHP_INT_MAX ? null : (new \DateTimeImmutable('@'.$end)) | ||||||
|  |                     ->setTimezone($this->tz), | ||||||
|  |                 \array_values( | ||||||
|  |                     \array_intersect_key( | ||||||
|  |                         $this->metadatas, | ||||||
|  |                         \array_flip(\array_unique($metadata)) | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|  |             ]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->computed = true; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function process(array $intersections): array | ||||||
|  |     { | ||||||
|  |         $result = []; | ||||||
|  |         $starts = []; | ||||||
|  |         $ends   = []; | ||||||
|  |         $metadatas = []; | ||||||
|  |  | ||||||
|  |         while (null !== ($current = \array_pop($intersections))) { | ||||||
|  |             list($cStart, $cEnd, $cMetadata) = $current; | ||||||
|  |             $n = count($cMetadata); | ||||||
|  |  | ||||||
|  |             foreach ($intersections as list($iStart, $iEnd, $iMetadata)) { | ||||||
|  |                 $start = max($cStart, $iStart); | ||||||
|  |                 $end = min($cEnd, $iEnd); | ||||||
|  |  | ||||||
|  |                 if ($start <= $end) { | ||||||
|  |                     if (FALSE !== ($key = \array_search($start, $starts))) { | ||||||
|  |                         if ($ends[$key] === $end) { | ||||||
|  |                             $metadatas[$key] = \array_unique(\array_merge($metadatas[$key], $iMetadata)); | ||||||
|  |                             continue; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     $starts[] = $start; | ||||||
|  |                     $ends[]   = $end; | ||||||
|  |                     $metadatas[] = \array_unique(\array_merge($iMetadata, $cMetadata)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // recompose results | ||||||
|  |         foreach ($starts as $k => $start) { | ||||||
|  |             $result[] = [$start, $ends[$k], \array_unique($metadatas[$k])]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function addToIntersections(array $intersections, array $intersection) | ||||||
|  |     { | ||||||
|  |         $foundExisting = false; | ||||||
|  |         list($nStart, $nEnd, $nMetadata) = $intersection; | ||||||
|  |  | ||||||
|  |         \array_walk($intersections,  | ||||||
|  |             function(&$i, $key) use ($nStart, $nEnd, $nMetadata, $foundExisting) { | ||||||
|  |                 if ($foundExisting) { | ||||||
|  |                     return; | ||||||
|  |                 }; | ||||||
|  |                 if ($i[0] === $nStart && $i[1] === $nEnd) { | ||||||
|  |                     $foundExisting = true; | ||||||
|  |                     $i[2] = \array_merge($i[2], $nMetadata); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         if (!$foundExisting) { | ||||||
|  |             $intersections[] = $intersection; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $intersections; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function hasIntersections(): bool | ||||||
|  |     { | ||||||
|  |         if (!$this->computed) { | ||||||
|  |             throw new \LogicException(sprintf("You cannot call the method %s before ". | ||||||
|  |                 "'process'", __METHOD)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return count($this->intersections) > 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getIntersections(): array | ||||||
|  |     { | ||||||
|  |         if (!$this->computed) { | ||||||
|  |             throw new \LogicException(sprintf("You cannot call the method %s before ". | ||||||
|  |                 "'process'", __METHOD)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->intersections; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -17,6 +17,93 @@ components: | |||||||
|           type: integer |           type: integer | ||||||
|         name: |         name: | ||||||
|           type: string |           type: string | ||||||
|  |     Address: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         address_id: | ||||||
|  |           type: integer | ||||||
|  |         text: | ||||||
|  |           type: string | ||||||
|  |         postcode: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             name: | ||||||
|  |               type: string | ||||||
|  |  | ||||||
|  |     Country: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         id: | ||||||
|  |           type: integer | ||||||
|  |         name: | ||||||
|  |           type: object | ||||||
|  |         countryCode: | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|  |     PostalCode: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         id: | ||||||
|  |           type: integer | ||||||
|  |         name: | ||||||
|  |           type: string | ||||||
|  |         code: | ||||||
|  |           type: string | ||||||
|  |         country: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             id: | ||||||
|  |               type: integer | ||||||
|  |             name: | ||||||
|  |               type: object | ||||||
|  |             countryCode: | ||||||
|  |               type: string | ||||||
|  |  | ||||||
|  |     AddressReference: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         id: | ||||||
|  |           type: integer | ||||||
|  |         refId: | ||||||
|  |           type: string | ||||||
|  |         street: | ||||||
|  |           type: string | ||||||
|  |         streetNumber: | ||||||
|  |           type: string | ||||||
|  |         postcode: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             id: | ||||||
|  |               type: integer | ||||||
|  |             name: | ||||||
|  |               type: string | ||||||
|  |             code: | ||||||
|  |               type: string | ||||||
|  |             country: | ||||||
|  |               type: object | ||||||
|  |               properties: | ||||||
|  |                 id: | ||||||
|  |                   type: integer | ||||||
|  |                 name: | ||||||
|  |                   type: object | ||||||
|  |                 countryCode: | ||||||
|  |                   type: string | ||||||
|  |         municipalityCode: | ||||||
|  |           type: string | ||||||
|  |         source: | ||||||
|  |           type: string | ||||||
|  |         point: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             type: | ||||||
|  |               type: string | ||||||
|  |             coordinates: | ||||||
|  |               type: array | ||||||
|  |               items: | ||||||
|  |                 type: number | ||||||
|  |               minItems: 2 | ||||||
|  |               maxItems: 2 | ||||||
|  |  | ||||||
|  |  | ||||||
| paths: | paths: | ||||||
|   /1.0/search.json: |   /1.0/search.json: | ||||||
| @@ -29,7 +116,7 @@ paths: | |||||||
|       description: > |       description: > | ||||||
|         **Warning**: This is currently a stub (not really implemented |         **Warning**: This is currently a stub (not really implemented | ||||||
|  |  | ||||||
|         The search is performed across multiple entities. The entities must be listed into  |         The search is performed across multiple entities. The entities must be listed into | ||||||
|         `type` parameters. |         `type` parameters. | ||||||
|  |  | ||||||
|         The results are ordered by relevance, from the most to the lowest relevant. |         The results are ordered by relevance, from the most to the lowest relevant. | ||||||
| @@ -55,4 +142,286 @@ paths: | |||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
|           description: "OK" |           description: "OK" | ||||||
|  |   /1.0/main/address.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a list of all Chill addresses | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |     post: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: create a new address | ||||||
|  |       requestBody: | ||||||
|  |         required: true | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               type: object | ||||||
|  |               properties: | ||||||
|  |                 buildingName: | ||||||
|  |                   type: string | ||||||
|  |                 corridor: | ||||||
|  |                   type: string | ||||||
|  |                 distribution: | ||||||
|  |                   type: string | ||||||
|  |                 extra: | ||||||
|  |                   type: string | ||||||
|  |                 flat: | ||||||
|  |                   type: string | ||||||
|  |                 floor: | ||||||
|  |                   type: string | ||||||
|  |                 isNoAddress: | ||||||
|  |                   type: boolean | ||||||
|  |                 point: | ||||||
|  |                   type: array | ||||||
|  |                   items: | ||||||
|  |                     type: number | ||||||
|  |                   minItems: 2 | ||||||
|  |                   maxItems: 2 | ||||||
|  |                 postcode: | ||||||
|  |                   $ref: '#/components/schemas/PostalCode' | ||||||
|  |                 steps: | ||||||
|  |                   type: string | ||||||
|  |                 street: | ||||||
|  |                   type: string | ||||||
|  |                 streetNumber: | ||||||
|  |                   type: string | ||||||
|  |       responses: | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |         404: | ||||||
|  |           description: "Not found" | ||||||
|  |         200: | ||||||
|  |           description: "OK" | ||||||
|  |         422: | ||||||
|  |           description: "Unprocessable entity (validation errors)" | ||||||
|  |         400: | ||||||
|  |           description: "transition cannot be applyed" | ||||||
|  |     patch: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: patch an address | ||||||
|  |       requestBody: | ||||||
|  |         required: true | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               type: object | ||||||
|  |               properties: | ||||||
|  |                 buildingName: | ||||||
|  |                   type: string | ||||||
|  |                 corridor: | ||||||
|  |                   type: string | ||||||
|  |                 distribution: | ||||||
|  |                   type: string | ||||||
|  |                 extra: | ||||||
|  |                   type: string | ||||||
|  |                 flat: | ||||||
|  |                   type: string | ||||||
|  |                 floor: | ||||||
|  |                   type: string | ||||||
|  |                 isNoAddress: | ||||||
|  |                   type: boolean | ||||||
|  |                 point: | ||||||
|  |                   type: array | ||||||
|  |                   items: | ||||||
|  |                     type: number | ||||||
|  |                   minItems: 2 | ||||||
|  |                   maxItems: 2 | ||||||
|  |                 postcode: | ||||||
|  |                   $ref: '#/components/schemas/PostalCode' | ||||||
|  |                 steps: | ||||||
|  |                   type: string | ||||||
|  |                 street: | ||||||
|  |                   type: string | ||||||
|  |                 streetNumber: | ||||||
|  |                   type: string | ||||||
|  |                 validFrom: | ||||||
|  |                   type: string | ||||||
|  |                 validTo: | ||||||
|  |                   type: string | ||||||
|  |       responses: | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |         404: | ||||||
|  |           description: "Not found" | ||||||
|  |         200: | ||||||
|  |           description: "OK" | ||||||
|  |         422: | ||||||
|  |           description: "Unprocessable entity (validation errors)" | ||||||
|  |         400: | ||||||
|  |           description: "transition cannot be applyed" | ||||||
|  |  | ||||||
|  |   /1.0/main/address/{id}.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return an address by id | ||||||
|  |       parameters: | ||||||
|  |         - name: id | ||||||
|  |           in: path | ||||||
|  |           required: true | ||||||
|  |           description: The address id | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |             format: integer | ||||||
|  |             minimum: 1 | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/Address' | ||||||
|  |         404: | ||||||
|  |           description: "not found" | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |  | ||||||
|  |   /1.0/main/address-reference.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a list of all reference addresses | ||||||
|  |       parameters: | ||||||
|  |         - in: query | ||||||
|  |           name: postal_code | ||||||
|  |           required: false | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |           description: The id of a postal code to filter the reference addresses | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |   /1.0/main/address-reference/{id}.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a reference address by id | ||||||
|  |       parameters: | ||||||
|  |         - name: id | ||||||
|  |           in: path | ||||||
|  |           required: true | ||||||
|  |           description: The reference address id | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |             format: integer | ||||||
|  |             minimum: 1 | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/AddressReference' | ||||||
|  |         404: | ||||||
|  |           description: "not found" | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |  | ||||||
|  |   /1.0/main/postal-code.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a list of all postal-code | ||||||
|  |       parameters: | ||||||
|  |         - in: query | ||||||
|  |           name: country | ||||||
|  |           required: false | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |           description: The id of a country to filter the postal code | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |     post: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: create a new PostalCode | ||||||
|  |       requestBody: | ||||||
|  |         required: true | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               type: object | ||||||
|  |               properties: | ||||||
|  |                 name: | ||||||
|  |                   type: string | ||||||
|  |                 code: | ||||||
|  |                   type: string | ||||||
|  |                 country: | ||||||
|  |                   $ref: '#/components/schemas/Country' | ||||||
|  |       responses: | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |         404: | ||||||
|  |           description: "Not found" | ||||||
|  |         200: | ||||||
|  |           description: "OK" | ||||||
|  |         422: | ||||||
|  |           description: "Unprocessable entity (validation errors)" | ||||||
|  |         400: | ||||||
|  |           description: "transition cannot be applyed" | ||||||
|  |  | ||||||
|  |   /1.0/main/postal-code/{id}.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a postal code by id | ||||||
|  |       parameters: | ||||||
|  |         - name: id | ||||||
|  |           in: path | ||||||
|  |           required: true | ||||||
|  |           description: The postal code id | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |             format: integer | ||||||
|  |             minimum: 1 | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/PostalCode' | ||||||
|  |         404: | ||||||
|  |           description: "not found" | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|  |  | ||||||
|  |   /1.0/main/country.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a list of all countries | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |   /1.0/main/country/{id}.json: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - address | ||||||
|  |       summary: Return a country by id | ||||||
|  |       parameters: | ||||||
|  |         - name: id | ||||||
|  |           in: path | ||||||
|  |           required: true | ||||||
|  |           description: The country id | ||||||
|  |           schema: | ||||||
|  |             type: integer | ||||||
|  |             format: integer | ||||||
|  |             minimum: 1 | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: "ok" | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 $ref: '#/components/schemas/Country' | ||||||
|  |         404: | ||||||
|  |           description: "not found" | ||||||
|  |         401: | ||||||
|  |           description: "Unauthorized" | ||||||
|   | |||||||
| @@ -58,13 +58,14 @@ services: | |||||||
|         arguments: |         arguments: | ||||||
|             - "@chill.main.helper.translatable_string" |             - "@chill.main.helper.translatable_string" | ||||||
|             - '@Symfony\Component\Routing\Generator\UrlGeneratorInterface' |             - '@Symfony\Component\Routing\Generator\UrlGeneratorInterface' | ||||||
|             - '@chill.main.form.choice_loader.postal_code' |             - '@Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader' | ||||||
|             - '@Symfony\Component\Translation\TranslatorInterface' |             - '@Symfony\Component\Translation\TranslatorInterface' | ||||||
|         tags: |         tags: | ||||||
|             - { name: form.type } |             - { name: form.type } | ||||||
|  |  | ||||||
|     chill.main.form.choice_loader.postal_code: |     Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader: | ||||||
|         class: Chill\MainBundle\Form\ChoiceLoader\PostalCodeChoiceLoader |         autowire: true | ||||||
|  |         autoconfigure: true | ||||||
|  |  | ||||||
|     chill.main.form.type.export: |     chill.main.form.type.export: | ||||||
|         class: Chill\MainBundle\Form\Type\Export\ExportType |         class: Chill\MainBundle\Form\Type\Export\ExportType | ||||||
|   | |||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | declare(strict_types=1); | ||||||
|  |  | ||||||
|  | namespace Chill\Migrations\Main; | ||||||
|  |  | ||||||
|  | use Doctrine\DBAL\Schema\Schema; | ||||||
|  | use Doctrine\Migrations\AbstractMigration; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Auto-generated Migration: Please modify to your needs! | ||||||
|  |  */ | ||||||
|  | final class Version20210616134328 extends AbstractMigration | ||||||
|  | { | ||||||
|  |     public function getDescription(): string | ||||||
|  |     { | ||||||
|  |         return ''; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function up(Schema $schema): void | ||||||
|  |     { | ||||||
|  |         $this->addSql('ALTER TABLE chill_main_postal_code ADD origin INT DEFAULT NULL'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function down(Schema $schema): void | ||||||
|  |     { | ||||||
|  |         $this->addSql('ALTER TABLE chill_main_postal_code DROP origin'); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,6 +6,7 @@ use Chill\MainBundle\CRUD\Controller\ApiController; | |||||||
| use Symfony\Component\HttpFoundation\Request; | use Symfony\Component\HttpFoundation\Request; | ||||||
| use Symfony\Component\HttpFoundation\Response; | use Symfony\Component\HttpFoundation\Response; | ||||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||||
|  | use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; | ||||||
| use Symfony\Component\HttpFoundation\Exception\BadRequestException; | use Symfony\Component\HttpFoundation\Exception\BadRequestException; | ||||||
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||||||
| use Symfony\Component\Validator\Validator\ValidatorInterface; | use Symfony\Component\Validator\Validator\ValidatorInterface; | ||||||
| @@ -116,6 +117,20 @@ $workflow = $this->registry->get($accompanyingPeriod); | |||||||
|         return $this->addRemoveSomething('socialissue', $id, $request, $_format, 'socialIssue', SocialIssue::class, [ 'groups' => [ 'read' ] ]); |         return $this->addRemoveSomething('socialissue', $id, $request, $_format, 'socialIssue', SocialIssue::class, [ 'groups' => [ 'read' ] ]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function workApi($id, Request $request, string $_format): Response | ||||||
|  |     { | ||||||
|  |         return $this->addRemoveSomething( | ||||||
|  |             'work', | ||||||
|  |             $id, | ||||||
|  |             $request, | ||||||
|  |             $_format, | ||||||
|  |             'work', | ||||||
|  |             AccompanyingPeriodWork::class, | ||||||
|  |             [ 'groups' => [ 'accompanying_period_work:create' ] ], | ||||||
|  |             true // force persist | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public function requestorApi($id, Request $request, string $_format): Response |     public function requestorApi($id, Request $request, string $_format): Response | ||||||
|     { |     { | ||||||
|         /** @var AccompanyingPeriod $accompanyingPeriod */ |         /** @var AccompanyingPeriod $accompanyingPeriod */ | ||||||
|   | |||||||
| @@ -86,8 +86,19 @@ class AccompanyingCourseController extends Controller | |||||||
|      */ |      */ | ||||||
|     public function indexAction(AccompanyingPeriod $accompanyingCourse): Response |     public function indexAction(AccompanyingPeriod $accompanyingCourse): Response | ||||||
|     { |     { | ||||||
|  |         // compute some warnings | ||||||
|  |         // get persons without household | ||||||
|  |         $withoutHousehold = []; | ||||||
|  |         foreach ($accompanyingCourse->getParticipations() as | ||||||
|  |             $p) { | ||||||
|  |             if (FALSE === $p->getPerson()->isSharingHousehold()) { | ||||||
|  |                 $withoutHousehold[] = $p->getPerson(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [ |         return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [ | ||||||
|             'accompanyingCourse' => $accompanyingCourse |             'accompanyingCourse' => $accompanyingCourse, | ||||||
|  |             'withoutHousehold' => $withoutHousehold | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Controller; | ||||||
|  |  | ||||||
|  | use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||||
|  | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||||
|  | use Symfony\Component\HttpFoundation\Response; | ||||||
|  | use Symfony\Component\HttpFoundation\Request; | ||||||
|  | use Symfony\Component\Routing\Annotation\Route; | ||||||
|  | use Symfony\Component\Serializer\SerializerInterface; | ||||||
|  | use Symfony\Component\Translation\TranslatorInterface; | ||||||
|  |  | ||||||
|  | class AccompanyingCourseWorkController extends AbstractController | ||||||
|  | { | ||||||
|  |     private TranslatorInterface $trans; | ||||||
|  |     private SerializerInterface $serializer; | ||||||
|  |  | ||||||
|  |     public function __construct(TranslatorInterface $trans, SerializerInterface $serializer) | ||||||
|  |     { | ||||||
|  |         $this->trans = $trans; | ||||||
|  |         $this->serializer = $serializer; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * @Route( | ||||||
|  |      *  "{_locale}/person/accompanying-period/{id}/work/new", | ||||||
|  |      *  methods={"GET", "POST"} | ||||||
|  |      *  ) | ||||||
|  |      */ | ||||||
|  |     public function createWork(AccompanyingPeriod $period) | ||||||
|  |     { | ||||||
|  |         // TODO ACL | ||||||
|  |  | ||||||
|  |         if ($period->getSocialIssues()->count() === 0) { | ||||||
|  |             $this->addFlash('error', $this->trans->trans( | ||||||
|  |                 "accompanying_work.You must add at least ". | ||||||
|  |                 "one social issue on accompanying period") | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             return $this->redirectToRoute('chill_person_accompanying_course_index', [ | ||||||
|  |                 'accompanying_period_id' => $period->getId() | ||||||
|  |             ]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $json = $this->serializer->normalize($period, 'json', [ "groups" => [ "read" ]]); | ||||||
|  |  | ||||||
|  |         return $this->render('@ChillPerson/AccompanyingCourseWork/create.html.twig', [ | ||||||
|  |             'accompanyingCourse' => $period, | ||||||
|  |             'json' => $json | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Controller; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\CRUD\Controller\ApiController; | ||||||
|  | use Chill\MainBundle\Entity\Address; | ||||||
|  | use Symfony\Component\HttpFoundation\Request; | ||||||
|  | use Symfony\Component\HttpFoundation\Response; | ||||||
|  |  | ||||||
|  | class HouseholdApiController extends ApiController | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     public function householdAddressApi($id, Request $request, string $_format): Response | ||||||
|  |     { | ||||||
|  |         return $this->addRemoveSomething('address', $id, $request, $_format, 'address', Address::class, [ 'groups' => [ 'read' ] ]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -2,19 +2,35 @@ | |||||||
|  |  | ||||||
| namespace Chill\PersonBundle\Controller; | namespace Chill\PersonBundle\Controller; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\Entity\Address; | ||||||
|  | use Chill\PersonBundle\Form\HouseholdType; | ||||||
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||||||
|  | use Symfony\Component\Form\FormInterface; | ||||||
| use Symfony\Component\HttpFoundation\Request; | use Symfony\Component\HttpFoundation\Request; | ||||||
| use Symfony\Component\HttpFoundation\Response; | use Symfony\Component\HttpFoundation\Response; | ||||||
| use Symfony\Component\Routing\Annotation\Route; | use Symfony\Component\Routing\Annotation\Route; | ||||||
|  | use Symfony\Component\Translation\TranslatorInterface; | ||||||
| use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||||||
| use Chill\PersonBundle\Entity\Household\Household; | use Chill\PersonBundle\Entity\Household\Household; | ||||||
| use Chill\PersonBundle\Entity\Household\Position; | use Chill\PersonBundle\Entity\Household\Position; | ||||||
|  | use Chill\PersonBundle\Repository\Household\PositionRepository; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @Route("/{_locale}/person/household") |  * @Route("/{_locale}/person/household") | ||||||
|  */ |  */ | ||||||
| class HouseholdController extends AbstractController | class HouseholdController extends AbstractController | ||||||
| { | { | ||||||
|  |     private TranslatorInterface $translator; | ||||||
|  |  | ||||||
|  |     private PositionRepository $positionRepository; | ||||||
|  |  | ||||||
|  |     public function __construct(TranslatorInterface $translator, PositionRepository $positionRepository) | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         $this->translator = $translator; | ||||||
|  |         $this->positionRepository = $positionRepository; | ||||||
|  |     } | ||||||
|  |      | ||||||
|     /** |     /** | ||||||
|      * @Route( |      * @Route( | ||||||
|      *      "/{household_id}/summary", |      *      "/{household_id}/summary", | ||||||
| @@ -27,48 +43,26 @@ class HouseholdController extends AbstractController | |||||||
|     { |     { | ||||||
|         // TODO ACL |         // TODO ACL | ||||||
|  |  | ||||||
|         $positions = $this->getDoctrine()->getManager() |         $positions = $this->positionRepository | ||||||
|             ->getRepository(Position::class) |             ->findByActiveOrdered() | ||||||
|             ->findAll() |  | ||||||
|             ; |             ; | ||||||
|  |  | ||||||
|         // little performance improvement:  |         // little performance improvement:  | ||||||
|         // initialize members collection, which will avoid |         // initialize members collection, which will avoid | ||||||
|         // some queries |         // some queries | ||||||
|         $household->getMembers()->initialize(); |         $household->getMembers()->initialize(); | ||||||
|  |  | ||||||
|  |         if ($request->query->has('edit')) { | ||||||
|  |             $form = $this->createMetadataForm($household); | ||||||
|  |         } else { | ||||||
|  |             $form = null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return $this->render('@ChillPerson/Household/summary.html.twig', |         return $this->render('@ChillPerson/Household/summary.html.twig', | ||||||
|             [ |             [ | ||||||
|                 'household' => $household, |                 'household' => $household, | ||||||
|                 'positions' => $positions |                 'positions' => $positions, | ||||||
|             ] |                 'form' => NULL !== $form ? $form->createView() : null, | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * @Route( |  | ||||||
|      *      "/{household_id}/members", |  | ||||||
|      *      name="chill_person_household_members", |  | ||||||
|      *      methods={"GET", "HEAD"} |  | ||||||
|      *  ) |  | ||||||
|      *  @ParamConverter("household", options={"id" = "household_id"}) |  | ||||||
|      */ |  | ||||||
|     public function members(Request $request, Household $household) |  | ||||||
|     { |  | ||||||
|         // TODO ACL |  | ||||||
|         $positions = $this->getDoctrine()->getManager() |  | ||||||
|             ->getRepository(Position::class) |  | ||||||
|             ->findAll() |  | ||||||
|             ; |  | ||||||
|  |  | ||||||
|         // little performance improvement:  |  | ||||||
|         // initialize members collection, which will avoid |  | ||||||
|         // some queries |  | ||||||
|         $household->getMembers()->initialize(); |  | ||||||
|  |  | ||||||
|         return $this->render('@ChillPerson/Household/members.html.twig', |  | ||||||
|             [ |  | ||||||
|                 'household' => $household, |  | ||||||
|                 'positions' => $positions |  | ||||||
|             ] |             ] | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| @@ -84,6 +78,16 @@ class HouseholdController extends AbstractController | |||||||
|     public function addresses(Request $request, Household $household) |     public function addresses(Request $request, Household $household) | ||||||
|     { |     { | ||||||
|         // TODO ACL |         // TODO ACL | ||||||
|  |  | ||||||
|  |         //TODO put these lines into a validator constraint on household->getAddress | ||||||
|  |         $addresses = $household->getAddresses(); | ||||||
|  |         $cond = True; | ||||||
|  |         for ($i=0; $i < count($addresses) - 1; $i++) { | ||||||
|  |             if ($addresses[$i]->getValidFrom() != $addresses[$i + 1]->getValidTo()) { | ||||||
|  |                 $cond = False; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return $this->render('@ChillPerson/Household/addresses.html.twig', |         return $this->render('@ChillPerson/Household/addresses.html.twig', | ||||||
|             [ |             [ | ||||||
|                 'household' => $household |                 'household' => $household | ||||||
| @@ -91,6 +95,7 @@ class HouseholdController extends AbstractController | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @Route( |      * @Route( | ||||||
|      *      "/{household_id}/address/move", |      *      "/{household_id}/address/move", | ||||||
| @@ -102,10 +107,83 @@ class HouseholdController extends AbstractController | |||||||
|     public function addressMove(Request $request, Household $household) |     public function addressMove(Request $request, Household $household) | ||||||
|     { |     { | ||||||
|         // TODO ACL |         // TODO ACL | ||||||
|  |  | ||||||
|         return $this->render('@ChillPerson/Household/address_move.html.twig', |         return $this->render('@ChillPerson/Household/address_move.html.twig', | ||||||
|             [ |             [ | ||||||
|                 'household' => $household |                 'household' => $household | ||||||
|             ] |             ] | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @Route( | ||||||
|  |      *      "/{household_id}/address/edit", | ||||||
|  |      *      name="chill_person_household_address_edit", | ||||||
|  |      *      methods={"GET", "HEAD", "POST"} | ||||||
|  |      *  ) | ||||||
|  |      *  @ParamConverter("household", options={"id" = "household_id"}) | ||||||
|  |      */ | ||||||
|  |     public function addressEdit(Request $request, Household $household) | ||||||
|  |     { | ||||||
|  |         // TODO ACL | ||||||
|  |         //$address = $this->findAddressById($household, $address_id); //TODO | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         return $this->render('@ChillPerson/Household/address_edit.html.twig', | ||||||
|  |             [ | ||||||
|  |                 'household' => $household, | ||||||
|  |                 //'address' => $address, | ||||||
|  |             ] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @Route( | ||||||
|  |      *      "/{household_id}/members/metadata/edit", | ||||||
|  |      *      name="chill_person_household_members_metadata_edit", | ||||||
|  |      *      methods={"GET", "POST"} | ||||||
|  |      *  ) | ||||||
|  |      *  @ParamConverter("household", options={"id" = "household_id"}) | ||||||
|  |      */ | ||||||
|  |     public function editHouseholdMetadata(Request $request, Household $household) | ||||||
|  |     { | ||||||
|  |         // TODO ACL | ||||||
|  |         $form = $this->createMetadataForm($household); | ||||||
|  |  | ||||||
|  |         $form->handleRequest($request); | ||||||
|  |  | ||||||
|  |         if ($form->isSubmitted() and $form->isValid()) { | ||||||
|  |             $this->getDoctrine()->getManager()->flush(); | ||||||
|  |  | ||||||
|  |             $this->addFlash('success', $this->translator->trans('household.data_saved')); | ||||||
|  |  | ||||||
|  |             return $this->redirectToRoute('chill_person_household_summary', [ | ||||||
|  |                 'household_id' => $household->getId() | ||||||
|  |             ]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->render('@ChillPerson/Household/edit_member_metadata.html.twig', [ | ||||||
|  |             'household' => $household, | ||||||
|  |             'form' => $form->createView() | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function createMetadataForm(Household $household): FormInterface | ||||||
|  |     { | ||||||
|  |         $form = $this->createForm( | ||||||
|  |             HouseholdType::class, | ||||||
|  |             $household, | ||||||
|  |             [ | ||||||
|  |                 'action' => $this->generateUrl( | ||||||
|  |                     'chill_person_household_members_metadata_edit', | ||||||
|  |                     [ | ||||||
|  |                         'household_id' => $household->getId() | ||||||
|  |                     ] | ||||||
|  |                 ) | ||||||
|  |             ] | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         return $form; | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -50,8 +50,12 @@ class HouseholdMemberController extends ApiController | |||||||
|  |  | ||||||
|         // TODO ACL |         // TODO ACL | ||||||
|         // |         // | ||||||
|         // TODO validation |         $errors = $editor->validate(); | ||||||
|         // |  | ||||||
|  |         if (count($errors) > 0) { | ||||||
|  |             return $this->json($errors, 422); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $em = $this->getDoctrine()->getManager(); |         $em = $this->getDoctrine()->getManager(); | ||||||
|  |  | ||||||
|         // if new household, persist it |         // if new household, persist it | ||||||
| @@ -155,7 +159,9 @@ class HouseholdMemberController extends ApiController | |||||||
|     { |     { | ||||||
|         // TODO ACL |         // TODO ACL | ||||||
|  |  | ||||||
|         $form = $this->createForm(HouseholdMemberType::class, $member); |         $form = $this->createForm(HouseholdMemberType::class, $member, [ | ||||||
|  |             'validation_groups' => [ 'household_memberships' ]  | ||||||
|  |         ]); | ||||||
|         $form->handleRequest($request); |         $form->handleRequest($request); | ||||||
|  |  | ||||||
|         if ($form->isSubmitted() && $form->isValid()) { |         if ($form->isSubmitted() && $form->isValid()) { | ||||||
| @@ -167,7 +173,7 @@ class HouseholdMemberController extends ApiController | |||||||
|  |  | ||||||
|             return $this->redirect( |             return $this->redirect( | ||||||
|                 $request->get('returnPath', null) ?? |                 $request->get('returnPath', null) ?? | ||||||
|                 $this->generator->generate('chill_person_household_members', [ 'household_id' =>  |                 $this->generator->generate('chill_person_household_summary', [ 'household_id' =>  | ||||||
|                     $member->getHousehold()->getId() ]) |                     $member->getHousehold()->getId() ]) | ||||||
|                 ); |                 ); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ class PersonAddressController extends AbstractController | |||||||
|      * @var ValidatorInterface |      * @var ValidatorInterface | ||||||
|      */ |      */ | ||||||
|     protected $validator; |     protected $validator; | ||||||
|      |  | ||||||
|     /** |     /** | ||||||
|      * PersonAddressController constructor. |      * PersonAddressController constructor. | ||||||
|      * |      * | ||||||
| @@ -55,7 +55,7 @@ class PersonAddressController extends AbstractController | |||||||
|     { |     { | ||||||
|         $this->validator = $validator; |         $this->validator = $validator; | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     public function listAction($person_id) |     public function listAction($person_id) | ||||||
|     { |     { | ||||||
|         $person = $this->getDoctrine()->getManager() |         $person = $this->getDoctrine()->getManager() | ||||||
|   | |||||||
| @@ -22,7 +22,8 @@ use Chill\MainBundle\Security\Authorization\AuthorizationHelper; | |||||||
| use Symfony\Component\Security\Core\Role\Role; | use Symfony\Component\Security\Core\Role\Role; | ||||||
| use Chill\MainBundle\CRUD\Controller\ApiController; | use Chill\MainBundle\CRUD\Controller\ApiController; | ||||||
| use Symfony\Component\HttpFoundation\Request; | use Symfony\Component\HttpFoundation\Request; | ||||||
|  | use Symfony\Component\HttpFoundation\Response; | ||||||
|  | use Chill\MainBundle\Entity\Address; | ||||||
|  |  | ||||||
| class PersonApiController extends ApiController | class PersonApiController extends ApiController | ||||||
| { | { | ||||||
| @@ -35,7 +36,7 @@ class PersonApiController extends ApiController | |||||||
|     { |     { | ||||||
|         $this->authorizationHelper = $authorizationHelper; |         $this->authorizationHelper = $authorizationHelper; | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     protected function createEntity(string $action, Request $request): object |     protected function createEntity(string $action, Request $request): object | ||||||
|     { |     { | ||||||
|         $person = parent::createEntity($action, $request); |         $person = parent::createEntity($action, $request); | ||||||
| @@ -47,4 +48,10 @@ class PersonApiController extends ApiController | |||||||
|  |  | ||||||
|         return $person; |         return $person; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function personAddressApi($id, Request $request, string $_format): Response | ||||||
|  |     { | ||||||
|  |         return $this->addRemoveSomething('address', $id, $request, $_format, 'address', Address::class, [ 'groups' => [ 'read' ] ]); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -41,6 +41,8 @@ use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; | |||||||
| use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; | use Chill\PersonBundle\Repository\PersonNotDuplicateRepository; | ||||||
| use Symfony\Component\Validator\Validator\ValidatorInterface; | use Symfony\Component\Validator\Validator\ValidatorInterface; | ||||||
| use Doctrine\ORM\EntityManagerInterface; | use Doctrine\ORM\EntityManagerInterface; | ||||||
|  | use Symfony\Component\Routing\Annotation\Route; | ||||||
|  | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||||||
|  |  | ||||||
| final class PersonController extends AbstractController | final class PersonController extends AbstractController | ||||||
| { | { | ||||||
| @@ -416,4 +418,29 @@ final class PersonController extends AbstractController | |||||||
|  |  | ||||||
|         return $person; |         return $person; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * | ||||||
|  |      * @Route( | ||||||
|  |      *      "/{_locale}/person/household/{person_id}/history", | ||||||
|  |      *      name="chill_person_household_person_history", | ||||||
|  |      *      methods={"GET", "POST"} | ||||||
|  |      *  ) | ||||||
|  |      *  @ParamConverter("person", options={"id" = "person_id"}) | ||||||
|  |      */ | ||||||
|  |     public function householdHistoryByPerson(Request $request, Person $person): Response | ||||||
|  |     { | ||||||
|  |         $this->denyAccessUnlessGranted('CHILL_PERSON_SEE', $person, | ||||||
|  |                 "You are not allowed to see this person."); | ||||||
|  |  | ||||||
|  |         $event = new PrivacyEvent($person); | ||||||
|  |         $this->eventDispatcher->dispatch(PrivacyEvent::PERSON_PRIVACY_EVENT, $event); | ||||||
|  |  | ||||||
|  |         return $this->render( | ||||||
|  |             '@ChillPerson/Person/household_history.html.twig', | ||||||
|  |             [ | ||||||
|  |                 'person' => $person | ||||||
|  |             ] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Controller; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\Serializer\Model\Collection; | ||||||
|  | use Chill\MainBundle\Pagination\PaginatorFactory; | ||||||
|  | use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository; | ||||||
|  | use Chill\MainBundle\CRUD\Controller\ApiController; | ||||||
|  | use Symfony\Component\HttpFoundation\Request; | ||||||
|  | use Symfony\Component\HttpFoundation\JsonResponse; | ||||||
|  |  | ||||||
|  | class SocialWorkSocialActionApiController extends ApiController | ||||||
|  | { | ||||||
|  |     private SocialIssueRepository $socialIssueRepository; | ||||||
|  |  | ||||||
|  |     private PaginatorFactory $paginator; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param SocialIssueRepository $socialIssueRepository | ||||||
|  |      */ | ||||||
|  |     public function __construct(SocialIssueRepository $socialIssueRepository, PaginatorFactory $paginator) | ||||||
|  |     { | ||||||
|  |         $this->socialIssueRepository = $socialIssueRepository; | ||||||
|  |         $this->paginator = $paginator; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     public function listBySocialIssueApi($id, Request $request) | ||||||
|  |     { | ||||||
|  |         $socialIssue = $this->socialIssueRepository | ||||||
|  |             ->find($id); | ||||||
|  |  | ||||||
|  |         if (NULL === $socialIssue) { | ||||||
|  |             throw $this->createNotFoundException("socialIssue not found"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $socialActions = $socialIssue->getRecursiveSocialActions(); | ||||||
|  |         $pagination = $this->paginator->create(count($socialActions)); | ||||||
|  |         // max one page | ||||||
|  |         $pagination->setItemsPerPage(count($socialActions)); | ||||||
|  |  | ||||||
|  |         $collection = new Collection($socialActions, $pagination); | ||||||
|  |          | ||||||
|  |  | ||||||
|  |         return $this->json($collection, JsonResponse::HTTP_OK, [], [ "groups" => [ "read" ]]); | ||||||
|  |     }  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -4,11 +4,15 @@ namespace Chill\PersonBundle\DataFixtures\ORM; | |||||||
|  |  | ||||||
| use Chill\PersonBundle\Entity\Person; | use Chill\PersonBundle\Entity\Person; | ||||||
| use Chill\PersonBundle\Entity\Household\Household; | use Chill\PersonBundle\Entity\Household\Household; | ||||||
|  | use Chill\MainBundle\Entity\PostalCode; | ||||||
|  | use Chill\MainBundle\Entity\Address; | ||||||
| use Chill\PersonBundle\Household\MembersEditorFactory; | use Chill\PersonBundle\Household\MembersEditorFactory; | ||||||
| use Doctrine\Bundle\FixturesBundle\Fixture; | use Doctrine\Bundle\FixturesBundle\Fixture; | ||||||
| use Doctrine\ORM\EntityManagerInterface; | use Doctrine\ORM\EntityManagerInterface; | ||||||
| use Doctrine\Persistence\ObjectManager; | use Doctrine\Persistence\ObjectManager; | ||||||
| use Doctrine\Common\DataFixtures\DependentFixtureInterface; | use Doctrine\Common\DataFixtures\DependentFixtureInterface; | ||||||
|  | use Nelmio\Alice\Loader\NativeLoader; | ||||||
|  | use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes; | ||||||
|  |  | ||||||
| class LoadHousehold extends Fixture implements DependentFixtureInterface | class LoadHousehold extends Fixture implements DependentFixtureInterface | ||||||
| { | { | ||||||
| @@ -16,12 +20,15 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface | |||||||
|  |  | ||||||
|     private EntityManagerInterface $em; |     private EntityManagerInterface $em; | ||||||
|  |  | ||||||
|  |     private NativeLoader $loader; | ||||||
|  |  | ||||||
|     private CONST NUMBER_OF_HOUSEHOLD = 10; |     private CONST NUMBER_OF_HOUSEHOLD = 10; | ||||||
|  |  | ||||||
|     public function __construct(MembersEditorFactory $editorFactory, EntityManagerInterface $em) |     public function __construct(MembersEditorFactory $editorFactory, EntityManagerInterface $em) | ||||||
|     { |     { | ||||||
|         $this->editorFactory = $editorFactory; |         $this->editorFactory = $editorFactory; | ||||||
|         $this->em = $em; |         $this->em = $em; | ||||||
|  |         $this->loader = new NativeLoader(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function load(ObjectManager $manager) |     public function load(ObjectManager $manager) | ||||||
| @@ -53,6 +60,8 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface | |||||||
|             $household = new Household(); |             $household = new Household(); | ||||||
|             $manager->persist($household); |             $manager->persist($household); | ||||||
|  |  | ||||||
|  |             $this->addAddressToHousehold($household, clone $startDate, $manager); | ||||||
|  |  | ||||||
|             $movement = $this->editorFactory->createEditor($household); |             $movement = $this->editorFactory->createEditor($household); | ||||||
|  |  | ||||||
|             // load adults |             // load adults | ||||||
| @@ -89,6 +98,55 @@ class LoadHousehold extends Fixture implements DependentFixtureInterface | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private function addAddressToHousehold(Household $household, \DateTimeImmutable $date, ObjectManager $manager) | ||||||
|  |     { | ||||||
|  |         if (\random_int(0, 10) > 8) { | ||||||
|  |             //  20% of household without address | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $nb = \random_int(1, 6); | ||||||
|  |  | ||||||
|  |         $i = 0; | ||||||
|  |         while ($i < $nb) { | ||||||
|  |             $address = $this->createAddress(); | ||||||
|  |             $address->setValidFrom(\DateTime::createFromImmutable($date));  | ||||||
|  |  | ||||||
|  |             if (\random_int(0, 20) < 1) { | ||||||
|  |                 $date = $date->add(new \DateInterval('P'.\random_int(8, 52).'W')); | ||||||
|  |                 $address->setValidTo(\DateTime::createFromImmutable($date)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $household->addAddress($address); | ||||||
|  |             $manager->persist($address); | ||||||
|  |  | ||||||
|  |             $date = $date->add(new \DateInterval('P'.\random_int(8, 52).'W')); | ||||||
|  |             $i++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function createAddress(): Address | ||||||
|  |     { | ||||||
|  |         $objectSet = $this->loader->loadData([ | ||||||
|  |             Address::class => [ | ||||||
|  |                 'address1' => [ | ||||||
|  |                     'street' => '<fr_FR:streetName()>', | ||||||
|  |                     'streetNumber' => '<fr_FR:buildingNumber()>', | ||||||
|  |                     'postCode' => $this->getPostalCode() | ||||||
|  |                 ] | ||||||
|  |             ] | ||||||
|  |         ]); | ||||||
|  |  | ||||||
|  |         return $objectSet->getObjects()['address1']; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function getPostalCode(): PostalCode | ||||||
|  |     { | ||||||
|  |         $ref = LoadPostalCodes::$refs[\array_rand(LoadPostalCodes::$refs)]; | ||||||
|  |  | ||||||
|  |         return $this->getReference($ref); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private function preparePersonIds() |     private function preparePersonIds() | ||||||
|     { |     { | ||||||
|         $this->personIds = $this->em |         $this->personIds = $this->em | ||||||
|   | |||||||
| @@ -201,7 +201,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac | |||||||
|            'workflows' => [ |            'workflows' => [ | ||||||
|                 'accompanying_period_lifecycle' => [ |                 'accompanying_period_lifecycle' => [ | ||||||
|                     'type' =>  'state_machine', |                     'type' =>  'state_machine', | ||||||
|                     'audit_trail' => [  |                     'audit_trail' => [ | ||||||
|                         'enabled' =>  true |                         'enabled' =>  true | ||||||
|                     ], |                     ], | ||||||
|                     'marking_store' => [ |                     'marking_store' => [ | ||||||
| @@ -354,15 +354,15 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac | |||||||
|                     'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseApiController::class, |                     'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseApiController::class, | ||||||
|                     'actions' => [ |                     'actions' => [ | ||||||
|                         '_entity' => [ |                         '_entity' => [ | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_PATCH => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_PATCH => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_PUT => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_PUT => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                             ], |                             ], | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_GET => true,  |                                 Request::METHOD_GET => true, | ||||||
|                                 Request::METHOD_PUT => true,  |                                 Request::METHOD_PUT => true, | ||||||
|                                 Request::METHOD_PATCH => true,  |                                 Request::METHOD_PATCH => true, | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                         'participation' => [ |                         'participation' => [ | ||||||
| @@ -379,73 +379,86 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac | |||||||
|                         ], |                         ], | ||||||
|                         'resource' => [ |                         'resource' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_DELETE => true, |                                 Request::METHOD_DELETE => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE |                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                         'comment' => [ |                         'comment' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_DELETE => true, |                                 Request::METHOD_DELETE => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE |                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                         'requestor' => [ |                         'requestor' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_DELETE => true, |                                 Request::METHOD_DELETE => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE |                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                         'scope' => [ |                         'scope' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_DELETE => true, |                                 Request::METHOD_DELETE => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE |                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|                         'socialissue' => [ |                         'socialissue' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_DELETE => true, |                                 Request::METHOD_DELETE => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'controller_action' => 'socialIssueApi', |                             'controller_action' => 'socialIssueApi', | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE |                                 Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|  |                         'work' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_POST => true, | ||||||
|  |                                 Request::METHOD_DELETE => false, | ||||||
|  |                                 Request::METHOD_GET => false, | ||||||
|  |                                 Request::METHOD_HEAD => false, | ||||||
|  |                             ], | ||||||
|  |                             'controller_action' => 'workApi', | ||||||
|  |                             'roles' => [ | ||||||
|  |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|  |                                 Request::METHOD_DELETE => 'ALWAYS_FAILS', | ||||||
|  |                             ] | ||||||
|  |                         ], | ||||||
|  |  | ||||||
|                         'confirm' => [ |                         'confirm' => [ | ||||||
|                             'methods' => [ |                             'methods' => [ | ||||||
|                                 Request::METHOD_POST => true,  |                                 Request::METHOD_POST => true, | ||||||
|                                 Request::METHOD_GET => false, |                                 Request::METHOD_GET => false, | ||||||
|                                 Request::METHOD_HEAD => false, |                                 Request::METHOD_HEAD => false, | ||||||
|                             ], |                             ], | ||||||
|                             'roles' => [  |                             'roles' => [ | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE, | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
| @@ -510,8 +523,48 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac | |||||||
|                                 Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE, |                                 Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE, | ||||||
|                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE, |                                 Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE, | ||||||
|  |  | ||||||
|  |                             ], | ||||||
|  |                         ], | ||||||
|  |                         'address' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_POST => true, | ||||||
|  |                                 Request::METHOD_DELETE => true, | ||||||
|  |                                 Request::METHOD_GET => false, | ||||||
|  |                                 Request::METHOD_HEAD => false, | ||||||
|  |                             ], | ||||||
|  |                             'controller_action' => 'personAddressApi' | ||||||
|  |                         ], | ||||||
|  |                     ] | ||||||
|  |                 ], | ||||||
|  |                 [ | ||||||
|  |                     'class' => \Chill\PersonBundle\Entity\Household\Household::class, | ||||||
|  |                     'controller' => \Chill\PersonBundle\Controller\HouseholdApiController::class, | ||||||
|  |                     'name' => 'household', | ||||||
|  |                     'base_path' => '/api/1.0/person/household', | ||||||
|  |                     'base_role' => 'ROLE_USER', | ||||||
|  |                     'actions' => [ | ||||||
|  |                         '_index' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_GET => true, | ||||||
|  |                                 Request::METHOD_HEAD => true, | ||||||
|  |                             ], | ||||||
|  |                         ], | ||||||
|  |                         '_entity' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_GET => true, | ||||||
|  |                                 Request::METHOD_HEAD => true, | ||||||
|  |                                 Request::METHOD_POST=> true, | ||||||
|                             ] |                             ] | ||||||
|                         ], |                         ], | ||||||
|  |                         'address' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_POST => true, | ||||||
|  |                                 Request::METHOD_DELETE => true, | ||||||
|  |                                 Request::METHOD_GET => false, | ||||||
|  |                                 Request::METHOD_HEAD => false, | ||||||
|  |                             ], | ||||||
|  |                             'controller_action' => 'householdAddressApi' | ||||||
|  |                         ], | ||||||
|                     ] |                     ] | ||||||
|                 ], |                 ], | ||||||
|                 [ |                 [ | ||||||
| @@ -533,6 +586,49 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac | |||||||
|                         ], |                         ], | ||||||
|                     ] |                     ] | ||||||
|                 ], |                 ], | ||||||
|  |                 [ | ||||||
|  |                     'class' => \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, | ||||||
|  |                     'name' => 'social_action', | ||||||
|  |                     'base_path' => '/api/1.0/person/social/social-action', | ||||||
|  |                     'controller' => \Chill\PersonBundle\Controller\SocialWorkSocialActionApiController::class, | ||||||
|  |                     // TODO: acl | ||||||
|  |                     'base_role' => 'ROLE_USER', | ||||||
|  |                     'actions' => [ | ||||||
|  |                         '_entity' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_GET => true, | ||||||
|  |                                 Request::METHOD_HEAD => true, | ||||||
|  |                             ], | ||||||
|  |                             'roles' => [ | ||||||
|  |                                 Request::METHOD_GET => 'ROLE_USER', | ||||||
|  |                                 Request::METHOD_HEAD => 'ROLE_USER', | ||||||
|  |                             ] | ||||||
|  |                         ], | ||||||
|  |                         '_index' => [ | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_GET => true, | ||||||
|  |                                 Request::METHOD_HEAD => true, | ||||||
|  |                             ], | ||||||
|  |                             'roles' => [ | ||||||
|  |                                 Request::METHOD_GET => 'ROLE_USER', | ||||||
|  |                                 Request::METHOD_HEAD => 'ROLE_USER', | ||||||
|  |                             ] | ||||||
|  |                         ], | ||||||
|  |                         'listBySocialIssue' => [ | ||||||
|  |                             'single-collection' => 'collection', | ||||||
|  |                             'path' => '/by-social-issue/{id}.{_format}', | ||||||
|  |                             'methods' => [ | ||||||
|  |                                 Request::METHOD_GET => true, | ||||||
|  |                                 Request::METHOD_HEAD => true, | ||||||
|  |                             ], | ||||||
|  |                             'roles' => [ | ||||||
|  |                                 Request::METHOD_GET => 'ROLE_USER', | ||||||
|  |                                 Request::METHOD_HEAD => 'ROLE_USER', | ||||||
|  |                             ] | ||||||
|  |  | ||||||
|  |                         ] | ||||||
|  |                     ] | ||||||
|  |                 ], | ||||||
|             ] |             ] | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ namespace Chill\PersonBundle\Entity; | |||||||
| use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; | use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; | ||||||
| use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; | use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; | ||||||
| use Chill\MainBundle\Entity\Scope; | use Chill\MainBundle\Entity\Scope; | ||||||
|  | use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; | ||||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; | use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive; | ||||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; | use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; | ||||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin; | use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin; | ||||||
| @@ -39,6 +40,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; | |||||||
| use Chill\MainBundle\Entity\User; | use Chill\MainBundle\Entity\User; | ||||||
| use Symfony\Component\Serializer\Annotation\Groups; | use Symfony\Component\Serializer\Annotation\Groups; | ||||||
| use Symfony\Component\Serializer\Annotation\DiscriminatorMap; | use Symfony\Component\Serializer\Annotation\DiscriminatorMap; | ||||||
|  | use Symfony\Component\Validator\Constraints as Assert; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * AccompanyingPeriod Class |  * AccompanyingPeriod Class | ||||||
| @@ -280,6 +282,15 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface | |||||||
|      */ |      */ | ||||||
|     private \DateTimeInterface $updatedAt; |     private \DateTimeInterface $updatedAt; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\OneToMany( | ||||||
|  |      *  targetEntity=AccompanyingPeriodWork::class, | ||||||
|  |      *  mappedBy="accompanyingPeriod" | ||||||
|  |      * ) | ||||||
|  |      * @Assert\Valid(traverse=true) | ||||||
|  |      */ | ||||||
|  |     private Collection $works; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * AccompanyingPeriod constructor. |      * AccompanyingPeriod constructor. | ||||||
|      * |      * | ||||||
| @@ -292,6 +303,7 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface | |||||||
|         $this->scopes = new ArrayCollection(); |         $this->scopes = new ArrayCollection(); | ||||||
|         $this->socialIssues = new ArrayCollection(); |         $this->socialIssues = new ArrayCollection(); | ||||||
|         $this->comments = new ArrayCollection(); |         $this->comments = new ArrayCollection(); | ||||||
|  |         $this->works = new ArrayCollection(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -896,4 +908,28 @@ class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface | |||||||
|  |  | ||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return AccompanyingPeriodWork[] | ||||||
|  |      */ | ||||||
|  |     public function getWorks(): Collection | ||||||
|  |     { | ||||||
|  |         return $this->works; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function addWork(AccompanyingPeriodWork $work): self | ||||||
|  |     { | ||||||
|  |         $this->works[] = $work; | ||||||
|  |         $work->setAccompanyingPeriod($this); | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function removeWork(AccompanyingPeriodWork $work): self | ||||||
|  |     { | ||||||
|  |         $this->work->removeElement($work); | ||||||
|  |         $work->setAccompanyingPeriod(null); | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,86 +7,123 @@ use Chill\PersonBundle\Entity\SocialWork\SocialAction; | |||||||
| use Chill\MainBundle\Entity\User; | use Chill\MainBundle\Entity\User; | ||||||
| use Chill\PersonBundle\Entity\AccompanyingPeriod; | use Chill\PersonBundle\Entity\AccompanyingPeriod; | ||||||
| use Chill\ThirdPartyBundle\Entity\ThirdParty; | use Chill\ThirdPartyBundle\Entity\ThirdParty; | ||||||
|  | use DateTimeInterface; | ||||||
| use Doctrine\Common\Collections\ArrayCollection; | use Doctrine\Common\Collections\ArrayCollection; | ||||||
| use Doctrine\Common\Collections\Collection; | use Doctrine\Common\Collections\Collection; | ||||||
| use Doctrine\ORM\Mapping as ORM; | use Doctrine\ORM\Mapping as ORM; | ||||||
|  | use Symfony\Component\Serializer\Annotation as Serializer; | ||||||
|  | use Chill\MainBundle\Doctrine\Model\TrackCreationInterface; | ||||||
|  | use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface; | ||||||
|  | use Symfony\Component\Validator\Constraints as Assert; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @ORM\Entity |  * @ORM\Entity | ||||||
|  * @ORM\Table(name="chill_person_accompanying_period_work") |  * @ORM\Table(name="chill_person_accompanying_period_work") | ||||||
|  |  * @Serializer\DiscriminatorMap( | ||||||
|  |  *      typeProperty="type", | ||||||
|  |  *      mapping={ | ||||||
|  |  *          "accompanying_period_work":AccompanyingPeriodWork::class | ||||||
|  |  *      } | ||||||
|  |  *  ) | ||||||
|  */ |  */ | ||||||
|  class AccompanyingPeriodWork |  class AccompanyingPeriodWork implements TrackCreationInterface, TrackUpdateInterface | ||||||
| { | { | ||||||
|     /** |     /** | ||||||
|      * @ORM\Id |      * @ORM\Id | ||||||
|      * @ORM\GeneratedValue |      * @ORM\GeneratedValue | ||||||
|      * @ORM\Column(type="integer") |      * @ORM\Column(type="integer") | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $id; |     private ?int $id; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="text") |      * @ORM\Column(type="text") | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $note; |     private string $note = ""; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) |      * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class) | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $accompanyingPeriod; |     private ?AccompanyingPeriod $accompanyingPeriod = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToOne(targetEntity=SocialAction::class) |      * @ORM\ManyToOne(targetEntity=SocialAction::class) | ||||||
|  |      * @Serializer\Groups({"accompanying_period_work:create", "read"}) | ||||||
|      */ |      */ | ||||||
|     private $socialAction; |     private ?SocialAction $socialAction = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="datetime") |      * @ORM\Column(type="datetime_immutable") | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $createdAt; |     private ?\DateTimeImmutable $createdAt = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToOne(targetEntity=User::class) |      * @ORM\ManyToOne(targetEntity=User::class) | ||||||
|      * @ORM\JoinColumn(nullable=false) |      * @ORM\JoinColumn(nullable=false) | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $createdBy; |     private ?User $createdBy = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="datetime") |      * @ORM\Column(type="datetime_immutable") | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $startDate; |     private ?\DateTimeImmutable $updatedAt = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="datetime") |      * @ORM\ManyToOne(targetEntity=User::class) | ||||||
|  |      * @ORM\JoinColumn(nullable=false) | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      */ |      */ | ||||||
|     private $endDate; |     private ?User $updatedBy = null; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="date_immutable") | ||||||
|  |      * @Serializer\Groups({"accompanying_period_work:create"}) | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|  |      */ | ||||||
|  |     private \DateTimeImmutable $startDate; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="date_immutable", nullable=true, options={"default":null}) | ||||||
|  |      * @Serializer\Groups({"accompanying_period_work:create", "read"}) | ||||||
|  |      * @Assert\GreaterThan(propertyPath="startDate", | ||||||
|  |      *    message="accompanying_course_work.The endDate should be greater than the start date" | ||||||
|  |      *  ) | ||||||
|  |      */ | ||||||
|  |     private ?\DateTimeImmutable $endDate = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToOne(targetEntity=ThirdParty::class) |      * @ORM\ManyToOne(targetEntity=ThirdParty::class) | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|      * |      * | ||||||
|      * In schema : traitant |      * In schema : traitant | ||||||
|      */ |      */ | ||||||
|     private $handlingThierParty; |     private ?ThirdParty $handlingThierParty = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="boolean") |      * @ORM\Column(type="boolean") | ||||||
|      */ |      */ | ||||||
|     private $createdAutomatically; |     private bool $createdAutomatically = false; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="text") |      * @ORM\Column(type="text") | ||||||
|      */ |      */ | ||||||
|     private $createdAutomaticallyReason; |     private string $createdAutomaticallyReason = ""; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="accompanyingPeriodWork") |      * @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="accompanyingPeriodWork") | ||||||
|      */ |      */ | ||||||
|     private $goals; |     private Collection $goals; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks") |      * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks") | ||||||
|      * @ORM\JoinTable(name="chill_person_accompanying_period_work_result") |      * @ORM\JoinTable(name="chill_person_accompanying_period_work_result") | ||||||
|      */ |      */ | ||||||
|     private $results; |     private Collection $results; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToMany(targetEntity=ThirdParty::class) |      * @ORM\ManyToMany(targetEntity=ThirdParty::class) | ||||||
| @@ -94,7 +131,7 @@ use Doctrine\ORM\Mapping as ORM; | |||||||
|      * |      * | ||||||
|      * In schema : intervenants |      * In schema : intervenants | ||||||
|     */ |     */ | ||||||
|     private $thirdParties; |     private Collection $thirdParties; | ||||||
|  |  | ||||||
|     public function __construct() |     public function __construct() | ||||||
|     { |     { | ||||||
| @@ -125,8 +162,17 @@ use Doctrine\ORM\Mapping as ORM; | |||||||
|         return $this->accompanyingPeriod; |         return $this->accompanyingPeriod; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Internal: you should use `$accompanyingPeriod->removeWork($work);` or | ||||||
|  |      * `$accompanyingPeriod->addWork($work);` | ||||||
|  |      */ | ||||||
|     public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self |     public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self | ||||||
|     { |     { | ||||||
|  |         if ($this->accompanyingPeriod instanceof AccompanyingPeriod && | ||||||
|  |             $accompanyingPeriod !== $this->accompanyingPeriod) { | ||||||
|  |             throw new \LogicException("A work cannot change accompanyingPeriod"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $this->accompanyingPeriod = $accompanyingPeriod; |         $this->accompanyingPeriod = $accompanyingPeriod; | ||||||
|  |  | ||||||
|         return $this; |         return $this; | ||||||
| @@ -144,7 +190,7 @@ use Doctrine\ORM\Mapping as ORM; | |||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getCreatedAt(): ?\DateTimeInterface |     public function getCreatedAt(): ?\DateTimeImmutable | ||||||
|     { |     { | ||||||
|         return $this->createdAt; |         return $this->createdAt; | ||||||
|     } |     } | ||||||
| @@ -168,6 +214,30 @@ use Doctrine\ORM\Mapping as ORM; | |||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function setUpdatedBy(User $user): TrackUpdateInterface | ||||||
|  |     { | ||||||
|  |         $this->updatedBy = $user; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function setUpdatedAt(DateTimeInterface $datetime): TrackUpdateInterface | ||||||
|  |     { | ||||||
|  |         $this->updatedAt = $datetime; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getUpdatedAt(): ?\DateTimeImmutable | ||||||
|  |     { | ||||||
|  |         return $this->updatedAt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getUpdatedBy(): ?User | ||||||
|  |     { | ||||||
|  |         return $this->updatedBy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public function getStartDate(): ?\DateTimeInterface |     public function getStartDate(): ?\DateTimeInterface | ||||||
|     { |     { | ||||||
|         return $this->startDate; |         return $this->startDate; | ||||||
|   | |||||||
| @@ -7,8 +7,12 @@ use Doctrine\ORM\Mapping as ORM; | |||||||
| use Doctrine\Common\Collections\Collection; | use Doctrine\Common\Collections\Collection; | ||||||
| use Doctrine\Common\Collections\Criteria; | use Doctrine\Common\Collections\Criteria; | ||||||
| use Symfony\Component\Serializer\Annotation as Serializer; | use Symfony\Component\Serializer\Annotation as Serializer; | ||||||
|  | use Symfony\Component\Validator\Constraints as Assert; | ||||||
|  | use Symfony\Component\Validator\Context\ExecutionContextInterface; | ||||||
| use Chill\MainBundle\Entity\Address; | use Chill\MainBundle\Entity\Address; | ||||||
| use Chill\PersonBundle\Entity\Household\HouseholdMember; | use Chill\PersonBundle\Entity\Household\HouseholdMember; | ||||||
|  | use Chill\PersonBundle\Validator\Constraints\Household\MaxHolder; | ||||||
|  | use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @ORM\Entity |  * @ORM\Entity | ||||||
| @@ -18,6 +22,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember; | |||||||
|  * @Serializer\DiscriminatorMap(typeProperty="type", mapping={ |  * @Serializer\DiscriminatorMap(typeProperty="type", mapping={ | ||||||
|  *    "household"=Household::class |  *    "household"=Household::class | ||||||
|  * }) |  * }) | ||||||
|  |  * @MaxHolder(groups={"household_memberships"}) | ||||||
|  */ |  */ | ||||||
| class Household | class Household | ||||||
| { | { | ||||||
| @@ -37,6 +42,7 @@ class Household | |||||||
|      *     cascade={"persist", "remove", "merge", "detach"}) |      *     cascade={"persist", "remove", "merge", "detach"}) | ||||||
|      * @ORM\JoinTable(name="chill_person_household_to_addresses") |      * @ORM\JoinTable(name="chill_person_household_to_addresses") | ||||||
|      * @ORM\OrderBy({"validFrom" = "DESC"}) |      * @ORM\OrderBy({"validFrom" = "DESC"}) | ||||||
|  |      * @Serializer\Groups({"write"}) | ||||||
|      */ |      */ | ||||||
|     private Collection $addresses; |     private Collection $addresses; | ||||||
|  |  | ||||||
| @@ -45,14 +51,30 @@ class Household | |||||||
|      *      targetEntity=HouseholdMember::class, |      *      targetEntity=HouseholdMember::class, | ||||||
|      *      mappedBy="household" |      *      mappedBy="household" | ||||||
|      *  ) |      *  ) | ||||||
|      * @Serializer\Groups({"read"}) |      * @Serializer\Groups({"write", "read"}) | ||||||
|      */ |      */ | ||||||
|     private Collection $members; |     private Collection $members; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_members_") | ||||||
|  |      */ | ||||||
|  |     private CommentEmbeddable $commentMembers; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="boolean", name="waiting_for_birth", options={"default": false}) | ||||||
|  |      */ | ||||||
|  |     private bool $waitingForBirth = false; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="date_immutable", name="waiting_for_birth_date", nullable=true, options={"default": null}) | ||||||
|  |      */ | ||||||
|  |     private ?\DateTimeImmutable $waitingForBirthDate = null; | ||||||
|  |  | ||||||
|     public function __construct() |     public function __construct() | ||||||
|     { |     { | ||||||
|         $this->addresses = new ArrayCollection(); |         $this->addresses = new ArrayCollection(); | ||||||
|         $this->members = new ArrayCollection(); |         $this->members = new ArrayCollection(); | ||||||
|  |         $this->commentMembers = new CommentEmbeddable(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getId(): ?int |     public function getId(): ?int | ||||||
| @@ -66,6 +88,12 @@ class Household | |||||||
|      */ |      */ | ||||||
|     public function addAddress(Address $address) |     public function addAddress(Address $address) | ||||||
|     { |     { | ||||||
|  |         foreach ($this->getAddresses() as $a) { | ||||||
|  |             if ($a->getValidFrom() < $address->getValidFrom() && $a->getValidTo() === NULL) { | ||||||
|  |                 $a->setValidTo($address->getValidFrom()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         $this->addresses[] = $address; |         $this->addresses[] = $address; | ||||||
|  |  | ||||||
|         return $this; |         return $this; | ||||||
| @@ -83,6 +111,7 @@ class Household | |||||||
|      * By default, the addresses are ordered by date, descending (the most |      * By default, the addresses are ordered by date, descending (the most | ||||||
|      * recent first) |      * recent first) | ||||||
|      * |      * | ||||||
|  |      * @Assert\Callback(methods={"validate"}) | ||||||
|      * @return \Chill\MainBundle\Entity\Address[] |      * @return \Chill\MainBundle\Entity\Address[] | ||||||
|      */ |      */ | ||||||
|     public function getAddresses() |     public function getAddresses() | ||||||
| @@ -90,6 +119,27 @@ class Household | |||||||
|         return $this->addresses; |         return $this->addresses; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @Serializer\Groups({ "read" }) | ||||||
|  |      * @Serializer\SerializedName("current_address") | ||||||
|  |      */ | ||||||
|  |     public function getCurrentAddress(\DateTime $at = null): ?Address | ||||||
|  |     { | ||||||
|  |         $at = $at === null ? new \DateTime('today') : $at; | ||||||
|  |  | ||||||
|  |         $addrs = $this->getAddresses()->filter(function (Address $a) use ($at) { | ||||||
|  |             return $a->getValidFrom() < $at && ( | ||||||
|  |                 NULL === $a->getValidTo() || $at < $a->getValidTo() | ||||||
|  |             ); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         if ($addrs->count() > 0) { | ||||||
|  |             return $addrs->first(); | ||||||
|  |         } else { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return Collection|HouseholdMember[] |      * @return Collection|HouseholdMember[] | ||||||
|      */ |      */ | ||||||
| @@ -98,7 +148,59 @@ class Household | |||||||
|         return $this->members; |         return $this->members; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function getMembersOnRange(\DateTimeImmutable $from, ?\DateTimeImmutable $to): Collection | ||||||
|  |     { | ||||||
|  |         $criteria = new Criteria(); | ||||||
|  |         $expr = Criteria::expr(); | ||||||
|  |  | ||||||
|  |         $criteria->where( | ||||||
|  |             $expr->gte('startDate', $from) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         if (NULL !== $to) { | ||||||
|  |             $criteria->andWhere( | ||||||
|  |                 $expr->orX( | ||||||
|  |                     $expr->lte('endDate', $to), | ||||||
|  |                     $expr->eq('endDate', NULL) | ||||||
|  |                 ), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $this->getMembers() | ||||||
|  |             ->matching($criteria) | ||||||
|  |             ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getMembersDuringMembership(HouseholdMember $membership) | ||||||
|  |     { | ||||||
|  |         return $this->getMembersOnRange( | ||||||
|  |             $membership->getStartDate(), | ||||||
|  |             $membership->getEndDate() | ||||||
|  |         )->filter( | ||||||
|  |             function(HouseholdMember $m) use ($membership) { | ||||||
|  |                 return $m !== $membership; | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getMembersHolder(): Collection | ||||||
|  |     { | ||||||
|  |         $criteria = new Criteria(); | ||||||
|  |         $expr = Criteria::expr(); | ||||||
|  |  | ||||||
|  |         $criteria->where( | ||||||
|  |             $expr->eq('holder', true) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         return $this->getMembers()->matching($criteria); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection |     public function getCurrentMembers(?\DateTimeImmutable $now = null): Collection | ||||||
|  |     { | ||||||
|  |         return $this->getMembers()->matching($this->buildCriteriaCurrentMembers($now)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function buildCriteriaCurrentMembers(?\DateTimeImmutable $now = null): Criteria | ||||||
|     { |     { | ||||||
|         $criteria = new Criteria(); |         $criteria = new Criteria(); | ||||||
|         $expr = Criteria::expr(); |         $expr = Criteria::expr(); | ||||||
| @@ -114,7 +216,53 @@ class Household | |||||||
|                 $expr->gte('endDate', $date) |                 $expr->gte('endDate', $date) | ||||||
|             )); |             )); | ||||||
|  |  | ||||||
|         return $this->getMembers()->matching($criteria); |         return $criteria; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return HouseholdMember[] | ||||||
|  |      */ | ||||||
|  |     public function getCurrentMembersOrdered(?\DateTimeImmutable $now = null): Collection | ||||||
|  |     { | ||||||
|  |         $members = $this->getCurrentMembers($now); | ||||||
|  |          | ||||||
|  |         $members->getIterator() | ||||||
|  |             ->uasort( | ||||||
|  |                 function (HouseholdMember $a, HouseholdMember $b) { | ||||||
|  |                     if ($a->getPosition()->getOrdering() < $b->getPosition()->getOrdering()) { | ||||||
|  |                         return -1; | ||||||
|  |                     } | ||||||
|  |                     if ($a->getPosition()->getOrdering() > $b->getPosition()->getOrdering()) { | ||||||
|  |                         return 1; | ||||||
|  |                     } | ||||||
|  |                     if ($a->isHolder() && !$b->isHolder()) { | ||||||
|  |                         return 1; | ||||||
|  |                     } | ||||||
|  |                     if (!$a->isHolder() && $b->isHolder()) { | ||||||
|  |                         return -1; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     return 0; | ||||||
|  |                 } | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         return $members; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * get current members ids | ||||||
|  |      * | ||||||
|  |      * Used in serialization | ||||||
|  |      * | ||||||
|  |      * @Serializer\Groups({"read"}) | ||||||
|  |      * @Serializer\SerializedName("current_members_id") | ||||||
|  |      * | ||||||
|  |      */ | ||||||
|  |     public function getCurrentMembersIds(?\DateTimeImmutable $now = null): Collection | ||||||
|  |     { | ||||||
|  |         return $this->getCurrentMembers($now)->map( | ||||||
|  |             fn (HouseholdMember $m) => $m->getId() | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -192,4 +340,54 @@ class Household | |||||||
|         return $this; |         return $this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function getCommentMembers(): CommentEmbeddable | ||||||
|  |     { | ||||||
|  |         return $this->commentMembers; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function setCommentMembers(CommentEmbeddable $commentMembers): self | ||||||
|  |     { | ||||||
|  |         $this->commentMembers = $commentMembers; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getWaitingForBirth(): bool | ||||||
|  |     { | ||||||
|  |         return $this->waitingForBirth; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function setWaitingForBirth(bool $waitingForBirth): self | ||||||
|  |     { | ||||||
|  |         $this->waitingForBirth = $waitingForBirth; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getWaitingForBirthDate(): ?\DateTimeImmutable | ||||||
|  |     { | ||||||
|  |         return $this->waitingForBirthDate; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function setWaitingForBirthDate(?\DateTimeImmutable $waitingForBirthDate): self | ||||||
|  |     { | ||||||
|  |         $this->waitingForBirthDate = $waitingForBirthDate; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function validate(ExecutionContextInterface $context, $payload) | ||||||
|  |     { | ||||||
|  |         $addresses = $this->getAddresses(); | ||||||
|  |         $cond = True; | ||||||
|  |         for ($i=0; $i < count($addresses) - 1; $i++) { | ||||||
|  |             if ($addresses[$i]->getValidFrom() != $addresses[$i + 1]->getValidTo()) { | ||||||
|  |                 $cond = False; | ||||||
|  |                 $context->buildViolation('The address are not sequentials. The validFrom date of one address should be equal to the validTo date of the previous address.') | ||||||
|  |                     ->atPath('addresses') | ||||||
|  |                     ->addViolation(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         dump($cond); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ use Chill\PersonBundle\Entity\Person; | |||||||
| use Chill\PersonBundle\Entity\Household\Household; | use Chill\PersonBundle\Entity\Household\Household; | ||||||
| use Chill\PersonBundle\Entity\Household\Position; | use Chill\PersonBundle\Entity\Household\Position; | ||||||
| use Symfony\Component\Serializer\Annotation as Serializer; | use Symfony\Component\Serializer\Annotation as Serializer; | ||||||
|  | use Symfony\Component\Validator\Constraints as Assert; | ||||||
|  |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -28,18 +29,25 @@ class HouseholdMember | |||||||
|     /** |     /** | ||||||
|      * @ORM\ManyToOne(targetEntity=Position::class) |      * @ORM\ManyToOne(targetEntity=Position::class) | ||||||
|      * @Serializer\Groups({"read"}) |      * @Serializer\Groups({"read"}) | ||||||
|  |      * @Assert\NotNull(groups={"household_memberships"}) | ||||||
|      */ |      */ | ||||||
|     private ?Position $position = null; |     private ?Position $position = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) |      * @ORM\Column(type="date_immutable", nullable=true, options={"default": null}) | ||||||
|      * @Serializer\Groups({"read"}) |      * @Serializer\Groups({"read"}) | ||||||
|  |      * @Assert\NotNull(groups={"household_memberships"}) | ||||||
|      */ |      */ | ||||||
|     private ?\DateTimeImmutable $startDate = null; |     private ?\DateTimeImmutable $startDate = null; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @ORM\Column(type="date_immutable", nullable= true, options={"default": null}) |      * @ORM\Column(type="date_immutable", nullable= true, options={"default": null}) | ||||||
|      * @Serializer\Groups({"read"}) |      * @Serializer\Groups({"read"}) | ||||||
|  |      * @Assert\GreaterThan( | ||||||
|  |      *      propertyPath="startDate", | ||||||
|  |      *      message="household_membership.The end date must be after start date", | ||||||
|  |      *      groups={"household_memberships"} | ||||||
|  |      * ) | ||||||
|      */ |      */ | ||||||
|     private ?\DateTimeImmutable $endDate = null; |     private ?\DateTimeImmutable $endDate = null; | ||||||
|  |  | ||||||
| @@ -67,6 +75,8 @@ class HouseholdMember | |||||||
|      *  targetEntity="\Chill\PersonBundle\Entity\Person" |      *  targetEntity="\Chill\PersonBundle\Entity\Person" | ||||||
|      * ) |      * ) | ||||||
|      * @Serializer\Groups({"read"}) |      * @Serializer\Groups({"read"}) | ||||||
|  |      * @Assert\Valid(groups={"household_memberships"}) | ||||||
|  |      * @Assert\NotNull(groups={"household_memberships"}) | ||||||
|      */ |      */ | ||||||
|     private ?Person $person = null; |     private ?Person $person = null; | ||||||
|  |  | ||||||
| @@ -76,6 +86,8 @@ class HouseholdMember | |||||||
|      * @ORM\ManyToOne( |      * @ORM\ManyToOne( | ||||||
|      *  targetEntity="\Chill\PersonBundle\Entity\Household\Household" |      *  targetEntity="\Chill\PersonBundle\Entity\Household\Household" | ||||||
|      * ) |      * ) | ||||||
|  |      * @Assert\Valid(groups={"household_memberships"}) | ||||||
|  |      * @Assert\NotNull(groups={"household_memberships"}) | ||||||
|      */ |      */ | ||||||
|     private ?Household $household = null; |     private ?Household $household = null; | ||||||
|  |  | ||||||
| @@ -194,4 +206,13 @@ class HouseholdMember | |||||||
|     { |     { | ||||||
|         return $this->holder; |         return $this->holder; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public function isCurrent(\DateTimeImmutable $at = null): bool | ||||||
|  |     { | ||||||
|  |         $at = NULL === $at ? new \DateTimeImmutable('now'): $at; | ||||||
|  |  | ||||||
|  |         return $this->getStartDate() < $at && ( | ||||||
|  |             NULL === $this->getEndDate() || $at < $this->getEndDate() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,72 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Entity\Household; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\Entity\Address; | ||||||
|  | use Chill\PersonBundle\Entity\Household\Household; | ||||||
|  | use Chill\PersonBundle\Entity\Person; | ||||||
|  | use Doctrine\ORM\Mapping as ORM; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @ORM\Entity(readOnly=true) | ||||||
|  |  * @ORM\Table(name="view_chill_person_household_address") | ||||||
|  |  */ | ||||||
|  | class PersonHouseholdAddress | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="date_immutable") | ||||||
|  |      */ | ||||||
|  |     private $validFrom; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Column(type="date_immutable", nullable=true) | ||||||
|  |      */ | ||||||
|  |     private $validTo; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Id | ||||||
|  |      * @ORM\ManyToOne(targetEntity=Person::class) | ||||||
|  |      * @ORM\JoinColumn(nullable=false) | ||||||
|  |      */ | ||||||
|  |     private $person; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Id | ||||||
|  |      * @ORM\ManyToOne(targetEntity=Household::class) | ||||||
|  |      * @ORM\JoinColumn(nullable=false) | ||||||
|  |      */ | ||||||
|  |     private $household; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\Id | ||||||
|  |      * @ORM\ManyToOne(targetEntity=Address::class) | ||||||
|  |      * @ORM\JoinColumn(nullable=false) | ||||||
|  |      */ | ||||||
|  |     private $address; | ||||||
|  |  | ||||||
|  |     public function getValidFrom(): ?\DateTimeInterface | ||||||
|  |     { | ||||||
|  |         return $this->validFrom; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getValidTo(): ?\DateTimeImmutable | ||||||
|  |     { | ||||||
|  |         return $this->validTo; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getPerson(): ?Person | ||||||
|  |     { | ||||||
|  |         return $this->person; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getHousehold(): ?Household | ||||||
|  |     { | ||||||
|  |         return $this->relation; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getAddress(): ?Address | ||||||
|  |     { | ||||||
|  |         return $this->address; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -41,6 +41,7 @@ use Doctrine\Common\Collections\ArrayCollection; | |||||||
| use Doctrine\Common\Collections\Criteria; | use Doctrine\Common\Collections\Criteria; | ||||||
| use Symfony\Component\Validator\Context\ExecutionContextInterface; | use Symfony\Component\Validator\Context\ExecutionContextInterface; | ||||||
| use Symfony\Component\Serializer\Annotation\DiscriminatorMap; | use Symfony\Component\Serializer\Annotation\DiscriminatorMap; | ||||||
|  | use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Person Class |  * Person Class | ||||||
| @@ -370,6 +371,14 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|      */ |      */ | ||||||
|     private array $currentHouseholdAt = []; |     private array $currentHouseholdAt = []; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @ORM\OneToMany( | ||||||
|  |      *  targetEntity=PersonHouseholdAddress::class, | ||||||
|  |      *  mappedBy="person" | ||||||
|  |      * ) | ||||||
|  |      */ | ||||||
|  |     private Collection $householdAddresses; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Person constructor. |      * Person constructor. | ||||||
|      * |      * | ||||||
| @@ -383,6 +392,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|         $this->altNames = new ArrayCollection(); |         $this->altNames = new ArrayCollection(); | ||||||
|         $this->otherPhoneNumbers = new ArrayCollection(); |         $this->otherPhoneNumbers = new ArrayCollection(); | ||||||
|         $this->householdParticipations = new ArrayCollection(); |         $this->householdParticipations = new ArrayCollection(); | ||||||
|  |         $this->householdAddresses = new ArrayCollection(); | ||||||
|  |  | ||||||
|         if ($opening === null) { |         if ($opening === null) { | ||||||
|             $opening = new \DateTime(); |             $opening = new \DateTime(); | ||||||
| @@ -1294,16 +1304,69 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|         return $this->householdParticipations; |         return $this->householdParticipations; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getCurrentHousehold(?\DateTimeImmutable $at = null): ?Household |     /** | ||||||
|  |      * Get participation where the person does share the household. | ||||||
|  |      * | ||||||
|  |      * Order by startDate, desc | ||||||
|  |      */ | ||||||
|  |     public function getHouseholdParticipationsShareHousehold(): Collection | ||||||
|     { |     { | ||||||
|         $criteria = new Criteria(); |         $criteria = new Criteria(); | ||||||
|         $expr = Criteria::expr(); |         $expr = Criteria::expr(); | ||||||
|         $date = NULL === $at ? new \DateTimeImmutable('now') : $at; |  | ||||||
|  |         $criteria | ||||||
|  |             ->where( | ||||||
|  |                 $expr->eq('shareHousehold', true) | ||||||
|  |             ) | ||||||
|  |             ->orderBy(['startDate' => Criteria::DESC]) | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         return $this->getHouseholdParticipations() | ||||||
|  |             ->matching($criteria) | ||||||
|  |             ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get participation where the person does not share the household. | ||||||
|  |      * | ||||||
|  |      * Order by startDate, desc | ||||||
|  |      */ | ||||||
|  |     public function getHouseholdParticipationsNotShareHousehold(): Collection | ||||||
|  |     { | ||||||
|  |         $criteria = new Criteria(); | ||||||
|  |         $expr = Criteria::expr(); | ||||||
|  |  | ||||||
|  |         $criteria | ||||||
|  |             ->where( | ||||||
|  |                 $expr->eq('shareHousehold', false) | ||||||
|  |             ) | ||||||
|  |             ->orderBy(['startDate' => Criteria::DESC]) | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         return $this->getHouseholdParticipations() | ||||||
|  |             ->matching($criteria) | ||||||
|  |             ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getCurrentHousehold(?\DateTimeImmutable $at = null): ?Household | ||||||
|  |     { | ||||||
|  |         $participation = $this->getCurrentHouseholdParticipationShareHousehold($at); | ||||||
|  |  | ||||||
|  |         return $participation instanceof HouseholdMember ? | ||||||
|  |             $participation->getHousehold() | ||||||
|  |             : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getCurrentHouseholdParticipationShareHousehold(?\DateTimeImmutable $at = null): ?HouseholdMember | ||||||
|  |     { | ||||||
|  |         $criteria = new Criteria(); | ||||||
|  |         $expr = Criteria::expr(); | ||||||
|  |         $date = NULL === $at ? new \DateTimeImmutable('today') : $at; | ||||||
|         $datef = $date->format('Y-m-d'); |         $datef = $date->format('Y-m-d'); | ||||||
|  |  | ||||||
|         if ( |         if ( | ||||||
|             NULL !== ($this->currentHouseholdAt[$datef] ?? NULL)) { |             NULL !== ($this->currentHouseholdParticipationAt[$datef] ?? NULL)) { | ||||||
|             return $this->currentHouseholdAt[$datef]; |             return $this->currentHouseholdParticipationAt[$datef]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         $criteria |         $criteria | ||||||
| @@ -1312,7 +1375,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|                     $expr->lte('startDate', $date), |                     $expr->lte('startDate', $date), | ||||||
|                     $expr->orX( |                     $expr->orX( | ||||||
|                         $expr->isNull('endDate'), |                         $expr->isNull('endDate'), | ||||||
|                         $expr->gte('endDate', $date) |                         $expr->gt('endDate', $date) | ||||||
|                     ), |                     ), | ||||||
|                     $expr->eq('shareHousehold', true) |                     $expr->eq('shareHousehold', true) | ||||||
|                 ) |                 ) | ||||||
| @@ -1323,8 +1386,7 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|             ; |             ; | ||||||
|  |  | ||||||
|         return $participations->count() > 0 ? |         return $participations->count() > 0 ? | ||||||
|             $this->currentHouseholdAt[$datef] = $participations->first() |             $this->currentHouseholdParticipationAt[$datef] = $participations->first() | ||||||
|                 ->getHousehold() |  | ||||||
|             : null; |             : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1333,121 +1395,35 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI | |||||||
|         return NULL !== $this->getCurrentHousehold($at); |         return NULL !== $this->getCurrentHousehold($at); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getGenderComment(): CommentEmbeddable |     public function getHouseholdAddresses(): Collection | ||||||
|     { |     { | ||||||
|         return $this->genderComment; |         return $this->householdAddresses; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function setGenderComment(CommentEmbeddable $genderComment): self |     public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address | ||||||
|     { |     { | ||||||
|         $this->genderComment = $genderComment; |         $at = $at === null ? new \DateTimeImmutable('today') : $at; | ||||||
|  |         $criteria = new Criteria(); | ||||||
|  |         $expr = Criteria::expr(); | ||||||
|  |  | ||||||
|         return $this; |         $criteria->where( | ||||||
|  |                 $expr->lte('validFrom', $at) | ||||||
|  |             ) | ||||||
|  |             ->andWhere( | ||||||
|  |                 $expr->orX( | ||||||
|  |                     $expr->isNull('validTo'), | ||||||
|  |                     $expr->gte('validTo', $at) | ||||||
|  |                 ) | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |         $addrs = $this->getHouseholdAddresses() | ||||||
|  |             ->matching($criteria) | ||||||
|  |             ; | ||||||
|  |  | ||||||
|  |         if ($addrs->count() > 0) { | ||||||
|  |             return $addrs->first()->getAddress(); | ||||||
|  |         } else { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getMaritalStatusComment(): CommentEmbeddable |  | ||||||
|     { |  | ||||||
|         return $this->maritalStatusComment; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setMaritalStatusComment(CommentEmbeddable $maritalStatusComment): self |  | ||||||
|     { |  | ||||||
|         $this->maritalStatusComment = $maritalStatusComment; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getDeathdate(): ?\DateTimeInterface |  | ||||||
|     { |  | ||||||
|         return $this->deathdate; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setDeathdate(?\DateTimeInterface $deathdate): self |  | ||||||
|     { |  | ||||||
|         $this->deathdate = $deathdate; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getMaritalStatusDate(): ?\DateTimeInterface |  | ||||||
|     { |  | ||||||
|         return $this->maritalStatusDate; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setMaritalStatusDate(?\DateTimeInterface $maritalStatusDate): self |  | ||||||
|     { |  | ||||||
|         $this->maritalStatusDate = $maritalStatusDate; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getAcceptSMS(): ?bool |  | ||||||
|     { |  | ||||||
|         return $this->acceptSMS; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setAcceptSMS(bool $acceptSMS): self |  | ||||||
|     { |  | ||||||
|         $this->acceptSMS = $acceptSMS; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getAcceptEmail(): ?bool |  | ||||||
|     { |  | ||||||
|         return $this->acceptEmail; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setAcceptEmail(bool $acceptEmail): self |  | ||||||
|     { |  | ||||||
|         $this->acceptEmail = $acceptEmail; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getNumberOfChildren(): ?int |  | ||||||
|     { |  | ||||||
|         return $this->numberOfChildren; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setNumberOfChildren(int $numberOfChildren): self |  | ||||||
|     { |  | ||||||
|         $this->numberOfChildren = $numberOfChildren; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function getCreatedBy(): ?User |  | ||||||
|     { |  | ||||||
|         return $this->createdBy; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setCreatedBy(User $createdBy): self |  | ||||||
|     { |  | ||||||
|         $this->createdBy = $createdBy; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setCreatedAt(\DateTimeInterface $datetime): self |  | ||||||
|     { |  | ||||||
|         $this->createdAt = $datetime; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setUpdatedBy(User $user): self |  | ||||||
|     { |  | ||||||
|         $this->updatedBy = $user; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public function setUpdatedAt(\DateTimeInterface $datetime): self |  | ||||||
|     { |  | ||||||
|         $this->updatedAt = $datetime; |  | ||||||
|  |  | ||||||
|         return $this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,10 +5,17 @@ namespace Chill\PersonBundle\Entity\SocialWork; | |||||||
| use Doctrine\Common\Collections\ArrayCollection; | use Doctrine\Common\Collections\ArrayCollection; | ||||||
| use Doctrine\Common\Collections\Collection; | use Doctrine\Common\Collections\Collection; | ||||||
| use Doctrine\ORM\Mapping as ORM; | use Doctrine\ORM\Mapping as ORM; | ||||||
|  | use Symfony\Component\Serializer\Annotation as Serializer; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @ORM\Entity |  * @ORM\Entity | ||||||
|  * @ORM\Table(name="chill_person_social_action") |  * @ORM\Table(name="chill_person_social_action") | ||||||
|  |  * @Serializer\DiscriminatorMap( | ||||||
|  |  *      typeProperty="type", | ||||||
|  |  *      mapping={ | ||||||
|  |  *          "social_work_social_action":SocialAction::class | ||||||
|  |  *      } | ||||||
|  |  *  ) | ||||||
|  */ |  */ | ||||||
| class SocialAction | class SocialAction | ||||||
| { | { | ||||||
|   | |||||||
| @@ -35,7 +35,8 @@ class HouseholdMemberType extends AbstractType | |||||||
|         } |         } | ||||||
|         $builder |         $builder | ||||||
|             ->add('comment', ChillTextareaType::class, [ |             ->add('comment', ChillTextareaType::class, [ | ||||||
|                 'label' => 'household.Comment' |                 'label' => 'household.Comment', | ||||||
|  |                 'required' => false | ||||||
|             ]) |             ]) | ||||||
|         ; |         ; | ||||||
|     }  |     }  | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								src/Bundle/ChillPersonBundle/Form/HouseholdType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/Bundle/ChillPersonBundle/Form/HouseholdType.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Form; | ||||||
|  |  | ||||||
|  | use Chill\MainBundle\Form\Type\ChillDateType; | ||||||
|  | use Chill\MainBundle\Form\Type\CommentType; | ||||||
|  | use Chill\PersonBundle\Entity\Household\Household; | ||||||
|  | use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | ||||||
|  | use Symfony\Component\Form\AbstractType; | ||||||
|  | use Symfony\Component\Form\FormBuilderInterface; | ||||||
|  | use Symfony\Component\OptionsResolver\OptionsResolver; | ||||||
|  |  | ||||||
|  | class HouseholdType extends AbstractType | ||||||
|  | { | ||||||
|  |     public function buildForm(FormBuilderInterface $builder, array $options) | ||||||
|  |     { | ||||||
|  |         $builder | ||||||
|  |             ->add('commentMembers', CommentType::class, [ | ||||||
|  |                 'label' => 'household.comment_membership', | ||||||
|  |                 'required' => false  | ||||||
|  |             ]) | ||||||
|  |             ->add('waitingForBirth', CheckboxType::class, [ | ||||||
|  |                 'required' => false, | ||||||
|  |                 'label' => 'household.expecting_birth' | ||||||
|  |             ]) | ||||||
|  |             ->add('waitingForBirthDate', ChillDateType::class, [ | ||||||
|  |                 'required' => false, | ||||||
|  |                 'label' => 'household.date_expecting_birth', | ||||||
|  |                 'input' => 'datetime_immutable' | ||||||
|  |             ]) | ||||||
|  |         ; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function configureOptions(OptionsResolver $resolver) | ||||||
|  |     { | ||||||
|  |         $resolver->setDefaults([ | ||||||
|  |             'data_class' => Household::class, | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,6 +3,7 @@ | |||||||
| namespace Chill\PersonBundle\Household; | namespace Chill\PersonBundle\Household; | ||||||
|  |  | ||||||
| use Symfony\Component\Validator\ConstraintViolationListInterface; | use Symfony\Component\Validator\ConstraintViolationListInterface; | ||||||
|  | use Symfony\Component\Validator\ConstraintViolationList; | ||||||
| use Doctrine\Common\Collections\Criteria; | use Doctrine\Common\Collections\Criteria; | ||||||
| use Chill\PersonBundle\Entity\Household\HouseholdMember; | use Chill\PersonBundle\Entity\Household\HouseholdMember; | ||||||
| use Chill\PersonBundle\Entity\Household\Position; | use Chill\PersonBundle\Entity\Household\Position; | ||||||
| @@ -19,9 +20,11 @@ class MembersEditor | |||||||
|     private array $persistables = []; |     private array $persistables = []; | ||||||
|     private array $membershipsAffected = []; |     private array $membershipsAffected = []; | ||||||
|  |  | ||||||
|  |     public const VALIDATION_GROUP = 'household_memberships'; | ||||||
|  |  | ||||||
|     public function __construct(ValidatorInterface $validator, ?Household $household) |     public function __construct(ValidatorInterface $validator, ?Household $household) | ||||||
|     { |     { | ||||||
|         $this->validation = $validator; |         $this->validator = $validator; | ||||||
|         $this->household = $household; |         $this->household = $household; | ||||||
|     } |     } | ||||||
|      |      | ||||||
| @@ -41,12 +44,12 @@ class MembersEditor | |||||||
|         $this->household->addMember($membership); |         $this->household->addMember($membership); | ||||||
|  |  | ||||||
|         if ($position->getShareHousehold()) { |         if ($position->getShareHousehold()) { | ||||||
|             foreach ($person->getHouseholdParticipations() as $participation) { |             foreach ($person->getHouseholdParticipationsShareHousehold() as $participation) { | ||||||
|                 if (FALSE === $participation->getShareHousehold()) { |                 if ($participation === $membership) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if ($participation === $membership) { |                 if ($participation->getStartDate() > $membership->getStartDate()) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -92,7 +95,18 @@ class MembersEditor | |||||||
|  |  | ||||||
|     public function validate(): ConstraintViolationListInterface |     public function validate(): ConstraintViolationListInterface | ||||||
|     { |     { | ||||||
|  |         if ($this->hasHousehold()) { | ||||||
|  |             $list = $this->validator | ||||||
|  |                 ->validate($this->getHousehold(), null, [ self::VALIDATION_GROUP ]); | ||||||
|  |         } else { | ||||||
|  |             $list = new ConstraintViolationList(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         foreach ($this->membershipsAffected as $m) { | ||||||
|  |             $list->addAll($this->validator->validate($m, null, [ self::VALIDATION_GROUP ])); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $list; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public function getPersistable(): array |     public function getPersistable(): array | ||||||
| @@ -100,6 +114,7 @@ class MembersEditor | |||||||
|         return $this->persistables; |         return $this->persistables; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     public function getHousehold(): ?Household |     public function getHousehold(): ?Household | ||||||
|     { |     { | ||||||
|         return $this->household; |         return $this->household; | ||||||
|   | |||||||
| @@ -35,13 +35,6 @@ class HouseholdMenuBuilder implements LocalMenuBuilderInterface | |||||||
|                 'household_id' => $household->getId() |                 'household_id' => $household->getId() | ||||||
|         ]]) |         ]]) | ||||||
|         ->setExtras(['order' => 10]); |         ->setExtras(['order' => 10]); | ||||||
|  |  | ||||||
|         $menu->addChild($this->translator->trans('household.Household members'), [ |  | ||||||
|             'route' => 'chill_person_household_members', |  | ||||||
|             'routeParameters' => [ |  | ||||||
|                 'household_id' => $household->getId() |  | ||||||
|         ]]) |  | ||||||
|         ->setExtras(['order' => 20]); |  | ||||||
|          |          | ||||||
|         $menu->addChild($this->translator->trans('household.Addresses'), [ |         $menu->addChild($this->translator->trans('household.Addresses'), [ | ||||||
|             'route' => 'chill_person_household_addresses', |             'route' => 'chill_person_household_addresses', | ||||||
|   | |||||||
| @@ -64,6 +64,16 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface | |||||||
|                 'order' => 50 |                 'order' => 50 | ||||||
|             ]); |             ]); | ||||||
|  |  | ||||||
|  |         $menu->addChild($this->translator->trans('household.person history'), [ | ||||||
|  |           'route' => 'chill_person_household_person_history', | ||||||
|  |           'routeParameters' => [ | ||||||
|  |             'person_id' => $parameters['person']->getId() | ||||||
|  |           ] | ||||||
|  |         ]) | ||||||
|  |           ->setExtras([ | ||||||
|  |             'order' => 99999 | ||||||
|  |           ]); | ||||||
|  |  | ||||||
|         $menu->addChild($this->translator->trans('Person duplicate'), [ |         $menu->addChild($this->translator->trans('Person duplicate'), [ | ||||||
|           'route' => 'chill_person_duplicate_view', |           'route' => 'chill_person_duplicate_view', | ||||||
|           'routeParameters' => [ |           'routeParameters' => [ | ||||||
| @@ -71,7 +81,7 @@ class PersonMenuBuilder implements LocalMenuBuilderInterface | |||||||
|           ] |           ] | ||||||
|         ]) |         ]) | ||||||
|           ->setExtras([ |           ->setExtras([ | ||||||
|             'order' => 51 |             'order' => 99999 | ||||||
|           ]); |           ]); | ||||||
|          |          | ||||||
|         if ($this->showAccompanyingPeriod === 'visible') { |         if ($this->showAccompanyingPeriod === 'visible') { | ||||||
|   | |||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Chill\PersonBundle\Repository\Household; | ||||||
|  |  | ||||||
|  | use Chill\PersonBundle\Entity\Household\PersonHouseholdAddress; | ||||||
|  | use Doctrine\ORM\EntityManagerInterface; | ||||||
|  | use Doctrine\ORM\EntityRepository; | ||||||
|  | use Doctrine\Persistence\ObjectRepository; | ||||||
|  |  | ||||||
|  | final class PersonHouseholdAddressRepository implements ObjectRepository | ||||||
|  | { | ||||||
|  |     private EntityRepository $repository; | ||||||
|  |  | ||||||
|  |     public function __construct(EntityManagerInterface $em) | ||||||
|  |     { | ||||||
|  |         $this->repository = $em->getRepository(PersonHouseholdAddress::class); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function find($id, $lockMode = null, $lockVersion = null): ?PersonHouseholdAddress | ||||||
|  |     { | ||||||
|  |         return $this->repository->find($id, $lockMode, $lockVersion); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function findOneBy(array $criteria, array $orderBy = null): ?PersonHouseholdAddress | ||||||
|  |     { | ||||||
|  |         return $this->repository->findOneBy($criteria, $orderBy); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return PersonHouseholdAddress[] | ||||||
|  |      */ | ||||||
|  |     public function findAll(): array | ||||||
|  |     { | ||||||
|  |         return $this->repository->findAll(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return PersonHouseholdAddress[] | ||||||
|  |      */ | ||||||
|  |     public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array | ||||||
|  |     { | ||||||
|  |         return $this->repository->findBy($criteria, $orderBy, $limit, $offset); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getClassName() { | ||||||
|  |         return PersonHouseholdAddress::class; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -7,14 +7,9 @@ use Chill\PersonBundle\Entity\Household\Position; | |||||||
| //use Doctrine\Persistence\ManagerRegistry; | //use Doctrine\Persistence\ManagerRegistry; | ||||||
| use Doctrine\ORM\EntityManagerInterface; | use Doctrine\ORM\EntityManagerInterface; | ||||||
| use Doctrine\ORM\EntityRepository; | use Doctrine\ORM\EntityRepository; | ||||||
|  | use Doctrine\Persistence\ObjectRepository; | ||||||
|  |  | ||||||
| /** | final class PositionRepository implements ObjectRepository | ||||||
|  * @method Position|null find($id, $lockMode = null, $lockVersion = null) |  | ||||||
|  * @method Position|null findOneBy(array $criteria, array $orderBy = null) |  | ||||||
|  * @method Position[]    findAll() |  | ||||||
|  * @method Position[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |  | ||||||
|  */ |  | ||||||
| final class PositionRepository |  | ||||||
| { | { | ||||||
|     private EntityRepository $repository; |     private EntityRepository $repository; | ||||||
|  |  | ||||||
| @@ -30,4 +25,45 @@ final class PositionRepository | |||||||
|     { |     { | ||||||
|         return $this->repository->findAll(); |         return $this->repository->findAll(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return Position[] | ||||||
|  |      */ | ||||||
|  |     public function findByActiveOrdered(): array | ||||||
|  |     { | ||||||
|  |         return $this->repository->createQueryBuilder('p') | ||||||
|  |             ->select('p') | ||||||
|  |             ->orderBy('p.ordering', 'ASC') | ||||||
|  |             ->getQuery() | ||||||
|  |             ->getResult(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return Position[] | ||||||
|  |      */ | ||||||
|  |     public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array | ||||||
|  |     { | ||||||
|  |         return $this->repository->findBy($criteria, $orderBy, $limit, $offset); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return Position[] | ||||||
|  |      */ | ||||||
|  |     public function findOneBy(array $criteria): array | ||||||
|  |     { | ||||||
|  |         return $this->repository->findOneBy($criteria); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return Position | ||||||
|  |      */ | ||||||
|  |     public function find($id) | ||||||
|  |     { | ||||||
|  |         return $this->repository->find($id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getClassName() | ||||||
|  |     { | ||||||
|  |         return Position::class; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,8 +6,9 @@ use Chill\PersonBundle\Entity\SocialWork\SocialIssue; | |||||||
| use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; | ||||||
| use Doctrine\ORM\EntityManagerInterface; | use Doctrine\ORM\EntityManagerInterface; | ||||||
| use Doctrine\ORM\EntityRepository; | use Doctrine\ORM\EntityRepository; | ||||||
|  | use Doctrine\Persistence\ObjectRepository; | ||||||
|  |  | ||||||
| final class SocialIssueRepository | final class SocialIssueRepository implements ObjectRepository | ||||||
| { | { | ||||||
|     private EntityRepository $repository; |     private EntityRepository $repository; | ||||||
|  |  | ||||||
| @@ -15,4 +16,44 @@ final class SocialIssueRepository | |||||||
|     { |     { | ||||||
|         $this->repository = $entityManager->getRepository(SocialIssue::class); |         $this->repository = $entityManager->getRepository(SocialIssue::class); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     public function find($id) | ||||||
|  |     { | ||||||
|  |         return $this->repository->find($id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     public function findAll() | ||||||
|  |     { | ||||||
|  |         return $this->repository->findAll(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) | ||||||
|  |     { | ||||||
|  |         return $this->repository->findBy($criteria, $orderBy, $limit, $offset); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     public function findOneBy(array $criteria) | ||||||
|  |     { | ||||||
|  |         return $this->findOneBy($criteria); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * {@inheritDoc} | ||||||
|  |      */ | ||||||
|  |     public function getClassName() | ||||||
|  |     { | ||||||
|  |         return SocialIssue::class; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,2 +1,3 @@ | |||||||
| require('./sass/person.scss'); | require('./sass/person.scss'); | ||||||
| require('./sass/person_with_period.scss'); | require('./sass/person_with_period.scss'); | ||||||
|  | require('./sass/household_banner.scss'); | ||||||
|   | |||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | import { ShowHide } from 'ShowHide/show_hide.js'; | ||||||
|  |  | ||||||
|  |   let | ||||||
|  |     k = document.getElementById('waitingForBirthContainer'), | ||||||
|  |     waitingForBirthDate = document.getElementById('waitingForBirthDateContainer') | ||||||
|  |   ; | ||||||
|  |  | ||||||
|  |   console.log(k ); | ||||||
|  |  | ||||||
|  |   new ShowHide({ | ||||||
|  |     'container': [waitingForBirthDate], | ||||||
|  |     'froms': [k ], | ||||||
|  |     'event_name': 'input', | ||||||
|  |     'debug': true, | ||||||
|  |     'test': function(froms, event) { | ||||||
|  |       for (let f of froms.values()) { | ||||||
|  |         console.log(f); | ||||||
|  |         for (let input of f.querySelectorAll('input').values()) { | ||||||
|  |           return input.checked; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | .banner-household { | ||||||
|  |   .current-members-explain { | ||||||
|  |     font-weight: 700; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -86,3 +86,83 @@ div.person-view { | |||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  *  HOUSEHOLD | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | div.household__address, div.person__address { | ||||||
|  |     div.row { | ||||||
|  |         height: 100px; | ||||||
|  |         width: 100%; | ||||||
|  |         position: relative; | ||||||
|  |         & > div { | ||||||
|  |             position: absolute; | ||||||
|  |             display: table; | ||||||
|  |             height: 100%; | ||||||
|  |             border: 1px dotted #c3c3c3; | ||||||
|  |         } | ||||||
|  |         div.household__address--date, div.person__address--date { | ||||||
|  |             width: 30%; | ||||||
|  |             background-color: #c3c3c3; | ||||||
|  |             height: 100%; | ||||||
|  |             div.cell { | ||||||
|  |                 box-sizing: border-box; | ||||||
|  |                 position: relative; | ||||||
|  |                 height: 100%; | ||||||
|  |                 width: 100%; | ||||||
|  |                 margin-left: 50%; | ||||||
|  |                 div.pill { | ||||||
|  |                     position: absolute; | ||||||
|  |                     box-sizing: border-box; | ||||||
|  |                     width: 120px; | ||||||
|  |                     height: 40px; | ||||||
|  |                     bottom: -20px; | ||||||
|  |                     background-color: white; | ||||||
|  |                     padding: 10px; | ||||||
|  |                     border-radius: 30px; | ||||||
|  |                     left: -60px; | ||||||
|  |                     text-align: center; | ||||||
|  |                     z-index: 10; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         div.household__address--content, div.person__address--content { | ||||||
|  |             width: 70%; | ||||||
|  |             left: 30%; | ||||||
|  |             text-align: left; | ||||||
|  |             background-color: #ececec; | ||||||
|  |             border: 1px solid #888; | ||||||
|  |             div.cell { | ||||||
|  |                 display: table-cell; | ||||||
|  |                 padding: 5px 30px; | ||||||
|  |                 vertical-align: middle; | ||||||
|  |                 & > div { | ||||||
|  |                     display: flex; | ||||||
|  |                     justify-content: space-between; | ||||||
|  |                 } | ||||||
|  |                 i.dot::before, i.dot::after { | ||||||
|  |                     position: absolute; | ||||||
|  |                     width: 20px; | ||||||
|  |                     height: 20px; | ||||||
|  |                     content: ''; | ||||||
|  |                     border: 0; | ||||||
|  |                     background-color: white; | ||||||
|  |                     border-radius: 50%; | ||||||
|  |                     border: 5px solid #c3c3c3; | ||||||
|  |                     z-index: 10; | ||||||
|  |                     left: -15px; | ||||||
|  |                     bottom: -15px; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | div.household__address-move { | ||||||
|  |     div.household__address-move__create { | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| /// complete and overwrite flex-table in chillmain.scss | /// complete and overwrite flex-table in chillmain.scss | ||||||
| div.list-with-period, | div.list-with-period, | ||||||
| div.list-household-members, | div.list-household-members { | ||||||
| div.list-household-members--summary {  |  | ||||||
|  |  | ||||||
|     .chill-entity__person { |     .chill-entity__person { | ||||||
|        .chill-entity__person__first-name, |        .chill-entity__person__first-name, | ||||||
|   | |||||||
| @@ -0,0 +1,190 @@ | |||||||
|  | <template> | ||||||
|  |  | ||||||
|  |   <h2>{{ $t('pick_social_issue') }}</h2> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   <div id="awc_create_form"> | ||||||
|  |  | ||||||
|  |     <div id="picking"> | ||||||
|  |       <p>{{ $t('pick_social_issue_linked_with_action') }}</p> | ||||||
|  |          | ||||||
|  |       <div v-for="si in socialIssues"> | ||||||
|  |         <input type="radio" v-bind:value="si.id" name="socialIssue" v-model="socialIssuePicked"> {{ si.title.fr }} | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div v-if="hasSocialIssuePicked"> | ||||||
|  |         <h2>{{ $t('pick_an_action') }}</h2> | ||||||
|  |  | ||||||
|  |         <vue-multiselect | ||||||
|  |           v-model="socialActionPicked" | ||||||
|  |           label="text" | ||||||
|  |           :options="socialActionsReachables" | ||||||
|  |           :searchable="true" | ||||||
|  |           :close-on-select="true" | ||||||
|  |           :show-labels="true" | ||||||
|  |           track-by="id" | ||||||
|  |         ></vue-multiselect>  | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div v-if="isLoadingSocialActions"> | ||||||
|  |         <p>spinner</p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <div v-if="hasSocialActionPicked" id="start_date"> | ||||||
|  |       <p><label>{{ $t('startDate') }}</label> <input type="date" v-model="startDate" /></p> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <div v-if="hasSocialActionPicked" id="end_date"> | ||||||
|  |       <p><label>{{ $t('endDate') }}</label> <input type="date" v-model="endDate" /></p> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <div id="confirm"> | ||||||
|  |       <div v-if="hasErrors"> | ||||||
|  |         <p>{{ $t('form_has_errors') }}</p> | ||||||
|  |  | ||||||
|  |         <ul> | ||||||
|  |           <li v-for="e in errors"> | ||||||
|  |             {{ e }} | ||||||
|  |           </li> | ||||||
|  |         </ul> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div> | ||||||
|  |         <ul class="record_actions"> | ||||||
|  |           <li class="cancel"> | ||||||
|  |             <a href="#" class="sc-button bt-cancel"> | ||||||
|  |               {{ $t('action.cancel') }} | ||||||
|  |             </a> | ||||||
|  |           </li> | ||||||
|  |           <li v-if="hasSocialActionPicked"> | ||||||
|  |             <button class="sc-button bt-save" v-show="!isPostingWork" @click="submit"> | ||||||
|  |               {{ $t('action.save') }} | ||||||
|  |             </button> | ||||||
|  |             <button class="sc-button bt-save" v-show="isPostingWork" disabled> | ||||||
|  |               {{ $t('Save') }} | ||||||
|  |             </button> | ||||||
|  |           </li> | ||||||
|  |         </ul> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  |  | ||||||
|  | #awc_create_form { | ||||||
|  |   display: grid; | ||||||
|  |   grid-template-areas:  | ||||||
|  |     "picking picking" | ||||||
|  |     "start_date end_date" | ||||||
|  |     "confirm confirm" | ||||||
|  |     ; | ||||||
|  |   grid-template-columns: 50% 50%; | ||||||
|  |   column-gap: 1.5rem; | ||||||
|  |  | ||||||
|  |   #picking { | ||||||
|  |     grid-area: picking; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #start_date { | ||||||
|  |     grid-area: start_date; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #end_date { | ||||||
|  |     grid-area: end_date; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #confirm { | ||||||
|  |     grid-area: confirm; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import { mapState, mapActions, mapGetters } from 'vuex'; | ||||||
|  | import VueMultiselect from 'vue-multiselect'; | ||||||
|  | import { dateToISO, ISOToDate } from 'ChillMainAssets/js/date.js'; | ||||||
|  |  | ||||||
|  | const i18n = { | ||||||
|  |   messages: { | ||||||
|  |     fr: { | ||||||
|  |       startDate: "Date de début", | ||||||
|  |       endDate: "Date de fin", | ||||||
|  |       form_has_errors: "Le formulaire comporte des erreurs", | ||||||
|  |       pick_social_issue: "Choisir une problématique sociale", | ||||||
|  |       pick_an_action: "Choisir une action d'accompagnement", | ||||||
|  |       pick_social_issue_linked_with_action: "Indiquez la problématique sociale liée à l'action d'accompagnement", | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   name: 'App', | ||||||
|  |   components: { | ||||||
|  |     VueMultiselect, | ||||||
|  |   }, | ||||||
|  |   methods: { | ||||||
|  |     submit() { | ||||||
|  |       this.$store.dispatch('submit'); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   i18n, | ||||||
|  |   computed: { | ||||||
|  |     ...mapState([ | ||||||
|  |       'socialIssues', | ||||||
|  |       'socialActionsReachables', | ||||||
|  |       'errors', | ||||||
|  |     ]), | ||||||
|  |     ...mapGetters([ | ||||||
|  |       'hasSocialIssuePicked', | ||||||
|  |       'hasSocialActionPicked', | ||||||
|  |       'isLoadingSocialActions', | ||||||
|  |       'isPostingWork', | ||||||
|  |       'hasErrors', | ||||||
|  |     ]), | ||||||
|  |     socialIssuePicked: { | ||||||
|  |       get() { | ||||||
|  |         let s = this.$store.state.socialIssuePicked; | ||||||
|  |  | ||||||
|  |         if (s === null) { | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return s.id; | ||||||
|  |       }, | ||||||
|  |       set(value) { | ||||||
|  |         this.$store.dispatch('pickSocialIssue', value); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     socialActionPicked: { | ||||||
|  |       get() { | ||||||
|  |         return this.$store.state.socialActionPicked; | ||||||
|  |       }, | ||||||
|  |       set(value) { | ||||||
|  |         this.$store.commit('setSocialAction', value); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     startDate: { | ||||||
|  |       get() { | ||||||
|  |         let d = this.$store.state.startDate; | ||||||
|  |         return dateToISO(d); | ||||||
|  |       }, | ||||||
|  |       set(value) { | ||||||
|  |         this.$store.commit('setStartDate', ISOToDate(value)); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     endDate: { | ||||||
|  |       get() { | ||||||
|  |         return dateToISO(this.$store.state.endDate); | ||||||
|  |       }, | ||||||
|  |       set(value) { | ||||||
|  |         this.$store.commit('setEndDate', ISOToDate(value)); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | </script> | ||||||
| @@ -0,0 +1,15 @@ | |||||||
|  | import { createApp } from 'vue'; | ||||||
|  | import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'; | ||||||
|  | import { store } from './store'; | ||||||
|  | import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n' | ||||||
|  | import App from './App.vue'; | ||||||
|  |  | ||||||
|  | const i18n = _createI18n(personMessages); | ||||||
|  |  | ||||||
|  | const app = createApp({ | ||||||
|  |    template: `<app></app>`, | ||||||
|  | }) | ||||||
|  | .use(store) | ||||||
|  | .use(i18n) | ||||||
|  | .component('app', App) | ||||||
|  | .mount('#accompanying_course_work_create'); | ||||||
| @@ -0,0 +1,155 @@ | |||||||
|  |  | ||||||
|  | import { createStore } from 'vuex'; | ||||||
|  | import { datetimeToISO } from 'ChillMainAssets/js/date.js'; | ||||||
|  | import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js'; | ||||||
|  | import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js'; | ||||||
|  |  | ||||||
|  | const debug = process.env.NODE_ENV !== 'production'; | ||||||
|  |  | ||||||
|  | const store = createStore({ | ||||||
|  |   strict: debug, | ||||||
|  |   state: { | ||||||
|  |     accompanyingCourse: window.accompanyingCourse, | ||||||
|  |     socialIssues: window.accompanyingCourse.socialIssues, | ||||||
|  |     socialIssuePicked: null, | ||||||
|  |     socialActionsReachables: [], | ||||||
|  |     socialActionPicked: null, | ||||||
|  |     startDate: new Date(), | ||||||
|  |     endDate: null, | ||||||
|  |     isLoadingSocialActions: false, | ||||||
|  |     isPostingWork: false, | ||||||
|  |     errors: [], | ||||||
|  |   }, | ||||||
|  |   getters: { | ||||||
|  |     hasSocialActionPicked(state) { | ||||||
|  |       console.log(state.socialActionPicked); | ||||||
|  |       return null !== state.socialActionPicked; | ||||||
|  |     }, | ||||||
|  |     hasSocialIssuePicked(state) { | ||||||
|  |       console.log(state.socialIssuePicked); | ||||||
|  |       return null !== state.socialIssuePicked; | ||||||
|  |     }, | ||||||
|  |     isLoadingSocialActions(state) { | ||||||
|  |       return state.isLoadingSocialActions; | ||||||
|  |     }, | ||||||
|  |     isPostingWork(state) { | ||||||
|  |       return state.isPostingWork; | ||||||
|  |     }, | ||||||
|  |     buildPayloadCreate(state) { | ||||||
|  |       let payload = { | ||||||
|  |         type: 'accompanying_period_work', | ||||||
|  |         social_action: { | ||||||
|  |           type: 'social_work_social_action', | ||||||
|  |           id: state.socialActionPicked.id | ||||||
|  |         }, | ||||||
|  |         startDate: { | ||||||
|  |           datetime: datetimeToISO(state.startDate) | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       if (null !== state.endDate) { | ||||||
|  |         payload.endDate = { | ||||||
|  |           datetime: datetimeToISO(state.endDate) | ||||||
|  |         }; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return payload; | ||||||
|  |     }, | ||||||
|  |     hasErrors(state) { | ||||||
|  |       return state.errors.length > 0; | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   mutations: { | ||||||
|  |     setSocialActionsReachables(state, actions) { | ||||||
|  |       console.log('set social action reachables'); | ||||||
|  |       console.log(actions); | ||||||
|  |  | ||||||
|  |       state.socialActionsReachables = actions; | ||||||
|  |     }, | ||||||
|  |     setSocialAction(state, socialAction) { | ||||||
|  |       console.log('socialAction', socialAction); | ||||||
|  |       state.socialActionPicked = socialAction; | ||||||
|  |     }, | ||||||
|  |     setSocialIssue(state, socialIssueId) { | ||||||
|  |       if (socialIssueId === null) { | ||||||
|  |         state.socialIssuePicked = null; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       state.socialIssuePicked = state.socialIssues | ||||||
|  |         .find(e => e.id === socialIssueId); | ||||||
|  |     }, | ||||||
|  |     setIsLoadingSocialActions(state, s) { | ||||||
|  |       state.isLoadingSocialActions = s; | ||||||
|  |     }, | ||||||
|  |     setPostingWork(state) { | ||||||
|  |       state.isPostingWork = true; | ||||||
|  |     }, | ||||||
|  |     setStartDate(state, date) { | ||||||
|  |       state.startDate = date; | ||||||
|  |     }, | ||||||
|  |     setEndDate(state, date) { | ||||||
|  |       state.endDate = date; | ||||||
|  |     }, | ||||||
|  |     addErrors(state, { errors, cancel_posting }) { | ||||||
|  |       console.log('add errors', errors); | ||||||
|  |       state.errors = errors; | ||||||
|  |       if (cancel_posting) { | ||||||
|  |         state.isPostingWork = false; | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   actions: { | ||||||
|  |     pickSocialIssue({ commit }, socialIssueId) { | ||||||
|  |       console.log('pick social issue'); | ||||||
|  |  | ||||||
|  |       commit('setIsLoadingSocialActions', true); | ||||||
|  |       commit('setSocialIssue', null); | ||||||
|  |       commit('setSocialActionsReachables', []); | ||||||
|  |  | ||||||
|  |       findSocialActionsBySocialIssue(socialIssueId).then( | ||||||
|  |         (response) => { | ||||||
|  |           console.log(response); | ||||||
|  |           console.log(response.results); | ||||||
|  |           commit('setSocialIssue', socialIssueId); | ||||||
|  |           commit('setSocialActionsReachables', response.results); | ||||||
|  |           commit('setIsLoadingSocialActions', false); | ||||||
|  |         }) | ||||||
|  |         .catch(err => { | ||||||
|  |           console.error(err); | ||||||
|  |         }); | ||||||
|  |     }, | ||||||
|  |     submit({ commit, getters, state }) { | ||||||
|  |       console.log('submit'); | ||||||
|  |       let  | ||||||
|  |         payload = getters.buildPayloadCreate, | ||||||
|  |         errors = []; | ||||||
|  |  | ||||||
|  |       commit('setPostingWork'); | ||||||
|  |  | ||||||
|  |       create(state.accompanyingCourse.id, payload) | ||||||
|  |         .then( ({status, data}) => { | ||||||
|  |           console.log('created return', { status, data}); | ||||||
|  |           if (status === 200) { | ||||||
|  |             console.log('created, nothing to do here any more. Bye-bye!'); | ||||||
|  |             window.location.assign(`/fr/person/accompanying-period/work/${data.id}/edit`); | ||||||
|  |           } else if (status === 422) { | ||||||
|  |             console.log(data); | ||||||
|  |             for (let i in data.violations) { | ||||||
|  |               console.log(i); | ||||||
|  |               console.log(data.violations[i].title); | ||||||
|  |               errors.push(data.violations[i].title); | ||||||
|  |             } | ||||||
|  |             console.log('errors after reseponse handling', errors); | ||||||
|  |             console.log({errors, cancel_posting: true}); | ||||||
|  |             commit('addErrors', { errors, cancel_posting: true }); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     }, | ||||||
|  |   }, | ||||||
|  |   | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export { store }; | ||||||
| @@ -0,0 +1,119 @@ | |||||||
|  | <template> | ||||||
|  |    <div class='household__address-move'> | ||||||
|  |       <div class='household__address-move__create'> | ||||||
|  |          <div> | ||||||
|  |             <h2>{{ $t('create_a_new_address') }}</h2> | ||||||
|  |             <add-address | ||||||
|  |                @addNewAddress="addNewAddress"> | ||||||
|  |             </add-address> | ||||||
|  |          </div> | ||||||
|  |          <div> | ||||||
|  |             <show-address | ||||||
|  |                v-if="newAddress" | ||||||
|  |                v-bind:address="newAddress"> | ||||||
|  |             </show-address> | ||||||
|  |          </div> | ||||||
|  |       </div> | ||||||
|  |       <div class='household__address-move__valid'> | ||||||
|  |          <h2>{{ $t('move_date') }}</h2> | ||||||
|  |          <input | ||||||
|  |             type="date" | ||||||
|  |             name="validFrom" | ||||||
|  |             :placeholder="$t('validFrom')" | ||||||
|  |             v-model="validFrom"/> | ||||||
|  |          <div v-if="errors.length > 0"> | ||||||
|  |             {{ errors }} | ||||||
|  |          </div> | ||||||
|  |       </div> | ||||||
|  |       <div> | ||||||
|  |          <ul class="record_actions sticky-form-buttons"> | ||||||
|  |             <li class="cancel"> | ||||||
|  |                <a :href=backUrl class="sc-button bt-cancel">{{ $t('back_to_the_list') }}</a> | ||||||
|  |             </li> | ||||||
|  |             <li> | ||||||
|  |                <button type="submit" class="sc-button bt-update centered" @click="addToHousehold"> | ||||||
|  |                   {{ $t('add_an_address_to_household') }} | ||||||
|  |                </button> | ||||||
|  |             </li> | ||||||
|  |          </ul> | ||||||
|  |       </div> | ||||||
|  |    </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  |  | ||||||
|  | import AddAddress from 'ChillMainAssets/vuejs/_components/AddAddress.vue'; | ||||||
|  | import ShowAddress from 'ChillMainAssets/vuejs/_components/ShowAddress.vue'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |    name: 'App', | ||||||
|  |    components: { | ||||||
|  |       AddAddress, | ||||||
|  |       ShowAddress | ||||||
|  |    }, | ||||||
|  |    data() { | ||||||
|  |       return { | ||||||
|  |          edit: window.mode === 'edit', | ||||||
|  |          householdId: window.householdId, | ||||||
|  |          backUrl: `/fr/person/household/${householdId}/addresses`, //TODO better way to pass this | ||||||
|  |          validFrom: new Date().toISOString().split('T')[0] | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    computed: { | ||||||
|  |       newAddress() { | ||||||
|  |          return this.$store.state.newAddress; | ||||||
|  |       }, | ||||||
|  |       errors() { | ||||||
|  |          return this.$store.state.errorMsg; | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    methods: { | ||||||
|  |       addNewAddress({ address, modal }) { | ||||||
|  |          console.log('@@@ CLICK button addNewAdress', address); | ||||||
|  |  | ||||||
|  |          let createdAddress = { | ||||||
|  |             'isNoAddress': address.isNoAddress, | ||||||
|  |             'street': address.isNoAddress ? '' : address.street, | ||||||
|  |             'streetNumber': address.isNoAddress ? '' : address.streetNumber, | ||||||
|  |             'postcode': {'id': address.selected.city.id}, | ||||||
|  |             'floor': address.floor, | ||||||
|  |             'corridor': address.corridor, | ||||||
|  |             'steps': address.steps, | ||||||
|  |             'flat': address.flat, | ||||||
|  |             'buildingName': address.buildingName, | ||||||
|  |             'distribution': address.distribution, | ||||||
|  |             'extra': address.extra | ||||||
|  |          }; | ||||||
|  |  | ||||||
|  |          if (address.selected.address.point !== undefined){ | ||||||
|  |             createdAddress = Object.assign(createdAddress, { | ||||||
|  |                'point': address.selected.address.point.coordinates | ||||||
|  |             }); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if(address.writeNewPostalCode){ | ||||||
|  |             let newPostalCode = address.newPostalCode; | ||||||
|  |             newPostalCode = Object.assign(newPostalCode, { | ||||||
|  |                 'country': {'id': address.selected.country.id }, | ||||||
|  |             }); | ||||||
|  |             createdAddress = Object.assign(createdAddress, { | ||||||
|  |                'newPostalCode': newPostalCode | ||||||
|  |             }); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          this.$store.dispatch('addAddress', createdAddress); | ||||||
|  |  | ||||||
|  |          modal.showModal = false; | ||||||
|  |       }, | ||||||
|  |       addToHousehold() { | ||||||
|  |         this.$store.dispatch('addDateToAddressAndAddressToHousehold', { | ||||||
|  |            householdId: this.householdId, | ||||||
|  |            addressId: this.$store.state.newAddress.address_id, | ||||||
|  |            body: { validFrom: {datetime: `${this.validFrom}T00:00:00+0100`}} | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | </script> | ||||||
|  |  | ||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | /* | ||||||
|  | * Endpoint household | ||||||
|  | * method POST, post Household instance | ||||||
|  | * | ||||||
|  | *  @id     integer - id of household | ||||||
|  | *  @body   Object - dictionary with changes to post | ||||||
|  | */ | ||||||
|  | export const postAddressToHousehold = (householdId, addressId) => { | ||||||
|  |    const body = { | ||||||
|  |       'id': addressId | ||||||
|  |    }; | ||||||
|  |    const url = `/api/1.0/person/household/${householdId}/address.json` | ||||||
|  |    return fetch(url, { | ||||||
|  |          method: 'POST', | ||||||
|  |          headers: {'Content-Type': 'application/json;charset=utf-8'}, | ||||||
|  |          body: JSON.stringify(body) | ||||||
|  |       }) | ||||||
|  |       .then(response => { | ||||||
|  |          if (response.ok) { return response.json(); } | ||||||
|  |          throw Error('Error with request resource response'); | ||||||
|  |       }); | ||||||
|  | }; | ||||||
|  |  | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | <template> | ||||||
|  |    <div class="container"> | ||||||
|  |       <VueMultiselect | ||||||
|  |          v-model="value" | ||||||
|  |          @select="selectAddress" | ||||||
|  |          name="field" | ||||||
|  |          track-by="id" | ||||||
|  |          label="value" | ||||||
|  |          :custom-label="transName" | ||||||
|  |          :multiple="false" | ||||||
|  |          :placeholder="$t('select_address')" | ||||||
|  |          :options="addresses"> | ||||||
|  |       </VueMultiselect> | ||||||
|  |    </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import VueMultiselect from 'vue-multiselect'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |    name: 'SelectHouseholdAddress', | ||||||
|  |    components: { VueMultiselect }, | ||||||
|  |    props: ['address'], | ||||||
|  |    data() { | ||||||
|  |       return { | ||||||
|  |          value: null | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    computed: { | ||||||
|  |       addresses() { | ||||||
|  |          return this.address.loaded.addresses; | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    methods: { | ||||||
|  |       transName(value) { | ||||||
|  |          return `${value.text} ${value.postcode.name}` | ||||||
|  |       }, | ||||||
|  |       selectAddress(value) { | ||||||
|  |          this.address.selected.address = value; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style src="vue-multiselect/dist/vue-multiselect.css"></style> | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | import { createApp } from 'vue' | ||||||
|  | import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n' | ||||||
|  | import { appMessages } from './js/i18n' | ||||||
|  | import { store } from './store' | ||||||
|  |  | ||||||
|  | import App from './App.vue'; | ||||||
|  |  | ||||||
|  | const root = window.vueRootComponent; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | *  Load all App component, for Household edition page | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | const i18n = _createI18n(appMessages); | ||||||
|  |  | ||||||
|  | const app = createApp({ | ||||||
|  |    template: `<app></app>`, | ||||||
|  | }) | ||||||
|  | .use(store) | ||||||
|  | .use(i18n) | ||||||
|  | .component('app', App) | ||||||
|  | .mount('#household-address'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | import { addressMessages } from 'ChillMainAssets/vuejs/Address/js/i18n' | ||||||
|  |  | ||||||
|  | const appMessages = { | ||||||
|  |    fr: { | ||||||
|  |       select_a_existing_address: 'Sélectionner une adresse existante', | ||||||
|  |       create_a_new_address: 'Créer une nouvelle adresse', | ||||||
|  |       add_an_address_to_household: 'Déménager le ménage', | ||||||
|  |       validFrom: 'Date du déménagement', | ||||||
|  |       move_date: 'Date du déménagement', | ||||||
|  |       back_to_the_list: 'Retour à la liste' | ||||||
|  |    } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | Object.assign(appMessages.fr, addressMessages.fr); | ||||||
|  |  | ||||||
|  | export { | ||||||
|  |    appMessages | ||||||
|  | }; | ||||||
| @@ -0,0 +1,90 @@ | |||||||
|  | import 'es6-promise/auto'; | ||||||
|  | import { createStore } from 'vuex'; | ||||||
|  |  | ||||||
|  | import { postAddress, postPostalCode, patchAddress } from 'ChillMainAssets/vuejs/_api/AddAddress'; | ||||||
|  | import { postAddressToHousehold } from '../api'; | ||||||
|  |  | ||||||
|  | const debug = process.env.NODE_ENV !== 'production'; | ||||||
|  |  | ||||||
|  | const store = createStore({ | ||||||
|  |    strict: debug, | ||||||
|  |    state: { | ||||||
|  |       newAddress: {}, | ||||||
|  |       household: {}, | ||||||
|  |       validFrom: {}, | ||||||
|  |       errorMsg: [] | ||||||
|  |    }, | ||||||
|  |    getters: { | ||||||
|  |    }, | ||||||
|  |    mutations: { | ||||||
|  |       catchError(state, error) { | ||||||
|  |          state.errorMsg.push(error); | ||||||
|  |       }, | ||||||
|  |       addAddress(state, address) { | ||||||
|  |          console.log('@M addAddress address', address); | ||||||
|  |          state.newAddress = address; | ||||||
|  |       }, | ||||||
|  |       addAddressToHousehold(state, household) { | ||||||
|  |          console.log('@M addAddressToHousehold household', household); | ||||||
|  |          state.household = household; | ||||||
|  |       }, | ||||||
|  |       addDateToAddress(state, validFrom) { | ||||||
|  |          console.log('@M addDateToAddress address.validFrom', validFrom); | ||||||
|  |          state.validFrom = validFrom; | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    actions: { | ||||||
|  |       addAddress({ commit }, payload) { | ||||||
|  |          console.log('@A addAddress payload', payload); | ||||||
|  |  | ||||||
|  |          if('newPostalCode' in payload){ | ||||||
|  |             postPostalCode(payload.newPostalCode) | ||||||
|  |                .then(postalCode => { | ||||||
|  |                   let body = payload; | ||||||
|  |                   body.postcode = {'id': postalCode.id }, | ||||||
|  |                   postAddress(body) | ||||||
|  |                      .then(address => new Promise((resolve, reject) => { | ||||||
|  |                         commit('addAddress', address); | ||||||
|  |                         resolve(); | ||||||
|  |                      })) | ||||||
|  |                      .catch((error) => { | ||||||
|  |                         commit('catchError', error); | ||||||
|  |                      }); | ||||||
|  |                }) | ||||||
|  |  | ||||||
|  |          } else { | ||||||
|  |             postAddress(payload) | ||||||
|  |                .then(address => new Promise((resolve, reject) => { | ||||||
|  |                   commit('addAddress', address); | ||||||
|  |                   resolve(); | ||||||
|  |                })) | ||||||
|  |                .catch((error) => { | ||||||
|  |                   commit('catchError', error); | ||||||
|  |                }); | ||||||
|  |          } | ||||||
|  |       }, | ||||||
|  |       addDateToAddressAndAddressToHousehold({ commit }, payload) { | ||||||
|  |          console.log('@A addDateToAddressAndAddressToHousehold payload', payload); | ||||||
|  |  | ||||||
|  |          patchAddress(payload.addressId, payload.body) | ||||||
|  |             .then(address => new Promise((resolve, reject) => { | ||||||
|  |                commit('addDateToAddress', address.validFrom); | ||||||
|  |                resolve(); | ||||||
|  |             }).then( | ||||||
|  |                postAddressToHousehold(payload.householdId, payload.addressId) | ||||||
|  |                   .then(household => new Promise((resolve, reject) => { | ||||||
|  |                      commit('addAddressToHousehold', household); | ||||||
|  |                      resolve(); | ||||||
|  |                })) | ||||||
|  |                .catch((error) => { | ||||||
|  |                   commit('catchError', error); | ||||||
|  |                }) | ||||||
|  |             )) | ||||||
|  |             .catch((error) => { | ||||||
|  |                commit('catchError', error); | ||||||
|  |             }); | ||||||
|  |       }, | ||||||
|  |    } | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | export { store }; | ||||||
| @@ -1,13 +1,13 @@ | |||||||
| <template> | <template> | ||||||
|   <household></household> |   <household></household> | ||||||
|   <concerned></concerned> |   <concerned v-if="hasHouseholdOrLeave"></concerned> | ||||||
|   <dates></dates> |   <dates v-if="showConfirm"></dates> | ||||||
|   <confirmation></confirmation> |   <confirmation v-if="showConfirm"></confirmation> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script> | <script> | ||||||
|  |  | ||||||
| import { mapState } from 'vuex'; | import { mapGetters } from 'vuex'; | ||||||
| import Concerned from './components/Concerned.vue'; | import Concerned from './components/Concerned.vue'; | ||||||
| import Household from './components/Household.vue'; | import Household from './components/Household.vue'; | ||||||
| import Dates from './components/Dates.vue'; | import Dates from './components/Dates.vue'; | ||||||
| @@ -22,11 +22,14 @@ export default { | |||||||
|     Confirmation, |     Confirmation, | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     // for debugging purpose |     ...mapGetters([ | ||||||
|     // (not working) |       'hasHouseholdOrLeave', | ||||||
|     //...mapState({ |       'hasPersonsWellPositionnated', | ||||||
|     //  'concerned', 'household', 'positions' |     ]), | ||||||
|     //  }) |     showConfirm () { | ||||||
|  |       return this.$store.getters.hasHouseholdOrLeave  | ||||||
|  |         && this.$store.getters.hasPersonsWellPositionnated; | ||||||
|  |     }, | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,32 +16,13 @@ const householdMove = (payload) => { | |||||||
|        if (response.ok) { |        if (response.ok) { | ||||||
|          return response.json(); |          return response.json(); | ||||||
|        } |        } | ||||||
|        throw Error('Error with testing move');  |  | ||||||
|     }); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const householdMoveTest = (payload) => { |  | ||||||
|    const url = `/api/1.0/person/household/members/move/test.json`; |  | ||||||
|    return fetch(url, { |  | ||||||
|      method: 'POST', |  | ||||||
|      headers: { |  | ||||||
|         'Content-Type': 'application/json', |  | ||||||
|      }, |  | ||||||
|      body: JSON.stringify(payload), |  | ||||||
|     }) |  | ||||||
|     .then(response => { |  | ||||||
|        if (response.status === 422) { |        if (response.status === 422) { | ||||||
|          return response.json(); |          return response.json(); | ||||||
|        } |        } | ||||||
|        if (response.ok) { |  | ||||||
|          // return an empty array if ok |  | ||||||
|          return new Promise((resolve, reject) => resolve({ violations: [] }) ); |  | ||||||
|        } |  | ||||||
|        throw Error('Error with testing move');  |        throw Error('Error with testing move');  | ||||||
|     }); |     }); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export {  | export {  | ||||||
|   householdMove, |   householdMove, | ||||||
|   householdMoveTest |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -21,16 +21,13 @@ | |||||||
|       <div v-for="conc in concUnpositionned" |       <div v-for="conc in concUnpositionned" | ||||||
|         class="item-bloc" |         class="item-bloc" | ||||||
|         v-bind:key="conc.person.id" |         v-bind:key="conc.person.id" | ||||||
|         draggable="true" |  | ||||||
|         @dragstart="onStartDragConcern($event, conc.person.id)" |  | ||||||
|       > |       > | ||||||
|         <div class="item-row person"> |         <div class="item-row person"> | ||||||
|           <div class="item-col box-person"> |           <div class="item-col box-person"> | ||||||
|             <div> |             <div> | ||||||
|               <img src="~ChillMainAssets/img/draggable.svg" class="drag-icon" /> |  | ||||||
|               <person :person="conc.person"></person> |               <person :person="conc.person"></person> | ||||||
|             </div> |             </div> | ||||||
|             <div> |             <div v-if="conc.person.birthdate !== null"> | ||||||
|               {{ $t('person.born', {'gender': conc.person.gender} ) }} |               {{ $t('person.born', {'gender': conc.person.gender} ) }} | ||||||
|               {{ $d(conc.person.birthdate.datetime, 'short') }} |               {{ $d(conc.person.birthdate.datetime, 'short') }} | ||||||
|             </div> |             </div> | ||||||
| @@ -89,21 +86,18 @@ | |||||||
|       v-for="position in positions" |       v-for="position in positions" | ||||||
|     > |     > | ||||||
|       <h3>{{ position.label.fr }}</h3> |       <h3>{{ position.label.fr }}</h3> | ||||||
|       <div class="flex-table list-household-members"> |  | ||||||
|  |       <div v-if="concByPosition(position.id).length > 0" class="flex-table list-household-members"> | ||||||
|         <member-details |         <member-details | ||||||
|           v-for="conc in concByPosition(position.id)" |           v-for="conc in concByPosition(position.id)" | ||||||
|           v-bind:key="conc.person.id" |           v-bind:key="conc.person.id" | ||||||
|           v-bind:conc="conc" |           v-bind:conc="conc" | ||||||
|         > |         > | ||||||
|         </member-details> |         </member-details> | ||||||
|         <div |       </div> | ||||||
|           class="droppable_zone" |  | ||||||
|           @drop="onDropConcern($event, position.id)" |       <div v-else> | ||||||
|           @dragover.prevent |         <p class="chill-no-data-statement">{{ $t('household_members_editor.concerned.no_person_in_position') }}</p> | ||||||
|           @dragenter.prevent |  | ||||||
|         > |  | ||||||
|           {{ $t('household_members_editor.drop_persons_here', {'position': position.label.fr }) }} |  | ||||||
|         </div> |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| @@ -120,22 +114,6 @@ div.person { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .drag-icon { |  | ||||||
|   height: 1.1em; |  | ||||||
|   margin-right: 0.5em; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .droppable_zone { |  | ||||||
|   background-color: var(--chill-llight-gray); |  | ||||||
|   color: white; |  | ||||||
|   font-size: large; |  | ||||||
|   text-align: center; |  | ||||||
|   display: table-cell; |  | ||||||
|   vertical-align: middle; |  | ||||||
|   padding: 1em; |  | ||||||
|   background: linear-gradient(to top, var(--chill-light-gray), 30%, var(--chill-llight-gray)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .move_to { | .move_to { | ||||||
|   .move_hint { |   .move_hint { | ||||||
|     text-align: center; |     text-align: center; | ||||||
| @@ -194,18 +172,8 @@ export default { | |||||||
|       this.$refs.addPersons.resetSearch(); // to cast child method |       this.$refs.addPersons.resetSearch(); // to cast child method | ||||||
|       modal.showModal = false; |       modal.showModal = false; | ||||||
|     }, |     }, | ||||||
|     onStartDragConcern(evt, person_id) { |  | ||||||
|       evt.dataTransfer.dropEffect = 'move' |  | ||||||
|       evt.dataTransfer.effectAllowed = 'move' |  | ||||||
|       evt.dataTransfer.setData('application/x.person', person_id) |  | ||||||
|     }, |  | ||||||
|     onDropConcern(evt, position_id) { |  | ||||||
|       const person_id = Number(evt.dataTransfer.getData('application/x.person')); |  | ||||||
|       this.moveToPosition(person_id, position_id); |  | ||||||
|     }, |  | ||||||
|     moveToPosition(person_id, position_id) { |     moveToPosition(person_id, position_id) { | ||||||
|       this.$store.dispatch('markPosition', { person_id, position_id }); |       this.$store.dispatch('markPosition', { person_id, position_id }); | ||||||
|  |  | ||||||
|     }, |     }, | ||||||
|     removeConcerned(conc) { |     removeConcerned(conc) { | ||||||
|       this.$store.dispatch('removeConcerned', conc); |       this.$store.dispatch('removeConcerned', conc); | ||||||
|   | |||||||
| @@ -12,6 +12,9 @@ | |||||||
|     <li v-for="(msg, index) in warnings"> |     <li v-for="(msg, index) in warnings"> | ||||||
|       {{ $t(msg.m, msg.a) }} |       {{ $t(msg.m, msg.a) }} | ||||||
|     </li> |     </li> | ||||||
|  |     <li v-for="msg in errors"> | ||||||
|  |       {{ msg }} | ||||||
|  |     </li> | ||||||
|   </ul>   |   </ul>   | ||||||
|  |  | ||||||
|   <ul class="record_actions sticky-form-buttons"> |   <ul class="record_actions sticky-form-buttons"> | ||||||
| @@ -34,8 +37,9 @@ export default { | |||||||
|   computed: { |   computed: { | ||||||
|     ...mapState({ |     ...mapState({ | ||||||
|       warnings: (state) => state.warnings, |       warnings: (state) => state.warnings, | ||||||
|       hasNoWarnings: (state) => state.warnings.length === 0, |       errors: (state) => state.errors, | ||||||
|       hasWarnings: (state) => state.warnings.length > 0, |       hasNoWarnings: (state) => state.warnings.length === 0 && state.errors.length === 0, | ||||||
|  |       hasWarnings: (state) => state.warnings.length > 0 || state.errors.length > 0, | ||||||
|     }), |     }), | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|   | |||||||
| @@ -2,11 +2,8 @@ | |||||||
|   <h2>{{ $t('household_members_editor.household_part') }}</h2> |   <h2>{{ $t('household_members_editor.household_part') }}</h2> | ||||||
|  |  | ||||||
|   <div v-if="hasHousehold"> |   <div v-if="hasHousehold"> | ||||||
|     <span v-if="isHouseholdNew"> |     <div> | ||||||
|       {{ $t('household_members_editor.household.new_household') }} |       <household-viewer :household="household"></household-viewer> | ||||||
|     </span> |  | ||||||
|     <div v-else> |  | ||||||
|       Ménage existant  |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   <div v-else-if="isForceLeaveWithoutHousehold"> |   <div v-else-if="isForceLeaveWithoutHousehold"> | ||||||
| @@ -39,16 +36,20 @@ | |||||||
| <script> | <script> | ||||||
|  |  | ||||||
| import { mapGetters } from 'vuex'; | import { mapGetters } from 'vuex'; | ||||||
|  | import HouseholdViewer from 'ChillPersonAssets/vuejs/_components/Household/Household.vue'; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|   name: 'Household', |   name: 'Household', | ||||||
|  |   components: { | ||||||
|  |     HouseholdViewer, | ||||||
|  |   }, | ||||||
|   computed: { |   computed: { | ||||||
|     ...mapGetters([ |     ...mapGetters([ | ||||||
|       'hasHousehold', |       'hasHousehold', | ||||||
|       'isHouseholdNew', |       'isHouseholdNew', | ||||||
|     ]), |     ]), | ||||||
|     household() { |     household() { | ||||||
|       return this.$store.household; |       return this.$store.state.household; | ||||||
|     }, |     }, | ||||||
|     allowHouseholdCreate() { |     allowHouseholdCreate() { | ||||||
|       return this.$store.state.allowHouseholdCreate; |       return this.$store.state.allowHouseholdCreate; | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ | |||||||
|             {{ $t('household_members_editor.holder') }} |             {{ $t('household_members_editor.holder') }} | ||||||
|           </span> |           </span> | ||||||
|         </div> |         </div> | ||||||
|         <div>{{ $t('person.born', {'gender': conc.person.gender} ) }}</div> |         <div v-if="conc.person.birthdate !== null">{{ $t('person.born', {'gender': conc.person.gender} ) }}</div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="item-col box-where"> |       <div class="item-col box-where"> | ||||||
|         <ul class="list-content fa-ul"> |         <ul class="list-content fa-ul"> | ||||||
|   | |||||||
| @@ -19,12 +19,13 @@ const appMessages = { | |||||||
|         move_to: "Déplacer vers", |         move_to: "Déplacer vers", | ||||||
|         persons_to_positionnate: 'Usagers à positionner', |         persons_to_positionnate: 'Usagers à positionner', | ||||||
|         persons_leaving: "Usagers quittant leurs ménages", |         persons_leaving: "Usagers quittant leurs ménages", | ||||||
|  |         no_person_in_position: "Aucun usager ne sera ajouté à cette position", | ||||||
|       }, |       }, | ||||||
|       drop_persons_here: "Glissez-déposez ici les usagers pour la position \"{position}\"", |       drop_persons_here: "Glissez-déposez ici les usagers pour la position \"{position}\"", | ||||||
|       all_positionnated: "Tous les usagers sont positionnés", |       all_positionnated: "Tous les usagers sont positionnés", | ||||||
|       holder: "Titulaire", |       holder: "Titulaire", | ||||||
|       is_holder: "Sera titulaire", |       is_holder: "Est titulaire", | ||||||
|       is_not_holder: "Ne sera pas titulaire", |       is_not_holder: "N'est pas titulaire", | ||||||
|       remove_position: "Retirer des {position}", |       remove_position: "Retirer des {position}", | ||||||
|       remove_concerned: "Ne plus transférer", |       remove_concerned: "Ne plus transférer", | ||||||
|       household_part: "Ménage de destination", |       household_part: "Ménage de destination", | ||||||
|   | |||||||
| @@ -19,13 +19,22 @@ const store = createStore({ | |||||||
|   state: { |   state: { | ||||||
|     concerned, |     concerned, | ||||||
|     household: window.household_members_editor_data.household, |     household: window.household_members_editor_data.household, | ||||||
|     positions: window.household_members_editor_data.positions, |     positions: window.household_members_editor_data.positions.sort((a, b) => { | ||||||
|  |         if (a.ordering < b.ordering) { | ||||||
|  |           return -1; | ||||||
|  |         } | ||||||
|  |         if (a.ordering > b.ordering) { | ||||||
|  |           return 1; | ||||||
|  |         } | ||||||
|  |         return 0; | ||||||
|  |       }), | ||||||
|     startDate: new Date(), |     startDate: new Date(), | ||||||
|     allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate, |     allowHouseholdCreate: window.household_members_editor_data.allowHouseholdCreate, | ||||||
|     allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch, |     allowHouseholdSearch: window.household_members_editor_data.allowHouseholdSearch, | ||||||
|     allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold, |     allowLeaveWithoutHousehold: window.household_members_editor_data.allowLeaveWithoutHousehold, | ||||||
|     forceLeaveWithoutHousehold: false, |     forceLeaveWithoutHousehold: false, | ||||||
|     warnings: [], |     warnings: [], | ||||||
|  |     errors: [] | ||||||
|   }, |   }, | ||||||
|   getters: { |   getters: { | ||||||
|     isHouseholdNew(state) { |     isHouseholdNew(state) { | ||||||
| @@ -34,6 +43,13 @@ const store = createStore({ | |||||||
|     hasHousehold(state) { |     hasHousehold(state) { | ||||||
|       return state.household !== null; |       return state.household !== null; | ||||||
|     }, |     }, | ||||||
|  |     hasHouseholdOrLeave(state) { | ||||||
|  |       return state.household !== null || state.forceLeaveWithoutHousehold; | ||||||
|  |     }, | ||||||
|  |     hasPersonsWellPositionnated(state, getters) { | ||||||
|  |       return getters.needsPositionning === false | ||||||
|  |         || (getters.persons.length > 0 && getters.concUnpositionned.length === 0); | ||||||
|  |     }, | ||||||
|     persons(state) {  |     persons(state) {  | ||||||
|       return state.concerned.map(conc => conc.person); |       return state.concerned.map(conc => conc.person); | ||||||
|     }, |     }, | ||||||
| @@ -149,7 +165,7 @@ const store = createStore({ | |||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|     createHousehold(state) { |     createHousehold(state) { | ||||||
|       state.household = { type: 'household', members: [], address: null } |       state.household = { type: 'household', members: [], current_address: null } | ||||||
|       state.forceLeaveWithoutHousehold = false; |       state.forceLeaveWithoutHousehold = false; | ||||||
|     }, |     }, | ||||||
|     forceLeaveWithoutHousehold(state) { |     forceLeaveWithoutHousehold(state) { | ||||||
| @@ -161,6 +177,11 @@ const store = createStore({ | |||||||
|     }, |     }, | ||||||
|     setWarnings(state, warnings) { |     setWarnings(state, warnings) { | ||||||
|       state.warnings = warnings; |       state.warnings = warnings; | ||||||
|  |       // reset errors, which should come from servers | ||||||
|  |       state.errors.splice(0, state.errors.length); | ||||||
|  |     }, | ||||||
|  |     setErrors(state, errors) { | ||||||
|  |       state.errors = errors; | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   actions: { |   actions: { | ||||||
| @@ -172,8 +193,9 @@ const store = createStore({ | |||||||
|       commit('markPosition', { person_id, position_id }); |       commit('markPosition', { person_id, position_id }); | ||||||
|       dispatch('computeWarnings'); |       dispatch('computeWarnings'); | ||||||
|     }, |     }, | ||||||
|     toggleHolder({ commit }, conc) { |     toggleHolder({ commit, dispatch }, conc) { | ||||||
|       commit('toggleHolder', conc); |       commit('toggleHolder', conc); | ||||||
|  |       dispatch('computeWarnings'); | ||||||
|     }, |     }, | ||||||
|     removePosition({ commit, dispatch }, conc) { |     removePosition({ commit, dispatch }, conc) { | ||||||
|       commit('removePosition', conc); |       commit('removePosition', conc); | ||||||
| @@ -191,8 +213,9 @@ const store = createStore({ | |||||||
|       commit('forceLeaveWithoutHousehold'); |       commit('forceLeaveWithoutHousehold'); | ||||||
|       dispatch('computeWarnings'); |       dispatch('computeWarnings'); | ||||||
|     }, |     }, | ||||||
|     setStartDate({ commit }, date) { |     setStartDate({ commit, dispatch }, date) { | ||||||
|       commit('setStartDate', date); |       commit('setStartDate', date); | ||||||
|  |       dispatch('computeWarnings'); | ||||||
|     }, |     }, | ||||||
|     setComment({ commit }, payload) { |     setComment({ commit }, payload) { | ||||||
|       commit('setComment', payload); |       commit('setComment', payload); | ||||||
| @@ -216,19 +239,34 @@ const store = createStore({ | |||||||
|  |  | ||||||
|       commit('setWarnings', warnings); |       commit('setWarnings', warnings); | ||||||
|     }, |     }, | ||||||
|     confirm({ getters, state }) { |     confirm({ getters, state, commit }) { | ||||||
|       let payload = getters.buildPayload, |       let payload = getters.buildPayload, | ||||||
|  |         errors = [], | ||||||
|         person_id, |         person_id, | ||||||
|         household_id; |         household_id, | ||||||
|       householdMove(payload).then(household => { |         error | ||||||
|         if (household === null) { |       ; | ||||||
|           person_id = getters.persons[0].id; |  | ||||||
|           window.location.replace(`/fr/person/${person_id}/general`); |         householdMove(payload).then(household => { | ||||||
|         } else { |           if (household === null) { | ||||||
|           household_id = household.id; |             person_id = getters.persons[0].id; | ||||||
|           // nothing to do anymore here, bye-bye ! |             window.location.replace(`/fr/person/${person_id}/general`); | ||||||
|           window.location.replace(`/fr/person/household/${household_id}/members`); |           } else { | ||||||
|         } |             if (household.type === 'household') { | ||||||
|  |               household_id = household.id; | ||||||
|  |               // nothing to do anymore here, bye-bye ! | ||||||
|  |               window.location.replace(`/fr/person/household/${household_id}/members`); | ||||||
|  |             } else { | ||||||
|  |               // we assume the answer was 422... | ||||||
|  |               error = household; | ||||||
|  |               for (let i in error.violations) { | ||||||
|  |                 let e = error.violations[i]; | ||||||
|  |                 errors.push(e.title);  | ||||||
|  |               } | ||||||
|  |  | ||||||
|  |               commit('setErrors', errors); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | const create = (accompanying_period_id, payload) => { | ||||||
|  |   const url = `/api/1.0/person/accompanying-course/${accompanying_period_id}/work.json`; | ||||||
|  |   let status; | ||||||
|  |   console.log('create', payload); | ||||||
|  |  | ||||||
|  |   return fetch(url, { | ||||||
|  |     method: 'POST', | ||||||
|  |     headers: { | ||||||
|  |       'Content-Type': 'application/json', | ||||||
|  |     }, | ||||||
|  |     body: JSON.stringify(payload), | ||||||
|  |   }) | ||||||
|  |     .then(response => { | ||||||
|  |       if (response.ok || response.status === 422) { | ||||||
|  |         status = response.status; | ||||||
|  |  | ||||||
|  |         return response.json(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       throw new Error("Error while retrieving social actions: " + response.status + | ||||||
|  |         " " + response.statusText); | ||||||
|  |     }) | ||||||
|  |     .then(data => { | ||||||
|  |       return new Promise((resolve, reject) => { | ||||||
|  |         resolve({ status, data }); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export { create }; | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  |  | ||||||
|  | const findSocialActionsBySocialIssue = (id) => { | ||||||
|  |   const url = `/api/1.0/person/social/social-action/by-social-issue/${id}.json`; | ||||||
|  |  | ||||||
|  |   return fetch(url) | ||||||
|  |     .then(response => { | ||||||
|  |       if (!response.ok) { | ||||||
|  |         throw new Error("Error while retrieving social actions " + response.status  | ||||||
|  |           + " " + response.statusText); | ||||||
|  |       } | ||||||
|  |       return response.json(); | ||||||
|  |     }) | ||||||
|  |     .catch(err => { | ||||||
|  |       throw err | ||||||
|  |     }) | ||||||
|  |   ; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export { | ||||||
|  |   findSocialActionsBySocialIssue | ||||||
|  | }; | ||||||
| @@ -0,0 +1,139 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="chill-entity chill-entity__household"> | ||||||
|  |     <!-- identifier --> | ||||||
|  |     <div v-if="isHouseholdNew()" class="identifier"> | ||||||
|  |       <i class="fa fa-home"></i> | ||||||
|  |       {{ $t('new_household') }} | ||||||
|  |     </div> | ||||||
|  |     <div v-else class="identifier"> | ||||||
|  |       <i class="fa fa-home"></i> | ||||||
|  |       {{ $t('household_number', { number: household.id } ) }} | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- member part -->  | ||||||
|  |     <div v-if="hasCurrentMembers" class="members"> | ||||||
|  |       <span class="current-members">{{ $t('current_members') }}: </span> | ||||||
|  |       <template v-for="(m, index) in currentMembers()" :key="m.id"> | ||||||
|  |         <person :person="m.person"></person> | ||||||
|  |         <span v-if="m.holder"> | ||||||
|  |            <span class="badge badge-primary">{{ $t('holder') }}</span> | ||||||
|  |         </span> | ||||||
|  |         <span v-if="index != (currentMembersLength() - 1)">, </span> | ||||||
|  |       </template> | ||||||
|  |     </div> | ||||||
|  |     <div v-else class="members"> | ||||||
|  |       <p class="chill-no-data-statement">{{ $t('no_members_yet') }}</p> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- address part --> | ||||||
|  |     <div v-if="hasAddress()" class="where"> | ||||||
|  |       <i class="fa fa-where"></i> | ||||||
|  |       <show-address :address="household.current_address"></show-address> | ||||||
|  |     </div> | ||||||
|  |     <div v-else class="where"> | ||||||
|  |       <i class="fa fa-where"></i> | ||||||
|  |       <p class="chill-no-data-statement">{{ $t('no_current_address') }}</p> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <style lang="scss"> | ||||||
|  | .chill-entity__household { | ||||||
|  |   display: grid; | ||||||
|  |   grid-template-areas:  | ||||||
|  |     "identifier identifier where" | ||||||
|  |     "who        who        where" | ||||||
|  |     ; | ||||||
|  |   grid-template-columns: | ||||||
|  |     auto        auto       30% | ||||||
|  |     ; | ||||||
|  |  | ||||||
|  |   .identifier { | ||||||
|  |     grid-area: identifier; | ||||||
|  |      | ||||||
|  |     font-size: 1.3em; | ||||||
|  |     font-weight: 700; | ||||||
|  |     color: var(--chill-blue); | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |   .members { | ||||||
|  |     grid-area: who; | ||||||
|  |  | ||||||
|  |     .current-members { | ||||||
|  |       font-weight: 700; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .where { | ||||||
|  |     grid-area: where | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  |  | ||||||
|  | import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue'; | ||||||
|  | import ShowAddress from 'ChillMainAssets/vuejs/_components/ShowAddress.vue'; | ||||||
|  |  | ||||||
|  | const i18n = { | ||||||
|  |   "messages": | ||||||
|  |     { | ||||||
|  |       "fr": | ||||||
|  |       { | ||||||
|  |         "household_number": "Ménage #{number}", | ||||||
|  |         "current_members": "Membres actuels", | ||||||
|  |         "no_current_address": "Sans adresse actuellement", | ||||||
|  |         "new_household": "Nouveau ménage", | ||||||
|  |         "no_members_yet": "Aucun membre actuellement", | ||||||
|  |         "holder": "titulaire", | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |   name: 'Household', | ||||||
|  |   props: ['household'], | ||||||
|  |   components: { | ||||||
|  |     Person, | ||||||
|  |     ShowAddress, | ||||||
|  |   }, | ||||||
|  |   i18n, | ||||||
|  |   methods: { | ||||||
|  |     hasCurrentMembers() { | ||||||
|  |       return this.household.current_members_id.length > 0; | ||||||
|  |     }, | ||||||
|  |     currentMembers() { | ||||||
|  |       return this.household.members.filter(m => this.household.current_members_id.includes(m.id)) | ||||||
|  |         .sort((a, b) => { | ||||||
|  |           if (a.position.ordering < b.position.ordering) { | ||||||
|  |             return -1;   | ||||||
|  |           } | ||||||
|  |           if (a.position.ordering > b.position.ordering) { | ||||||
|  |             return 1; | ||||||
|  |           } | ||||||
|  |           if (a.holder && !b.holder) { | ||||||
|  |             return -1; | ||||||
|  |           } | ||||||
|  |           if (!a.holder && b.holder) { | ||||||
|  |             return 1; | ||||||
|  |           } | ||||||
|  |           return 0; | ||||||
|  |         }); | ||||||
|  |     }, | ||||||
|  |     currentMembersLength() { | ||||||
|  |       return this.household.current_members_id.length; | ||||||
|  |     }, | ||||||
|  |     isHouseholdNew() { | ||||||
|  |       return !Number.isInteger(this.household.id); | ||||||
|  |     }, | ||||||
|  |     hasAddress() { | ||||||
|  |       return this.household.current_address !== null; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | </script> | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| <template> | <template> | ||||||
|   <span class="chill-entity chill-entity__person"> |   <span class="chill-entity chill-entity__person"> | ||||||
|     <span class="chill-entity__person__text"> |     <span class="chill-entity__person__text chill_denomination"> | ||||||
|       {{ person.text }} |       {{ person.text }} | ||||||
|     </span> |     </span> | ||||||
|   </span> |   </span> | ||||||
|   | |||||||
| @@ -18,6 +18,45 @@ | |||||||
|         </div> |         </div> | ||||||
|     {% endif %} |     {% endif %} | ||||||
|  |  | ||||||
|  |     {% if withoutHousehold|length > 0 %} | ||||||
|  |        <div class="alert alert-danger alert-with-actions"> | ||||||
|  |          <div class="message"> | ||||||
|  |         {{ 'Some peoples does not belong to any household currently. Add them to an household soon'|trans }} | ||||||
|  |          </div> | ||||||
|  |          <ul class="record_actions"> | ||||||
|  |            <li> | ||||||
|  |             <button class="btn btn-primary" data-toggle="collapse" href="#withoutHouseholdList"> | ||||||
|  |               {{ 'Add to household now'|trans }} | ||||||
|  |             </button> | ||||||
|  |            </li> | ||||||
|  |          </ul> | ||||||
|  |        </div> | ||||||
|  |  | ||||||
|  |         <div id="withoutHouseholdList" class="collapse"> | ||||||
|  |           <form method="GET" action="{{ chill_path_add_return_path('chill_person_household_members_editor') }}"> | ||||||
|  |  | ||||||
|  |             <h3>{{ 'household.Select people to move'|trans }}</h3> | ||||||
|  |             <ul> | ||||||
|  |               {% for p in withoutHousehold %} | ||||||
|  |               <li> | ||||||
|  |                 <input type="checkbox" name="persons[]" value="{{ p.id }}" /> | ||||||
|  |                 {{ p|chill_entity_render_box }} | ||||||
|  |               </li> | ||||||
|  |               {% endfor %} | ||||||
|  |             </ul> | ||||||
|  |  | ||||||
|  |             <ul class="record_actions"> | ||||||
|  |               <li> | ||||||
|  |                 <button type="submit" class="sc-button bt-edit"> | ||||||
|  |                   {{ 'household.Household editor'|trans }} | ||||||
|  |                 </button> | ||||||
|  |               </li> | ||||||
|  |             </ul> | ||||||
|  |           </form> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |     {% endif %} | ||||||
|  |  | ||||||
|     <h2>{{ 'Associated peoples'|trans }}</h2> |     <h2>{{ 'Associated peoples'|trans }}</h2> | ||||||
|     <div class="flex-table"> |     <div class="flex-table"> | ||||||
|     {% for p in accompanyingCourse.participations %} |     {% for p in accompanyingCourse.participations %} | ||||||
|   | |||||||
| @@ -0,0 +1,24 @@ | |||||||
|  | {% extends '@ChillPerson/AccompanyingCourse/layout.html.twig' %} | ||||||
|  |  | ||||||
|  | {% block title 'accompanying_course_work.Create accompanying course work'|trans %} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  |   <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
|  |   <div id="accompanying_course_work_create"></div> | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block js %} | ||||||
|  |   <script type="text/javascript"> | ||||||
|  |     window.accompanyingCourse = {{ json|json_encode|raw }}; | ||||||
|  |   </script> | ||||||
|  |  | ||||||
|  |   {{ encore_entry_script_tags('accompanying_course_work_create') }} | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block css %} | ||||||
|  |   {{ parent() }} | ||||||
|  |   {{ encore_entry_link_tags('accompanying_course_work_create') }} | ||||||
|  | {% endblock %} | ||||||
| @@ -18,31 +18,27 @@ | |||||||
|  |  | ||||||
| {% set activeRouteKey = '' %} | {% set activeRouteKey = '' %} | ||||||
|  |  | ||||||
| {% block title 'Update address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) %} | {% block title 'Modify address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) %} | ||||||
|  |  | ||||||
| {% block personcontent %} | {% block personcontent %} | ||||||
|  |  | ||||||
|     <h1>{{ 'Update address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1> |     {% block content %} | ||||||
|  |         <h1>{{ block('title') }}</h1> | ||||||
|  |         <div id="address"></div> | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
|     {{ form_start(form) }} |     {% block stylesheets %} | ||||||
|  |         <link href="{{ asset('build/address.css') }}" type="text/css" rel="stylesheet" /> | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
|     {{ form_row(form.isNoAddress) }} |     {% block js %} | ||||||
|     {{ form_row(form.street) }} |         <script type="text/javascript"> | ||||||
|     {{ form_row(form.streetNumber) }} |             window.personId = {{ person.id|e('js') }}; | ||||||
|     {{ form_row(form.postCode) }} |             window.addressId = {{ address.id|e('js') }}; | ||||||
|     {{ form_row(form.validFrom) }} |             window.mode = 'edit'; | ||||||
|  |             window.vueRootComponent = 'app'; | ||||||
|     <ul class="record_actions sticky-form-buttons"> |         </script> | ||||||
|         <li class="cancel"> |         {{ encore_entry_script_tags('address') }} | ||||||
|             <a href="{{ path('chill_person_address_list', { 'person_id' : person.id } ) }}" class="sc-button bt-cancel"> |     {% endblock %} | ||||||
|                     {{ 'Back to the list'|trans }} |  | ||||||
|                 </a> |  | ||||||
|         </li> |  | ||||||
|         <li> |  | ||||||
|             {{ form_row(form.submit, { 'attr' : { 'class': 'sc-button bt-save' }, 'label': 'Save' } ) }} |  | ||||||
|         </li> |  | ||||||
|     </ul> |  | ||||||
|  |  | ||||||
|     {{ form_end(form) }} |  | ||||||
|  |  | ||||||
| {% endblock personcontent %} | {% endblock personcontent %} | ||||||
|   | |||||||
| @@ -26,64 +26,76 @@ | |||||||
|  |  | ||||||
|     <h1>{{ 'Addresses\'history for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1> |     <h1>{{ 'Addresses\'history for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1> | ||||||
|  |  | ||||||
|     <table class="records_list"> |     <div class="person__address"> | ||||||
|         <thead> |  | ||||||
|             <tr> |         <div class="row"> | ||||||
|                 <th>{{ 'Valid from'|trans }}</th> |             <div class="person__address--date"></div> | ||||||
|                 <th>{{ 'Address'|trans }}</th> |             <div class="person__address--content"> | ||||||
|                 <th> </th> |                 <div class="cell"> | ||||||
|             </tr> |                     <div> | ||||||
|         </thead> |                         {% if person.addresses|length == 0 %} | ||||||
|         <tbody> |                         <span class="chill-no-data-statement">{{ 'No address given'|trans }}</span> | ||||||
|             {% if person.addresses|length == 0 %} |                         {% endif %} | ||||||
|             <tr> |                         <a class="sc-button bt-create" | ||||||
|                 <td colspan="3"> |                         href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}"> | ||||||
|                     <span class="chill-no-data-statement">{{ 'No address given'|trans }}</span> |  | ||||||
|                     <a href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}"> |  | ||||||
|                         {{ 'Add an address'|trans }} |                         {{ 'Add an address'|trans }} | ||||||
|                     </a> |                         </a> | ||||||
|                 </td> |                     </div> | ||||||
|             </tr> |                 </div> | ||||||
|             {% else %} |             </div> | ||||||
|             {% for address in person.addresses %} |         </div> | ||||||
|             <tr> |  | ||||||
|                 <td><strong>{{ 'Since %date%'|trans( { '%date%' : address.validFrom|format_date('long') } ) }}</strong></td> |  | ||||||
|  |  | ||||||
|                 <td> |         {% for address in person.addresses %} | ||||||
|                 {{ address_macros._render(address, { 'with_valid_from' : false, 'has_no_address': true } ) }} |         <div class="row"> | ||||||
|                 </td> |             <div class="person__address--date"> | ||||||
|  |                 <div class="cell"> | ||||||
|  |                     <div class="pill"> | ||||||
|  |                     {% if address.validFrom is not empty %} | ||||||
|  |                     {{ address.validFrom|format_date('long') }} | ||||||
|  |                     {% endif %} | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="person__address--content"> | ||||||
|  |                 <div class="cell"> | ||||||
|  |                     <i class="dot"></i> | ||||||
|  |                     <div> | ||||||
|  |                         {% if address.isNoAddress == true %} | ||||||
|  |                         <div class="chill_address_is_noaddress">{{ 'address.consider homeless'|trans }}</div> | ||||||
|  |                         {% else %} | ||||||
|  |                         <div> | ||||||
|  |                             {% if address.street is not empty %} | ||||||
|  |                             <div class="street"> | ||||||
|  |                                 <i class="fa fa-fw fa-map-marker"></i> | ||||||
|  |                                 <span class="streetNumber">{{ address.street }}</span> | ||||||
|  |                                 {% if address.streetNumber is not empty %} | ||||||
|  |                                     <span class="streetNumber">, {{ address.streetNumber }}</span> | ||||||
|  |                                 {% endif %} | ||||||
|  |                             </div> | ||||||
|  |                             {% endif %} | ||||||
|  |                             {% if address.postCode is not empty %} | ||||||
|  |                             <div class="postCode"> | ||||||
|  |                                 <span>{{ address.postCode.code }}</span> <span>{{ address.postCode.name }}</span> | ||||||
|  |                                 <span class="country">({{ address.postCode.country.name|localize_translatable_string }})</span> | ||||||
|  |                             </div> | ||||||
|  |                             {% endif %} | ||||||
|  |                         </div> | ||||||
|  |                         {% endif %} | ||||||
|  |                         <a href="{{ path('chill_person_address_edit', { 'person_id': person.id, 'address_id' : address.id } ) }}" class="sc-button bt-edit"></a> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         {% endfor %} | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|                 <td> |     <ul class="record_actions"> | ||||||
|                     <ul class="record_actions"> |         <li class="cancel"> | ||||||
|                         <li> |             <a href="{{ path('chill_person_view', { 'person_id' : person.id } ) }}" class="sc-button bt-cancel"> | ||||||
|                             <a href="{{ path('chill_person_address_edit', { 'person_id': person.id, 'address_id' : address.id } ) }}" class="sc-button bt-edit"></a> |                     {{ 'Back to the person details'|trans }} | ||||||
|                         </li> |                 </a> | ||||||
|                     </ul> |         </li> | ||||||
|                 </td> |     </ul> | ||||||
|             </tr> |  | ||||||
|  |  | ||||||
|             {% endfor %} |  | ||||||
|             {% endif %} |  | ||||||
|         </tbody> |  | ||||||
|     </table> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         <ul class="record_actions"> |  | ||||||
|             <li class="cancel"> |  | ||||||
|                 <a href="{{ path('chill_person_view', { 'person_id' : person.id } ) }}" class="sc-button bt-cancel"> |  | ||||||
|                         {{ 'Back to the person details'|trans }} |  | ||||||
|                     </a> |  | ||||||
|             </li> |  | ||||||
|             <li> |  | ||||||
|  |  | ||||||
|                 <a href="{{ path('chill_person_address_new', { 'person_id' : person.id } ) }}" class="sc-button bt-create"> |  | ||||||
|                         {{ 'Add an address'|trans }} |  | ||||||
|                     </a> |  | ||||||
|             </li> |  | ||||||
|         </ul> |  | ||||||
|  |  | ||||||
| {% endblock personcontent %} | {% endblock personcontent %} | ||||||
|   | |||||||
| @@ -22,36 +22,6 @@ | |||||||
|  |  | ||||||
| {% block personcontent %} | {% block personcontent %} | ||||||
|  |  | ||||||
|     <h1>{{ 'New address for %name%'|trans({ '%name%': person.firstName ~ ' ' ~ person.lastName } ) }}</h1> |  | ||||||
|  |  | ||||||
|     {{ form_start(form) }} |  | ||||||
|  |  | ||||||
|     {{ form_row(form.isNoAddress) }} |  | ||||||
|     {{ form_row(form.street) }} |  | ||||||
|     {{ form_errors(form.street) }} |  | ||||||
|     {{ form_row(form.streetNumber) }} |  | ||||||
|     {{ form_errors(form.streetNumber) }} |  | ||||||
|     {{ form_row(form.postCode) }} |  | ||||||
|     {{ form_errors(form.postCode) }} |  | ||||||
|     {{ form_row(form.validFrom) }} |  | ||||||
|     {{ form_errors(form.validFrom) }} |  | ||||||
|  |  | ||||||
|     <ul class="record_actions sticky-form-buttons"> |  | ||||||
|         <li class="cancel"> |  | ||||||
|             <a href="{{ path('chill_person_address_list', { 'person_id' : person.id } ) }}" class="sc-button bt-cancel"> |  | ||||||
|                     {{ 'Back to the list'|trans }} |  | ||||||
|                 </a> |  | ||||||
|         </li> |  | ||||||
|         <li> |  | ||||||
|             {{ form_row(form.submit, { 'attr' : { 'class': 'sc-button bt-create'  }, 'label': 'Create' } ) }} |  | ||||||
|         </li> |  | ||||||
|     </ul> |  | ||||||
|  |  | ||||||
|     {{ form_end(form) }} |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     NEW FORM |  | ||||||
|  |  | ||||||
|     {% block content %} |     {% block content %} | ||||||
|         <h1>{{ block('title') }}</h1> |         <h1>{{ block('title') }}</h1> | ||||||
|         <div id="address"></div> |         <div id="address"></div> | ||||||
| @@ -62,6 +32,11 @@ | |||||||
|     {% endblock %} |     {% endblock %} | ||||||
|  |  | ||||||
|     {% block js %} |     {% block js %} | ||||||
|  |         <script type="text/javascript"> | ||||||
|  |             window.personId = {{ person.id|e('js') }}; | ||||||
|  |             window.mode = 'new'; | ||||||
|  |             window.vueRootComponent = 'app'; | ||||||
|  |         </script> | ||||||
|         {{ encore_entry_script_tags('address') }} |         {{ encore_entry_script_tags('address') }} | ||||||
|     {% endblock %} |     {% endblock %} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ | |||||||
| <ul class="record_actions sticky-form-buttons"> | <ul class="record_actions sticky-form-buttons"> | ||||||
|   <li class="cancel"> |   <li class="cancel"> | ||||||
|     <a  |     <a  | ||||||
|       href="{{ chill_return_path_or('chill_person_household_members', { 'household_id': household.id}) }}" |       href="{{ chill_return_path_or('chill_person_household_summary', { 'household_id': household.id}) }}" | ||||||
|       class="sc-button bt-cancel" |       class="sc-button bt-cancel" | ||||||
|       > |       > | ||||||
|       {{ 'Cancel'|trans }} |       {{ 'Cancel'|trans }} | ||||||
|   | |||||||
| @@ -0,0 +1,26 @@ | |||||||
|  | {% extends '@ChillPerson/Household/layout.html.twig' %} | ||||||
|  |  | ||||||
|  | {% block title 'Edit household address'|trans %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  |     <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
|  |     <div> | ||||||
|  |         <div id="household-address"></div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     {% block stylesheets %} | ||||||
|  |         <link href="{{ asset('build/address.css') }}" type="text/css" rel="stylesheet" /> | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
|  |     {% block js %} | ||||||
|  |        <script type="text/javascript"> | ||||||
|  |             window.householdId = {{ household.id|e('js') }}; | ||||||
|  |             window.addressId = {{ address.id|e('js') }}; | ||||||
|  |             window.mode = 'edit'; | ||||||
|  |             window.vueRootComponent = 'app'; | ||||||
|  |         </script> | ||||||
|  |         {{ encore_entry_script_tags('household_address') }} | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
| @@ -3,9 +3,22 @@ | |||||||
| {% block title 'Move household'|trans %} | {% block title 'Move household'|trans %} | ||||||
|  |  | ||||||
| {% block content %} | {% block content %} | ||||||
| <h1>{{ block('title') }}</h1> |     <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
| <p>Household with id {{ household.id }}</p> |     <div> | ||||||
|  |         <div id="household-address"></div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     {% block stylesheets %} | ||||||
|  |         <link href="{{ asset('build/address.css') }}" type="text/css" rel="stylesheet" /> | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
|  |     {% block js %} | ||||||
|  |        <script type="text/javascript"> | ||||||
|  |             window.householdId = {{ household.id|e('js') }}; | ||||||
|  |             window.vueRootComponent = 'app'; | ||||||
|  |         </script> | ||||||
|  |         {{ encore_entry_script_tags('household_address') }} | ||||||
|  |     {% endblock %} | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|   | |||||||
| @@ -4,12 +4,67 @@ | |||||||
|  |  | ||||||
| {% block content %} | {% block content %} | ||||||
| <h1>{{ block('title') }}</h1> | <h1>{{ block('title') }}</h1> | ||||||
|  | <div class="household"> | ||||||
|  |  | ||||||
| <p>Household with id {{ household.id }}</p> |     <div class="household__address"> | ||||||
|  |  | ||||||
| <a class="sc-button bt-update"  |         <div class="row"> | ||||||
|   href="{{ chill_path_add_return_path('chill_person_household_address_move', { 'household_id': household.id }) }}"> |             <div class="household__address--date"></div> | ||||||
|   {{ 'Move household'|trans }} |             <div class="household__address--content"> | ||||||
| </a> |                 <div class="cell"> | ||||||
|  |                     <a class="sc-button bt-create" | ||||||
|  |                     href="{{ chill_path_add_return_path('chill_person_household_address_move', { 'household_id': household.id }) }}"> | ||||||
|  |                     {{ 'Move household'|trans }} | ||||||
|  |                     </a> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         {% for address in household.addresses %} | ||||||
|  |         <div class="row"> | ||||||
|  |             <div class="household__address--date"> | ||||||
|  |                 <div class="cell"> | ||||||
|  |                     <div class="pill"> | ||||||
|  |                     {% if address.validFrom is not empty %} | ||||||
|  |                     {{ address.validFrom|format_date('long') }} | ||||||
|  |                     {% endif %} | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="household__address--content"> | ||||||
|  |                 <div class="cell"> | ||||||
|  |                     <i class="dot"></i> | ||||||
|  |                     <div> | ||||||
|  |                         {% if address.isNoAddress == true %} | ||||||
|  |                         <div class="chill_address_is_noaddress">{{ 'address.consider homeless'|trans }}</div> | ||||||
|  |                         {% else %} | ||||||
|  |                         <div> | ||||||
|  |                             {% if address.street is not empty %} | ||||||
|  |                             <div class="street"> | ||||||
|  |                                 <i class="fa fa-fw fa-map-marker"></i> | ||||||
|  |                                 <span class="streetNumber">{{ address.street }}</span> | ||||||
|  |                                 {% if address.streetNumber is not empty %} | ||||||
|  |                                     <span class="streetNumber">, {{ address.streetNumber }}</span> | ||||||
|  |                                 {% endif %} | ||||||
|  |                             </div> | ||||||
|  |                             {% endif %} | ||||||
|  |                             {% if address.postCode is not empty %} | ||||||
|  |                             <div class="postCode"> | ||||||
|  |                                 <span>{{ address.postCode.code }}</span> <span>{{ address.postCode.name }}</span> | ||||||
|  |                                 <span class="country">({{ address.postCode.country.name|localize_translatable_string }})</span> | ||||||
|  |                             </div> | ||||||
|  |                             {% endif %} | ||||||
|  |  | ||||||
|  |                         </div> | ||||||
|  |                         {% endif %} | ||||||
|  |                         <a href="{{ path('chill_person_household_address_edit', { 'household_id': household.id, 'address_id' : address.id } ) }}" class="sc-button bt-edit"></a> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         {% endfor %} | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|   | |||||||
| @@ -12,25 +12,35 @@ | |||||||
|  |  | ||||||
|             <div class="grid-3" id="banner-flags"></div> |             <div class="grid-3" id="banner-flags"></div> | ||||||
|  |  | ||||||
|             <div class="grid-3" id="banner-status"></div> |             <div class="grid-3" id="banner-status"> | ||||||
|  |               {% set address = household.currentAddress %} | ||||||
|  |               {% if address is empty %} | ||||||
|  |               <p class="chill-no-data-statement">{{ 'household.Household does not have any address currently'|trans }}</p> | ||||||
|  |               {% else %} | ||||||
|  |               <div> | ||||||
|  |                 {{ address|chill_entity_render_box({'multiline': true, 'with_valid_from': false}) }} | ||||||
|  |               </div> | ||||||
|  |               {% endif %} | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|     <div class="grid-12 parent" id="header-accompanying_course-details" > |     <div class="grid-12 parent" id="header-accompanying_course-details" > | ||||||
|         <div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent"> |         <div class="grid-10 push-1 grid-mobile-12 grid-tablet-12 push-mobile-0 push-tablet-0 parent"> | ||||||
|             <div id="banner-misc"> |             <div id="banner-misc"> | ||||||
|               {%- set persons = household.getCurrentPersons() -%} |               {%- set members = household.getCurrentMembersOrdered() -%} | ||||||
|               {%- if persons|length > 0 -%} |               {%- if members|length > 0 -%} | ||||||
|               <span class="current-members-explain"> |               <span class="current-members-explain"> | ||||||
|               {{- 'household.Current household members'|trans }}: |               {{- 'household.Current household members'|trans }}: | ||||||
|               </span> |               </span> | ||||||
|               {%- for p in persons|slice(0, 5) -%} |               {%- for m in members|slice(0, 5) -%} | ||||||
|               {{- p|chill_entity_render_box({'addLink': false}) -}} |               {{- m.person|chill_entity_render_box({'addLink': false}) -}} | ||||||
|  |               {%- if m.holder %} <span class="badge badge-primary">{{ 'household.holder'|trans }}</span>{% endif -%} | ||||||
|               {%- if false == loop.last -%}, {% endif -%} |               {%- if false == loop.last -%}, {% endif -%} | ||||||
|               {%- endfor -%} |               {%- endfor -%} | ||||||
|               {% if persons|length > 5 %} |               {% if members|length > 5 %} | ||||||
|               <span class="current-members-more"> |               <span class="current-members-more"> | ||||||
|               {{ 'household.and x other persons'|trans({'x': persons|length-5}) }} |               {{ 'household.and x other persons'|trans({'x': members|length-5}) }} | ||||||
|               </span> |               </span> | ||||||
|               {% endif %}  |               {% endif %}  | ||||||
|               {%- endif -%} |               {%- endif -%} | ||||||
|   | |||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | {% extends '@ChillPerson/Household/layout.html.twig' %} | ||||||
|  |  | ||||||
|  | {% block title 'household.Edit member metadata'|trans %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  | <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
|  | {{ form_start(form) }} | ||||||
|  |  | ||||||
|  | {{ form_row(form.commentMembers) }} | ||||||
|  |  | ||||||
|  | <div id="waitingForBirthContainer"> | ||||||
|  | {{ form_row(form.waitingForBirth) }} | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | <div id="waitingForBirthDateContainer"> | ||||||
|  | {{ form_row(form.waitingForBirthDate) }} | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | <ul class="record_actions sticky-form-buttons"> | ||||||
|  |   <li class="cancel"> | ||||||
|  |     <a  | ||||||
|  |       href="{{ chill_path_add_return_path('chill_person_household_members', { 'household_id': household.id }) }}" | ||||||
|  |       class="sc-button bt-cancel" | ||||||
|  |       /> | ||||||
|  |       {{ 'Cancel'|trans }} | ||||||
|  |     </a> | ||||||
|  |   </li> | ||||||
|  |   <li> | ||||||
|  |     <button type="submit" class="sc-button bt-save"> | ||||||
|  |       {{ 'Save'|trans }} | ||||||
|  |     </button> | ||||||
|  |   </li> | ||||||
|  | </ul> | ||||||
|  |  | ||||||
|  | {{ form_end(form) }} | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block js %} | ||||||
|  | {{ encore_entry_script_tags('household_edit_metadata') }} | ||||||
|  | {% endblock %} | ||||||
| @@ -5,6 +5,60 @@ | |||||||
| {% block content %} | {% block content %} | ||||||
| <h1>{{ block('title') }}</h1> | <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
|  | {% if form is not null %} | ||||||
|  |   {{ form_start(form) }} | ||||||
|  |  | ||||||
|  |   {{ form_row(form.commentMembers) }} | ||||||
|  |  | ||||||
|  |   <div id="waitingForBirthContainer"> | ||||||
|  |   {{ form_row(form.waitingForBirth) }} | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  |   <div id="waitingForBirthDateContainer"> | ||||||
|  |   {{ form_row(form.waitingForBirthDate) }} | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  |   <ul class="record_actions"> | ||||||
|  |     <li> | ||||||
|  |       <button type="submit" class="sc-button bt-save"> | ||||||
|  |         {{ 'Save'|trans }} | ||||||
|  |       </button> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  |   {{ form_end(form) }} | ||||||
|  |  | ||||||
|  | {% else %} | ||||||
|  |  | ||||||
|  |   {% if not household.commentMembers.isEmpty() %} | ||||||
|  |       {{ household.commentMembers|chill_entity_render_box }} | ||||||
|  |   {% endif %} | ||||||
|  |  | ||||||
|  |   {% if household.waitingForBirth %} | ||||||
|  |     {% if household.waitingForBirthDate is not null %} | ||||||
|  |     {{ 'household.Expecting for birth on date'|trans({ 'date': household.waitingForBirthDate|format_date('long') }) }} | ||||||
|  |     {% else %} | ||||||
|  |     {{ 'household.Expecting for birth'|trans }} | ||||||
|  |     {% endif %} | ||||||
|  |   {% else %} | ||||||
|  |     <p class="chill-no-data-statement"> | ||||||
|  |       {{ 'household.Any expecting birth'|trans }} | ||||||
|  |     </p> | ||||||
|  |   {% endif %} | ||||||
|  |  | ||||||
|  |   <ul class="record_actions"> | ||||||
|  |     <li> | ||||||
|  |       <a  | ||||||
|  |         href="{{ chill_path_add_return_path('chill_person_household_members', { 'household_id': household.id, 'edit': 1 }) }}" | ||||||
|  |         class="sc-button bt-edit" | ||||||
|  |         > | ||||||
|  |         {{ 'household.Comment and expecting birth'|trans }} | ||||||
|  |       </a> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
| {% for p in positions %} | {% for p in positions %} | ||||||
|   <h3>{{ p.label|localize_translatable_string }}</h3> |   <h3>{{ p.label|localize_translatable_string }}</h3> | ||||||
|  |  | ||||||
| @@ -149,3 +203,7 @@ | |||||||
| </ul> | </ul> | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block js %} | ||||||
|  | {{ encore_entry_script_tags('household_edit_metadata') }} | ||||||
|  | {% endblock %} | ||||||
|   | |||||||
| @@ -5,49 +5,233 @@ | |||||||
| {% block content %} | {% block content %} | ||||||
| <h1>{{ block('title') }}</h1> | <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
| <h2>{{ 'household.Current household members'|trans }}</h2> | <h2>{{ 'household.Current address'|trans }}</h2> | ||||||
|  |  | ||||||
|  | {% set address = household.currentAddress %} | ||||||
|  |  | ||||||
|  | {% if address is empty %} | ||||||
|  | <p class="chill-no-data-statement">{{ 'household.Household does not have any address currently'|trans }}</p> | ||||||
|  | {% else %} | ||||||
|  | <div> | ||||||
|  |   {{ address|chill_entity_render_box({'multiline': true}) }} | ||||||
|  | </div> | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | <h2>{{ 'household.Household members'|trans }}</h2> | ||||||
|  |  | ||||||
|  | {% if form is not null %} | ||||||
|  |   {{ form_start(form) }} | ||||||
|  |  | ||||||
|  |   {{ form_row(form.commentMembers) }} | ||||||
|  |  | ||||||
|  |   <div id="waitingForBirthContainer"> | ||||||
|  |   {{ form_row(form.waitingForBirth) }} | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  |   <div id="waitingForBirthDateContainer"> | ||||||
|  |   {{ form_row(form.waitingForBirthDate) }} | ||||||
|  |   </div> | ||||||
|  |  | ||||||
|  |   <ul class="record_actions"> | ||||||
|  |     <li> | ||||||
|  |       <button type="submit" class="sc-button bt-save"> | ||||||
|  |         {{ 'Save'|trans }} | ||||||
|  |       </button> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  |   {{ form_end(form) }} | ||||||
|  | {% else %} | ||||||
|  |  | ||||||
|  |   {% if not household.commentMembers.isEmpty() %} | ||||||
|  |       {{ household.commentMembers|chill_entity_render_box }} | ||||||
|  |   {% endif %} | ||||||
|  |  | ||||||
|  |   {% if household.waitingForBirth %} | ||||||
|  |     {% if household.waitingForBirthDate is not null %} | ||||||
|  |     {{ 'household.Expecting for birth on date'|trans({ 'date': household.waitingForBirthDate|format_date('long') }) }} | ||||||
|  |     {% else %} | ||||||
|  |     {{ 'household.Expecting for birth'|trans }} | ||||||
|  |     {% endif %} | ||||||
|  |   {% else %} | ||||||
|  |     <p class="chill-no-data-statement"> | ||||||
|  |       {{ 'household.Any expecting birth'|trans }} | ||||||
|  |     </p> | ||||||
|  |   {% endif %} | ||||||
|  |  | ||||||
|  |   <ul class="record_actions"> | ||||||
|  |     <li> | ||||||
|  |       <a  | ||||||
|  |         href="{{ chill_path_add_return_path('chill_person_household_summary', { 'household_id': household.id, 'edit': 1 }) }}" | ||||||
|  |         class="sc-button bt-edit" | ||||||
|  |         > | ||||||
|  |         {{ 'household.Comment and expecting birth'|trans }} | ||||||
|  |       </a> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  |  | ||||||
| {% for p in positions %} | {% for p in positions %} | ||||||
| {%- set members = household.currentMembersByPosition(p) %} |   <h3>{{ p.label|localize_translatable_string }}</h3> | ||||||
| {% if members|length > 0 %} |  | ||||||
| <h3>{{ p.label|localize_translatable_string }}</h3> |  | ||||||
|  |  | ||||||
| {% if false == p.shareHousehold %} |   {% if false == p.shareHousehold %} | ||||||
| <p>{{ 'household.Those members does not share address'|trans }}</p> |     <p>{{ 'household.Those members does not share address'|trans }}</p> | ||||||
| {% endif %} |   {% endif %} | ||||||
|  |  | ||||||
| <div class="flex-table list-household-members--summary"> |   {%- set members = household.currentMembersByPosition(p) %} | ||||||
|   {% for m in members %} |   {% if members|length > 0 %} | ||||||
|   <div class="item-bloc"> |     <div class="flex-table list-household-members"> | ||||||
|     <div class="item-row person"> |       {% for m in members %} | ||||||
|       <div class="item-col box-person"> |         <div class="item-bloc"> | ||||||
|         <div> |           <div class="item-row person"> | ||||||
|           {{ m.person|chill_entity_render_box({'addLink': true}) }} |             <div class="item-col box-person"> | ||||||
|           {% if m.holder %} |               <div> | ||||||
|             <span class="badge badge-primary">{{ 'household.holder'|trans }}</span> |                 {{ m.person|chill_entity_render_box({'addLink': true}) }} | ||||||
|  |                 {% if m.holder %} | ||||||
|  |                   <span class="badge badge-primary">{{ 'household.holder'|trans }}</span> | ||||||
|  |                 {% endif %} | ||||||
|  |               </div> | ||||||
|  |               <div> | ||||||
|  |                 {{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }} | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="item-col box-where"> | ||||||
|  |               <ul class="list-content fa-ul"> | ||||||
|  |                 {% if m.startDate is not empty %} | ||||||
|  |                   <li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li> | ||||||
|  |                 {% endif %} | ||||||
|  |                 {% if m.endDate is not empty %} | ||||||
|  |                   <li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li> | ||||||
|  |                 {% endif %} | ||||||
|  |               </ul> | ||||||
|  |               <ul class="record_actions"> | ||||||
|  |                 <li> | ||||||
|  |                   <a  | ||||||
|  |                   href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"  | ||||||
|  |                   class="sc-button bt-edit"  | ||||||
|  |                   /> | ||||||
|  |                     {{ 'household.Update membership'|trans }} | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|  |                 <li> | ||||||
|  |                   <a  | ||||||
|  |                   href="{{ chill_path_add_return_path( | ||||||
|  |                     'chill_person_household_members_editor',  | ||||||
|  |                     { | ||||||
|  |                       'persons': [ m.person.id ], | ||||||
|  |                       'household': household.id | ||||||
|  |                     } ) }}" | ||||||
|  |                     class="sc-button"  | ||||||
|  |                     /> | ||||||
|  |                     <i class="fa fa-arrows-h"></i> | ||||||
|  |                     {{ 'household.Change position'|trans }} | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|  |                 <li> | ||||||
|  |                   <a  | ||||||
|  |                   href="{{ chill_path_add_return_path( | ||||||
|  |                     'chill_person_household_members_editor',  | ||||||
|  |                     { | ||||||
|  |                       'persons': [ m.person.id ],  | ||||||
|  |                       'allow_leave_without_household': true  | ||||||
|  |                     } ) }}" | ||||||
|  |                     class="sc-button"  | ||||||
|  |                     /> | ||||||
|  |                     <i class="fa fa-sign-out"></i> | ||||||
|  |                     {{ 'household.Leave'|trans }} | ||||||
|  |                   </a> | ||||||
|  |                 </li> | ||||||
|  |               </ul> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           {% if m.comment is not empty %} | ||||||
|  |             <div class="item-row comment"> | ||||||
|  |               <blockquote class="chill-user-quote"> | ||||||
|  |                 {{ m.comment|chill_markdown_to_html }} | ||||||
|  |               </blockquote> | ||||||
|  |             </div> | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         </div> |         </div> | ||||||
|         <div> |       {% endfor %} | ||||||
|           {{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }} |     </div> | ||||||
|         </div> |   {% else %} | ||||||
|       </div> |   <p class="chill-no-data-statement">{{ 'household.Any persons into this position'|trans }}</p> | ||||||
|       <div class="item-col box-where"> |   {% endif %} | ||||||
|         <ul class="list-content fa-ul"> |  | ||||||
|           {% if m.startDate is not empty %} |   {% set members = household.nonCurrentMembersByPosition(p) %} | ||||||
|             <li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li> |   {% if members|length > 0 %} | ||||||
|           {% endif %} |     <p><!-- force a space after table --></p> | ||||||
|           {% if m.endDate is not empty %} |     <button class="sc-button bt-green" type="button" data-toggle="collapse" data-target="#nonCurrent_{{ p.id }}" aria-expanded="false" aria-controls="collapse non current members"> | ||||||
|             <li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li> |       {{ 'household.Show future or past memberships'|trans({'length': members|length}) }} | ||||||
|           {% endif %} |     </button> | ||||||
|         </ul> |  | ||||||
|  |     <div id="nonCurrent_{{ p.id }}" class="collapse"> | ||||||
|  |       <div class="flex-table list-household-members"> | ||||||
|  |         {% for m in members %} | ||||||
|  |           <div class="item-bloc"> | ||||||
|  |             <div class="item-row person"> | ||||||
|  |               <div class="item-col box-person"> | ||||||
|  |                 <div> | ||||||
|  |                   {{ m.person|chill_entity_render_box({'addLink': true}) }} | ||||||
|  |                   {% if m.holder %} | ||||||
|  |                     <span class="badge badge-primary">{{ 'household.holder'|trans }}</span> | ||||||
|  |                   {% endif %} | ||||||
|  |                 </div> | ||||||
|  |                 <div> | ||||||
|  |                   {{ 'Born the date'|trans({ 'gender': m.person.gender, 'birthdate': m.person.birthdate|format_date('long') }) }} | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="item-col box-where"> | ||||||
|  |                 <ul class="list-content fa-ul"> | ||||||
|  |                   {% if m.startDate is not empty %} | ||||||
|  |                     <li>{{ 'Since %date%'|trans({'%date%': m.startDate|format_date('long') }) }}</li> | ||||||
|  |                   {% endif %} | ||||||
|  |                   {% if m.endDate is not empty %} | ||||||
|  |                     <li>{{ 'Until %date%'|trans({'%date%': m.endDate|format_date('long') }) }}</li> | ||||||
|  |                   {% endif %} | ||||||
|  |                 </ul> | ||||||
|  |                 <ul class="record_actions"> | ||||||
|  |                   <li> | ||||||
|  |                     <a  | ||||||
|  |                     href="{{ chill_path_add_return_path('chill_person_household_member_edit', { 'id': m.id }) }}"  | ||||||
|  |                     class="sc-button bt-edit"  | ||||||
|  |                     /> | ||||||
|  |                       {{ 'household.Update membership'|trans }} | ||||||
|  |                     </a> | ||||||
|  |                   </li> | ||||||
|  |                 </ul> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             {% if m.comment is not empty %} | ||||||
|  |             <div class="item-row comment"> | ||||||
|  |               <blockquote class="chill-user-quote"> | ||||||
|  |                 {{ m.comment|chill_markdown_to_html }} | ||||||
|  |               </blockquote> | ||||||
|  |             </div> | ||||||
|  |             {% endif %} | ||||||
|  |           </div> | ||||||
|  |         {% endfor %} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |  | ||||||
|   {% endfor %} |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
| {% endif %} |  | ||||||
|  |  | ||||||
|  |   {% endif %} | ||||||
| {% endfor %} | {% endfor %} | ||||||
|  |  | ||||||
|  | <ul class="record_actions"> | ||||||
|  |   <li> | ||||||
|  |     <a  | ||||||
|  |     href="{{ chill_path_add_return_path('chill_person_household_members_editor', {'household': household.id }) }}" | ||||||
|  |       class="sc-button bt-create"> | ||||||
|  |       {{ 'household.Add a member'|trans }} | ||||||
|  |     </a> | ||||||
|  |   </li> | ||||||
|  | </ul> | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block js %} | ||||||
|  | {{ encore_entry_script_tags('household_edit_metadata') }} | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,201 @@ | |||||||
|  | {% extends "@ChillPerson/layout.html.twig"  %} | ||||||
|  |  | ||||||
|  | {% set activeRouteKey = 'chill_person_view' %} | ||||||
|  |  | ||||||
|  | {% block title 'household.Household history for %name%'|trans({'name': person|chill_entity_render_string}) %} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | {% block personcontent %} | ||||||
|  | <h1>{{ block('title') }}</h1> | ||||||
|  |  | ||||||
|  | <h2>{{ 'household.Household shared'|trans }}</h2> | ||||||
|  |  | ||||||
|  | {% set memberships = person.getHouseholdParticipationsShareHousehold() %} | ||||||
|  |  | ||||||
|  | {% if memberships|length == 0 %} | ||||||
|  |   <p class="chill-no-data-statement">{{ 'household.Never in any household'|trans }}</p> | ||||||
|  |  | ||||||
|  |   <ul class="record_actions"> | ||||||
|  |     <li> | ||||||
|  |       <a class="sc-button" | ||||||
|  |         href="{{  | ||||||
|  |           chill_path_add_return_path( | ||||||
|  |           'chill_person_household_members_editor',  | ||||||
|  |             { 'persons': [ person.id ]}) }}" | ||||||
|  |       > | ||||||
|  |         <i class="fa fa-sign-out"></i> | ||||||
|  |         {{ 'household.Join'|trans }} | ||||||
|  |       </a> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  |  | ||||||
|  | {% else %} | ||||||
|  |  | ||||||
|  |   <div class="household"> | ||||||
|  |       <div class="household__address"> | ||||||
|  |  | ||||||
|  |           {% if not person.isSharingHousehold() %} | ||||||
|  |             <div class="row"> | ||||||
|  |                 <div class="household__address--date"></div> | ||||||
|  |                 <div class="household__address--content"> | ||||||
|  |                     <div class="cell"> | ||||||
|  |                       <a class="sc-button" | ||||||
|  |                       href="{{  | ||||||
|  |                         chill_path_add_return_path( | ||||||
|  |                         'chill_person_household_members_editor',  | ||||||
|  |                           { 'persons': [ person.id ]}) }}" | ||||||
|  |                       > | ||||||
|  |                       <i class="fa fa-sign-out"></i> | ||||||
|  |                       {{ 'household.Join'|trans }} | ||||||
|  |                       </a> | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |           {% endif %} | ||||||
|  |  | ||||||
|  |           {% for p in memberships %} | ||||||
|  |           <div class="row"> | ||||||
|  |               <div class="household__address--date"> | ||||||
|  |                   <div class="cell"> | ||||||
|  |                       <div class="pill"> | ||||||
|  |                         {{ p.startDate|format_date('long') }} | ||||||
|  |                       </div> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |               <div class="household__address--content"> | ||||||
|  |                   <div class="cell"> | ||||||
|  |                       <i class="dot"></i> | ||||||
|  |                       <div> | ||||||
|  |                         <div> | ||||||
|  |                           <p> | ||||||
|  |                           <i class="fa fa-home"></i> | ||||||
|  |                           <a  | ||||||
|  |                              href="{{ chill_path_add_return_path( | ||||||
|  |                               'chill_person_household_summary', | ||||||
|  |                               { 'household_id': p.household.id } | ||||||
|  |                               ) }}" | ||||||
|  |                               > | ||||||
|  |                           {{ 'household.Household number'|trans({'household_num': p.household.id }) }} | ||||||
|  |                           </a> | ||||||
|  |                           </p> | ||||||
|  |                           <p>{{ p.position.label|localize_translatable_string }} {% if p.holder %}<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>{% endif %}  | ||||||
|  |                         </div> | ||||||
|  |                         <div> | ||||||
|  |                           {% set simultaneous = p.household.getMembersDuringMembership(p) %} | ||||||
|  |                           {% if simultaneous|length == 0 %} | ||||||
|  |                             <p class="chill-no-data-statement"> | ||||||
|  |                             {{ 'household.Any simultaneous members'|trans }} | ||||||
|  |                             </p> | ||||||
|  |                           {% else %} | ||||||
|  |                             {{ 'household.Members at same time'|trans }}:  | ||||||
|  |                             {% for p in simultaneous -%} | ||||||
|  |                               {{- p.person|chill_entity_render_box({'addLink': true }) -}} | ||||||
|  |                               {%- if p.holder %} <span class="badge badge-primary">{{'household.holder'|trans }}</span> {% endif %} | ||||||
|  |                               {%- if not loop.last %}, {% endif -%} | ||||||
|  |                             {%- endfor -%} | ||||||
|  |                             {% endif %} | ||||||
|  |                             <ul class="record_actions"> | ||||||
|  |                               <li> | ||||||
|  |                                 <a  | ||||||
|  |                                   href="{{ chill_path_add_return_path('chill_person_household_member_edit', { id: p.id }) }}" | ||||||
|  |                                   class="sc-button bt-edit" | ||||||
|  |                                   ></a> | ||||||
|  |                               </li> | ||||||
|  |                               {% if p.isCurrent() %} | ||||||
|  |                                 <li> | ||||||
|  |                                   <a class="sc-button" | ||||||
|  |                                   href="{{ chill_path_add_return_path( | ||||||
|  |                                     'chill_person_household_members_editor',  | ||||||
|  |                                       { 'persons': [ person.id ], 'allow_leave_without_household': true }) }}" | ||||||
|  |                                   > | ||||||
|  |                                   <i class="fa fa-sign-out"></i> | ||||||
|  |                                   {{ 'household.Leave'|trans }} | ||||||
|  |                                   </a> | ||||||
|  |                                 </li> | ||||||
|  |                               {% endif %} | ||||||
|  |                             </ul> | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |           </div> | ||||||
|  |           {% endfor %} | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |   </div> | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | <h2>{{ 'household.Household not shared'|trans }}</h2> | ||||||
|  |  | ||||||
|  | {% set memberships = person.getHouseholdParticipationsNotShareHousehold() %} | ||||||
|  |  | ||||||
|  | {% if memberships|length == 0 %} | ||||||
|  |   <p class="chill-no-data-statement">{{ 'household.Never in any household'|trans }}</p> | ||||||
|  | {% else %} | ||||||
|  |   <table> | ||||||
|  |     <thead> | ||||||
|  |       <tr> | ||||||
|  |         <th>{{ 'household.from'|trans }}</th> | ||||||
|  |         <th>{{ 'household.to'|trans }}</th> | ||||||
|  |         <th>{{ 'household.Household'|trans }}</th> | ||||||
|  |       </tr> | ||||||
|  |     </thead> | ||||||
|  |     <tbody> | ||||||
|  |       {% for p in memberships %} | ||||||
|  |       <tr> | ||||||
|  |         <td>{{ p.startDate|format_date('long') }}</td> | ||||||
|  |         <td> | ||||||
|  |             {% if p.endDate is not empty %} | ||||||
|  |               {{ p.endDate|format_date('long') }} | ||||||
|  |             {% else %} | ||||||
|  |               {{ 'household.Membership currently running'|trans }} | ||||||
|  |             {% endif %} | ||||||
|  |         </td> | ||||||
|  |         <td> | ||||||
|  |           <div> | ||||||
|  |             <p> | ||||||
|  |             <i class="fa fa-home"></i> | ||||||
|  |             <a  | ||||||
|  |                href="{{ chill_path_add_return_path( | ||||||
|  |                 'chill_person_household_summary', | ||||||
|  |                 { 'household_id': p.household.id } | ||||||
|  |                 ) }}" | ||||||
|  |                 > | ||||||
|  |             {{ 'household.Household number'|trans({'household_num': p.household.id }) }} | ||||||
|  |             </a> | ||||||
|  |             </p> | ||||||
|  |             <p>{{ p.position.label|localize_translatable_string }} {% if p.holder %}<span class="badge badge-primary">{{ 'household.holder'|trans }}</span>{% endif %}  | ||||||
|  |           </div> | ||||||
|  |           <div> | ||||||
|  |             {% set simultaneous = p.household.getMembersDuringMembership(p) %} | ||||||
|  |             {% if simultaneous|length == 0 %} | ||||||
|  |               <p class="chill-no-data-statement"> | ||||||
|  |               {{ 'household.Any simultaneous members'|trans }} | ||||||
|  |               </p> | ||||||
|  |             {% else %} | ||||||
|  |               {{ 'household.Members at same time'|trans }}:  | ||||||
|  |               {% for p in simultaneous -%} | ||||||
|  |                 {{- p.person|chill_entity_render_box({'addLink': true }) -}} | ||||||
|  |                 {%- if p.holder %} <span class="badge badge-primary">{{'household.holder'|trans }}</span> {% endif %} | ||||||
|  |                 {%- if not loop.last %}, {% endif -%} | ||||||
|  |               {%- endfor -%} | ||||||
|  |             {% endif %} | ||||||
|  |           </div> | ||||||
|  |         </td> | ||||||
|  |         <td> | ||||||
|  |           <ul class="record_actions"> | ||||||
|  |             <li> | ||||||
|  |               <a  | ||||||
|  |                 href="{{ chill_path_add_return_path('chill_person_household_member_edit', { id: p.id }) }}" | ||||||
|  |                 class="sc-button bt-edit" | ||||||
|  |                 ></a> | ||||||
|  |             </li> | ||||||
|  |           </ul> | ||||||
|  |         </td> | ||||||
|  |       </tr> | ||||||
|  |       {% endfor %} | ||||||
|  |     </tbody> | ||||||
|  |   </table> | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user