浏览代码

Merge branch 'release/2.5'

Olivier Massot 6 月之前
父节点
当前提交
3b498de551
共有 100 个文件被更改,包括 1295 次插入695 次删除
  1. 44 15
      .gitlab-ci.yml
  2. 51 0
      Dockerfile
  3. 64 66
      composer.json
  4. 1 0
      config/bundles.php
  5. 2 0
      config/opentalent/products.yaml
  6. 9 1
      config/packages/api_platform.yaml
  7. 6 3
      config/packages/dh_auditor.yaml
  8. 6 6
      config/packages/docker/messenger.yaml
  9. 3 0
      config/packages/liip_imagine.yaml
  10. 2 2
      config/secrets/docker/docker.DATABASE_AUDIT_URL.f1a8d2.php
  11. 10 6
      config/services.yaml
  12. 16 0
      doc/audit.md
  13. 10 0
      docker/entrypoint.sh
  14. 31 2
      phpstan.neon.dist
  15. 1 1
      sql/schema-extensions/002-view_federation_structures.sql
  16. 3 1
      sql/schema-extensions/003-view_search_user.sql
  17. 5 4
      src/ApiResources/Access/AdminAccess.php
  18. 3 3
      src/ApiResources/Dolibarr/DolibarrAccount.php
  19. 2 0
      src/ApiResources/Export/ExportRequestInterface.php
  20. 2 0
      src/ApiResources/OnlineRegistration/RegistrationAvailability.php
  21. 2 0
      src/ApiResources/OnlineRegistration/RegistrationStatus.php
  22. 6 4
      src/ApiResources/Organization/OrganizationCreationRequest.php
  23. 2 0
      src/ApiResources/Organization/OrganizationDeletionRequest.php
  24. 2 0
      src/ApiResources/Organization/OrganizationMemberCreationRequest.php
  25. 2 2
      src/ApiResources/Profile/AccessProfile.php
  26. 5 5
      src/ApiResources/Profile/OrganizationProfile.php
  27. 2 0
      src/Commands/AddSubdomainCommand.php
  28. 4 2
      src/Commands/CronCommand.php
  29. 19 8
      src/Commands/Doctrine/SchemaUpdateCommand.php
  30. 2 0
      src/Commands/Doctrine/SchemaValidateCommand.php
  31. 5 3
      src/Commands/PostUpgrade/V0_2/PostUpgradeCommand.php
  32. 2 0
      src/Commands/SetupEnvCommand.php
  33. 2 0
      src/DataFixtures/AppFixtures.php
  34. 10 4
      src/Doctrine/AbstractExtension.php
  35. 0 1
      src/Doctrine/Access/CurrentAccessExtension.php
  36. 2 2
      src/Doctrine/Custom/Search/RestrictToOrganizationIdExtension.php
  37. 9 7
      src/Doctrine/ORM/AST/SphericalDistance.php
  38. 279 98
      src/Entity/Access/Access.php
  39. 4 2
      src/Entity/Access/AccessCommunication.php
  40. 6 3
      src/Entity/Access/AccessFamily.php
  41. 4 2
      src/Entity/Access/AccessNetworkSetting.php
  42. 4 2
      src/Entity/Access/AccessSocial.php
  43. 2 0
      src/Entity/Access/FunctionType.php
  44. 2 2
      src/Entity/Access/OrganizationFunction.php
  45. 3 1
      src/Entity/Access/OrganizationResponsability.php
  46. 4 4
      src/Entity/Access/PersonalizedList.php
  47. 1 1
      src/Entity/Access/Preferences.php
  48. 6 2
      src/Entity/AccessWish/AccessFamilyWish.php
  49. 4 2
      src/Entity/AccessWish/AccessTmp.php
  50. 19 15
      src/Entity/AccessWish/AccessWish.php
  51. 9 6
      src/Entity/AccessWish/DocumentWish.php
  52. 16 13
      src/Entity/AccessWish/EducationStudentWish.php
  53. 11 6
      src/Entity/Awin/Product.php
  54. 32 137
      src/Entity/Billing/AbstractBillAccounting.php
  55. 3 2
      src/Entity/Billing/AbstractBillingIntangible.php
  56. 10 1
      src/Entity/Billing/AbstractBillingPayer.php
  57. 11 8
      src/Entity/Billing/AccessBilling.php
  58. 6 4
      src/Entity/Billing/AccessFictionalIntangible.php
  59. 3 1
      src/Entity/Billing/AccessIntangible.php
  60. 0 4
      src/Entity/Billing/AccessPayer.php
  61. 3 3
      src/Entity/Billing/AdvancePayment.php
  62. 6 2
      src/Entity/Billing/Afi.php
  63. 7 3
      src/Entity/Billing/BergerLevrault.php
  64. 132 3
      src/Entity/Billing/Bill.php
  65. 2 2
      src/Entity/Billing/BillAccessDetail.php
  66. 3 3
      src/Entity/Billing/BillAccounting.php
  67. 2 0
      src/Entity/Billing/BillAccountingInterface.php
  68. 3 3
      src/Entity/Billing/BillCredit.php
  69. 3 1
      src/Entity/Billing/BillDebitBalance.php
  70. 9 7
      src/Entity/Billing/BillLine.php
  71. 18 13
      src/Entity/Billing/BillPayment.php
  72. 4 1
      src/Entity/Billing/BillPeriod.php
  73. 9 3
      src/Entity/Billing/BillSchedule.php
  74. 3 0
      src/Entity/Billing/BillScheduleDate.php
  75. 3 1
      src/Entity/Billing/BillTotalDetail.php
  76. 9 6
      src/Entity/Billing/BillingExportSetting.php
  77. 9 8
      src/Entity/Billing/BillingIntangibleExcludeDate.php
  78. 11 6
      src/Entity/Billing/BillingSetting.php
  79. 23 5
      src/Entity/Billing/BillingSettingRent.php
  80. 8 5
      src/Entity/Billing/Ciril.php
  81. 8 4
      src/Entity/Billing/CirilCivil.php
  82. 7 13
      src/Entity/Billing/EducationalProjectIntangible.php
  83. 4 18
      src/Entity/Billing/EducationalProjectPayer.php
  84. 5 3
      src/Entity/Billing/FamilyQuotient.php
  85. 8 7
      src/Entity/Billing/FamilyQuotientBand.php
  86. 8 4
      src/Entity/Billing/FamilyQuotientBandDetail.php
  87. 41 2
      src/Entity/Billing/FamilyQuotientModel.php
  88. 8 5
      src/Entity/Billing/Jvs.php
  89. 8 4
      src/Entity/Billing/Odyssee.php
  90. 3 0
      src/Entity/Billing/PayboxPaymentReturn.php
  91. 4 2
      src/Entity/Billing/PayfipPaymentReturn.php
  92. 9 5
      src/Entity/Billing/Pes.php
  93. 6 4
      src/Entity/Billing/PesSetting.php
  94. 10 4
      src/Entity/Billing/ResidenceArea.php
  95. 8 5
      src/Entity/Billing/SddBank.php
  96. 15 10
      src/Entity/Billing/SddRegie.php
  97. 3 1
      src/Entity/Billing/SddTeneur.php
  98. 64 57
      src/Entity/Booking/AbstractBooking.php
  99. 17 2
      src/Entity/Booking/AbstractBookingRecur.php
  100. 6 6
      src/Entity/Booking/Attendance.php

+ 44 - 15
.gitlab-ci.yml

@@ -1,5 +1,7 @@
 stages:
+  - build
   - test
+  - analysis
 
 variables:
   APP_ENV: staging
@@ -9,27 +11,29 @@ variables:
 cache:
   paths:
     - ./vendor
+    - ./composer.lock
+    - ~/.composer/cache
 
-before_script:
-  - bash tests/ci_docker_install.sh > /dev/null
-  - php -v
-
-static_analysis:
+build_image:
+  stage: build
+  image: docker:20.10
+  before_script:
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
   script:
-    - php vendor/bin/phpstan analyse -c phpstan.neon.dist --error-format gitlab > phpstan.json  # Display code quality in MR
-    - php vendor/bin/phpstan analyse -c phpstan.neon.dist
-  artifacts:
-    when: always
-    reports:
-      codequality: phpstan.json
+    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
+    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
+    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+    - docker push $CI_REGISTRY_IMAGE:latest
 
-code_style:
-  script:
-    - php vendor/bin/php-cs-fixer check --config=.php-cs-fixer.dist.php
+.default_config: &default_config
+  image: $CI_REGISTRY_IMAGE:latest
+  before_script:
+    - composer config -g cache-dir .composer-cache
+    - HOST=ci composer install --prefer-dist --no-progress --no-interaction
 
 unit:
+  <<: *default_config
   stage: test
-
   script:
     - php vendor/phpunit/phpunit/phpunit --configuration phpunit.xml.dist --colors=never --no-interaction --testsuite=unit
     #- php vendor/phpunit/phpunit/phpunit --configuration phpunit.xml.dist --colors=never --no-interaction --no-coverage --testsuite=application
@@ -43,3 +47,28 @@ unit:
 
   coverage: '/^\s*Lines:\s*\d+.\d+\%/'
 
+#doctrine:
+#  <<: *default_config
+#  stage: test
+#  script:
+#    - php bin/console d:s:v
+
+code_quality:
+  <<: *default_config
+  stage: analysis
+  script:
+    - php vendor/bin/phpstan analyse -c phpstan.neon.dist --error-format gitlab > phpstan.json || true    # Display code quality in MR
+    - php vendor/bin/phpstan analyse -c phpstan.neon.dist
+  artifacts:
+    when: always
+    reports:
+      codequality: phpstan.json
+
+code_style:
+  <<: *default_config
+  stage: analysis
+  before_script:
+    - composer config -g cache-dir .composer-cache
+    - HOST=ci composer install --prefer-dist --no-progress --no-interaction
+  script:
+    - php vendor/bin/php-cs-fixer check --config=.php-cs-fixer.dist.php

+ 51 - 0
Dockerfile

@@ -0,0 +1,51 @@
+FROM php:8.2-fpm
+
+# Installation des dépendances système
+RUN apt-get update && apt-get install -yqq --no-install-recommends \
+    apt-utils gnupg git build-essential openssh-client zip \
+    unzip zlib1g-dev libicu-dev g++ libzip-dev libpng-dev libtidy-dev libssl-dev \
+    libxslt-dev libxrender-dev libxrender1 libxt6 libxtst6 openssh-server \
+    xorg iputils-ping wget gdebi ca-certificates wget \
+    fontconfig acl procps libmagickwand-dev imagemagick \
+    && apt-get clean \
+    && rm -rf /var/lib/apt/lists/*
+
+# Installation des extensions PHP
+RUN docker-php-ext-install intl zip xsl \
+    && pecl install xdebug imagick \
+    && docker-php-ext-enable xdebug imagick
+
+# Configure docker
+#docker-php-ext-configure intl
+#docker-php-ext-install intl opcache pdo_mysql exif bcmath calendar gd tidy
+#docker-php-ext-enable libxslt-dev
+#docker-php-ext-enable intl
+#docker-php-ext-enable exif
+#docker-php-ext-enable tidy
+#docker-php-ext-install zip
+#docker-php-ext-install xsl
+#pecl install apcu-5.1.21
+
+# Configuration SSH
+RUN mkdir -p /root/.ssh && \
+    chmod 700 /root/.ssh && \
+    echo "StrictHostKeyChecking no" >> /root/.ssh/config
+
+# Installation de Composer
+COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
+
+# Configuration PHP
+RUN echo "memory_limit=2096M" > /usr/local/etc/php/conf.d/memory-limit.ini
+
+# Configuration Git
+RUN git config --global user.email "exploitation@opentalent.fr" && \
+    git config --global user.name "git"
+
+
+WORKDIR /app
+
+COPY docker/entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
+

+ 64 - 66
composer.json

@@ -2,10 +2,6 @@
   "type": "project",
   "license": "proprietary",
   "repositories": [
-    {
-      "type": "vcs",
-      "url": "ssh://git@gitlab.2iopenservice.com/vincent/foselastica.git"
-    },
     {
       "type": "vcs",
       "url": "ssh://git@gitlab.2iopenservice.com/opentalent/phpdocx-15.5.git"
@@ -15,90 +11,92 @@
     "php": ">=8.2",
     "ext-ctype": "*",
     "ext-iconv": "*",
-    "api-platform/core": "3.1.7",
+    "api-platform/core": "^4.0",
     "beberlei/doctrineextensions": "^1.3",
-    "blackfire/php-sdk": "^1.23",
     "composer/package-versions-deprecated": "^1.11",
-    "doctrine/dbal": "^2.6",
-    "doctrine/doctrine-bundle": "^2.1",
-    "doctrine/doctrine-migrations-bundle": "^3.0",
-    "doctrine/orm": "^2.17",
-    "dompdf/dompdf": "^3.0",
-    "egulias/email-validator": "^3.0",
+    "damienharper/auditor-bundle": "^6.2",
+    "doctrine/dbal": "^3.9",
+    "doctrine/doctrine-bundle": "2.13",
+    "doctrine/doctrine-migrations-bundle": "^3.4",
+    "doctrine/orm": "^3.3",
+    "dompdf/dompdf": "^3.1",
+    "egulias/email-validator": "^4.0",
     "jbouzekri/phumbor-bundle": "^3.1.0",
-    "knplabs/knp-gaufrette-bundle": "^0.8.0",
-    "knplabs/knp-snappy-bundle": "^1.9",
-    "lcobucci/jwt": "^4.1",
-    "lexik/jwt-authentication-bundle": "^2.8",
-    "liip/imagine-bundle": "^2.12",
+    "knplabs/knp-gaufrette-bundle": "0.9.0",
+    "knplabs/knp-snappy-bundle": "^1.10",
+    "lcobucci/jwt": "^5.5",
+    "lexik/jwt-authentication-bundle": "^3.1",
+    "liip/imagine-bundle": "^2.13",
     "lorenzo/pinky": "^1.0",
-    "myclabs/php-enum": "^1.7",
-    "nelmio/cors-bundle": "^2.1",
+    "myclabs/php-enum": "^1.8",
+    "nelmio/cors-bundle": "^2.5",
     "nette/php-generator": "^4.1",
-    "odolbeau/phone-number-bundle": "^3.1",
+    "odolbeau/phone-number-bundle": "^4.1",
     "olinox14/path-php": "^0.1.8",
     "opentalent/phpdocx": "dev-master",
-    "phpdocumentor/reflection-docblock": "^5.2",
-    "phpstan/phpdoc-parser": "^1.0",
+    "phpdocumentor/reflection-docblock": "^5.6",
+    "phpstan/phpdoc-parser": "^1.16",
     "ramsey/uuid": "^4.2",
     "ramsey/uuid-doctrine": "^2.0",
-    "symfony/asset": "6.3.*",
-    "symfony/console": "6.3.*",
-    "symfony/doctrine-messenger": "6.3.*",
-    "symfony/dotenv": "6.3.*",
-    "symfony/error-handler": "6.3.*",
-    "symfony/expression-language": "6.3.*",
+    "symfony/asset": "7.2.*",
+    "symfony/console": "7.2.*",
+    "symfony/doctrine-messenger": "7.2.*",
+    "symfony/dotenv": "7.2.*",
+    "symfony/error-handler": "7.2.*",
+    "symfony/expression-language": "7.2.*",
     "symfony/flex": "^1.3.1",
-    "symfony/framework-bundle": "6.3.*",
-    "symfony/http-client": "6.3.*",
-    "symfony/intl": "6.3.*",
-    "symfony/lock": "6.3.*",
-    "symfony/mailer": "6.3.*",
+    "symfony/framework-bundle": "7.2.*",
+    "symfony/http-client": "7.2.*",
+    "symfony/intl": "7.2.*",
+    "symfony/lock": "7.2.*",
+    "symfony/mailer": "7.2.*",
     "symfony/mercure": "^0.6.1",
     "symfony/mercure-bundle": "^0.3.4",
-    "symfony/messenger": "6.3.*",
+    "symfony/messenger": "7.2.*",
     "symfony/monolog-bundle": "^3.7",
     "symfony/polyfill-intl-icu": "^1.21",
     "symfony/polyfill-intl-messageformatter": "^1.24",
-    "symfony/property-access": "6.3.*",
-    "symfony/property-info": "6.3.*",
-    "symfony/security-bundle": "6.3.*",
-    "symfony/serializer": "6.3.*",
-    "symfony/translation": "6.3.*",
-    "symfony/twig-bundle": "6.3.*",
-    "symfony/uid": "6.3.*",
-    "symfony/validator": "6.3.*",
-    "symfony/yaml": "6.3.*",
-    "twig/cssinliner-extra": "^3.4",
-    "twig/extra-bundle": "^3.4",
-    "twig/inky-extra": "^3.4",
-    "vincent/foselastica": "1.3.1",
-    "webonyx/graphql-php": "^14.3",
+    "symfony/property-access": "7.2.*",
+    "symfony/property-info": "7.2.*",
+    "symfony/security-bundle": "7.2.*",
+    "symfony/serializer": "7.2.*",
+    "symfony/translation": "7.2.*",
+    "symfony/twig-bundle": "7.2.*",
+    "symfony/uid": "7.2.*",
+    "symfony/validator": "7.2.*",
+    "symfony/yaml": "7.2.*",
+    "twig/cssinliner-extra": "^3.20",
+    "twig/extra-bundle": "^3.20",
+    "twig/inky-extra": "^3.20",
+    "webonyx/graphql-php": "^15.19",
     "xantios/mimey": "*"
   },
   "require-dev": {
-    "cyclonedx/cyclonedx-php-composer": "^3.4",
+    "cyclonedx/cyclonedx-php-composer": "^5.2",
     "dg/bypass-finals": "^1.4",
     "doctrine/doctrine-fixtures-bundle": "^3.4",
-    "friendsofphp/php-cs-fixer": "^3.52.1",
-    "hautelook/alice-bundle": "^2.11",
-    "justinrainbow/json-schema": "^5.2",
+    "ergebnis/phpstan-rules": "^2.5",
+    "friendsofphp/php-cs-fixer": "^3.69",
+    "hautelook/alice-bundle": "^2.14",
+    "justinrainbow/json-schema": "^6.1",
     "phpstan/extension-installer": "^1.2",
-    "phpstan/phpstan": "^1.10",
-    "phpstan/phpstan-doctrine": "^1.3",
-    "phpstan/phpstan-phpunit": "^1.3",
-    "phpstan/phpstan-symfony": "^1.3",
+    "phpstan/phpstan": "^2.1",
+    "phpstan/phpstan-deprecation-rules": "^2.0",
+    "phpstan/phpstan-doctrine": "^2.0",
+    "phpstan/phpstan-phpunit": "^2.0",
+    "phpstan/phpstan-symfony": "^2.0",
     "phpunit/phpunit": "^9.6",
-    "rector/rector": "^1.2",
-    "symfony/browser-kit": "6.3.*",
-    "symfony/css-selector": "6.3.*",
-    "symfony/debug-bundle": "6.3.*",
+    "rector/rector": "^2.0",
+    "symfony/browser-kit": "7.2.*",
+    "symfony/css-selector": "7.2.*",
+    "symfony/debug-bundle": "7.2.*",
     "symfony/maker-bundle": "^1.48",
-    "symfony/phpunit-bridge": "6.3.*",
-    "symfony/stopwatch": "6.3.*",
-    "symfony/web-profiler-bundle": "6.3.*",
-    "timeweb/phpstan-enum": "^3.1",
-    "zenstruck/foundry": "^1.31"
+    "symfony/phpunit-bridge": "7.2.*",
+    "symfony/stopwatch": "7.2.*",
+    "symfony/web-profiler-bundle": "7.2.*",
+    "theofidry/alice-data-fixtures": "1.7.2",
+    "timeweb/phpstan-enum": "^4.0",
+    "zenstruck/foundry": "2.3"
   },
   "config": {
     "optimize-autoloader": true,
@@ -155,7 +153,7 @@
   "extra": {
     "symfony": {
       "allow-contrib": false,
-      "require": "6.3.*"
+      "require": "7.2.*"
     },
     "phpstan": {
       "includes": [

+ 1 - 0
config/bundles.php

@@ -25,4 +25,5 @@ return [
     Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'docker' => true, 'test' => false, 'staging' => true],
     Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'docker' => true, 'test' => false, 'staging' => true],
     Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true],
+    DH\AuditorBundle\DHAuditorBundle::class => ['all' => true],
 ];

+ 2 - 0
config/opentalent/products.yaml

@@ -20,6 +20,8 @@ parameters:
           - SubdomainAvailability
           - UserSearchItem
           - DolibarrDocDownload
+          - Download
+          - Upload
         roles:
           - ROLE_IMPORT
           - ROLE_TAGG

+ 9 - 1
config/packages/api_platform.yaml

@@ -1,6 +1,6 @@
 api_platform:
     title: 'Opentalent API'
-    version: '3.1'
+    version: '2.5'
     enable_swagger_ui: false
     enable_re_doc: false
     mapping:
@@ -21,3 +21,11 @@ api_platform:
             ## property is true which means that from now on null values are omitted during serialization.
             ## we don't want this => surcharge default value to false
             skip_null_values: false
+    graphql:
+        graphql_playground: false
+    use_symfony_listeners: true
+    mercure:
+        enabled: true
+    messenger:
+        enabled: true
+    # To enable collecting denormalization errors

+ 6 - 3
config/packages/dh_auditor.yaml.ori → config/packages/dh_auditor.yaml

@@ -1,12 +1,15 @@
 # Full configuration reference available at:
 # https://damienharper.github.io/auditor-docs/docs/auditor-bundle/configuration/reference.html
-# todo : mettre en place le script de nettoyage automatique des audits (le chiffre est le nombre de mois) : bin/console audit:clean 12 --no-confirm
 dh_auditor:
-    timezone: 'Europe/Paris'
     enabled: true
+    timezone: 'Europe/Paris'
     providers:
         doctrine:
-            table_prefix: null
+            table_prefix: ''
             table_suffix: '_audit'
+            entities:
+                App\Entity\Organization\Parameters: ~
+            auditing_services:
+                - '@doctrine.orm.default_entity_manager'
             storage_services:
                 - '@doctrine.orm.audit_entity_manager'

+ 6 - 6
config/packages/docker/messenger.yaml

@@ -1,9 +1,9 @@
 # Désactive le fonctionnement asynchrone de messenger en mode dev
 # > commenter pour tester avec un fonctionnement asynchrone
 #   (dans ce cas, la commande `messenger:consume async` doit être en cours d'exécution)
-framework:
-    messenger:
-        transports:
-            # https://symfony.com/doc/current/messenger.html#transport-configuration
-            async: 'sync://'
-            failed: 'sync://'
+#framework:
+#    messenger:
+#        transports:
+#            # https://symfony.com/doc/current/messenger.html#transport-configuration
+#            async: 'sync://'
+#            failed: 'sync://'

+ 3 - 0
config/packages/liip_imagine.yaml

@@ -19,6 +19,9 @@ liip_imagine:
 
     data_loader: stream.storage
 
+    twig:
+        mode: 'lazy'
+
     filter_sets:
         sm:
             filters:

+ 2 - 2
config/secrets/docker/docker.DATABASE_AUDIT_URL.f1a8d2.php

@@ -1,3 +1,3 @@
-<?php // docker.DATABASE_AUDIT_URL.f1a8d2 on Thu, 16 Nov 2023 10:21:36 +0000
+<?php // docker.DATABASE_AUDIT_URL.f1a8d2 on Thu, 17 Apr 2025 15:29:02 +0000
 
-return "\x92\x26\xEA\x07\xB9\xE90\x27\xCCX\x94W\x05\x5D\x1E\xDF\x27\xAFG\xD0\xADx\xF1\x96\x81\x01\xDFE\xAF\xA6\xF4s\x83\x9DL\x02\xFE\x06r\xE5\x5C\xF4\xB4\xBBc\x88\xA1n9\x19\xA5\xD52o\xAE\xAF\xA5Vk\x3Dc\x1Ds\x9E\xFC\xD0\xF2\xAAca\xBD\x93\xC7\x01\xAC\x91N\xD3\xED\xE0\xE1qh\x28\xCD\x83\x3F\x7F\x9A\x9D\xDF\xB8\xCCn\xD7\xE3\x9Ax\x0C\xC9\x27";
+return "\x05\x60\x5E-\xCD\xA3l\x95\xBB\x28_H\xEC\xD7h\x13\x81\xAC\xE2\x25\xEE\x20\xC2\xCB\xD2\x5B6\x87\xA7\x94\xC11\xDD\xE3\xB1G\x94\x3Adtm\x9E\x85\x8E\xBBp\xE2\x1A\xF8\xA7r\xD5\x84\xB0Jd9\xC4\xE6C\xE5k\x05\xF2\xE8\xE2\x8F\x12\xA9o\x1F\xB3\x04\xE1\x97\xFE\xC6\x5BD\x09N\x1B\xD0\xDD\x94\x2A\xD1lzb\x8F\xA9qzT\xBE\xE4\xA4\x97\xB0x\xE8M\x9A\xF6G\x14\x3A\xF55\xFD\x25";

+ 10 - 6
config/services.yaml

@@ -68,6 +68,9 @@ services:
     App\Tests\Fixture\:
         resource: '%kernel.project_dir%/tests/Fixture/*'
 
+    Doctrine\ORM\Tools\Console\EntityManagerProvider: '@doctrine.orm.command.entity_manager_provider'
+
+
     #########################################
     ##  TAG Services ##
     _instanceof:
@@ -119,14 +122,15 @@ services:
     #########################################
     ##  LISTENER ##
 
-    #########################################
-    ##  ELASTIC SERVICE ##
-    App\Service\Elasticsearch\EducationNotationUpdater:
-        arguments:
-            - '@fos_elastica.object_persister.search.educationNotation'
-
     #########################################
     ## AutoWiring du service container ##
     Symfony\Component\DependencyInjection\ContainerInterface: '@service_container'
     #########################################
 
+    doctrine.schema_update_command:
+        class: App\Commands\Doctrine\SchemaUpdateCommand
+        arguments:
+            - '@doctrine.orm.command.entity_manager_provider'
+        tags: ['console.command']
+
+

+ 16 - 0
doc/audit.md

@@ -0,0 +1,16 @@
+# Tables Audit
+
+Les tables audit sont alimentées par le bundle [auditor-bundle](https://damienharper.github.io/auditor-docs/docs/auditor-bundle/configuration/storage.html).
+
+Ces tables sont stockées dans une DB nommée "opentalent_audit", accessible via l'entity manager
+`@doctrine.orm.audit_entity_manager`.
+
+Pour créer la DB : 
+
+    php bin/console doctrine:database:create --connection=audit
+
+Pour mettre à jour le schéma de la DB : 
+
+    php bin/console audit:schema:update --force
+
+

+ 10 - 0
docker/entrypoint.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Configuration SSH au démarrage du conteneur
+if [ ! -z "$SSH_PRIVATE_KEY" ]; then
+    eval $(ssh-agent -s)
+    echo "$SSH_PRIVATE_KEY" | ssh-add -
+fi
+
+# Exécute la commande passée au conteneur
+exec "$@"

+ 31 - 2
phpstan.neon.dist

@@ -8,6 +8,35 @@ parameters:
     # on ignore les erreurs qui proviennent d'un importe d'un attribut PhpStorm
     # on ignore les erreurs qui imposent d'indiquer le type d'un iterable dans la phpDoc (cf: https://phpstan.org/blog/solving-phpstan-no-value-type-specified-in-iterable-type)
     ignoreErrors :
-        - '#Attribute class JetBrains\\PhpStorm\\[a-zA-Z]+ does not exist.#'
-        - identifier: missingType.generics
+        - '#Attribute class JetBrains\\PhpStorm\\[a-zA-Z]+ does not exist\.#'
+        - identifier: 'missingType.generics'
+        - '#Class App\\Repository\\.+Repository has PHPDoc tag @method for method .+\(\) parameter .* with no value type specified in iterable type array\.$#'
+#        - '#Property .+::\$id \(int\|null\) is never assigned int so it can be removed from the property type\.$#'
+        - '#Constructor of ApiPlatform\\Metadata\\[a-zA-Z]+ is invoked with named argument#'
 
+    ergebnis:
+        # TODO: simplify by using allRules: false (https://github.com/ergebnis/phpstan-rules?tab=readme-ov-file#enabling-rules-one-by-one)
+        final:
+            enabled: false
+        noExtends:
+            enabled: false
+        noNullableReturnTypeDeclaration:
+            enabled: false
+        noParameterWithNullableTypeDeclaration:
+            enabled: false
+        noParameterWithNullDefaultValue:
+            enabled: false
+        noCompact:
+            enabled: false
+        noIsset:
+            enabled: false
+        finalInAbstractClass:
+            enabled: false
+        noConstructorParameterWithDefaultValue:
+            enabled: false
+        noParameterWithContainerTypeDeclaration:
+            enabled: false
+        noSwitch:
+            enabled: false
+        privateInFinalClass:
+            enabled: false

+ 1 - 1
sql/schema-extensions/002-view_federation_structures.sql

@@ -1,6 +1,6 @@
 CREATE OR REPLACE VIEW view_federation_structures AS
     SELECT o.id, o.name, o.logo_id as logoId, o.description, o.image_id as imageId, o.principalType as type,
-           o.portailVisibility,
+           (o.principalType LIKE '%_FEDERATION') as isFederation, o.portailVisibility,
            IF(p.website is not null AND p.website != '', p.website, CONCAT('https://', p.subDomain, '.opentalent.fr')) AS website,
            CONCAT('[', GROUP_CONCAT(COLUMN_JSON(COLUMN_CREATE(
                    'type', oa.type, 'latitude', a.latitude, 'longitude', a.longitude,

+ 3 - 1
sql/schema-extensions/003-view_search_user.sql

@@ -22,5 +22,7 @@ CREATE OR REPLACE VIEW view_search_user AS
         opentalent.Person p
         ON
             a.person_id = p.id
-    WHERE adminAccess IS FALSE;
+    WHERE a.adminAccess IS FALSE
+      AND a.isPseudonymised IS FALSE
+      AND a.newAccess IS FALSE;
 

+ 5 - 4
src/ApiResources/Access/AdminAccess.php

@@ -7,7 +7,7 @@ namespace App\ApiResources\Access;
 use ApiPlatform\Metadata\ApiProperty;
 use ApiPlatform\Metadata\ApiResource;
 use ApiPlatform\Metadata\Get;
-use ApiPlatform\Metadata\Put;
+use ApiPlatform\Metadata\Patch;
 use App\ApiResources\ApiResourcesInterface;
 use App\State\Processor\Access\AdminAccessProcessor;
 use App\State\Provider\Access\AdminAccessProvider;
@@ -20,12 +20,13 @@ use Symfony\Component\Validator\Constraints as Assert;
     operations: [
         new Get(
             uriTemplate: '/admin-access/{id}',
-            defaults: ['id' => 0]
+            defaults: ['id' => 0],
+            security: 'object.getOrganizationId() == user.getOrganization().getId()'
         ),
-        new Put(
+        new Patch(
             uriTemplate: '/admin-access/{id}',
             defaults: ['id' => 0],
-            security: '(is_granted("ROLE_ADMIN_CORE") and object.getOrganizationId() == user.getOrganization().getId() )'
+            security: '(is_granted("ROLE_ORGANIZATION") and object.getOrganizationId() == user.getOrganization().getId() )'
         ),
     ],
     provider: AdminAccessProvider::class,

+ 3 - 3
src/ApiResources/Dolibarr/DolibarrAccount.php

@@ -65,7 +65,6 @@ class DolibarrAccount implements ApiResourcesInterface
 
     /**
      * Last order of the society.
-     * @var DolibarrOrder|null
      */
     #[Groups('dolibarr_get')]
     private ?DolibarrOrder $order = null;
@@ -130,12 +129,12 @@ class DolibarrAccount implements ApiResourcesInterface
         return $this;
     }
 
-    public function getContract(): ?object
+    public function getContract(): ?DolibarrContract
     {
         return $this->contract;
     }
 
-    public function setContract(?object $contract): self
+    public function setContract(?DolibarrContract $contract): self
     {
         $this->contract = $contract;
 
@@ -150,6 +149,7 @@ class DolibarrAccount implements ApiResourcesInterface
     public function setOrder(?DolibarrOrder $order): self
     {
         $this->order = $order;
+
         return $this;
     }
 

+ 2 - 0
src/ApiResources/Export/ExportRequestInterface.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\Export;
 
 interface ExportRequestInterface

+ 2 - 0
src/ApiResources/OnlineRegistration/RegistrationAvailability.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\OnlineRegistration;
 
 use ApiPlatform\Metadata\ApiProperty;

+ 2 - 0
src/ApiResources/OnlineRegistration/RegistrationStatus.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\OnlineRegistration;
 
 use ApiPlatform\Metadata\ApiProperty;

+ 6 - 4
src/ApiResources/Organization/OrganizationCreationRequest.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\Organization;
 
 use ApiPlatform\Metadata\ApiProperty;
@@ -371,24 +373,24 @@ class OrganizationCreationRequest
         return $this;
     }
 
-    public function getPresident(): ?OrganizationMemberCreationRequest
+    public function getPresident(): int|OrganizationMemberCreationRequest|null
     {
         return $this->president;
     }
 
-    public function setPresident(?OrganizationMemberCreationRequest $president): self
+    public function setPresident(int|OrganizationMemberCreationRequest|null $president): self
     {
         $this->president = $president;
 
         return $this;
     }
 
-    public function getDirector(): ?OrganizationMemberCreationRequest
+    public function getDirector(): int|OrganizationMemberCreationRequest|null
     {
         return $this->director;
     }
 
-    public function setDirector(?OrganizationMemberCreationRequest $director): self
+    public function setDirector(int|OrganizationMemberCreationRequest|null $director): self
     {
         $this->director = $director;
 

+ 2 - 0
src/ApiResources/Organization/OrganizationDeletionRequest.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\Organization;
 
 use ApiPlatform\Metadata\ApiProperty;

+ 2 - 0
src/ApiResources/Organization/OrganizationMemberCreationRequest.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\ApiResources\Organization;
 
 use App\Enum\Core\FileTypeEnum;

+ 2 - 2
src/ApiResources/Profile/AccessProfile.php

@@ -56,14 +56,14 @@ class AccessProfile implements ApiResourcesInterface
 
     /** @var list<string> $roles */
     #[Groups('access_profile_read')]
-    private ?array $roles = [];
+    private array $roles = [];
 
     #[Groups('access_profile_read')]
     private ?int $activityYear = null;
 
     /** @var bool[] $historical */
     #[Groups('access_profile_read')]
-    private ?array $historical = [];
+    private array $historical = [];
 
     #[Groups('access_profile_read')]
     private bool $isGuardian = false;

+ 5 - 5
src/ApiResources/Profile/OrganizationProfile.php

@@ -48,14 +48,14 @@ class OrganizationProfile implements ApiResourcesInterface
 
     /** @var list<string> $modules */
     #[Groups('access_profile_read')]
-    private ?array $modules = [];
+    private array $modules = [];
 
     #[Groups('access_profile_read')]
     private bool $hasChildren = false;
 
     /** @var list<OrganizationProfile> */
     #[Groups('access_profile_read')]
-    private ?array $parents = [];
+    private array $parents = [];
 
     #[Groups('access_profile_read')]
     private bool $showAdherentList = false;
@@ -71,7 +71,7 @@ class OrganizationProfile implements ApiResourcesInterface
     private ?PrincipalTypeEnum $principalType = null;
 
     #[Groups('access_profile_read')]
-    private ?bool $trialActive = false;
+    private bool $trialActive = false;
 
     #[Groups('access_profile_read')]
     private ?int $trialCountDown = null;
@@ -256,9 +256,9 @@ class OrganizationProfile implements ApiResourcesInterface
         return $this->trialActive;
     }
 
-    public function setTrialActive(?bool $trialActive): self
+    public function setTrialActive(bool $trialActive): self
     {
-        $this->trialActive = $trialActive ?? false;
+        $this->trialActive = $trialActive;
 
         return $this;
     }

+ 2 - 0
src/Commands/AddSubdomainCommand.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Commands;
 
 use App\Repository\Organization\OrganizationRepository;

+ 4 - 2
src/Commands/CronCommand.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Commands;
 
 use App\Service\Cron\CronjobInterface;
@@ -132,7 +134,7 @@ class CronCommand extends Command
         }
 
         if ($action === self::ACTION_RUN_ALL) {
-            $jobs = $this->cronjobIterator->getAll();
+            $jobs = iterator_to_array($this->cronjobIterator->getAll());
         }
 
         if ($action === self::ACTION_RUN) {
@@ -221,7 +223,7 @@ class CronCommand extends Command
             $this->output->writeln($formatter->formatSection($job->name(), $msg));
             $this->logger->info($job->name().' - '.$msg);
         } catch (\Throwable $e) {
-            $this->logger->critical($e);
+            $this->logger->critical((string) $e);
             $this->output->write('An error happened while running the process : '.$e);
 
             return Command::FAILURE;

+ 19 - 8
src/Commands/Doctrine/SchemaUpdateCommand.php

@@ -1,13 +1,16 @@
 <?php
 
+declare(strict_types=1);
+
 /** @noinspection PhpUnused */
 
 namespace App\Commands\Doctrine;
 
-use App\Service\Utils\FileUtils;
 use App\Service\Utils\PathUtils;
-use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
+use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
+use Doctrine\ORM\Tools\Console\EntityManagerProvider;
 use Doctrine\ORM\Tools\SchemaTool;
+use Path\Path;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Style\SymfonyStyle;
@@ -15,21 +18,29 @@ use Symfony\Component\Console\Style\SymfonyStyle;
 /**
  * Overrides the default doctrine:schema:update command.
  */
-class SchemaUpdateCommand extends UpdateSchemaDoctrineCommand
+class SchemaUpdateCommand extends UpdateCommand
 {
+    public static function getDefaultName(): string
+    {
+        return 'doctrine:schema:update';
+    }
+
     public function __construct(
-        private readonly FileUtils $fileUtils,
+        private readonly EntityManagerProvider $entityManagerProvider,
     ) {
-        parent::__construct();
+        parent::__construct($this->entityManagerProvider);
     }
 
-    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): ?int
+    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui): int
     {
         $output->writeln('-- Executing pre-update scripts');
 
         // Lists schema extensions scripts in the '/sql/schema-extensions' dir
         $schemaExtensionsDir = PathUtils::join(PathUtils::getProjectDir(), 'sql', 'schema-extensions');
-        $scripts = $this->fileUtils->list($schemaExtensionsDir, '*.sql');
+
+        $schemaExtensionsDir = (new Path(PathUtils::getProjectDir()))->append('sql', 'schema-extensions');
+
+        $scripts = $schemaExtensionsDir->glob('*.sql');
         sort($scripts);
 
         // Execute those scripts in alphabetical order
@@ -37,7 +48,7 @@ class SchemaUpdateCommand extends UpdateSchemaDoctrineCommand
         $conn = $em->getConnection();
 
         foreach ($scripts as $script) {
-            $sql = $this->fileUtils->getFileContent($script);
+            $sql = $script->getContent();
             $conn->executeQuery($sql);
         }
 

+ 2 - 0
src/Commands/Doctrine/SchemaValidateCommand.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 /** @noinspection PhpUnused */
 
 namespace App\Commands\Doctrine;

+ 5 - 3
src/Commands/PostUpgrade/V0_2/PostUpgradeCommand.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Commands\PostUpgrade\V0_2;
 
 use Psr\Log\LoggerInterface;
@@ -154,7 +156,7 @@ class PostUpgradeCommand extends Command
             $this->logger->info('Subdomain table was successfully populated');
         } catch (\Exception $e) {
             $opentalentCnn->rollBack();
-            $this->logger->error($e);
+            $this->logger->error((string) $e);
             $this->logger->critical('Error while populating the subdomains, abort and rollback');
         }
     }
@@ -179,7 +181,7 @@ class PostUpgradeCommand extends Command
             $this->logger->info('Events uuid were successfully generated');
         } catch (\Exception $e) {
             $opentalentCnn->rollBack();
-            $this->logger->error($e);
+            $this->logger->error((string) $e);
             $this->logger->critical('Error while generating events uuids, abort and rollback');
         }
     }
@@ -200,7 +202,7 @@ class PostUpgradeCommand extends Command
             $this->logger->info('Files statuses were successfully updated');
         } catch (\Exception $e) {
             $opentalentCnn->rollBack();
-            $this->logger->error($e);
+            $this->logger->error((string) $e);
             $this->logger->critical('Error while updating file statuses, abort and rollback');
         }
     }

+ 2 - 0
src/Commands/SetupEnvCommand.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Commands;
 
 use App\Service\Utils\PathUtils;

+ 2 - 0
src/DataFixtures/AppFixtures.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\DataFixtures;
 
 use Doctrine\Bundle\FixturesBundle\Fixture;

+ 10 - 4
src/Doctrine/AbstractExtension.php

@@ -30,13 +30,19 @@ abstract class AbstractExtension implements QueryCollectionExtensionInterface, Q
     /**
      * Called by doctrine when getting an item.
      *
-     * @param list<int> $identifiers
-     * @param mixed[]   $context
+     * @param array<string, mixed> $identifiers
+     * @param mixed[]              $context
      *
      * @throws \Exception
      */
-    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, ?Operation $operation = null, array $context = []): void
-    {
+    public function applyToItem(
+        QueryBuilder $queryBuilder,
+        QueryNameGeneratorInterface $queryNameGenerator,
+        string $resourceClass,
+        array $identifiers,
+        ?Operation $operation = null,
+        array $context = [],
+    ): void {
         $this->apply($queryBuilder, $resourceClass, $operation);
     }
 

+ 0 - 1
src/Doctrine/Access/CurrentAccessExtension.php

@@ -7,7 +7,6 @@ namespace App\Doctrine\Access;
 use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
-use App\Entity\Custom\Search\UserSearchItem;
 use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
 use Doctrine\ORM\QueryBuilder;
 use Symfony\Bundle\SecurityBundle\Security;

+ 2 - 2
src/Doctrine/Custom/Search/RestrictToOrganizationIdExtension.php

@@ -8,7 +8,6 @@ use ApiPlatform\Metadata\Operation;
 use App\Doctrine\AbstractExtension;
 use App\Entity\Access\Access;
 use App\Entity\Custom\Search\UserSearchItem;
-use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
 use Doctrine\ORM\QueryBuilder;
 use Symfony\Bundle\SecurityBundle\Security;
 
@@ -20,7 +19,8 @@ final class RestrictToOrganizationIdExtension extends AbstractExtension
 {
     public function __construct(
         private readonly Security $security,
-    ) {}
+    ) {
+    }
 
     public function supports(string $resourceClass, ?Operation $operation): bool
     {

+ 9 - 7
src/Doctrine/ORM/AST/SphericalDistance.php

@@ -1,14 +1,16 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Doctrine\ORM\AST;
 
 use Doctrine\ORM\Query\AST\ASTException;
 use Doctrine\ORM\Query\AST\Functions\FunctionNode;
 use Doctrine\ORM\Query\AST\Node;
-use Doctrine\ORM\Query\Lexer;
 use Doctrine\ORM\Query\Parser;
 use Doctrine\ORM\Query\QueryException;
 use Doctrine\ORM\Query\SqlWalker;
+use Doctrine\ORM\Query\TokenType;
 
 /**
  * SphericalDistanceFunction ::= "SPHERICAL_DISTANCE" "(" ArithmeticPrimary "," ArithmeticPrimary "," ArithmeticPrimary "," ArithmeticPrimary ")".
@@ -40,16 +42,16 @@ class SphericalDistance extends FunctionNode
      */
     public function parse(Parser $parser): void
     {
-        $parser->match(Lexer::T_IDENTIFIER);
-        $parser->match(Lexer::T_OPEN_PARENTHESIS);
+        $parser->match(TokenType::T_IDENTIFIER);
+        $parser->match(TokenType::T_OPEN_PARENTHESIS);
         $this->latitude1 = $parser->ArithmeticPrimary();
-        $parser->match(Lexer::T_COMMA);
+        $parser->match(TokenType::T_COMMA);
         $this->longitude1 = $parser->ArithmeticPrimary();
-        $parser->match(Lexer::T_COMMA);
+        $parser->match(TokenType::T_COMMA);
         $this->latitude2 = $parser->ArithmeticPrimary();
-        $parser->match(Lexer::T_COMMA);
+        $parser->match(TokenType::T_COMMA);
         $this->longitude2 = $parser->ArithmeticPrimary();
-        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
+        $parser->match(TokenType::T_CLOSE_PARENTHESIS);
     }
 
     /**

+ 279 - 98
src/Entity/Access/Access.php

@@ -29,7 +29,6 @@ use App\Entity\Booking\EventUser;
 use App\Entity\Booking\ExamenConvocation;
 use App\Entity\Booking\PersonHoliday;
 use App\Entity\Booking\WorkByUser;
-use App\Entity\Core\AbstractControl;
 use App\Entity\Core\Notification;
 use App\Entity\Core\NotificationUser;
 use App\Entity\Core\Tagg;
@@ -38,6 +37,7 @@ use App\Entity\Education\EducationNotationConfig;
 use App\Entity\Education\EducationStudent;
 use App\Entity\Education\EducationTeacher;
 use App\Entity\Message\AbstractMessage;
+use App\Entity\Message\AbstractReport;
 use App\Entity\Message\Email;
 use App\Entity\Message\Mail;
 use App\Entity\Message\Sms;
@@ -52,15 +52,18 @@ use App\Entity\Person\PersonActivity;
 use App\Entity\Place\PlaceRepair;
 use App\Entity\Place\RoomRepair;
 use App\Entity\Product\Equipment;
+use App\Entity\Product\EquipmentControl;
 use App\Entity\Product\EquipmentLoan;
 use App\Entity\Product\EquipmentRepair;
 use App\Entity\Reward\AccessReward;
-use App\Entity\Traits\CreatedOnAndByTrait;
+use App\Entity\Shop\Orders;
+use App\Entity\SimulationSession\SimulationSession;
 use App\Entity\Token\Token;
+use App\Entity\Traits\CreatedOnAndByTrait;
 use App\Filter\ApiPlatform\Person\FullNameFilter;
 use App\Filter\ApiPlatform\Utils\InFilter;
 use App\Repository\Access\AccessRepository;
-// use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
@@ -75,10 +78,10 @@ use Symfony\Component\Serializer\Annotation\Groups;
  * Security :
  *
  *     @see ~/config/api_platform/Access/access.yaml
- *     @see App\Doctrine\Access\CurrentAccessExtension
+ *     @see \App\Doctrine\Access\CurrentAccessExtension
  */
 #[ApiResource]
-// #[Auditable]
+#[Auditable]
 #[ORM\Entity(repositoryClass: AccessRepository::class)]
 #[ApiFilter(filterClass: BooleanFilter::class, properties: ['person.isPhysical'])]
 #[ApiFilter(filterClass: FullNameFilter::class)]
@@ -126,80 +129,117 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
     #[ORM\Column(type: 'json', length: 4294967295, nullable: true)]
     private ?array $setting = [];
 
-    #[ORM\OneToOne(mappedBy: 'access', cascade: ['persist'], orphanRemoval: true)]
-    private AccessBilling $accessBilling;
+    #[ORM\OneToOne(mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private ?AccessBilling $accessBilling;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: PersonActivity::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, PersonActivity> */
+    #[ORM\OneToMany(targetEntity: PersonActivity::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $personActivity;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: OrganizationFunction::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, OrganizationFunction> */
+    #[ORM\OneToMany(targetEntity: OrganizationFunction::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $organizationFunction;
 
-    #[ORM\OneToMany(mappedBy: 'licensee', targetEntity: OrganizationLicence::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, OrganizationLicence> */
+    #[ORM\OneToMany(targetEntity: OrganizationLicence::class, mappedBy: 'licensee', cascade: ['persist', 'remove'])]
     private Collection $organizationLicences;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: PersonalizedList::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, PersonalizedList> */
+    #[ORM\OneToMany(targetEntity: PersonalizedList::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $personalizedLists;
 
-    #[ORM\OneToMany(mappedBy: 'recipientAccess', targetEntity: Notification::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Notification> */
+    #[ORM\OneToMany(targetEntity: Notification::class, mappedBy: 'recipientAccess', cascade: ['persist', 'remove'])]
     private Collection $notifications;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: NotificationUser::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, NotificationUser> */
+    #[ORM\OneToMany(targetEntity: NotificationUser::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $notificationUsers;
 
+    /**
+     * @var Collection<int, Access>
+     */
     #[ORM\ManyToMany(targetEntity: Access::class, mappedBy: 'children', cascade: ['persist'])]
     private Collection $guardians;
 
+    /**
+     * @var Collection<int, Access>
+     */
     #[ORM\ManyToMany(targetEntity: Access::class, inversedBy: 'guardians', cascade: ['persist'])]
     #[ORM\JoinTable(name: 'children_guardians')]
     #[ORM\JoinColumn(name: 'guardians_id', referencedColumnName: 'id')]
     #[ORM\InverseJoinColumn(name: 'children_id', referencedColumnName: 'id')]
     private Collection $children;
 
-    #[ORM\OneToMany(mappedBy: 'accessPayer', targetEntity: AccessPayer::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessPayer> */
+    #[ORM\OneToMany(targetEntity: AccessPayer::class, mappedBy: 'accessPayer', cascade: ['persist', 'remove'])]
     private Collection $billingPayers;
 
-    #[ORM\OneToMany(mappedBy: 'accessReceiver', targetEntity: AccessPayer::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessPayer> */
+    #[ORM\OneToMany(targetEntity: AccessPayer::class, mappedBy: 'accessReceiver', cascade: ['persist', 'remove'])]
     private Collection $billingReceivers;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AccessIntangible::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessIntangible> */
+    #[ORM\OneToMany(targetEntity: AccessIntangible::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $accessIntangibles;
 
     #[ORM\ManyToOne(inversedBy: 'publicationDirectors')]
-    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false)]
+    #[ORM\JoinColumn(referencedColumnName: 'id')]
     private ?Parameters $publicationDirector;
 
     #[ORM\ManyToOne(inversedBy: 'teachers')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
     private ?EducationNotationConfig $educationNotationConfig;
 
-    #[ORM\OneToMany(mappedBy: 'company', targetEntity: CompanyPerson::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, CompanyPerson> */
+    #[ORM\OneToMany(targetEntity: CompanyPerson::class, mappedBy: 'company', cascade: ['persist', 'remove'])]
     private Collection $companyPersonAccesses;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: CompanyPerson::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, CompanyPerson> */
+    #[ORM\OneToMany(targetEntity: CompanyPerson::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $companyPersonCompany;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: EducationStudent::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationStudent> */
+    #[ORM\OneToMany(targetEntity: EducationStudent::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $educationStudent;
 
-    #[ORM\ManyToMany(targetEntity: EducationStudent::class, mappedBy: 'teachers', cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationStudent> */
+    #[ORM\ManyToMany(targetEntity: EducationStudent::class, mappedBy: 'teachers', cascade: ['persist'])]
     private Collection $educationStudentByTeacher;
 
-    #[ORM\OneToMany(mappedBy: 'teacher', targetEntity: EducationTeacher::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationTeacher> */
+    #[ORM\OneToMany(targetEntity: EducationTeacher::class, mappedBy: 'teacher', cascade: ['persist', 'remove'])]
     private Collection $educationTeachers;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: PersonHoliday::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, PersonHoliday> */
+    #[ORM\OneToMany(targetEntity: PersonHoliday::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $holidays;
 
+    /**
+     * TODO: orphanRemoval?
+     *
+     * @var Collection<int, Course>
+     */
     #[ORM\ManyToMany(targetEntity: Course::class, mappedBy: 'students', cascade: ['persist'])]
     private Collection $courses;
 
+    /**
+     * TODO: orphanRemoval?
+     *
+     * @var Collection<int, Course>
+     */
     #[ORM\ManyToMany(targetEntity: Course::class, mappedBy: 'organizer', cascade: ['persist'])]
     private Collection $practicalCourses;
 
+    /** @var Collection<int, Event> */
     #[ORM\ManyToMany(targetEntity: Event::class, mappedBy: 'organizer', cascade: ['persist'])]
     private Collection $eventOrganizers;
 
+    /**
+     * TODO: orphanRemoval?
+     *
+     * @var Collection<int, EducationalProject>
+     */
     #[ORM\ManyToMany(targetEntity: EducationalProject::class, mappedBy: 'organizer', cascade: ['persist'])]
     private Collection $educationalProjectOrganizers;
 
@@ -213,141 +253,202 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
     #[ORM\Column]
     private bool $ielEnabled = false;
 
-    #[ORM\OneToMany(mappedBy: 'educationalProjectPayer', targetEntity: EducationalProjectPayer::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationalProjectPayer> */
+    #[ORM\OneToMany(targetEntity: EducationalProjectPayer::class, mappedBy: 'educationalProjectPayer', cascade: ['persist', 'remove'])]
     private Collection $billingEducationalProjectPayers;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: Bill::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $bills;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: BillLine::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, BillLine> */
+    #[ORM\OneToMany(targetEntity: BillLine::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $billLines;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: BillCredit::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, BillCredit> */
+    #[ORM\OneToMany(targetEntity: BillCredit::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $billCredits;
 
-    #[ORM\OneToMany(mappedBy: 'silentPartner', targetEntity: EducationalProject::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationalProject> */
+    #[ORM\OneToMany(targetEntity: EducationalProject::class, mappedBy: 'silentPartner', cascade: ['persist', 'remove'])]
     private Collection $silentPartners;
 
-    #[ORM\OneToMany(mappedBy: 'operationalPartner', targetEntity: EducationalProject::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EducationalProject> */
+    #[ORM\OneToMany(targetEntity: EducationalProject::class, mappedBy: 'operationalPartner', cascade: ['persist', 'remove'])]
     private Collection $operationalPartners;
 
-    #[ORM\OneToMany(mappedBy: 'guest', targetEntity: EventUser::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EventUser> */
+    #[ORM\OneToMany(targetEntity: EventUser::class, mappedBy: 'guest', cascade: ['persist', 'remove'])]
     private Collection $eventUsers;
 
-    #[ORM\OneToMany(mappedBy: 'student', targetEntity: ExamenConvocation::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, ExamenConvocation> */
+    #[ORM\OneToMany(targetEntity: ExamenConvocation::class, mappedBy: 'student', cascade: ['persist', 'remove'])]
     private Collection $examenConvocations;
 
-    #[ORM\OneToMany(mappedBy: 'provider', targetEntity: EquipmentRepair::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EquipmentRepair> */
+    #[ORM\OneToMany(targetEntity: EquipmentRepair::class, mappedBy: 'provider', cascade: ['persist', 'remove'])]
     private Collection $equipmentRepairProviders;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: Attendance::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Attendance> */
+    #[ORM\OneToMany(targetEntity: Attendance::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $attendances;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AttendanceBooking::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AttendanceBooking> */
+    #[ORM\OneToMany(targetEntity: AttendanceBooking::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $attendanceBookings;
 
-    #[ORM\OneToMany(mappedBy: 'replacement', targetEntity: Attendance::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Attendance> */
+    #[ORM\OneToMany(targetEntity: Attendance::class, mappedBy: 'replacement', cascade: ['persist', 'remove'])]
     private Collection $attendanceReplacements;
 
-    #[ORM\OneToMany(mappedBy: 'provider', targetEntity: RoomRepair::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, RoomRepair> */
+    #[ORM\OneToMany(targetEntity: RoomRepair::class, mappedBy: 'provider', cascade: ['persist', 'remove'])]
     private Collection $roomRepairProviders;
 
-    #[ORM\OneToMany(mappedBy: 'provider', targetEntity: PlaceRepair::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, PlaceRepair> */
+    #[ORM\OneToMany(targetEntity: PlaceRepair::class, mappedBy: 'provider', cascade: ['persist', 'remove'])]
     private Collection $placeRepairProviders;
 
-    #[ORM\OneToMany(mappedBy: 'author', targetEntity: Email::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Email> */
+    #[ORM\OneToMany(targetEntity: Email::class, mappedBy: 'author', cascade: ['persist', 'remove'])]
     private Collection $emails;
 
-    #[ORM\OneToMany(mappedBy: 'author', targetEntity: Mail::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Mail> */
+    #[ORM\OneToMany(targetEntity: Mail::class, mappedBy: 'author', cascade: ['persist', 'remove'])]
     private Collection $mails;
 
-    #[ORM\OneToMany(mappedBy: 'author', targetEntity: Sms::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Sms> */
+    #[ORM\OneToMany(targetEntity: Sms::class, mappedBy: 'author', cascade: ['persist', 'remove'])]
     private Collection $sms;
 
-    #[ORM\ManyToMany(targetEntity: Jury::class, mappedBy: 'members', orphanRemoval: true)]
+    /** @var Collection<int, Jury> */
+    #[ORM\ManyToMany(targetEntity: Jury::class, mappedBy: 'members', cascade: ['persist'])]
     private Collection $juryMembers;
 
-    #[ORM\OneToMany(mappedBy: 'contactPerson', targetEntity: Organization::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Organization> */
+    #[ORM\OneToMany(targetEntity: Organization::class, mappedBy: 'contactPerson', cascade: ['persist', 'remove'])]
     private Collection $organizationContacts;
 
-    #[ORM\OneToMany(mappedBy: 'member', targetEntity: CommissionMember::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, CommissionMember> */
+    #[ORM\OneToMany(targetEntity: CommissionMember::class, mappedBy: 'member', cascade: ['persist', 'remove'])]
     private Collection $commissionMembers;
 
-    #[ORM\OneToMany(mappedBy: 'supplier', targetEntity: Equipment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Equipment> */
+    #[ORM\OneToMany(targetEntity: Equipment::class, mappedBy: 'supplier', cascade: ['persist', 'remove'])]
     private Collection $equipmentSuppliers;
 
-    #[ORM\OneToMany(mappedBy: 'controlManager', targetEntity: Equipment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Equipment> */
+    #[ORM\OneToMany(targetEntity: Equipment::class, mappedBy: 'controlManager', cascade: ['persist', 'remove'])]
     private Collection $equipmentControlManagers;
 
-    #[ORM\OneToMany(mappedBy: 'editor', targetEntity: Equipment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Equipment> */
+    #[ORM\OneToMany(targetEntity: Equipment::class, mappedBy: 'editor', cascade: ['persist', 'remove'])]
     private Collection $equipmentEditors;
 
-    #[ORM\OneToMany(mappedBy: 'borrower', targetEntity: EquipmentLoan::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, EquipmentLoan> */
+    #[ORM\OneToMany(targetEntity: EquipmentLoan::class, mappedBy: 'borrower', cascade: ['persist', 'remove'])]
     private Collection $equipmentLoans;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: Equipment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Equipment> */
+    #[ORM\OneToMany(targetEntity: Equipment::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $equipments;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AccessFictionalIntangible::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessFictionalIntangible> */
+    #[ORM\OneToMany(targetEntity: AccessFictionalIntangible::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $accessFictionalIntangibles;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: Donor::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, Donor> */
+    #[ORM\OneToMany(targetEntity: Donor::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $donors;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AccessReward::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessReward> */
+    #[ORM\OneToMany(targetEntity: AccessReward::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $accessRewards;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: OrganizationResponsability::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, OrganizationResponsability> */
+    #[ORM\OneToMany(targetEntity: OrganizationResponsability::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $organizationResponsabilities;
 
-    #[ORM\OneToMany(mappedBy: 'accessOriginal', targetEntity: AccessWish::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, AccessWish> */
+    #[ORM\OneToMany(targetEntity: AccessWish::class, mappedBy: 'accessOriginal', cascade: ['persist', 'remove'])]
     private Collection $accessWishes;
 
-    #[ORM\OneToMany(mappedBy: 'student', targetEntity: WorkByUser::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
+    /** @var Collection<int, WorkByUser> */
+    #[ORM\OneToMany(targetEntity: WorkByUser::class, mappedBy: 'student', cascade: ['persist', 'remove'])]
     private Collection $workByUsers;
 
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'accesses', cascade: ['persist'])]
+    #[ORM\OrderBy(value: ['label' => 'ASC'])]
     #[ORM\JoinTable(name: 'tag_access')]
     #[ORM\JoinColumn(name: 'access_id', referencedColumnName: 'id')]
     #[ORM\InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id')]
     private Collection $tags;
 
-    #[ORM\ManyToMany(mappedBy: 'organizer', targetEntity: AbstractBooking::class, cascade: ['persist'], orphanRemoval: false)]
+    /** @var Collection<int, AbstractBooking> */
+    #[ORM\ManyToMany(targetEntity: AbstractBooking::class, mappedBy: 'organizer', cascade: ['persist'])]
     private Collection $bookingOrganizers;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AdvancePayment::class, cascade: [], orphanRemoval: true)]
+    /** @var Collection<int, AdvancePayment> */
+    #[ORM\OneToMany(targetEntity: AdvancePayment::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $advancePayments;
 
-    #[ORM\OneToMany(mappedBy: 'author', targetEntity: AbstractMessage::class, cascade: [], orphanRemoval: true)]
+    /** @var Collection<int, AbstractMessage> */
+    #[ORM\OneToMany(targetEntity: AbstractMessage::class, mappedBy: 'author', cascade: ['persist', 'remove'])]
     private Collection $messages;
 
-    #[ORM\OneToMany(mappedBy: 'managerControl', targetEntity: Equipment::class, cascade: [], orphanRemoval: false)]
+    /** @var Collection<int, Equipment> */
+    #[ORM\OneToMany(targetEntity: Equipment::class, mappedBy: 'managerControl', cascade: ['persist', 'remove'])]
     private Collection $equipmentManagerControls;
 
-    #[ORM\OneToMany(mappedBy: 'accompanist', targetEntity: AbstractControl::class, cascade: [], orphanRemoval: true)]
+    /** @var Collection<int, EquipmentControl> */
+    #[ORM\OneToMany(targetEntity: EquipmentControl::class, mappedBy: 'accompanist', cascade: ['persist', 'remove'])]
     private Collection $accompanistControl;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: AccessReward::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, AccessReward> */
+    #[ORM\OneToMany(targetEntity: AccessReward::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $rewards;
 
-    #[ORM\OneToOne(mappedBy: 'access', targetEntity: AccessSocial::class, cascade: ['persist'], orphanRemoval: true)]
-    private AccessSocial $accessSocial;
+    #[ORM\OneToOne(targetEntity: AccessSocial::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private ?AccessSocial $accessSocial;
 
-    #[ORM\OneToOne(mappedBy: 'access', targetEntity: AccessNetworkSetting::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
-    private AccessNetworkSetting $accessNetworkSetting;
+    #[ORM\OneToOne(targetEntity: AccessNetworkSetting::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private ?AccessNetworkSetting $accessNetworkSetting;
 
-    #[ORM\OneToOne(mappedBy: 'access', targetEntity: AccessCommunication::class, cascade: ['persist'], orphanRemoval: true)]
-    private AccessCommunication $accessCommunication;
+    #[ORM\OneToOne(targetEntity: AccessCommunication::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private ?AccessCommunication $accessCommunication;
 
-    #[ORM\OneToOne(mappedBy: 'access', targetEntity: CalendarSynchro::class, cascade: ['persist'], orphanRemoval: true)]
-    private CalendarSynchro $calendarSynchro;
+    #[ORM\OneToOne(targetEntity: CalendarSynchro::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private ?CalendarSynchro $calendarSynchro;
 
-    #[ORM\OneToMany(mappedBy: 'access', targetEntity: Token::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Token> */
+    #[ORM\OneToMany(targetEntity: Token::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
     private Collection $tokens;
 
-    #[ORM\OneToOne(mappedBy: 'access', cascade: ['persist'], fetch: 'EAGER', orphanRemoval: true)]
+    #[ORM\OneToOne(mappedBy: 'access', cascade: ['persist', 'remove'], fetch: 'EAGER')]
     private ?Preferences $preferences;
 
+    /** @var Collection<int, AbstractReport> */
+    #[ORM\OneToMany(targetEntity: AbstractReport::class, mappedBy: 'access', cascade: ['persist', 'remove'])]
+    private Collection $reports;
+
+    /** @var Collection<int, SimulationSession> */
+    #[ORM\OneToMany(targetEntity: SimulationSession::class, mappedBy: 'accessInitiator', cascade: ['persist', 'remove'])]
+    protected Collection $simulationSessionAccessInitiators;
+
+    /** @var Collection<int, SimulationSession> */
+    #[ORM\OneToMany(
+        targetEntity: SimulationSession::class,
+        mappedBy: 'accessSimulated',
+        cascade: ['persist', 'remove'],
+        orphanRemoval: false,
+    )]
+    protected Collection $simulationSessionAccessSimulateds;
+
+    /** @var Collection<int, Orders> */
+    #[ORM\OneToMany(targetEntity: Orders::class, mappedBy: 'access', cascade: [], orphanRemoval: true)]
+    protected Collection $orders;
+
     #[Pure]
     public function __construct()
     {
@@ -404,6 +505,9 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
         $this->emails = new ArrayCollection();
         $this->mails = new ArrayCollection();
         $this->sms = new ArrayCollection();
+        $this->simulationSessionAccessInitiators = new ArrayCollection();
+        $this->simulationSessionAccessSimulateds = new ArrayCollection();
+        $this->orders = new ArrayCollection();
     }
 
     public function getId(): ?int
@@ -1115,9 +1219,6 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
         return $this;
     }
 
-    /**
-     * @return Collection<int, Course>
-     */
     public function getPracticalCourses(): Collection
     {
         return $this->practicalCourses;
@@ -1224,12 +1325,7 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
 
     public function removeBill(Bill $bill): self
     {
-        if ($this->bills->removeElement($bill)) {
-            // set the owning side to null (unless already changed)
-            if ($bill->getAccess() === $this) {
-                $bill->setAccess(null);
-            }
-        }
+        $this->bills->removeElement($bill);
 
         return $this;
     }
@@ -1284,12 +1380,7 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
 
     public function removeBillCredit(BillCredit $billCredit): self
     {
-        if ($this->billCredits->removeElement($billCredit)) {
-            // set the owning side to null (unless already changed)
-            if ($billCredit->getAccess() === $this) {
-                $billCredit->setAccess(null);
-            }
-        }
+        $this->billCredits->removeElement($billCredit);
 
         return $this;
     }
@@ -2240,9 +2331,7 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
 
     public function removeAdvancePayment(AdvancePayment $advancePayment): self
     {
-        if ($this->advancePayments->removeElement($advancePayment)) {
-            $advancePayment->setAccess(null);
-        }
+        $this->advancePayments->removeElement($advancePayment);
 
         return $this;
     }
@@ -2300,7 +2389,7 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
         return $this->accompanistControl;
     }
 
-    public function addAccompanistControl(AbstractControl $accompanistControl): self
+    public function addAccompanistControl(EquipmentControl $accompanistControl): self
     {
         if (!$this->accompanistControl->contains($accompanistControl)) {
             $this->accompanistControl[] = $accompanistControl;
@@ -2310,7 +2399,7 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
         return $this;
     }
 
-    public function removeAccompanistControl(AbstractControl $accompanistControl): self
+    public function removeAccompanistControl(EquipmentControl $accompanistControl): self
     {
         if ($this->accompanistControl->removeElement($accompanistControl)) {
             //            $accompanistControl->setAccompanist(null);  // TODO: actuellement, pas nullable: conserver?
@@ -2343,48 +2432,48 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
         return $this;
     }
 
-    public function getAccessSocial(): AccessSocial
+    public function getAccessSocial(): ?AccessSocial
     {
         return $this->accessSocial;
     }
 
-    public function setAccessSocial(AccessSocial $accessSocial): self
+    public function setAccessSocial(?AccessSocial $accessSocial): self
     {
         $this->accessSocial = $accessSocial;
 
         return $this;
     }
 
-    public function getAccessNetworkSetting(): AccessNetworkSetting
+    public function getAccessNetworkSetting(): ?AccessNetworkSetting
     {
         return $this->accessNetworkSetting;
     }
 
-    public function setAccessNetworkSetting(AccessNetworkSetting $accessNetworkSetting): self
+    public function setAccessNetworkSetting(?AccessNetworkSetting $accessNetworkSetting): self
     {
         $this->accessNetworkSetting = $accessNetworkSetting;
 
         return $this;
     }
 
-    public function getAccessCommunication(): AccessCommunication
+    public function getAccessCommunication(): ?AccessCommunication
     {
         return $this->accessCommunication;
     }
 
-    public function setAccessCommunication(AccessCommunication $accessCommunication): self
+    public function setAccessCommunication(?AccessCommunication $accessCommunication): self
     {
         $this->accessCommunication = $accessCommunication;
 
         return $this;
     }
 
-    public function getCalendarSynchro(): CalendarSynchro
+    public function getCalendarSynchro(): ?CalendarSynchro
     {
         return $this->calendarSynchro;
     }
 
-    public function setCalendarSynchro(CalendarSynchro $calendarSynchro): self
+    public function setCalendarSynchro(?CalendarSynchro $calendarSynchro): self
     {
         $this->calendarSynchro = $calendarSynchro;
 
@@ -2429,4 +2518,96 @@ class Access implements UserInterface, PasswordAuthenticatedUserInterface
     {
         return $this->preferences;
     }
+
+    public function getReports(): Collection
+    {
+        return $this->reports;
+    }
+
+    public function addReport(AbstractReport $report): self
+    {
+        if (!$this->reports->contains($report)) {
+            $this->reports[] = $report;
+            $report->setAccess($this);
+        }
+
+        return $this;
+    }
+
+    public function removeReport(AbstractReport $report): self
+    {
+        if ($this->reports->removeElement($report)) {
+            $report->setAccess(null);
+        }
+
+        return $this;
+    }
+
+    public function getSimulationSessionAccessInitiators(): Collection
+    {
+        return $this->simulationSessionAccessInitiators;
+    }
+
+    public function addSimulationSessionAccessInitiator(SimulationSession $simulationSessionAccessInitiator): self
+    {
+        if (!$this->simulationSessionAccessInitiators->contains($simulationSessionAccessInitiator)) {
+            $this->simulationSessionAccessInitiators[] = $simulationSessionAccessInitiator;
+            $simulationSessionAccessInitiator->setAccessInitiator($this);
+        }
+
+        return $this;
+    }
+
+    public function removeSimulationSessionAccessInitiator(SimulationSession $simulationSessionAccessInitiator): self
+    {
+        $this->simulationSessionAccessInitiators->removeElement($simulationSessionAccessInitiator);
+
+        return $this;
+    }
+
+    public function getSimulationSessionAccessSimulateds(): Collection
+    {
+        return $this->simulationSessionAccessSimulateds;
+    }
+
+    public function addSimulationSessionAccessSimulated(SimulationSession $simulationSessionAccessSimulated): self
+    {
+        if (!$this->simulationSessionAccessSimulateds->contains($simulationSessionAccessSimulated)) {
+            $this->simulationSessionAccessSimulateds[] = $simulationSessionAccessSimulated;
+            $simulationSessionAccessSimulated->setAccessSimulated($this);
+        }
+
+        return $this;
+    }
+
+    public function removeSimulationSessionAccessSimulated(SimulationSession $simulationSessionAccessSimulated): self
+    {
+        $this->simulationSessionAccessSimulateds->removeElement($simulationSessionAccessSimulated);
+
+        return $this;
+    }
+
+    public function getOrders(): Collection
+    {
+        return $this->orders;
+    }
+
+    public function addOrder(Orders $order): self
+    {
+        if (!$this->orders->contains($order)) {
+            $this->orders[] = $order;
+            $order->setAccess($this);
+        }
+
+        return $this;
+    }
+
+    public function removeOrder(Orders $order): self
+    {
+        if ($this->orders->removeElement($order)) {
+            $order->setAccess(null);
+        }
+
+        return $this;
+    }
 }

+ 4 - 2
src/Entity/Access/AccessCommunication.php

@@ -5,11 +5,13 @@ declare(strict_types=1);
 namespace App\Entity\Access;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres d'abonnement à la com pour un Access.
  */
+#[ApiResource(operations: [])]
 // #[Auditable]
 #[ORM\Entity]
 class AccessCommunication
@@ -20,7 +22,7 @@ class AccessCommunication
     private ?int $id = null;
 
     #[ORM\OneToOne(inversedBy: 'accessCommunication', targetEntity: Access::class, cascade: ['persist'])]
-    protected Access $access;
+    protected ?Access $access;
 
     public function getId(): ?int
     {

+ 6 - 3
src/Entity/Access/AccessFamily.php

@@ -12,7 +12,8 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Fais le lien entre les Access dont les titulaires sont membres d'une même famille
+ * (cf. table 'children_guardians' pour les responsables légaux).
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -24,10 +25,12 @@ class AccessFamily
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'accessFamily', targetEntity: Access::class)]
+    /** @var Collection<int, Access> */
+    #[ORM\OneToMany(targetEntity: Access::class, mappedBy: 'accessFamily')]
     private Collection $accesses;
 
-    #[ORM\OneToMany(mappedBy: 'accessFamily', targetEntity: AccessFictionalIntangible::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, AccessFictionalIntangible> */
+    #[ORM\OneToMany(targetEntity: AccessFictionalIntangible::class, mappedBy: 'accessFamily', cascade: ['persist'], orphanRemoval: true)]
     private Collection $accessFictionalIntangibles;
 
     public function __construct()

+ 4 - 2
src/Entity/Access/AccessNetworkSetting.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Access;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class AccessNetworkSetting
 {
@@ -20,7 +22,7 @@ class AccessNetworkSetting
     private ?int $id = null;
 
     #[ORM\OneToOne(inversedBy: 'accessNetworkSetting', targetEntity: Access::class, cascade: ['persist'])]
-    protected Access $access;
+    protected ?Access $access;
 
     public function getId(): ?int
     {

+ 4 - 2
src/Entity/Access/AccessSocial.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Access;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Caractéristiques socio-professionnelles pour un Access.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class AccessSocial
 {
@@ -20,7 +22,7 @@ class AccessSocial
     private ?int $id = null;
 
     #[ORM\OneToOne(inversedBy: 'accessSocial', targetEntity: Access::class, cascade: ['persist'])]
-    protected Access $access;
+    protected ?Access $access;
 
     public function getId(): ?int
     {

+ 2 - 0
src/Entity/Access/FunctionType.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Entity\Access;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Enum\Access\FunctionEnum;
 use App\Enum\Access\RoleEnum;
 use App\Enum\Access\TypeFunctionEnum;
@@ -15,6 +16,7 @@ use Doctrine\ORM\Mapping as ORM;
  * Enum des fonctions que peuvent occuper un Access au sein d'une Organization.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity(repositoryClass: FunctionTypeRepository::class)]
 class FunctionType
 {

+ 2 - 2
src/Entity/Access/OrganizationFunction.php

@@ -33,7 +33,7 @@ class OrganizationFunction
 
     #[ORM\ManyToOne(inversedBy: 'organizationFunction')]
     #[ORM\JoinColumn(nullable: false)]
-    private ?Access $access = null;
+    private Access $access;
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(nullable: false)]
@@ -44,7 +44,7 @@ class OrganizationFunction
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private Activity $activity;
+    private ?Activity $activity = null;
 
     #[ORM\Column(length: 255, nullable: true, enumType: DeparturesCauseEnum::class)]
     private ?DeparturesCauseEnum $departureCause = null;

+ 3 - 1
src/Entity/Access/OrganizationResponsability.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Access;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Responsibilité d'un Access au sein d'une Organization.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class OrganizationResponsability
 {

+ 4 - 4
src/Entity/Access/PersonalizedList.php

@@ -13,7 +13,7 @@ use JetBrains\PhpStorm\Pure;
 use Symfony\Component\Serializer\Annotation\Groups;
 
 /**
- * Liste personnalisées.
+ * Listes personnalisées.
  *
  * Security :
  *
@@ -41,7 +41,7 @@ class PersonalizedList
 
     #[ORM\ManyToOne(inversedBy: 'personalizedLists')]
     #[ORM\JoinColumn(nullable: false)]
-    private ?Access $access = null;
+    private Access $access;
 
     #[ORM\Column(length: 200, nullable: true)]
     #[Groups(['lists:output'])]
@@ -61,7 +61,7 @@ class PersonalizedList
 
     #[ORM\Column(length: 150, nullable: true)]
     #[Groups(['lists:output'])]
-    private string $menuKey;
+    private ?string $menuKey = null;
 
     #[Pure]
     public function __construct()
@@ -125,7 +125,7 @@ class PersonalizedList
         return $this->access;
     }
 
-    public function setMenuKey(string $menuKey): self
+    public function setMenuKey(?string $menuKey): self
     {
         $this->menuKey = $menuKey;
 

+ 1 - 1
src/Entity/Access/Preferences.php

@@ -34,7 +34,7 @@ class Preferences
 
     #[ORM\OneToOne(inversedBy: 'preferences', fetch: 'EAGER')]
     #[ORM\JoinColumn(nullable: true)]
-    private Access $access;
+    private ?Access $access = null;
 
     #[ORM\Column(options: ['default' => true])]
     private bool $messageReport = true;

+ 6 - 2
src/Entity/AccessWish/AccessFamilyWish.php

@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace App\Entity\AccessWish;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
@@ -13,6 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
  * Famille à laquelle est rattaché un AccessWish.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class AccessFamilyWish
 {
@@ -21,14 +23,15 @@ class AccessFamilyWish
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'accessFamilyWish', targetEntity: AccessWish::class, cascade: ['remove'])]
+    /** @var Collection<int, AccessWish> */
+    #[ORM\OneToMany(targetEntity: AccessWish::class, mappedBy: 'accessFamilyWish', cascade: ['remove'])]
     private Collection $accessWishes;
 
     /**
      * Date de dernière mise à jour de l'entité.
      */
     #[ORM\Column(type: 'datetime', nullable: true)]
-    private \DateTimeInterface $updateDate;
+    private ?\DateTimeInterface $updateDate;
 
     #[ORM\Column]
     private bool $registrationCompleted = false;
@@ -36,6 +39,7 @@ class AccessFamilyWish
     #[ORM\Column]
     private bool $closeRegistration = false;
 
+    /** @var Collection<int, AccessWish> */
     #[ORM\OneToMany(mappedBy: 'accessFamilyWish', targetEntity: AccessWish::class, cascade: ['remove'], orphanRemoval: false)]
     protected Collection $accessWishesGuardians;
 

+ 4 - 2
src/Entity/AccessWish/AccessTmp.php

@@ -4,14 +4,16 @@ declare(strict_types=1);
 
 namespace App\Entity\AccessWish;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Organization\Organization;
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class AccessTmp
 {
@@ -21,7 +23,7 @@ class AccessTmp
     private ?int $id = null;
 
     #[ORM\ManyToOne]
-    private Organization $organization;
+    private ?Organization $organization = null;
 
     public function getId(): ?int
     {

+ 19 - 15
src/Entity/AccessWish/AccessWish.php

@@ -38,27 +38,31 @@ class AccessWish
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accessWishes')]
     #[ORM\JoinColumn(onDelete: 'CASCADE')]
-    private Access $accessOriginal;
+    private ?Access $accessOriginal = null;
 
     #[ORM\ManyToOne(inversedBy: 'accessWishes')]
-    private AccessFamilyWish $accessFamilyWish;
+    private ?AccessFamilyWish $accessFamilyWish = null;
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false, onDelete: 'SET NULL')]
     private File $image;
 
     #[ORM\ManyToOne]
-    private Country $addressCountry;
+    private ?Country $addressCountry = null;
 
-    #[ORM\OneToMany(mappedBy: 'accessWish', targetEntity: EducationStudentWish::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, EducationStudentWish> */
+    #[ORM\OneToMany(targetEntity: EducationStudentWish::class, mappedBy: 'accessWish', cascade: ['persist'], orphanRemoval: true)]
     private Collection $educationStudentWishes;
 
-    #[ORM\OneToMany(mappedBy: 'accessWishReregistrations', targetEntity: EducationStudentWish::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, EducationStudentWish> */
+    #[ORM\OneToMany(targetEntity: EducationStudentWish::class, mappedBy: 'accessWishReregistrations', cascade: ['persist'], orphanRemoval: true)]
     private Collection $educationStudentReregistrationsWishes;
 
-    #[ORM\OneToMany(mappedBy: 'accessWish', targetEntity: DocumentWish::class, cascade: ['persist'])]
+    /** @var Collection<int, DocumentWish> */
+    #[ORM\OneToMany(targetEntity: DocumentWish::class, mappedBy: 'accessWish', cascade: ['persist'])]
     private Collection $documentWishes;
 
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'accessWishes', cascade: ['persist'])]
     #[ORM\JoinTable(name: 'tag_accessWish')]
     #[ORM\JoinColumn(name: 'accessWish_id', referencedColumnName: 'id')]
@@ -70,21 +74,21 @@ class AccessWish
 
     #[ORM\ManyToOne(targetEntity: BillSchedule::class, cascade: ['persist'], inversedBy: 'accessWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected BillSchedule $billSchedule;
+    protected ?BillSchedule $billSchedule;
 
-    #[ORM\OneToOne(mappedBy: 'accessWishRib', targetEntity: DocumentWish::class, cascade: ['persist'])]
-    protected DocumentWish $documentRib;
+    #[ORM\OneToOne(targetEntity: DocumentWish::class, mappedBy: 'accessWishRib', cascade: ['persist'])]
+    protected ?DocumentWish $documentRib;
 
-    #[ORM\OneToOne(mappedBy: 'accessWishSepa', targetEntity: DocumentWish::class, cascade: ['persist'])]
-    protected DocumentWish $documentSepa;
+    #[ORM\OneToOne(targetEntity: DocumentWish::class, mappedBy: 'accessWishSepa', cascade: ['persist'])]
+    protected ?DocumentWish $documentSepa;
 
     #[ORM\ManyToOne(targetEntity: EducationCurriculumPack::class, cascade: [])]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected EducationCurriculumPack $wishPack;
+    protected ?EducationCurriculumPack $wishPack;
 
     #[ORM\ManyToOne(targetEntity: Place::class, cascade: [])]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected Place $favoritePlace;
+    protected ?Place $favoritePlace;
 
     /**
      * Date de création dde l'entité.
@@ -92,7 +96,7 @@ class AccessWish
      * @var \DateTime
      */
     #[ORM\Column(type: 'datetime', nullable: true)]
-    private \DateTimeInterface $createDate;
+    private ?\DateTimeInterface $createDate = null;
 
     /**
      * Date de dernière mise à jour de l'entité.
@@ -100,7 +104,7 @@ class AccessWish
      * @var \DateTime
      */
     #[ORM\Column(type: 'datetime', nullable: true)]
-    private \DateTimeInterface $updateDate;
+    private ?\DateTimeInterface $updateDate = null;
 
     public function __construct()
     {

+ 9 - 6
src/Entity/AccessWish/DocumentWish.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Entity\AccessWish;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use App\Entity\Person\Person;
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
@@ -12,9 +13,10 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class DocumentWish
 {
@@ -25,20 +27,21 @@ class DocumentWish
 
     #[ORM\ManyToOne(inversedBy: 'documentWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private AccessWish $accessWish;
+    private ?AccessWish $accessWish = null;
 
     #[ORM\ManyToOne(inversedBy: 'documentWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private Person $personOwner;
+    private ?Person $personOwner = null;
 
-    #[ORM\OneToMany(mappedBy: 'documentWish', targetEntity: File::class, orphanRemoval: true)]
+    /** @var Collection<int, File> */
+    #[ORM\OneToMany(targetEntity: File::class, mappedBy: 'documentWish', orphanRemoval: true)]
     private Collection $files;
 
     #[ORM\OneToOne(inversedBy: 'documentRib', targetEntity: AccessWish::class, cascade: [])]
-    protected AccessWish $accessWishRib;
+    protected ?AccessWish $accessWishRib;
 
     #[ORM\OneToOne(inversedBy: 'documentSepa', targetEntity: AccessWish::class, cascade: [])]
-    protected AccessWish $accessWishSepa;
+    protected ?AccessWish $accessWishSepa;
 
     public function __construct()
     {

+ 16 - 13
src/Entity/AccessWish/EducationStudentWish.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Entity\AccessWish;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Access\Access;
 use App\Entity\Booking\Course;
 use App\Entity\Education\Education;
@@ -19,9 +20,10 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class EducationStudentWish
 {
@@ -31,43 +33,44 @@ class EducationStudentWish
     private ?int $id = null;
 
     #[ORM\Column(length: 50, enumType: WishRegistrationEnum::class)]
-    private ?WishRegistrationEnum $wishRegistration = null;
+    private WishRegistrationEnum $wishRegistration;
 
     #[ORM\Column(length: 50, enumType: RegistrationStatusEnum::class)]
-    private ?RegistrationStatusEnum $registrationStatus = null;
+    private RegistrationStatusEnum $registrationStatus;
 
-    #[ORM\ManyToOne]
+    #[ORM\ManyToOne(targetEntity: Education::class, cascade: ['persist'], inversedBy: 'educationWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private Education $educationWish;
+    private ?Education $educationWish = null;
 
     #[ORM\ManyToOne(inversedBy: 'educationStudentWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private AccessWish $accessWish;
+    private ?AccessWish $accessWish = null;
 
     #[ORM\ManyToOne(inversedBy: 'educationStudentReregistrationsWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private AccessWish $accessWishReregistrations;
+    private ?AccessWish $accessWishReregistrations = null;
 
-    #[ORM\ManyToOne]
-    private EducationCurriculum $educationCurriculum;
+    #[ORM\ManyToOne(targetEntity: EducationCurriculum::class, cascade: ['persist'], inversedBy: 'educationStudentWish')]
+    private ?EducationCurriculum $educationCurriculum = null;
 
     #[ORM\ManyToOne]
-    private EducationStudent $educationStudent;
+    private ?EducationStudent $educationStudent = null;
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private EducationTiming $educationTiming;
+    private ?EducationTiming $educationTiming = null;
 
+    /** @var Collection<int, Access> */
     #[ORM\ManyToMany(targetEntity: Access::class, cascade: [], orphanRemoval: false)]
     protected Collection $teachers;
 
     #[ORM\ManyToOne(targetEntity: EquipmentList::class, cascade: [])]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected EquipmentList $speciality;
+    protected ?EquipmentList $speciality;
 
     #[ORM\ManyToOne(targetEntity: Course::class, cascade: [], inversedBy: 'educationStudentWishes')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected Course $course;
+    protected ?Course $course;
 
     public function __construct()
     {

+ 11 - 6
src/Entity/Awin/Product.php

@@ -7,9 +7,14 @@ namespace App\Entity\Awin;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * Produit / Évènement pour le portail fourni par le partenaire Awin.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 #[ORM\Table(name: 'AwinProduct')]
+#[ORM\Index(name: 'name_idx', columns: ['name'])]
+#[ORM\Index(name: 'slug_idx', columns: ['slug'])]
 class Product
 {
     #[ORM\Id]
@@ -89,16 +94,16 @@ class Product
     #[ORM\Column]
     protected mixed $priceMaxi;
 
-    #[ORM\Column(length: 255, options: ['nullable' => true])]
-    protected string $artists;
+    #[ORM\Column(length: 255, nullable: true)]
+    protected ?string $artists;
 
-    #[ORM\Column(length: 255, options: ['nullable' => true])]
-    protected string $uuid;
+    #[ORM\Column(length: 255, nullable: true)]
+    protected ?string $uuid;
 
-    #[ORM\Column(type: 'date', options: ['nullable' => true])]
+    #[ORM\Column(type: 'date', nullable: true)]
     protected ?\DateTimeInterface $createDate;
 
-    #[ORM\Column(type: 'date', options: ['nullable' => true])]
+    #[ORM\Column(type: 'date', nullable: true)]
     protected ?\DateTimeInterface $updateDate;
 
     public function getId(): int

+ 32 - 137
src/Entity/Billing/AbstractBillAccounting.php

@@ -19,53 +19,54 @@ use Doctrine\ORM\Mapping as ORM;
  *
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table BillAccounting, et supprimer l'attribut discr.
  */
-#[ORM\MappedSuperclass]
-abstract class AbstractBillAccounting
+#[ORM\Entity]
+#[ORM\Table(name: 'BillAccounting')]
+#[ORM\InheritanceType('SINGLE_TABLE')]
+#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
+#[ORM\DiscriminatorMap(
+    [
+        'billaccounting' => BillAccounting::class,
+        'bill' => Bill::class,
+        'billcredit' => BillCredit::class,
+        'advancepayment' => AdvancePayment::class,
+    ]
+)]
+class AbstractBillAccounting
 {
     #[ORM\Id]
     #[ORM\Column]
     #[ORM\GeneratedValue]
     protected ?int $id = null;
 
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr;
-
     #[ORM\ManyToOne(targetEntity: Organization::class)]
     #[ORM\JoinColumn(nullable: true)]
-    protected Organization $organization;
-
-    #[ORM\OneToMany(mappedBy: 'bill', targetEntity: BillLine::class, cascade: ['persist'], orphanRemoval: true)]
-    protected Collection $billLines;
-
-    #[ORM\OneToMany(mappedBy: 'bill', targetEntity: BillCredit::class, cascade: ['persist'], orphanRemoval: true)]
-    protected Collection $billCredits;
-
-    #[ORM\OneToMany(mappedBy: 'bill', targetEntity: BillPayment::class, cascade: ['persist'], orphanRemoval: true)]
-    protected Collection $billPayments;
+    protected ?Organization $organization;
 
     #[ORM\ManyToOne(inversedBy: 'billCredits')]
     #[ORM\JoinColumn(nullable: true)]
-    protected AbstractBillAccounting $bill;
+    protected ?Bill $bill;
 
-    #[ORM\OneToMany(mappedBy: 'bill', targetEntity: BillingIntangibleExcludeDate::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillingIntangibleExcludeDate> */
+    #[ORM\OneToMany(targetEntity: BillingIntangibleExcludeDate::class, mappedBy: 'bill', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $billingIntangibleExcludeDates;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(nullable: true)]
-    protected Pes $pes;
+    protected ?Pes $pes;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(nullable: true)]
-    protected BergerLevrault $bergerLevrault;
+    protected ?BergerLevrault $bergerLevrault;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(nullable: true)]
-    protected Ciril $ciril;
+    protected ?Ciril $ciril;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(nullable: true)]
-    protected Jvs $jvs;
+    protected ?Jvs $jvs;
 
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'billAccountings', cascade: ['persist'])]
     #[ORM\JoinTable(name: 'tag_billAccounting')]
     #[ORM\JoinColumn(name: 'billAccounting_id', referencedColumnName: 'id')]
@@ -77,34 +78,31 @@ abstract class AbstractBillAccounting
     protected Access $access;
 
     #[ORM\OneToOne(targetEntity: BillAccessDetail::class, cascade: ['persist'])]
-    protected BillAccessDetail $accessDetail;
+    protected ?BillAccessDetail $accessDetail;
 
     #[ORM\OneToOne(targetEntity: BillPeriod::class, cascade: ['persist'])]
-    protected BillPeriod $billPeriod;
+    protected ?BillPeriod $billPeriod;
 
     #[ORM\OneToOne(targetEntity: BillTotalDetail::class, cascade: ['persist'])]
-    protected BillTotalDetail $totalDetail;
+    protected ?BillTotalDetail $totalDetail;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
-    #[ORM\ManyToOne(targetEntity: Odyssee::class, inversedBy: 'bills', cascade: ['persist'])]
+    #[ORM\ManyToOne(targetEntity: Odyssee::class, cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false, onDelete: 'SET NULL')]
     protected Odyssee $odyssee; // TODO: sûr que c'est pas nullable?
 
-    #[ORM\ManyToOne(targetEntity: Afi::class, inversedBy: 'bills', cascade: ['persist'])]
+    #[ORM\ManyToOne(targetEntity: Afi::class, cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false, onDelete: 'SET NULL')]
     protected Afi $afi;
 
-    #[ORM\ManyToOne(targetEntity: CirilCivil::class, inversedBy: 'bills', cascade: ['persist'])]
+    #[ORM\ManyToOne(targetEntity: CirilCivil::class, cascade: ['persist'], inversedBy: 'bills')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false, onDelete: 'SET NULL')]
     protected CirilCivil $cirilCivil;
 
     public function __construct()
     {
-        $this->billLines = new ArrayCollection();
-        $this->billCredits = new ArrayCollection();
-        $this->billPayments = new ArrayCollection();
         $this->billingIntangibleExcludeDates = new ArrayCollection();
         $this->tags = new ArrayCollection();
     }
@@ -114,18 +112,6 @@ abstract class AbstractBillAccounting
         return $this->id;
     }
 
-    public function getDiscr(): ?string
-    {
-        return $this->discr;
-    }
-
-    public function setDiscr(string $discr): self
-    {
-        $this->discr = $discr;
-
-        return $this;
-    }
-
     public function getOrganization(): ?Organization
     {
         return $this->organization;
@@ -138,102 +124,12 @@ abstract class AbstractBillAccounting
         return $this;
     }
 
-    /**
-     * @return Collection<int, BillLine>
-     */
-    public function getBillLines(): Collection
-    {
-        return $this->billLines;
-    }
-
-    public function addBillLine(BillLine $billLine): self
-    {
-        if (!$this->billLines->contains($billLine)) {
-            $this->billLines[] = $billLine;
-            $billLine->setBill($this);
-        }
-
-        return $this;
-    }
-
-    public function removeBillLine(BillLine $billLine): self
-    {
-        if ($this->billLines->removeElement($billLine)) {
-            // set the owning side to null (unless already changed)
-            if ($billLine->getBill() === $this) {
-                $billLine->setBill(null);
-            }
-        }
-
-        return $this;
-    }
-
-    /**
-     * @return Collection<int, BillCredit>
-     */
-    public function getBillCredits(): Collection
-    {
-        return $this->billCredits;
-    }
-
-    public function addBillCredit(BillCredit $billCredit): self
-    {
-        if (!$this->billCredits->contains($billCredit)) {
-            $this->billCredits[] = $billCredit;
-            $billCredit->setBill($this);
-        }
-
-        return $this;
-    }
-
-    public function removeBillCredit(BillCredit $billCredit): self
-    {
-        if ($this->billCredits->removeElement($billCredit)) {
-            // set the owning side to null (unless already changed)
-            if ($billCredit->getBill() === $this) {
-                $billCredit->setBill(null);
-            }
-        }
-
-        return $this;
-    }
-
-    /**
-     * @return Collection<int, BillPayment>
-     */
-    public function getBillPayments(): Collection
-    {
-        return $this->billPayments;
-    }
-
-    public function addBillPayment(BillPayment $billPayment): self
-    {
-        if (!$this->billPayments->contains($billPayment)) {
-            $this->billPayments[] = $billPayment;
-            $billPayment->setBill($this);
-        }
-
-        return $this;
-    }
-
-    public function removeBillPayment(BillPayment $billPayment): self
-    {
-        if ($this->billPayments->removeElement($billPayment)) {
-            // set the owning side to null (unless already changed)
-            if ($billPayment->getBill() === $this) {
-                $billPayment->setBill(null);
-            }
-        }
-
-        return $this;
-    }
-
-    public function getBill(): ?self
+    public function getBill(): ?Bill
     {
         return $this->bill;
     }
 
-    public function setBill(?self $bill): self
+    public function setBill(?Bill $bill): self
     {
         $this->bill = $bill;
 
@@ -252,7 +148,6 @@ abstract class AbstractBillAccounting
     {
         if (!$this->billingIntangibleExcludeDates->contains($billingIntangibleExcludeDate)) {
             $this->billingIntangibleExcludeDates[] = $billingIntangibleExcludeDate;
-            /* @phpstan-ignore-next-line */
             $billingIntangibleExcludeDate->setBill($this);
         }
 
@@ -351,7 +246,7 @@ abstract class AbstractBillAccounting
         return $this->access;
     }
 
-    public function setAccess(?Access $access): self
+    public function setAccess(Access $access): self
     {
         $this->access = $access;
 

+ 3 - 2
src/Entity/Billing/AbstractBillingIntangible.php

@@ -19,8 +19,8 @@ use Doctrine\ORM\Mapping as ORM;
 #[ORM\InheritanceType('SINGLE_TABLE')]
 #[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
 #[ORM\DiscriminatorMap([
-    'access' => 'AccessIntangible',
-    'educationalproject' => 'EducationalProjectIntangible',
+    'access' => AccessIntangible::class,
+    'educationalproject' => EducationalProjectIntangible::class,
 ])]
 abstract class AbstractBillingIntangible
 {
@@ -33,6 +33,7 @@ abstract class AbstractBillingIntangible
     #[ORM\JoinColumn(nullable: false)]
     protected Intangible $intangible;
 
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'billingIntangibles', cascade: ['persist'])]
     #[ORM\JoinTable(name: 'tag_billingIntangible')]
     #[ORM\JoinColumn(name: 'billingIntangible_id', referencedColumnName: 'id')]

+ 10 - 1
src/Entity/Billing/AbstractBillingPayer.php

@@ -11,7 +11,16 @@ use Doctrine\ORM\Mapping as ORM;
  * l'EducationalProject concerné par la facture
  * Classe de base de @see  AccessPayer, EducationalProjectPayer.
  */
-#[ORM\MappedSuperclass]
+#[ORM\Entity]
+#[ORM\Table(name: 'BillingPayer')]
+#[ORM\InheritanceType('SINGLE_TABLE')]
+#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
+#[ORM\DiscriminatorMap(
+    [
+        'access' => AccessPayer::class,
+        'educationalproject' => EducationalProjectPayer::class,
+    ]
+)]
 abstract class AbstractBillingPayer
 {
     #[ORM\Id]

+ 11 - 8
src/Entity/Billing/AccessBilling.php

@@ -12,7 +12,7 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres de facturation pour un Access.
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -26,28 +26,31 @@ class AccessBilling
 
     #[ORM\OneToOne(inversedBy: 'accessBilling', cascade: ['persist'], fetch: 'EAGER')]
     #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
-    private Access $access;
+    private ?Access $access = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accessBilling')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private FamilyQuotient $familyQuotient;
+    private ?FamilyQuotient $familyQuotient = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accessBilling')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    private ResidenceArea $residenceArea;
+    private ?ResidenceArea $residenceArea = null;
 
-    #[ORM\OneToMany(mappedBy: 'accessBilling', targetEntity: BillPayment::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillPayment> */
+    #[ORM\OneToMany(targetEntity: BillPayment::class, mappedBy: 'accessBilling', cascade: ['persist'], orphanRemoval: true)]
     private Collection $billDetachedPayments;
 
-    #[ORM\OneToMany(mappedBy: 'accessBilling', targetEntity: BillDebitBalance::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillDebitBalance> */
+    #[ORM\OneToMany(targetEntity: BillDebitBalance::class, mappedBy: 'accessBilling', cascade: ['persist'], orphanRemoval: true)]
     private Collection $billDebitBalances;
 
-    #[ORM\OneToMany(mappedBy: 'accessBillingAccountBalanceReimbursement', targetEntity: BillPayment::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillPayment> */
+    #[ORM\OneToMany(targetEntity: BillPayment::class, mappedBy: 'accessBillingAccountBalanceReimbursement', cascade: ['persist'], orphanRemoval: true)]
     private Collection $accountBalanceReimbursements;
 
     #[ORM\ManyToOne(targetEntity: BillSchedule::class, cascade: ['persist'], inversedBy: 'accessBilling')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected BillSchedule $billSchedule;
+    protected ?BillSchedule $billSchedule;
 
     public function __construct()
     {

+ 6 - 4
src/Entity/Billing/AccessFictionalIntangible.php

@@ -14,7 +14,8 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Correspond à une ligne de facturation pour un Access
+ * (@see FictionalIntangible).
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -27,16 +28,17 @@ class AccessFictionalIntangible
     private ?int $id = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accessFictionalIntangibles')]
-    private Access $access;
+    private ?Access $access = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accessFictionalIntangibles')]
-    private AccessFamily $accessFamily;
+    private ?AccessFamily $accessFamily = null;
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(nullable: false)]
     private FictionalIntangible $fictionalIntangible;
 
-    #[ORM\OneToMany(mappedBy: 'accessFictionalIntangible', targetEntity: BillingIntangibleExcludeDate::class, cascade: ['persist'], orphanRemoval: true, )]
+    /** @var Collection<int, BillingIntangibleExcludeDate> */
+    #[ORM\OneToMany(targetEntity: BillingIntangibleExcludeDate::class, mappedBy: 'accessFictionalIntangible', cascade: ['persist'], orphanRemoval: true, )]
     private Collection $billingIntangibleExcludeDates;
 
     public function __construct()

+ 3 - 1
src/Entity/Billing/AccessIntangible.php

@@ -23,12 +23,14 @@ class AccessIntangible extends AbstractBillingIntangible
     #[ORM\ManyToOne(inversedBy: 'accessIntangibles')]
     private ?Access $access = null;
 
-    #[ORM\OneToMany(mappedBy: 'accessIntangible', targetEntity: BillingIntangibleExcludeDate::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillingIntangibleExcludeDate> */
+    #[ORM\OneToMany(targetEntity: BillingIntangibleExcludeDate::class, mappedBy: 'accessIntangible', cascade: ['persist'], orphanRemoval: true)]
     private Collection $billingIntangibleExcludeDates;
 
     public function __construct()
     {
         $this->billingIntangibleExcludeDates = new ArrayCollection();
+        parent::__construct();
     }
 
     public function setAccess(?Access $access): self

+ 0 - 4
src/Entity/Billing/AccessPayer.php

@@ -17,13 +17,9 @@ use Doctrine\ORM\Mapping as ORM;
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
-#[ORM\Table(name: 'BillingPayer')]
 #[ORM\Entity(repositoryClass: AccessPayerRepository::class)]
 class AccessPayer extends AbstractBillingPayer
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    private string $discr = 'access';
-
     #[ORM\ManyToOne(inversedBy: 'billingPayers')]
     private ?Access $accessPayer = null;
 

+ 3 - 3
src/Entity/Billing/AdvancePayment.php

@@ -7,12 +7,12 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 // #[Auditable]
-#[ORM\Table(name: 'BillAccounting')]
 #[ORM\Entity]
 class AdvancePayment extends AbstractBillAccounting
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr = 'advancepayment';
 }

+ 6 - 2
src/Entity/Billing/Afi.php

@@ -9,6 +9,9 @@ use App\Entity\Core\File;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class Afi
@@ -18,11 +21,12 @@ class Afi
     #[ORM\GeneratedValue]
     private int $id;
 
-    #[ORM\OneToMany(mappedBy: 'afi', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'afi', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function getId(): int
     {

+ 7 - 3
src/Entity/Billing/BergerLevrault.php

@@ -5,16 +5,19 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Fais le lien entre les factures (Bill) concernées par l'export et l'objet File généré
+ * NB: BergerLevrault est un format de données demandé par le Trésor Public pour l'export des données comptables.
  */
 // #[Auditable]
 #[ORM\Entity]
+#[ApiResource(operations: [])]
 class BergerLevrault
 {
     #[ORM\Id]
@@ -22,11 +25,12 @@ class BergerLevrault
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'bergerLevrault', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'bergerLevrault', cascade: ['persist'], orphanRemoval: true)]
     private Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function __construct()
     {

+ 132 - 3
src/Entity/Billing/Bill.php

@@ -5,14 +5,143 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 use ApiPlatform\Metadata\ApiResource;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * Facture.
+ */
 #[ApiResource(operations: [])]
 // #[Auditable]
-#[ORM\Table(name: 'BillAccounting')]
 #[ORM\Entity]
 class Bill extends AbstractBillAccounting
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr = 'bill';
+    /** @var Collection<int, BillLine> */
+    #[ORM\OneToMany(targetEntity: BillLine::class, mappedBy: 'bill', cascade: ['persist', 'remove'])]
+    protected Collection $billLines;
+
+    /** @var Collection<int, BillCredit> */
+    #[ORM\OneToMany(targetEntity: BillCredit::class, mappedBy: 'bill', cascade: ['persist', 'remove'])]
+    protected Collection $billCredits;
+
+    /** @var Collection<int, BillPayment> */
+    #[ORM\OneToMany(targetEntity: BillPayment::class, mappedBy: 'bill', cascade: ['persist', 'remove'])]
+    protected Collection $billPayments;
+
+    /** @var Collection<int, PayfipPaymentReturn> */
+    #[ORM\OneToMany(targetEntity: PayfipPaymentReturn::class, mappedBy: 'bill', cascade: ['persist', 'remove'])]
+    protected Collection $payfipPaymentReturns;
+
+    public function __construct()
+    {
+        $this->billLines = new ArrayCollection();
+        $this->billCredits = new ArrayCollection();
+        $this->billPayments = new ArrayCollection();
+        $this->payfipPaymentReturns = new ArrayCollection();
+        parent::__construct();
+    }
+
+    public function getBillLines(): Collection
+    {
+        return $this->billLines;
+    }
+
+    public function addBillLine(BillLine $billLine): self
+    {
+        if (!$this->billLines->contains($billLine)) {
+            $this->billLines[] = $billLine;
+            $billLine->setBill($this);
+        }
+
+        return $this;
+    }
+
+    public function removeBillLine(BillLine $billLine): self
+    {
+        $this->billLines->removeElement($billLine);
+
+        return $this;
+    }
+
+    public function getBillCredits(): Collection
+    {
+        return $this->billCredits;
+    }
+
+    public function addBillCredit(BillCredit $billCredit): self
+    {
+        if (!$this->billCredits->contains($billCredit)) {
+            $this->billCredits[] = $billCredit;
+            $billCredit->setBill($this);
+        }
+
+        return $this;
+    }
+
+    public function removeBillCredit(BillCredit $billCredit): self
+    {
+        if ($this->billCredits->removeElement($billCredit)) {
+            // set the owning side to null (unless already changed)
+            if ($billCredit->getBill() === $this) {
+                $billCredit->setBill(null);
+            }
+        }
+
+        return $this;
+    }
+
+    public function getBillPayments(): Collection
+    {
+        return $this->billPayments;
+    }
+
+    public function addBillPayment(BillPayment $billPayment): self
+    {
+        if (!$this->billPayments->contains($billPayment)) {
+            $this->billPayments[] = $billPayment;
+            $billPayment->setBill($this);
+        }
+
+        return $this;
+    }
+
+    public function removeBillPayment(BillPayment $billPayment): self
+    {
+        if ($this->billPayments->removeElement($billPayment)) {
+            // set the owning side to null (unless already changed)
+            if ($billPayment->getBill() === $this) {
+                $billPayment->setBill(null);
+            }
+        }
+
+        return $this;
+    }
+
+    public function getPayfipPaymentReturns(): Collection
+    {
+        return $this->payfipPaymentReturns;
+    }
+
+    public function addPayfipPaymentReturn(PayfipPaymentReturn $payfipPaymentReturn): self
+    {
+        if (!$this->payfipPaymentReturns->contains($payfipPaymentReturn)) {
+            $this->payfipPaymentReturns[] = $payfipPaymentReturn;
+            $payfipPaymentReturn->setBill($this);
+        }
+
+        return $this;
+    }
+
+    public function removePayfipPaymentReturn(PayfipPaymentReturn $payfipPaymentReturn): self
+    {
+        if ($this->payfipPaymentReturns->removeElement($payfipPaymentReturn)) {
+            // set the owning side to null (unless already changed)
+            if ($payfipPaymentReturn->getBill() === $this) {
+                $payfipPaymentReturn->setBill(null);
+            }
+        }
+
+        return $this;
+    }
 }

+ 2 - 2
src/Entity/Billing/BillAccessDetail.php

@@ -9,11 +9,11 @@ use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres d'un Access pour une facture (Bill).
  */
 // #[Auditable]
-#[ApiResource(operations: [])]
 #[ORM\Entity]
+#[ApiResource(operations: [])]
 class BillAccessDetail
 {
     #[ORM\Id]

+ 3 - 3
src/Entity/Billing/BillAccounting.php

@@ -7,12 +7,12 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * Facture ou avoir (@see Bill, BillCredit).
+ */
 #[ApiResource(operations: [])]
-#[ORM\Table(name: 'BillAccounting')]
 // #[Auditable]
 #[ORM\Entity]
 class BillAccounting extends AbstractBillAccounting
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr = 'billaccounting';
 }

+ 2 - 0
src/Entity/Billing/BillAccountingInterface.php

@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 namespace App\Entity\Billing;
 
 interface BillAccountingInterface

+ 3 - 3
src/Entity/Billing/BillCredit.php

@@ -7,12 +7,12 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * Avoir.
+ */
 #[ApiResource(operations: [])]
 // #[Auditable]
-#[ORM\Table(name: 'BillAccounting')]
 #[ORM\Entity]
 class BillCredit extends AbstractBillAccounting
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr = 'billcredit';
 }

+ 3 - 1
src/Entity/Billing/BillDebitBalance.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Trop-perçu conservé pour être reversé à la Bill suivante.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillDebitBalance
 {

+ 9 - 7
src/Entity/Billing/BillLine.php

@@ -12,7 +12,9 @@ use App\Entity\Product\EquipmentLoan;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Une ligne de facturation.
+ *
+ * @see BillAccounting
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -26,28 +28,28 @@ class BillLine
 
     #[ORM\ManyToOne(inversedBy: 'billLines')]
     #[ORM\JoinColumn(nullable: false)]
-    private AbstractBillAccounting $bill;
+    private Bill $bill;
 
     #[ORM\ManyToOne(inversedBy: 'billLines')]
-    private Access $access;
+    private ?Access $access = null;
 
     #[ORM\ManyToOne(inversedBy: 'billLines')]
-    private EducationalProject $educationalProject;
+    private ?EducationalProject $educationalProject = null;
 
     #[ORM\ManyToOne(inversedBy: 'billLines')]
-    private EquipmentLoan $equipmentLoan;
+    private ?EquipmentLoan $equipmentLoan = null;
 
     public function getId(): ?int
     {
         return $this->id;
     }
 
-    public function getBill(): ?AbstractBillAccounting
+    public function getBill(): Bill
     {
         return $this->bill;
     }
 
-    public function setBill(?AbstractBillAccounting $bill): self
+    public function setBill(Bill $bill): self
     {
         $this->bill = $bill;
 

+ 18 - 13
src/Entity/Billing/BillPayment.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Entity\Billing;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\Tagg;
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -11,9 +12,11 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Données de paiement d'une facture BillAccounting
+ * NB: il peut y avoir plusieurs lignes pour une facture en cas de paiement en plusieurs fois.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillPayment
 {
@@ -24,20 +27,22 @@ class BillPayment
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billPayments')]
     #[ORM\JoinColumn(nullable: true)]
-    private AbstractBillAccounting $bill;
+    private ?Bill $bill = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billDetachedPayments')]
     #[ORM\JoinColumn(nullable: true)]
-    private AccessBilling $accessBilling;
+    private ?AccessBilling $accessBilling = null;
 
-    #[ORM\OneToMany(mappedBy: 'billPayment', targetEntity: BillDebitBalance::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillDebitBalance> */
+    #[ORM\OneToMany(targetEntity: BillDebitBalance::class, mappedBy: 'billPayment', cascade: ['persist'], orphanRemoval: true)]
     #[ORM\JoinColumn(nullable: true)]
     private Collection $billDebitBalances;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'accountBalanceReimbursements')]
     #[ORM\JoinColumn(nullable: true)]
-    private AccessBilling $accessBillingAccountBalanceReimbursement;
+    private ?AccessBilling $accessBillingAccountBalanceReimbursement = null;
 
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'payments', cascade: ['persist'])]
     #[ORM\JoinTable(name: 'tag_billPayment')]
     #[ORM\JoinColumn(name: 'billPayment_id', referencedColumnName: 'id')]
@@ -46,11 +51,11 @@ class BillPayment
 
     #[ORM\ManyToOne(targetEntity: SddBank::class, cascade: ['persist'], inversedBy: 'billPayments')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected SddBank $sddBank;
+    protected ?SddBank $sddBank;
 
     #[ORM\ManyToOne(targetEntity: SddRegie::class, cascade: ['persist'], inversedBy: 'billPayments')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected SddRegie $sddRegie;
+    protected ?SddRegie $sddRegie;
 
     public function __construct()
     {
@@ -63,12 +68,12 @@ class BillPayment
         return $this->id;
     }
 
-    public function getBill(): ?AbstractBillAccounting
+    public function getBill(): ?Bill
     {
         return $this->bill;
     }
 
-    public function setBill(?AbstractBillAccounting $bill): self
+    public function setBill(?Bill $bill): self
     {
         $this->bill = $bill;
 
@@ -153,24 +158,24 @@ class BillPayment
         return $this;
     }
 
-    public function getSddBank(): SddBank
+    public function getSddBank(): ?SddBank
     {
         return $this->sddBank;
     }
 
-    public function setSddBank(SddBank $sddBank): self
+    public function setSddBank(?SddBank $sddBank): self
     {
         $this->sddBank = $sddBank;
 
         return $this;
     }
 
-    public function getSddRegie(): SddRegie
+    public function getSddRegie(): ?SddRegie
     {
         return $this->sddRegie;
     }
 
-    public function setSddRegie(SddRegie $sddRegie): self
+    public function setSddRegie(?SddRegie $sddRegie): self
     {
         $this->sddRegie = $sddRegie;
 

+ 4 - 1
src/Entity/Billing/BillPeriod.php

@@ -5,12 +5,15 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Période associée à une facture
+ * (Ex: Deuxième trimestre 2020).
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillPeriod
 {

+ 9 - 3
src/Entity/Billing/BillSchedule.php

@@ -10,6 +10,9 @@ use App\Entity\Organization\Organization;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillSchedule
@@ -23,13 +26,16 @@ class BillSchedule
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: false, onDelete: 'SET NULL')]
     protected Organization $organization;
 
-    #[ORM\OneToMany(mappedBy: 'billSchedule', targetEntity: BillScheduleDate::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillScheduleDate> */
+    #[ORM\OneToMany(targetEntity: BillScheduleDate::class, mappedBy: 'billSchedule', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $scheduleDates;
 
-    #[ORM\OneToMany(mappedBy: 'billSchedule', targetEntity: AccessBilling::class, cascade: [], orphanRemoval: false)] // TODO: à revoir
+    /** @var Collection<int, AccessBilling> */
+    #[ORM\OneToMany(targetEntity: AccessBilling::class, mappedBy: 'billSchedule', cascade: [], orphanRemoval: false)] // TODO: à revoir
     protected Collection $accessBilling;
 
-    #[ORM\OneToMany(mappedBy: 'billSchedule', targetEntity: AccessWish::class, cascade: [], orphanRemoval: false)] // TODO: à revoir
+    /** @var Collection<int, AccessWish> */
+    #[ORM\OneToMany(targetEntity: AccessWish::class, mappedBy: 'billSchedule', cascade: [], orphanRemoval: false)] // TODO: à revoir
     protected Collection $accessWishes;
 
     public function getId(): int

+ 3 - 0
src/Entity/Billing/BillScheduleDate.php

@@ -7,6 +7,9 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillScheduleDate

+ 3 - 1
src/Entity/Billing/BillTotalDetail.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Totaux des lignes BillLine d'une facture BillAccounting, avec calcul de TVA.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillTotalDetail
 {

+ 9 - 6
src/Entity/Billing/BillingExportSetting.php

@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -12,9 +13,10 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres généraux des exports de factures (Bill).
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillingExportSetting
 {
@@ -25,11 +27,12 @@ class BillingExportSetting
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(nullable: true)]
-    private SddTeneur $teneur;
+    private ?SddTeneur $teneur = null;
 
-    #[ORM\OneToOne(inversedBy: 'billingExportSetting', targetEntity: Organization::class, cascade: [])]
-    protected Organization $organization;
+    #[ORM\OneToOne(targetEntity: Organization::class, inversedBy: 'billingExportSetting', cascade: [])]
+    protected ?Organization $organization;
 
+    /** @var Collection<int, Access> */
     #[ORM\ManyToMany(targetEntity: Access::class, cascade: [], orphanRemoval: false)]
     protected Collection $stageManagers;
 
@@ -55,12 +58,12 @@ class BillingExportSetting
         return $this;
     }
 
-    public function getOrganization(): Organization
+    public function getOrganization(): ?Organization
     {
         return $this->organization;
     }
 
-    public function setOrganization(Organization $organization): self
+    public function setOrganization(?Organization $organization): self
     {
         $this->organization = $organization;
 

+ 9 - 8
src/Entity/Billing/BillingIntangibleExcludeDate.php

@@ -10,7 +10,8 @@ use App\Entity\Product\EquipmentLoan;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Indique les produits (BillingIntangible) déjà facturés
+ * TODO: confirmer la doc?
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -23,19 +24,19 @@ class BillingIntangibleExcludeDate
     private ?int $id = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingIntangibleExcludeDates')]
-    private AccessIntangible $accessIntangible;
+    private ?AccessIntangible $accessIntangible = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingIntangibleExcludeDates')]
-    private AccessFictionalIntangible $accessFictionalIntangible;
+    private ?AccessFictionalIntangible $accessFictionalIntangible = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingIntangibleExcludeDates')]
-    private EducationalProjectIntangible $educationalProjectIntangible;
+    private ?EducationalProjectIntangible $educationalProjectIntangible = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingIntangibleExcludeDates')]
-    private EquipmentLoan $equipmentLoan;
+    private ?EquipmentLoan $equipmentLoan = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingIntangibleExcludeDates')]
-    private Bill $bill;
+    private ?AbstractBillAccounting $bill = null;
 
     public function getId(): ?int
     {
@@ -90,12 +91,12 @@ class BillingIntangibleExcludeDate
         return $this;
     }
 
-    public function getBill(): ?Bill
+    public function getBill(): ?AbstractBillAccounting
     {
         return $this->bill;
     }
 
-    public function setBill(?Bill $bill): self
+    public function setBill(?AbstractBillAccounting $bill): self
     {
         $this->bill = $bill;
 

+ 11 - 6
src/Entity/Billing/BillingSetting.php

@@ -13,6 +13,9 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 use JetBrains\PhpStorm\Pure;
 
+/**
+ * Paramètres globaux d'une Organization concernant la facturation.
+ */
 #[ApiResource(operations: [])]
 // #[Auditable]
 #[ORM\Entity(repositoryClass: BillingSettingRepository::class)]
@@ -27,17 +30,19 @@ class BillingSetting
     #[ORM\JoinColumn(nullable: false)]
     private Organization $organization;
 
-    #[ORM\OneToMany(mappedBy: 'billingSetting', targetEntity: ResidenceArea::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, ResidenceArea> */
+    #[ORM\OneToMany(targetEntity: ResidenceArea::class, mappedBy: 'billingSetting', cascade: ['persist'], orphanRemoval: true)]
     private Collection $residenceAreas;
 
     #[ORM\Column(options: ['default' => false])]
     private bool $applyVat = false;
 
-    #[ORM\OneToMany(mappedBy: 'billingSetting', targetEntity: FamilyQuotient::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, FamilyQuotient> */
+    #[ORM\OneToMany(targetEntity: FamilyQuotient::class, mappedBy: 'billingSetting', cascade: ['persist'], orphanRemoval: true)]
     private Collection $familyQuotients;
 
-    #[ORM\OneToOne(mappedBy: 'billingSetting', targetEntity: BillingSettingRent::class, cascade: ['persist'])]
-    protected BillingSettingRent $billingSettingRent;
+    #[ORM\OneToOne(targetEntity: BillingSettingRent::class, mappedBy: 'billingSetting', cascade: ['persist', 'remove'])]
+    protected ?BillingSettingRent $billingSettingRent;
 
     #[Pure]
     public function __construct()
@@ -132,12 +137,12 @@ class BillingSetting
         return $this;
     }
 
-    public function getBillingSettingRent(): BillingSettingRent
+    public function getBillingSettingRent(): ?BillingSettingRent
     {
         return $this->billingSettingRent;
     }
 
-    public function setBillingSettingRent(BillingSettingRent $billingSettingRent): self
+    public function setBillingSettingRent(?BillingSettingRent $billingSettingRent): self
     {
         $this->billingSettingRent = $billingSettingRent;
 

+ 23 - 5
src/Entity/Billing/BillingSettingRent.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres globaux d'une Organization concernant la facturation liée à la location.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class BillingSettingRent
 {
@@ -19,15 +21,19 @@ class BillingSettingRent
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToOne(inversedBy: 'billingSettingRent', targetEntity: BillingSetting::class, cascade: [])]
-    protected BillingSetting $billingSetting;
+    #[ORM\OneToOne(targetEntity: BillingSetting::class, inversedBy: 'billingSettingRent', cascade: [])]
+    protected ?BillingSetting $billingSetting;
 
-    public function getBillingSetting(): BillingSetting
+    #[ORM\ManyToOne(targetEntity: FamilyQuotientModel::class, cascade: [], inversedBy: 'billingSettingRents')]
+    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
+    protected ?FamilyQuotientModel $familyQuotientModel;
+
+    public function getBillingSetting(): ?BillingSetting
     {
         return $this->billingSetting;
     }
 
-    public function setBillingSetting(BillingSetting $billingSetting): self
+    public function setBillingSetting(?BillingSetting $billingSetting): self
     {
         $this->billingSetting = $billingSetting;
 
@@ -38,4 +44,16 @@ class BillingSettingRent
     {
         return $this->id;
     }
+
+    public function getFamilyQuotientModel(): ?FamilyQuotientModel
+    {
+        return $this->familyQuotientModel;
+    }
+
+    public function setFamilyQuotientModel(?FamilyQuotientModel $familyQuotientModel): self
+    {
+        $this->familyQuotientModel = $familyQuotientModel;
+
+        return $this;
+    }
 }

+ 8 - 5
src/Entity/Billing/Ciril.php

@@ -5,15 +5,17 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class Ciril
 {
@@ -22,11 +24,12 @@ class Ciril
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'ciril', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'ciril', cascade: ['persist'], orphanRemoval: true)]
     private Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function __construct()
     {
@@ -68,12 +71,12 @@ class Ciril
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 8 - 4
src/Entity/Billing/CirilCivil.php

@@ -9,6 +9,9 @@ use App\Entity\Core\File;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class CirilCivil
@@ -18,11 +21,12 @@ class CirilCivil
     #[ORM\GeneratedValue]
     private int $id;
 
-    #[ORM\OneToMany(mappedBy: 'cirilCivil', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'cirilCivil', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function getId(): int
     {
@@ -58,12 +62,12 @@ class CirilCivil
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 7 - 13
src/Entity/Billing/EducationalProjectIntangible.php

@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace App\Entity\Billing;
 
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Booking\EducationalProject;
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -11,31 +12,24 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Fais le lien entre l'Access qui règle la facture et l'EducationalProject concerné.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class EducationalProjectIntangible extends AbstractBillingIntangible
 {
-    #[ORM\Id]
-    #[ORM\Column]
-    #[ORM\GeneratedValue]
-    protected ?int $id = null;
-
     #[ORM\ManyToOne(inversedBy: 'educationalProjectIntangibles')]
-    private EducationalProject $educationalProject;
+    private ?EducationalProject $educationalProject = null;
 
-    #[ORM\OneToMany(mappedBy: 'educationalProjectIntangible', targetEntity: BillingIntangibleExcludeDate::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, BillingIntangibleExcludeDate> */
+    #[ORM\OneToMany(targetEntity: BillingIntangibleExcludeDate::class, mappedBy: 'educationalProjectIntangible', cascade: ['persist'], orphanRemoval: true)]
     private Collection $billingIntangibleExcludeDates;
 
     public function __construct()
     {
         $this->billingIntangibleExcludeDates = new ArrayCollection();
-    }
-
-    public function getId(): ?int
-    {
-        return $this->id;
+        parent::__construct();
     }
 
     public function getEducationalProject(): ?EducationalProject

+ 4 - 18
src/Entity/Billing/EducationalProjectPayer.php

@@ -13,32 +13,18 @@ use Doctrine\ORM\Mapping as ORM;
 /**
  * @todo : A la suite de la migration, il faut supprimer le nom de la table pour avoir une table EducationalProjectPayer, et supprimer l'attribut discr.
  *
- * Classe ... qui ...
+ * Fais le lien entre l'Access qui règle la facture et l'EducationalProject concerné
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
-#[ORM\Table(name: 'BillingPayer')]
 #[ORM\Entity]
-class EducationalProjectPayer
+class EducationalProjectPayer extends AbstractBillingPayer
 {
-    #[ORM\Column(length: 255, nullable: false)]
-    private string $discr = 'educationalproject';
-
-    #[ORM\Id]
-    #[ORM\Column]
-    #[ORM\GeneratedValue]
-    private ?int $id = null;
-
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingEducationalProjectPayers')]
-    private Access $educationalProjectPayer;
+    private ?Access $educationalProjectPayer = null;
 
     #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'billingReceivers')]
-    private EducationalProject $educationalProjectReceiver;
-
-    public function getId(): ?int
-    {
-        return $this->id;
-    }
+    private ?EducationalProject $educationalProjectReceiver = null;
 
     public function getEducationalProjectPayer(): ?Access
     {

+ 5 - 3
src/Entity/Billing/FamilyQuotient.php

@@ -12,7 +12,7 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Définition d'une tranche de quotient familial.
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -28,10 +28,12 @@ class FamilyQuotient
     #[ORM\JoinColumn(nullable: false)]
     private BillingSetting $billingSetting;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotient', targetEntity: AccessBilling::class)]
+    /** @var Collection<int, AccessBilling> */
+    #[ORM\OneToMany(targetEntity: AccessBilling::class, mappedBy: 'familyQuotient')]
     private Collection $accessBilling;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotient', targetEntity: IntangibleDiscountDetail::class)]
+    /** @var Collection<int, IntangibleDiscountDetail> */
+    #[ORM\OneToMany(targetEntity: IntangibleDiscountDetail::class, mappedBy: 'familyQuotient')]
     private Collection $intangibleDiscountDetails;
 
     public function __construct()

+ 8 - 7
src/Entity/Billing/FamilyQuotientBand.php

@@ -9,6 +9,9 @@ use App\Entity\Product\IntangibleDiscountDetail;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class FamilyQuotientBand
@@ -22,15 +25,13 @@ class FamilyQuotientBand
     #[ORM\JoinColumn(nullable: false)]
     protected mixed $familyQuotientModel;
 
-    #[ORM\OneToMany(
-        mappedBy: 'familyQuotientBand',
-        targetEntity: FamilyQuotientBandDetail::class,
-        cascade: ['persist'],
-        orphanRemoval: true,
-    )]
+    /** @var Collection<int, FamilyQuotientBandDetail> */
+    //    #[ORM\Column(type: 'string')] // TODO: pourquoi c'est là ça?
+    #[ORM\OneToMany(targetEntity: FamilyQuotientBandDetail::class, mappedBy: 'familyQuotientBand', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $familyQuotientBandDetails;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotientBand', targetEntity: IntangibleDiscountDetail::class, cascade: [], orphanRemoval: true)]
+    /** @var Collection<int, IntangibleDiscountDetail> */
+    #[ORM\OneToMany(targetEntity: IntangibleDiscountDetail::class, mappedBy: 'familyQuotientBand', cascade: [], orphanRemoval: true)]
     protected Collection $intangibleDiscountDetails;
 
     public function getId(): int

+ 8 - 4
src/Entity/Billing/FamilyQuotientBandDetail.php

@@ -9,6 +9,9 @@ use App\Entity\Product\IntangibleDiscountDetail;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class FamilyQuotientBandDetail
@@ -24,9 +27,10 @@ class FamilyQuotientBandDetail
 
     #[ORM\ManyToOne(targetEntity: ResidenceArea::class, cascade: [], inversedBy: 'intangibleDiscountDetails')]
     #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
-    protected ResidenceArea $residenceArea;
+    protected ?ResidenceArea $residenceArea;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotientBandDetail', targetEntity: IntangibleDiscountDetail::class, cascade: [], orphanRemoval: true)]
+    /** @var Collection<int, IntangibleDiscountDetail> */
+    #[ORM\OneToMany(targetEntity: IntangibleDiscountDetail::class, mappedBy: 'familyQuotientBandDetail', cascade: [], orphanRemoval: true)]
     protected Collection $intangibleDiscountDetails;
 
     public function getId(): int
@@ -53,12 +57,12 @@ class FamilyQuotientBandDetail
         return $this;
     }
 
-    public function getResidenceArea(): ResidenceArea
+    public function getResidenceArea(): ?ResidenceArea
     {
         return $this->residenceArea;
     }
 
-    public function setResidenceArea(ResidenceArea $residenceArea): self
+    public function setResidenceArea(?ResidenceArea $residenceArea): self
     {
         $this->residenceArea = $residenceArea;
 

+ 41 - 2
src/Entity/Billing/FamilyQuotientModel.php

@@ -7,9 +7,13 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Organization\Organization;
 use App\Entity\Product\IntangiblePriceAndDiscount;
+use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class FamilyQuotientModel
@@ -23,12 +27,23 @@ class FamilyQuotientModel
     #[ORM\JoinColumn(nullable: false)]
     protected Organization $organization;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotientModel', targetEntity: FamilyQuotientBand::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, FamilyQuotientBand> */
+    #[ORM\OneToMany(targetEntity: FamilyQuotientBand::class, mappedBy: 'familyQuotientModel', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $familyQuotientBands;
 
-    #[ORM\OneToMany(mappedBy: 'familyQuotientModel', targetEntity: IntangiblePriceAndDiscount::class, cascade: [], orphanRemoval: false)]
+    /** @var Collection<int, IntangiblePriceAndDiscount> */
+    #[ORM\OneToMany(targetEntity: IntangiblePriceAndDiscount::class, mappedBy: 'familyQuotientModel', cascade: [], orphanRemoval: false)]
     protected Collection $intangiblePriceAndDiscounts;
 
+    /** @var Collection<int, BillingSettingRent> */
+    #[ORM\OneToMany(targetEntity: BillingSettingRent::class, mappedBy: 'familyQuotientModel', cascade: [], orphanRemoval: false)]
+    protected Collection $billingSettingRents;
+
+    public function __construct()
+    {
+        $this->billingSettingRents = new ArrayCollection();
+    }
+
     public function getId(): int
     {
         return $this->id;
@@ -100,4 +115,28 @@ class FamilyQuotientModel
 
         return $this;
     }
+
+    public function getBillingSettingRents(): Collection
+    {
+        return $this->billingSettingRents;
+    }
+
+    public function addBillingSettingRent(BillingSettingRent $billingSettingRent): self
+    {
+        if (!$this->billingSettingRents->contains($billingSettingRent)) {
+            $this->billingSettingRents[] = $billingSettingRent;
+            $billingSettingRent->setFamilyQuotientModel($this);
+        }
+
+        return $this;
+    }
+
+    public function removeBillingSettingRent(BillingSettingRent $billingSettingRent): self
+    {
+        if ($this->billingSettingRents->removeElement($billingSettingRent)) {
+            $billingSettingRent->setFamilyQuotientModel(null);
+        }
+
+        return $this;
+    }
 }

+ 8 - 5
src/Entity/Billing/Jvs.php

@@ -5,15 +5,17 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class Jvs
 {
@@ -22,11 +24,12 @@ class Jvs
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'jvs', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'jvs', cascade: ['persist'], orphanRemoval: true)]
     private Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function __construct()
     {
@@ -68,12 +71,12 @@ class Jvs
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 8 - 4
src/Entity/Billing/Odyssee.php

@@ -9,6 +9,9 @@ use App\Entity\Core\File;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class Odyssee
@@ -18,11 +21,12 @@ class Odyssee
     #[ORM\GeneratedValue]
     private int $id;
 
-    #[ORM\OneToMany(mappedBy: 'odyssee', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'odyssee', cascade: ['persist'], orphanRemoval: true)]
     protected Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function getId(): int
     {
@@ -60,12 +64,12 @@ class Odyssee
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 3 - 0
src/Entity/Billing/PayboxPaymentReturn.php

@@ -7,6 +7,9 @@ namespace App\Entity\Billing;
 use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
+/**
+ * TODO: documenter.
+ */
 #[ApiResource(operations: [])]
 #[ORM\Entity]
 class PayboxPaymentReturn

+ 4 - 2
src/Entity/Billing/PayfipPaymentReturn.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class PayfipPaymentReturn
 {
@@ -19,7 +21,7 @@ class PayfipPaymentReturn
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\ManyToOne(cascade: ['persist'])]
+    #[ORM\ManyToOne(targetEntity: Bill::class, inversedBy: 'payfipPaymentReturns', cascade: ['persist', 'remove'])]
     #[ORM\JoinColumn(nullable: false)]
     private Bill $bill;
 

+ 9 - 5
src/Entity/Billing/Pes.php

@@ -5,15 +5,18 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Paramètres généraux d'un export PES
+ * NB: PES est un format de données demandé par le Trésor Public pour l'export des données comptables.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class Pes
 {
@@ -22,11 +25,12 @@ class Pes
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'pes', targetEntity: Bill::class, cascade: ['persist'], orphanRemoval: true)]
+    /** @var Collection<int, Bill> */
+    #[ORM\OneToMany(targetEntity: Bill::class, mappedBy: 'pes', cascade: ['persist'], orphanRemoval: true)]
     private Collection $bills;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function __construct()
     {
@@ -68,12 +72,12 @@ class Pes
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 6 - 4
src/Entity/Billing/PesSetting.php

@@ -5,13 +5,15 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Organization\Organization;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: (Plus utilisée, à confirmer).
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class PesSetting
 {
@@ -21,19 +23,19 @@ class PesSetting
     private ?int $id = null;
 
     #[ORM\OneToOne(targetEntity: Organization::class, inversedBy: 'pesSetting', cascade: [])]
-    protected Organization $organization;
+    protected ?Organization $organization;
 
     public function getId(): ?int
     {
         return $this->id;
     }
 
-    public function getOrganization(): Organization
+    public function getOrganization(): ?Organization
     {
         return $this->organization;
     }
 
-    public function setOrganization(Organization $organization): self
+    public function setOrganization(?Organization $organization): self
     {
         $this->organization = $organization;
 

+ 10 - 4
src/Entity/Billing/ResidenceArea.php

@@ -8,12 +8,13 @@ use ApiPlatform\Metadata\ApiResource;
 use ApiPlatform\Metadata\Delete;
 use ApiPlatform\Metadata\Get;
 use ApiPlatform\Metadata\GetCollection;
+use ApiPlatform\Metadata\Patch;
 use ApiPlatform\Metadata\Post;
 use ApiPlatform\Metadata\Put;
 use App\Attribute\BillingSettingDefaultValue;
 use App\Entity\Product\IntangibleDiscountDetail;
 use App\Repository\Billing\ResidenceAreaRepository;
-// use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
@@ -30,6 +31,9 @@ use Doctrine\ORM\Mapping as ORM;
         new Get(
             security: 'is_granted(\'ROLE_ORGANIZATION_VIEW\') and object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
         ),
+        new Patch(
+            security: 'is_granted(\'ROLE_ORGANIZATION\') and object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
+        ),
         new Put(
             security: 'is_granted(\'ROLE_ORGANIZATION\') and object.getBillingSetting().getOrganization().getId() == user.getOrganization().getId()'
         ),
@@ -44,7 +48,7 @@ use Doctrine\ORM\Mapping as ORM;
         ),
     ]
 )]
-// #[Auditable]
+#[Auditable]
 #[BillingSettingDefaultValue(fieldName: 'billingSetting')]
 #[ORM\Entity(repositoryClass: ResidenceAreaRepository::class)]
 class ResidenceArea
@@ -58,10 +62,12 @@ class ResidenceArea
     #[ORM\JoinColumn(nullable: false)]
     private BillingSetting $billingSetting;
 
-    #[ORM\OneToMany(mappedBy: 'residenceArea', targetEntity: AccessBilling::class)]
+    /** @var Collection<int, AccessBilling> */
+    #[ORM\OneToMany(targetEntity: AccessBilling::class, mappedBy: 'residenceArea')]
     private Collection $accessBilling;
 
-    #[ORM\OneToMany(mappedBy: 'residenceArea', targetEntity: IntangibleDiscountDetail::class)]
+    /** @var Collection<int, IntangibleDiscountDetail> */
+    #[ORM\OneToMany(targetEntity: IntangibleDiscountDetail::class, mappedBy: 'residenceArea')]
     private Collection $intangibleDiscountDetails;
 
     #[ORM\Column(length: 255)]

+ 8 - 5
src/Entity/Billing/SddBank.php

@@ -5,15 +5,17 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class SddBank
 {
@@ -22,11 +24,12 @@ class SddBank
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'sddBank', targetEntity: BillPayment::class, cascade: [], orphanRemoval: false)]
+    /** @var Collection<int, BillPayment> */
+    #[ORM\OneToMany(targetEntity: BillPayment::class, mappedBy: 'sddBank', cascade: [], orphanRemoval: false)]
     protected Collection $billPayments;
 
     #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    protected ?File $file;
 
     public function __construct()
     {
@@ -62,12 +65,12 @@ class SddBank
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 

+ 15 - 10
src/Entity/Billing/SddRegie.php

@@ -5,16 +5,18 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use App\Entity\Core\File;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter
  * // TODO: possible factoriser avec SddBank?
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class SddRegie
 {
@@ -23,14 +25,17 @@ class SddRegie
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\OneToMany(mappedBy: 'sddRegie', targetEntity: BillPayment::class, cascade: [], orphanRemoval: false)]
+    /** @var Collection<int, BillPayment> */
+    #[ORM\OneToMany(mappedBy: 'sddRegime', targetEntity: BillPayment::class, cascade: [], orphanRemoval: false)]
     protected Collection $billPayments;
 
-    #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $file;
+    #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'], inversedBy: 'sddRegieFile')]
+    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
+    protected ?File $file;
 
-    #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'])]
-    protected File $bordereau;
+    #[ORM\OneToOne(targetEntity: File::class, cascade: ['persist'], inversedBy: 'sddRegieBordereau')]
+    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
+    protected ?File $bordereau;
 
     public function __construct()
     {
@@ -66,24 +71,24 @@ class SddRegie
         return $this;
     }
 
-    public function getFile(): File
+    public function getFile(): ?File
     {
         return $this->file;
     }
 
-    public function setFile(File $file): self
+    public function setFile(?File $file): self
     {
         $this->file = $file;
 
         return $this;
     }
 
-    public function getBordereau(): File
+    public function getBordereau(): ?File
     {
         return $this->bordereau;
     }
 
-    public function setBordereau(File $bordereau): self
+    public function setBordereau(?File $bordereau): self
     {
         $this->bordereau = $bordereau;
 

+ 3 - 1
src/Entity/Billing/SddTeneur.php

@@ -5,12 +5,14 @@ declare(strict_types=1);
 namespace App\Entity\Billing;
 
 // use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
+use ApiPlatform\Metadata\ApiResource;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * TODO: documenter.
  */
 // #[Auditable]
+#[ApiResource(operations: [])]
 #[ORM\Entity]
 class SddTeneur
 {

+ 64 - 57
src/Entity/Booking/AbstractBooking.php

@@ -10,7 +10,6 @@ use App\Entity\Access\Access;
 use App\Entity\Core\Tagg;
 use App\Entity\Product\Equipment;
 use App\Entity\Traits\ActivityYearTrait;
-use App\Enum\Booking\VisibilityEnum;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
@@ -22,7 +21,20 @@ use Doctrine\ORM\Mapping as ORM;
  */
 #[ActivityYearConstraintAware(startYearFieldName: 'startYear', endYearFieldName: 'endYear')]
 #[OrganizationDefaultValue(fieldName: 'organization')]
-#[ORM\MappedSuperclass]
+#[ORM\Entity]
+#[ORM\Table(name: 'Booking')]
+#[ORM\InheritanceType('SINGLE_TABLE')]
+#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
+#[ORM\DiscriminatorMap(
+    [
+        'educationalproject' => EducationalProject::class,
+        'course' => Course::class,
+        'event' => Event::class,
+        'examen' => Examen::class,
+        'organizationholiday' => OrganizationHoliday::class,
+        'personholiday' => PersonHoliday::class,
+    ]
+)]
 abstract class AbstractBooking
 {
     use ActivityYearTrait;
@@ -32,31 +44,37 @@ abstract class AbstractBooking
     #[ORM\GeneratedValue]
     protected ?int $id = null;
 
-    #[ORM\Column(length: 255, nullable: false)]
-    protected string $discr;
-
     #[ORM\Column]
     protected string $name;
 
+    /** @var Collection<int, Access> */
+    #[ORM\ManyToMany(targetEntity: Access::class, inversedBy: 'educationalProjectOrganizers')]
+    #[ORM\JoinTable(name: 'booking_organizer')]
+    #[ORM\JoinColumn(name: 'booking_id', referencedColumnName: 'id')]
+    #[ORM\InverseJoinColumn(name: 'organizer_id', referencedColumnName: 'id')]
+    protected Collection $organizer;
+
     #[ORM\Column(type: 'datetime', nullable: true)]
     protected ?\DateTimeInterface $datetimeStart = null;
 
     #[ORM\Column(type: 'datetime', nullable: true)]
     protected ?\DateTimeInterface $datetimeEnd = null;
 
-    #[ORM\Column(length: 50, nullable: false, enumType: VisibilityEnum::class)]
-    protected VisibilityEnum $visibility;
+    /** @var Collection<int, Equipment> */
+    #[ORM\ManyToMany(targetEntity: Equipment::class, cascade: [], orphanRemoval: false)]
+    #[ORM\JoinTable(name: 'booking_equipment')]
+    #[ORM\JoinColumn(name: 'booking_id')]
+    #[ORM\InverseJoinColumn(name: 'equipment_id')]
+    protected Collection $equipments;
 
     #[ORM\Column(unique: true)]
     protected string $uuid;
 
-    #[ORM\ManyToMany(targetEntity: Access::class, inversedBy: 'practicalCourses', cascade: [], orphanRemoval: false)]
-    protected Collection $organizer;
-
-    #[ORM\ManyToMany(targetEntity: Equipment::class, cascade: [], orphanRemoval: false)]
-    protected Collection $equipments;
-
+    /** @var Collection<int, Tagg> */
     #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'bookings', cascade: ['persist'], orphanRemoval: false)]
+    #[ORM\JoinTable(name: 'tag_booking')]
+    #[ORM\JoinColumn(name: 'booking_id')]
+    #[ORM\InverseJoinColumn(name: 'tag_id')]
     protected Collection $tags;
 
     public function __construct()
@@ -83,73 +101,49 @@ abstract class AbstractBooking
         return $this;
     }
 
-    public function setDatetimeStart(?\DateTimeInterface $datetimeStart = null): self
-    {
-        $this->datetimeStart = $datetimeStart;
-
-        return $this;
-    }
-
-    public function getDatetimeStart(): ?\DateTimeInterface
+    public function getOrganizer(): Collection
     {
-        return $this->datetimeStart;
+        return $this->organizer;
     }
 
-    public function setDatetimeEnd(?\DateTimeInterface $datetimeEnd = null): self
+    public function addOrganizer(Access $organizer): self
     {
-        $this->datetimeEnd = $datetimeEnd;
+        if (!$this->organizer->contains($organizer)) {
+            $this->organizer[] = $organizer;
+        }
 
         return $this;
     }
 
-    public function getDatetimeEnd(): ?\DateTimeInterface
-    {
-        return $this->datetimeEnd;
-    }
-
-    public function getVisibility(): ?VisibilityEnum
-    {
-        return $this->visibility;
-    }
-
-    public function setVisibility(?VisibilityEnum $visibility): self
+    public function removeOrganizer(Access $organizer): self
     {
-        $this->visibility = $visibility;
+        $this->organizer->removeElement($organizer);
 
         return $this;
     }
 
-    public function getUuid(): string
-    {
-        return $this->uuid;
-    }
-
-    public function setUuid(string $uuid): self
+    public function setDatetimeStart(?\DateTimeInterface $datetimeStart = null): self
     {
-        $this->uuid = $uuid;
+        $this->datetimeStart = $datetimeStart;
 
         return $this;
     }
 
-    public function getOrganizer(): Collection
+    public function getDatetimeStart(): ?\DateTimeInterface
     {
-        return $this->organizer;
+        return $this->datetimeStart;
     }
 
-    public function addOrganizer(Access $organizer): self
+    public function setDatetimeEnd(?\DateTimeInterface $datetimeEnd = null): self
     {
-        if (!$this->organizer->contains($organizer)) {
-            $this->organizer[] = $organizer;
-        }
+        $this->datetimeEnd = $datetimeEnd;
 
         return $this;
     }
 
-    public function removeOrganizer(Access $organizer): self
+    public function getDatetimeEnd(): ?\DateTimeInterface
     {
-        $this->organizer->removeElement($organizer);
-
-        return $this;
+        return $this->datetimeEnd;
     }
 
     public function getEquipments(): Collection
@@ -159,9 +153,7 @@ abstract class AbstractBooking
 
     public function addEquipment(Equipment $equipment): self
     {
-        if (!$this->equipments->contains($equipment)) {
-            $this->equipments[] = $equipment;
-        }
+        $this->equipments[] = $equipment;
 
         return $this;
     }
@@ -173,6 +165,18 @@ abstract class AbstractBooking
         return $this;
     }
 
+    public function getUuid(): string
+    {
+        return $this->uuid;
+    }
+
+    public function setUuid(string $uuid): self
+    {
+        $this->uuid = $uuid;
+
+        return $this;
+    }
+
     public function getTags(): Collection
     {
         return $this->tags;
@@ -182,6 +186,7 @@ abstract class AbstractBooking
     {
         if (!$this->tags->contains($tag)) {
             $this->tags[] = $tag;
+            $tag->addBooking($this);
         }
 
         return $this;
@@ -189,7 +194,9 @@ abstract class AbstractBooking
 
     public function removeTag(Tagg $tag): self
     {
-        $this->tags->removeElement($tag);
+        if ($this->tags->removeElement($tag)) {
+            $tag->removeBooking($this);
+        }
 
         return $this;
     }

+ 17 - 2
src/Entity/Booking/AbstractBookingRecur.php

@@ -7,9 +7,24 @@ namespace App\Entity\Booking;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Données de récurrence d'un évènement, classe de base des récurrences d'évènements.
+ *
+ * @see EventRecur, CourseRecur, EducationalProjectRecur, ExamenRecur, OrganizationHolidayRecur, PersonHolidayRecur
  */
-#[ORM\MappedSuperclass]
+#[ORM\Entity]
+#[ORM\Table(name: 'BookingRecur')]
+#[ORM\InheritanceType('SINGLE_TABLE')]
+#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
+#[ORM\DiscriminatorMap(
+    [
+        'educationalproject' => EducationalProjectRecur::class,
+        'course' => CourseRecur::class,
+        'event' => EventRecur::class,
+        'examen' => ExamenRecur::class,
+        'organizationholiday' => OrganizationHolidayRecur::class,
+        'personholiday' => PersonHolidayRecur::class,
+    ]
+)]
 abstract class AbstractBookingRecur
 {
     #[ORM\Id]

+ 6 - 6
src/Entity/Booking/Attendance.php

@@ -13,7 +13,7 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 
 /**
- * Classe ... qui ...
+ * Représente une période d'absence pour un Access dans une Organization.
  */
 #[ApiResource(operations: [])]
 // #[Auditable]
@@ -26,16 +26,16 @@ class Attendance
     private ?int $id = null;
 
     #[ORM\ManyToOne(inversedBy: 'attendances')]
-    private Organization $organization;
+    private ?Organization $organization = null;
 
     #[ORM\ManyToOne(inversedBy: 'attendances')]
-    private Access $access;
+    private ?Access $access = null;
 
     #[ORM\ManyToOne(inversedBy: 'attendanceReplacements')]
-    private Access $replacement;
+    private ?Access $replacement = null;
 
-    #[ORM\OneToMany(mappedBy: 'attendance', targetEntity: AttendanceBooking::class, cascade: ['persist'], orphanRemoval: true)]
-    #[ORM\JoinColumn(nullable: false)]
+    /** @var Collection<int, AttendanceBooking> */
+    #[ORM\OneToMany(targetEntity: AttendanceBooking::class, mappedBy: 'attendance', cascade: ['persist', 'remove'])]
     private Collection $attendanceBooking;
 
     public function __construct()

部分文件因为文件数量过多而无法显示