Selaa lähdekoodia

add DolibarrSync service and command

Olivier Massot 3 vuotta sitten
vanhempi
commit
b1f5c5a7c8

+ 6 - 0
.env

@@ -55,3 +55,9 @@ MOBYT_API_BASE_URI='https://app.mobyt.fr/API/v1.0/REST/'
 ###> AdminAssos configuration ###
 #DATABASE_ADMINASSOS_URL=mysql://root:mysql660@db:3306/adminassos?serverVersion=5.7
 ###< AdminAssos configuration ###
+
+###> symfony/lock ###
+# Choose one of the stores below
+# postgresql+advisory://db_user:db_password@localhost/db_name
+LOCK_DSN=semaphore
+###< symfony/lock ###

+ 1 - 0
composer.json

@@ -32,6 +32,7 @@
         "symfony/framework-bundle": "5.3.*",
         "symfony/http-client": "5.3.*",
         "symfony/intl": "5.3.*",
+        "symfony/lock": "5.3.*",
         "symfony/monolog-bundle": "^3.0",
         "symfony/property-access": "5.3.*",
         "symfony/property-info": "5.3.*",

+ 81 - 2
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "588c341ea52a6d0ebb842b76343e98ec",
+    "content-hash": "374a0d1bd7348ba6d31cf0679c9603b6",
     "packages": [
         {
             "name": "api-platform/core",
@@ -5145,6 +5145,85 @@
             ],
             "time": "2021-11-12T11:38:27+00:00"
         },
+        {
+            "name": "symfony/lock",
+            "version": "v5.3.14",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/lock.git",
+                "reference": "0f498d4b90b2a52a063243c461cd0dc5b3bab758"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/lock/zipball/0f498d4b90b2a52a063243c461cd0dc5b3bab758",
+                "reference": "0f498d4b90b2a52a063243c461cd0dc5b3bab758",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/log": "^1|^2|^3",
+                "symfony/deprecation-contracts": "^2.1",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "conflict": {
+                "doctrine/dbal": "<2.10"
+            },
+            "require-dev": {
+                "doctrine/dbal": "^2.10|^3.0",
+                "predis/predis": "~1.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Lock\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jérémy Derussé",
+                    "email": "jeremy@derusse.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "cas",
+                "flock",
+                "locking",
+                "mutex",
+                "redlock",
+                "semaphore"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/lock/tree/v5.3.14"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:51:59+00:00"
+        },
         {
             "name": "symfony/monolog-bridge",
             "version": "v5.3.7",
@@ -8783,5 +8862,5 @@
         "ext-iconv": "*"
     },
     "platform-dev": [],
-    "plugin-api-version": "2.1.0"
+    "plugin-api-version": "2.2.0"
 }

+ 2 - 0
config/packages/lock.yaml

@@ -0,0 +1,2 @@
+framework:
+    lock: '%env(LOCK_DSN)%'

+ 71 - 0
src/Commands/DolibarrSyncCommand.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Commands;
+
+use App\Service\Dolibarr\DolibarrSync\DolibarrSyncService;
+use Symfony\Component\Console\Attribute\AsCommand;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Command\LockableTrait;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Question\Question;
+
+#[AsCommand(
+    name: 'opentalent:dolibarr-sync',
+    description: 'Push the latest data from the Opentalent DB to dolibarr'
+)]
+class DolibarrSyncCommand extends Command
+{
+    use LockableTrait;
+
+    /**
+     * How many operations are shown each time the preview choice is made
+     */
+    const PREVIEW_CHUNK = 20;
+
+    public function __construct(
+        private DolibarrSyncService $dolibarrSyncService
+    ) {
+        parent::__construct();
+    }
+
+    protected function configure()
+    {
+        $this->addOption(
+            'preview',
+            'p',
+            InputOption::VALUE_NONE,
+            'Only preview the sync operations instead of executing it'
+        );
+    }
+
+    protected function execute(InputInterface $input, OutputInterface $output)
+    {
+        if (!$this->lock()) {
+            $output->writeln('The command is already running in another process.');
+            return Command::SUCCESS;
+        }
+
+        $output->writeln("Start the synchronization");
+        $output->writeln("Scanning...");
+
+        $operations = $this->dolibarrSyncService->scan();
+        $output->writeln(count($operations) . " operations to be executed");
+
+        if ($input->getOption('preview')) {
+            for ($i = 0; $i < count($operations); $i++) {
+                $output->writeln($i . '. ' . $operations[$i]->getLabel());
+                foreach ($operations[$i]->getChangeLog() as $message) {
+                    $output->writeln('   ' . $message);
+                }
+            }
+        } else {
+            $output->writeln("Executing...");
+//            $operations = $this->dolibarrSyncService->execute($operations);
+//            $output->writeln(count($operations) . " operations successfully executed");
+        }
+
+        return Command::SUCCESS;
+    }
+}

+ 38 - 0
src/Repository/Access/AccessRepository.php

@@ -114,4 +114,42 @@ class AccessRepository extends ServiceEntityRepository implements UserLoaderInte
 
         return $result;
     }
+
+    /**
+     * Returns the current number of accesses with the given active role for the
+     * organization.
+     *
+     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws \Doctrine\ORM\NoResultException
+     */
+    public function countAccessesWithActiveMission(int $organizationId, string $function): int {
+        $qb = $this->createQueryBuilder('a');
+        $qb->select('count(a.id)')
+            ->innerJoin('a.organizationFunction', 'ofn')
+            ->innerJoin('ofn.functionType', 'f')
+            ->innerJoin('a.organization', 'o')
+            ->where($qb->expr()->eq('f.mission', $qb->expr()->literal($function)))
+            ->andWhere($qb->expr()->eq('o.id', $organizationId));
+
+        DateConditions::addDateInPeriodCondition($qb, 'ofn', date('Y-m-d'));
+        $q = $qb->getQuery();
+        return (int)$q->getSingleResult($q::HYDRATE_SINGLE_SCALAR);
+    }
+
+    /**
+     * Returns the current number of accesses with an admin access to the
+     * organization.
+     *
+     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws \Doctrine\ORM\NoResultException
+     */
+    public function countAdminAccounts(int $organizationId): int {
+        $qb = $this->createQueryBuilder('a');
+        $qb->select('count(a.id)')
+            ->innerJoin('a.organization', 'o')
+            ->where($qb->expr()->eq('a.adminAccess', true))
+            ->andWhere($qb->expr()->eq('o.id', $organizationId));
+        $q = $qb->getQuery();
+        return (int)$q->getSingleResult($q::HYDRATE_SINGLE_SCALAR);
+    }
 }

+ 1 - 4
src/Repository/Organization/OrganizationRepository.php

@@ -3,12 +3,9 @@ declare(strict_types=1);
 
 namespace App\Repository\Organization;
 
+use App\DQL\DateConditions;
 use App\Entity\Organization\Organization;
-use App\Enum\Organization\OrganizationIdsEnum;
-use App\Enum\Organization\PrincipalTypeEnum;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\ORM\Query\ResultSetMapping;
-use Doctrine\ORM\Query\ResultSetMappingBuilder;
 use Doctrine\Persistence\ManagerRegistry;
 
 /**

+ 24 - 0
src/Service/Core/AddressPostalUtils.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Service\Core;
+
+use App\Entity\Core\AddressPostal;
+
+class AddressPostalUtils
+{
+    /**
+     * Concatenate and return the three streetAddress parts of the given address
+     *
+     * @param AddressPostal $addressPostal
+     * @param string $separator
+     * @return string
+     */
+    public static function getFullStreetAddress(AddressPostal $addressPostal, string $separator = '\n'): string {
+        return implode($separator, array_filter([
+            trim($addressPostal->getStreetAddress()),
+            trim($addressPostal->getStreetAddressSecond()),
+            trim($addressPostal->getStreetAddressThird())
+        ], function ($x) { return $x !== null and strlen($x) > 0; })
+        );
+    }
+}

+ 42 - 1
src/Service/Dolibarr/DolibarrSync/DolibarrSyncOperation.php

@@ -4,6 +4,7 @@ namespace App\Service\Dolibarr\DolibarrSync;
 
 use App\Service\Dolibarr\DolibarrApiService;
 use HttpException;
+use phpDocumentor\Reflection\Exception;
 use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
 use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
 use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
@@ -27,6 +28,7 @@ class DolibarrSyncOperation
     private string $method;
     private string $path;
     private array $parameters;
+    private array $currentData;
     private string $errorMessage = "";
 
     #[Required]
@@ -34,11 +36,12 @@ class DolibarrSyncOperation
         $this->dolibarrApiService = $dolibarrApiService;
     }
 
-    public function __construct(string $label, string $method, string $path, array $parameters = []) {
+    public function __construct(string $label, string $method, string $path, array $parameters = [], array $currentData = []) {
         $this->label = $label;
         $this->method = $method;
         $this->path = $path;
         $this->parameters = $parameters;
+        $this->currentData = $currentData;
     }
 
     /**
@@ -105,6 +108,44 @@ class DolibarrSyncOperation
         return $this->errorMessage;
     }
 
+    /**
+     * @return array
+     */
+    public function getCurrentData(): array
+    {
+        return $this->currentData;
+    }
+
+    /**
+     * Return an array of messages describing the change that this operation will bring
+     *
+     * @return array
+     * @throws \Exception
+     */
+    public function getChangeLog(): array {
+        $messages = [];
+        foreach ($this->parameters as $field => $newValue) {
+            if (!array_key_exists($field, $this->currentData)) {
+                throw new \Exception('Field does not exists in the current object data : ' . $field);
+            }
+            if (is_array($newValue)) {
+                foreach ($newValue as $subField => $newSubValue) {
+                    if (!array_key_exists($subField, $this->currentData[$field])) {
+                        throw new \Exception('Field does not exists in the current object data : ' . $field . '.' . $subField);
+                    }
+                    if ($newSubValue !== $this->currentData[$field][$subField]) {
+                        $messages[] = $field . '.' . $subField . ' : `'. $this->currentData[$field][$subField] . '` => `' . $newSubValue . '`';
+                    }
+                }
+            } else {
+                if ($newValue !== $this->currentData[$field]) {
+                    $messages[] = $field . ' : `' . $this->currentData[$field] . '` => `' . $newValue . '`';
+                }
+            }
+        }
+        return $messages;
+    }
+
     public function __toString(): string {
         return $this->getLabel() . " > " . $this->getMethod() . " " . $this->getPath() . " " . json_encode($this->getParameters());
     }

+ 245 - 74
src/Service/Dolibarr/DolibarrSync/DolibarrSyncService.php

@@ -2,14 +2,27 @@
 
 namespace App\Service\Dolibarr\DolibarrSync;
 
+use App\Entity\Access\Access;
 use App\Entity\Core\AddressPostal;
-use App\Entity\Organization\OrganizationAddressPostal;
+use App\Entity\Organization\Organization;
+use App\Enum\Access\FunctionEnum;
+use App\Enum\Core\ContactPointTypeEnum;
 use App\Enum\Network\NetworkEnum;
+use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
 use App\Enum\Organization\OrganizationIdsEnum;
+use App\Enum\Organization\SettingsProductEnum;
+use App\Repository\Access\AccessRepository;
 use App\Repository\Organization\OrganizationRepository;
+use App\Service\Core\AddressPostalUtils;
 use App\Service\Dolibarr\DolibarrApiService;
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\QueryBuilder;
 use Exception;
 use HttpException;
+use libphonenumber\PhoneNumber;
+use libphonenumber\PhoneNumberFormat;
+use libphonenumber\PhoneNumberUtil;
 
 /**
  * Push the data from the Opentalent DB into the Dolibarr DB, trough both applications
@@ -21,7 +34,8 @@ class DolibarrSyncService
 {
     public function __construct(
         private OrganizationRepository $organizationRepository,
-        private DolibarrApiService $dolibarrApiService
+        private AccessRepository $accessRepository,
+        private DolibarrApiService $dolibarrApiService,
     ) {}
 
     /**
@@ -38,107 +52,131 @@ class DolibarrSyncService
     public function scan(): array {
 
         // Index the dolibarr clients by organization ids
-        $dolibarrClientsIndex = [];
-        foreach ($this->dolibarrApiService->getAllClients() as $clientData) {
-            if (!$clientData["array_options"]["2iopen_organization_id"] > 0) {
-                $this->logScanError(
-                    "Missing organization id : " . $clientData["name"] . "(" . $clientData["code_client"] .")"
-                );
-                continue;
-            }
-
-            $dolibarrClientsIndex[$clientData["organization_id"]] = $clientData;
-        }
+        $dolibarrClientsIndex = $this->getDolibarrSocietiesIndex();
 
         // Index the dolibarr contacts by person ids
-        $dolibarrContactsIndex = [];
-        foreach ($this->dolibarrApiService->getAllOpentalentContacts() as $contactData) {
-            if (!$contactData["array_options"]["2iopen_person_id"] > 0) {
-                $this->logScanError(
-                    "Missing person id : " . $contactData["name"] . "(" . $clientData["code_client"] .")"
-                );
-                continue;
-            }
+        $dolibarrContactsIndex = $this->getDolibarrContactsIndex();
 
-            $dolibarrContactsIndex[$contactData["organization_id"]] = $contactData;
-        }
-
-        // Loop over the Opentalent organizations, and create the operations list
+        // Loop over the Opentalent organizations, and fill up the operations list
         $operations = [];
         foreach ($this->organizationRepository->findAll() as $organization) {
-            if (!isset($dolibarrClientsIndex["organization_id"])) {
+            if (!array_key_exists($organization->getId(), $dolibarrClientsIndex)) {
                 // this organization is not a client, probably a federation member
                 continue;
             }
 
-            $dolibarrClient = $dolibarrClientsIndex[$organization->getId()];
+            $dolibarrSociety = $dolibarrClientsIndex[$organization->getId()];
+
+            $putSocietyData = [];
+
+            // ** Sync name
+            if ($organization->getName() !== $dolibarrSociety['name']) {
+                $putSocietyData['name'] = $organization->getName();
+            }
 
             // ** Sync contact data of the client
             // Postal address
             /** @var AddressPostal | null */
-            $mainAddress = null;
-            foreach ($organization->getOrganizationAddressPostals() as $postalAddress) {
-                if ($postalAddress->getType() == 'PRINCIPAL') {
-                    $mainAddress = $postalAddress->getAddress();
-                }
-            }
+            $mainAddress = $this->getOrganizationPostalAddress($organization);
 
             if ($mainAddress !== null) {
-                $params = [];
-
-                $streetAddress = implode('\n', array_filter([
-                    trim($mainAddress->getStreetAddress()),
-                    trim($mainAddress->getStreetAddressSecond()),
-                    trim($mainAddress->getStreetAddressThird())
-                ]), function ($x) { return $x !== null and strlen($x) > 0; });
-
-                if ($streetAddress !== $dolibarrClient['address']) {
-                    $params['address'] = $streetAddress;
+                $streetAddress = AddressPostalUtils::getFullStreetAddress($mainAddress);
+                if (trim($mainAddress->getAddressOwner() ?? '') !== '') {
+                    $streetAddress = 'Chez ' . $mainAddress->getAddressOwner() . '\n' . $streetAddress;
                 }
 
-                if ($mainAddress->getPostalCode() !== $dolibarrClient['zip']) {
-                    $params['zip'] = $mainAddress->getPostalCode();
+                if ($streetAddress !== $dolibarrSociety['address']) {
+                    $putSocietyData['address'] = $streetAddress;
                 }
 
-                if ($mainAddress->getAddressCity() !== $dolibarrClient['town']) {
-                    $params['town'] = $mainAddress->getAddressCity();
+                if ($mainAddress->getPostalCode() !== $dolibarrSociety['zip']) {
+                    $putSocietyData['zip'] = $mainAddress->getPostalCode();
                 }
 
-                if ($params) {
-                    $operations[] = new DolibarrSyncOperation(
-                        'Organization ' . $organization->getId() . ': update address ' .
-                        '(current: ' . $params['address'] . ' ' . $params['zip'] . ' ' . $params['town'] . ')',
-                        'PUT',
-                        'thirdparties/' . $dolibarrClient['id'],
-                        $params
-                    );
+                if ($mainAddress->getAddressCity() !== $dolibarrSociety['town']) {
+                    $putSocietyData['town'] = $mainAddress->getAddressCity();
                 }
+            }
 
-                // Network
-                $network = $organization->getNetworkOrganizations()->first()->getNetwork();
-                $parentOrganizationId = null;
+            // Sync contact
+            $email = $this->getOrganizationEmail($organization);
+            if ($email !== $dolibarrSociety['email']) {
+                $putSocietyData['email'] = $email;
+            }
 
-                if ($network->getId() === NetworkEnum::CMF()) {
-                    $parentOrganizationId = OrganizationIdsEnum::CMF();
-                } else if ($network->getId() === NetworkEnum::FFEC()) {
-                    $parentOrganizationId = OrganizationIdsEnum::FFEC();
-                }
+            $phone = $this->getOrganizationPhone($organization);
+            if ($phone !== $dolibarrSociety['phone']) {
+                $putSocietyData['phone'] = $phone;
+            }
 
-                if ($parentOrganizationId !== null) {
-                    $parent = $this->dolibarrApiService->getSociety($parentOrganizationId);
+            // ** Sync Network
+            $network = $organization->getNetworkOrganizations()->first()->getNetwork();
+            $parentOrganizationId = null;
 
-                    $operations[] = new DolibarrSyncOperation(
-                        'Organization ' . $organization->getId() . ': update network ' .
-                        '(current: ' . $parent['parent'] . ')',
-                        'PUT',
-                        'thirdparties/' . $dolibarrClient['id'],
-                        ['parent' => $parent['id']]
-                    );
-                }
+            if ($network->getId() === NetworkEnum::CMF()->getValue()) {
+                $parentOrganizationId = OrganizationIdsEnum::CMF()->getValue();
+            } else if ($network->getId() === NetworkEnum::FFEC()->getValue()) {
+                $parentOrganizationId = OrganizationIdsEnum::FFEC()->getValue();
+            } else {
+                $parentOrganizationId = OrganizationIdsEnum::_2IOS()->getValue();
+            }
 
-                
+            $parent = $this->dolibarrApiService->getSociety($parentOrganizationId);
+            if ($parent['id'] !== $dolibarrSociety['parent']) {
+                $putSocietyData['parent'] = $parent['id'];
+            }
 
+            // More infos
+            $infosArray = [];
+
+            $product = $organization->getSettings()->getProduct();
+            if (
+                $product == SettingsProductEnum::SCHOOL()->getValue() ||
+                $product == SettingsProductEnum::SCHOOL_PREMIUM()->getValue()
+            ) {
+                $studentsCount = $this->accessRepository->countAccessesWithActiveMission(
+                    $organization->getId(), FunctionEnum::STUDENT()->getValue()
+                );
+                $infosArray[] = "Nombre d'élèves : " . $studentsCount;
             }
+
+            if (
+                $product == SettingsProductEnum::SCHOOL()->getValue() ||
+                $product == SettingsProductEnum::SCHOOL_PREMIUM()->getValue() ||
+                $product == SettingsProductEnum::ARTIST()->getValue() ||
+                $product == SettingsProductEnum::ARTIST_PREMIUM()->getValue()
+            ) {
+                $membersCount = $this->accessRepository->countAccessesWithActiveMission(
+                    $organization->getId(), FunctionEnum::ADHERENT()->getValue()
+                );
+                $infosArray[] = "Nombre d'adhérents : " . $membersCount;
+            }
+
+            $adminsCount = $this->accessRepository->countAdminAccounts(
+                $organization->getId()
+            );
+            $infosArray[] = "Nombre d'accès admin : " . $adminsCount;
+
+            $infos = implode('\n', $infosArray);
+            if ($infos !== $dolibarrSociety["array_options"]["options_2iopeninfoopentalent"]) {
+                // /!\ On est forcé de passer la sub-array entière pour mettre à jour le champs modifié, sinon
+                //     tous les autres champs seront mis à null...
+                $arrayOptions = $dolibarrSociety["array_options"];
+                $arrayOptions['options_2iopeninfoopentalent'] = $infos;
+                $putSocietyData['array_options'] = $arrayOptions;
+            }
+
+            $operations[] = new DolibarrSyncOperation(
+                'Update organization ' . $organization->getId() . ' - ' . $organization->getName(),
+                'PUT',
+                'thirdparties/' . $dolibarrSociety['id'],
+                $putSocietyData,
+                $dolibarrSociety
+            );
+
+            // ** Contacts
+
+
         }
 
         return $operations;
@@ -189,4 +227,137 @@ class DolibarrSyncService
     private function logScanError($errorMsg) {
         // TODO : implement
     }
+
+    /**
+     * Get the client societies dolibarr and index it by organization id
+     *
+     * @return array An index of the form [$organizationId => $dolibarrData]
+     * @throws HttpException
+     */
+    private function getDolibarrSocietiesIndex(): array
+    {
+        $index = [];
+        foreach ($this->dolibarrApiService->getAllClients() as $clientData) {
+            if (!$clientData["array_options"]["options_2iopen_organization_id"] > 0) {
+                $this->logScanError(
+                    "Missing organization id : " . $clientData["name"] . "(" . $clientData["code_client"] .")"
+                );
+                continue;
+            }
+
+            $index[$clientData["array_options"]["options_2iopen_organization_id"]] = $clientData;
+        }
+        return $index;
+    }
+
+    private function getDolibarrContactsIndex(): array {
+        $index = [];
+        foreach ($this->dolibarrApiService->getAllOpentalentContacts() as $contactData) {
+            if (!$contactData["array_options"]["options_2iopen_person_id"] > 0) {
+                $this->logScanError(
+                    "Missing person id : " . $contactData["lastname"] . " " . $contactData["firstname"] . "(id:" . $contactData["id"] .")"
+                );
+                continue;
+            }
+
+            $index[$contactData["array_options"]["options_2iopen_person_id"]] = $contactData;
+        }
+
+        return $index;
+    }
+
+    /**
+     * Retrieve the postal address of the organization
+     *
+     * @param Organization $organization
+     * @return null
+     */
+    private function getOrganizationPostalAddress(Organization $organization) {
+        $addressPriorities = [
+            AddressPostalOrganizationTypeEnum::ADDRESS_BILL()->getValue(),
+            AddressPostalOrganizationTypeEnum::ADDRESS_CONTACT()->getValue(),
+            AddressPostalOrganizationTypeEnum::ADDRESS_HEAD_OFFICE()->getValue(),
+            AddressPostalOrganizationTypeEnum::ADDRESS_PRACTICE()->getValue(),
+            AddressPostalOrganizationTypeEnum::ADDRESS_OTHER()->getValue()
+        ];
+
+        foreach ($addressPriorities as $addressType) {
+            foreach ($organization->getOrganizationAddressPostals() as $postalAddress) {
+                if ($postalAddress->getType() == $addressType) {
+                    return $postalAddress->getAddressPostal();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the phone for the organization
+     *
+     * @param Organization $organization
+     * @return null
+     */
+    private function getOrganizationPhone(Organization $organization) {
+        $contactPriorities = [
+            ContactPointTypeEnum::BILL()->getValue(),
+            ContactPointTypeEnum::CONTACT()->getValue(),
+            ContactPointTypeEnum::PRINCIPAL()->getValue(),
+            ContactPointTypeEnum::OTHER()->getValue()
+        ];
+
+        foreach ($contactPriorities as $contactType) {
+            foreach ($organization->getContactPoints() as $contactPoint) {
+                if ($contactPoint->getContactType() == $contactType) {
+                    if ($contactPoint->getTelphone() !== null) {
+                        return $this->formatPhoneNumber($contactPoint->getTelphone());
+                    }
+                    if ($contactPoint->getMobilPhone() !== null) {
+                        return $this->formatPhoneNumber($contactPoint->getMobilPhone());
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the email for the organization
+     *
+     * @param Organization $organization
+     * @return null
+     */
+    private function getOrganizationEmail(Organization $organization) {
+        $contactPriorities = [
+            ContactPointTypeEnum::BILL()->getValue(),
+            ContactPointTypeEnum::CONTACT()->getValue(),
+            ContactPointTypeEnum::PRINCIPAL()->getValue(),
+            ContactPointTypeEnum::OTHER()->getValue()
+        ];
+
+        foreach ($contactPriorities as $contactType) {
+            foreach ($organization->getContactPoints() as $contactPoint) {
+                if ($contactPoint->getContactType() == $contactType && $contactPoint->getEmail() !== null) {
+                    return $contactPoint->getEmail();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Formatte un numéro de téléphone, au format français si l'indicatif national
+     * est l'indicatif français ou s'il est manquant,
+     * au format international sinon.
+     *
+     * @param PhoneNumber $phoneNumber
+     * @return mixed
+     */
+    private function formatPhoneNumber(PhoneNumber $phoneNumber): string {
+        $phoneUtil = PhoneNumberUtil::getInstance();
+        if (!$phoneNumber->hasCountryCode() || $phoneNumber->getCountryCode() == 33) {
+            return $phoneUtil->format($phoneNumber, PhoneNumberFormat::NATIONAL);
+        } else {
+            return $phoneUtil->format($phoneNumber, PhoneNumberFormat::INTERNATIONAL);
+        }
+    }
 }

+ 12 - 0
symfony.lock

@@ -343,6 +343,18 @@
     "symfony/intl": {
         "version": "v5.2.3"
     },
+    "symfony/lock": {
+        "version": "5.3",
+        "recipe": {
+            "repo": "github.com/symfony/recipes",
+            "branch": "master",
+            "version": "5.2",
+            "ref": "a1c8800e40ae735206bb14586fdd6c4630a51b8d"
+        },
+        "files": [
+            "config/packages/lock.yaml"
+        ]
+    },
     "symfony/maker-bundle": {
         "version": "1.0",
         "recipe": {