Bladeren bron

fix conflicts

Olivier Massot 1 jaar geleden
bovenliggende
commit
8a1c9d7eb4

+ 1 - 0
src/Enum/Access/FunctionEnum.php

@@ -55,6 +55,7 @@ enum FunctionEnum: string
     case BENEFACTOR_MEMBER = 'BENEFACTOR_MEMBER';
     case HOUR_MEMBER = 'HOUR_MEMBER';
     case OTHER = 'OTHER';
+    case COM_STAFF = 'COM_STAFF';
 
 
     /**

+ 1 - 0
src/Enum/Organization/SettingsProductEnum.php

@@ -18,4 +18,5 @@ enum SettingsProductEnum: string
     case SCHOOL_PREMIUM = 'school-premium';
     case MANAGER = 'manager';
     case MANAGER_PREMIUM = 'manager-premium';
+    case EMPTY = '';
 }

+ 22 - 0
src/Service/Dolibarr/DolibarrApiService.php

@@ -151,4 +151,26 @@ class DolibarrApiService extends ApiRequestService
             throw $e;
         }
     }
+
+    /**
+     * Get the society tags
+     *
+     * @param int $socId The society ID
+     * @return array<int> The array of tags associated with the society
+     * @throws HttpException|\JsonException if an HTTP error occurs
+     */
+    public function getSocietyTagsIds(int $socId): array {
+        try {
+            return array_map(
+                function ($x) { return (int)$x['id']; },
+                $this->getJsonContent("/thirdparties/$socId/categories")
+            );
+        } catch (HttpException $e) {
+            if ($e->getStatusCode() === 404) {
+                // /!\ The dolibarr API will return a 404 error if no results are found...
+                return [];
+            }
+            throw $e;
+        }
+    }
 }

+ 88 - 6
src/Service/Dolibarr/DolibarrSyncService.php

@@ -12,6 +12,7 @@ use App\Enum\Access\RoleEnum;
 use App\Enum\Core\ContactPointTypeEnum;
 use App\Enum\Network\NetworkEnum;
 use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
+use App\Enum\Organization\LegalEnum;
 use App\Enum\Organization\OrganizationIdsEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\Enum\Person\GenderEnum;
@@ -22,6 +23,7 @@ use App\Service\Core\AddressPostalUtils;
 use App\Service\Organization\Utils;
 use App\Service\Rest\Operation\BaseRestOperation;
 use App\Service\Rest\Operation\CreateOperation;
+use App\Service\Rest\Operation\DeleteOperation;
 use App\Service\Rest\Operation\UpdateOperation;
 use App\Service\Utils\ArrayUtils;
 use Exception;
@@ -48,6 +50,18 @@ use Symfony\Contracts\Translation\TranslatorInterface;
  */
 class DolibarrSyncService
 {
+    private const ASSOCIATION_SCHOOL_TAG_ID = 67;
+    private const ASSOCIATION_PREMIUM_TAG_ID = 69;
+    private const LOCAL_AUTH_SCHOOL_TAG_ID = 68;
+    private const LOCAL_AUTH_PREMIUM_TAG_ID = 70;
+
+    private const SYNCHRONIZED_TAGS = [
+        self::ASSOCIATION_SCHOOL_TAG_ID => 'Association School',
+        self::ASSOCIATION_PREMIUM_TAG_ID => 'Association School Premium',
+        self::LOCAL_AUTH_SCHOOL_TAG_ID => 'CT School',
+        self::LOCAL_AUTH_PREMIUM_TAG_ID => 'CT School Premium'
+    ];
+
     private LoggerInterface $logger;
 
     public function __construct(
@@ -117,6 +131,8 @@ class DolibarrSyncService
                 continue;
             }
 
+            $dolibarrSocietyId = (int)$dolibarrSociety['id'];
+
             // Populate the expected contacts array
             $organizationMembers = $membersIndex[$organization->getId()] ?? [];
 
@@ -165,7 +181,6 @@ class DolibarrSyncService
                     $this->countWithMission([FunctionEnum::STUDENT->value], $organizationMembers);
             }
             if ($this->organizationUtils->isSchool($organization) || $this->organizationUtils->isArtist($organization)) {
-
                 $infos[] = $this->translator->trans('ADHERENTS_COUNT') . " : " .
                     $this->countWithMission([FunctionEnum::ADHERENT->value], $organizationMembers);
             }
@@ -196,14 +211,14 @@ class DolibarrSyncService
                 $operations[] = new UpdateOperation(
                     'Update society : ' . $organization->getName() . ' (' . $organization->getId() . ')',
                     'thirdparties',
-                    (int)$dolibarrSociety['id'],
+                    $dolibarrSocietyId,
                     $newSocietyData,
                     $dolibarrSociety
                 );
             }
 
             // ===== Update Contacts =====
-            $dolibarrSocietyContacts = $this->dolibarrApiService->getContacts((int)$dolibarrSociety['id']);
+            $dolibarrSocietyContacts = $this->dolibarrApiService->getContacts($dolibarrSocietyId);
             $contactsProcessed = [];
 
             foreach ($organizationMembers as $accessId => $missions) {
@@ -252,7 +267,7 @@ class DolibarrSyncService
 
                 if ($dolibarrContact === null) {
                     // New contact
-                    $newContactData['socid'] = (int)$dolibarrSociety['id'];
+                    $newContactData['socid'] = $dolibarrSocietyId;
 
                     $operations[] = new CreateOperation(
                         'New contact: ' . $person->getName() . ' ' . $person->getGivenName() . ' (' . $person->getId() . ')',
@@ -304,6 +319,38 @@ class DolibarrSyncService
                 }
             }
 
+            // ===== Update Tags =====
+            $currentTags = $this->dolibarrApiService->getSocietyTagsIds($dolibarrSocietyId);
+            $expectedTags = $this->getExpectedTagsFor($organization);
+
+            // Remove unexpected tags
+            foreach ($currentTags as $tagId) {
+                if (!array_key_exists($tagId, self::SYNCHRONIZED_TAGS)) {
+                    continue;
+                }
+
+                if (!in_array($tagId, $expectedTags)) {
+                    $operations[] = new DeleteOperation(
+                        "Delete tag: `" . self::SYNCHRONIZED_TAGS[$tagId] .
+                        '` from ' . $organization->getName() . ' (' . $organization->getId() . ')',
+                        "/thirdparties/$dolibarrSocietyId/categories",
+                        $tagId
+                    );
+                }
+            }
+
+            // Add missing tags
+            foreach ($expectedTags as $tagId) {
+                if (!in_array($tagId, $currentTags)) {
+                    $operations[] = new CreateOperation(
+                        "Add tag: `" . self::SYNCHRONIZED_TAGS[$tagId] .
+                        '` to ' . $organization->getName() . ' (' . $organization->getId() . ')',
+                        "/thirdparties/$dolibarrSocietyId/categories/$tagId",
+                        []
+                    );
+                }
+            }
+
             // Next society
             $i++;
             if ($progressionCallback !== null) {
@@ -383,6 +430,7 @@ class DolibarrSyncService
         }
 
         $this->logger->info('Execution ended');
+
         $this->logger->info('Done : ' . $done);
         $this->logger->info('Errors : ' . $errors);
         if ($unknown > 0) {
@@ -460,7 +508,7 @@ class DolibarrSyncService
             if (!array_key_exists($accessId, $index[$organizationId])) {
                 $index[$organizationId][$accessId] = [];
             }
-            $index[$organizationId][$accessId][] = $mission;
+            $index[$organizationId][$accessId][] = $mission->value;
         }
         return $index;
     }
@@ -641,7 +689,11 @@ class DolibarrSyncService
     {
         return count(array_filter(
             $members,
-            static function ($actualMissions) use ($missions) { return !empty(array_intersect($actualMissions, $missions)); }
+            static function ($actualMissions) use ($missions) {
+                return !empty(
+                    array_intersect($actualMissions, $missions)
+                );
+            }
         ));
     }
 
@@ -728,6 +780,36 @@ class DolibarrSyncService
         );
     }
 
+    /**
+     * @param Organization $organization
+     * @return array<int>
+     */
+    protected function getExpectedTagsFor(Organization $organization): array
+    {
+        $expectedTags = [];
+        $product = $organization->getSettings()->getProduct();
+
+        // Association, school or school premium
+        if ($organization->getLegalStatus() === LegalEnum::ASSOCIATION_LAW_1901) {
+            if ($product === SettingsProductEnum::SCHOOL) {
+                $expectedTags[] = self::ASSOCIATION_SCHOOL_TAG_ID;
+            }
+            if ($product === SettingsProductEnum::SCHOOL_PREMIUM) {
+                $expectedTags[] = self::ASSOCIATION_PREMIUM_TAG_ID;
+            }
+        }
+        // Local authorities, school or school premium
+        if ($organization->getLegalStatus() === LegalEnum::LOCAL_AUTHORITY) {
+            if ($product === SettingsProductEnum::SCHOOL) {
+                $expectedTags[] = self::LOCAL_AUTH_SCHOOL_TAG_ID;
+            }
+            if ($product === SettingsProductEnum::SCHOOL_PREMIUM) {
+                $expectedTags[] = self::LOCAL_AUTH_PREMIUM_TAG_ID;
+            }
+        }
+
+        return $expectedTags;
+    }
 
     /**
      * Post-validation of the execution of the operation.

+ 115 - 0
tests/Unit/Service/Dolibarr/DolibarrApiServiceTest.php

@@ -40,6 +40,50 @@ class DolibarrApiServiceTest extends TestCase
         $this->assertEquals(['id' => 1], $society);
     }
 
+    /**
+     * @see DolibarrApiService::getSociety()
+     */
+    public function testGetSocietyMissing(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSociety'])
+            ->getMock();
+
+        $organizationId = 123;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("thirdparties", [ "limit" => "1", "sqlfilters" => "(ef.2iopen_organization_id:=:" . $organizationId . ')'])
+            ->willThrowException(new HttpException(404));
+
+        $society = $dolibarrApiService->getSociety($organizationId);
+
+        $this->assertEquals(null, $society);
+    }
+
+    /**
+     * @see DolibarrApiService::getSociety()
+     */
+    public function testGetSocietyError(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSociety'])
+            ->getMock();
+
+        $organizationId = 123;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("thirdparties", [ "limit" => "1", "sqlfilters" => "(ef.2iopen_organization_id:=:" . $organizationId . ')'])
+            ->willThrowException(new HttpException(500));
+
+        $this->expectException(HttpException::class);
+
+        $dolibarrApiService->getSociety($organizationId);
+    }
+
     /**
      * @see DolibarrApiService::getActiveContract()
      */
@@ -309,4 +353,75 @@ class DolibarrApiServiceTest extends TestCase
 
         $dolibarrApiService->getActiveOpentalentContacts($socId);
     }
+
+    /**
+     * @see DolibarrApiService::getSocietyTagsIds()
+     */
+    public function testGetSocietyTagsIds(): void
+    {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSocietyTagsIds'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("/thirdparties/1/categories")
+            ->willReturn([['id' => '10'], ['id' => '20']]);
+
+        $this->assertEquals(
+            [10, 20],
+            $dolibarrApiService->getSocietyTagsIds($socId)
+        );
+    }
+
+    /**
+     * @see DolibarrApiService::getSocietyTagsIds()
+     */
+    public function testGetSocietyTagsIdsMissing(): void
+    {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSocietyTagsIds'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("/thirdparties/1/categories")
+            ->willThrowException(new HttpException(404));
+
+        $this->assertEquals(
+            [],
+            $dolibarrApiService->getSocietyTagsIds($socId)
+        );
+    }
+
+    /**
+     * @see DolibarrApiService::getSocietyTagsIds()
+     */
+    public function testGetSocietyTagsIdsError(): void
+    {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSocietyTagsIds'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("/thirdparties/1/categories")
+            ->willThrowException(new HttpException(500));
+
+        $this->expectException(HttpException::class);
+
+        $dolibarrApiService->getSocietyTagsIds($socId);
+    }
 }

+ 102 - 7
tests/Unit/Service/Dolibarr/DolibarrSyncServiceTest.php

@@ -18,6 +18,7 @@ use App\Enum\Access\RoleEnum;
 use App\Enum\Core\ContactPointTypeEnum;
 use App\Enum\Network\NetworkEnum;
 use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
+use App\Enum\Organization\LegalEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\Enum\Person\GenderEnum;
 use App\Repository\Access\AccessRepository;
@@ -56,6 +57,7 @@ class TestableDolibarrSyncService extends DolibarrSyncService {
     public function getPersonContact(Person $person): ?ContactPoint { return parent::getPersonContact($person); }
     public function formatContactPosition(array $missions, ?string $gender = 'X'): string { return parent::formatContactPosition($missions, $gender); }
     public function formatPhoneNumber(PhoneNumber $phoneNumber): string { return parent::formatPhoneNumber($phoneNumber); }
+    public function getExpectedTagsFor(Organization $organization): array { return parent::getExpectedTagsFor($organization); }
     public function validateResponse(ResponseInterface $response, BaseRestOperation $operation): void { parent::validateResponse($response, $operation); }
 }
 
@@ -140,7 +142,9 @@ class DolibarrSyncServiceTest extends TestCase
             'email' => 'foo@bar.net',
             'phone' => '0102030405',
             'networkId' => NetworkEnum::CMF->value,
-            'product' => SettingsProductEnum::SCHOOL
+            'product' => SettingsProductEnum::SCHOOL,
+            'networkId' => NetworkEnum::CMF->value,
+            'legalStatus' => LegalEnum::LOCAL_AUTHORITY
         ];
 
         $orgId2 = 20;
@@ -152,7 +156,8 @@ class DolibarrSyncServiceTest extends TestCase
             'email' => null,
             'phone' => null,
             'networkId' => null,
-            'product' => SettingsProductEnum::ARTIST
+            'product' => SettingsProductEnum::ARTIST,
+            'legalStatus' => LegalEnum::ASSOCIATION_LAW_1901
         ];
 
         $orgId3 = 30;
@@ -202,7 +207,6 @@ class DolibarrSyncServiceTest extends TestCase
             $accessId2 => [FunctionEnum::PRESIDENT->value]
         ];
 
-
         // ----- Opentalent : other vars -----
         $cmfId = 12097;
         $cmfDolibarrId = 12098;
@@ -502,6 +506,16 @@ class DolibarrSyncServiceTest extends TestCase
             ["Organization 30 not found in the Opentalent DB"],
         );
 
+        // Tags
+        $this->dolibarrApiService->method('getSocietyTagsIds')->willReturnMap([
+            [$socId1, [1, 68]],
+            [$socId2, [3, 67]]
+        ]);
+        $dolibarrSyncService->method('getExpectedTagsFor')->willReturnMap([
+            [$organization1, [67]],
+            [$organization2, []]
+        ]);
+
         // Expected progression callback triggers
         $progressionCallbackExpectedCalls = [[1, 3], [2, 3], [3, 3]];
         $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
@@ -516,7 +530,7 @@ class DolibarrSyncServiceTest extends TestCase
 
         $operations = $dolibarrSyncService->scan($progressionCallback);
 
-        $this->assertCount(5, $operations);
+        $this->assertCount(8, $operations);
 
         $this->assertEqualsCanonicalizing(
             [
@@ -550,10 +564,27 @@ class DolibarrSyncServiceTest extends TestCase
         );
 
         $this->assertEqualsCanonicalizing(
-            ['[PUT contacts/4]', 'statut : `1` => `0`'],
+            [
+                '[PUT contacts/4]',
+                'statut : `1` => `0`'
+            ],
             $operations[2]->getChangeLog()
         );
 
+        $this->assertEqualsCanonicalizing(
+            [
+                '[DELETE /thirdparties/1/categories/68]'
+            ],
+            $operations[3]->getChangeLog()
+        );
+
+        $this->assertEqualsCanonicalizing(
+            [
+                '[POST /thirdparties/1/categories/67]'
+            ],
+            $operations[4]->getChangeLog()
+        );
+
         $this->assertEqualsCanonicalizing(
             [
                 '[PUT thirdparties/2]',
@@ -568,7 +599,7 @@ class DolibarrSyncServiceTest extends TestCase
                 'array_options.options_2iopen_software_opentalent : (new) => `Opentalent Artist`',
                 "array_options.options_2iopeninfoopentalent : (new) => `Nombre d'adhérents : 0\nNombre d'accès admin : 2`",
             ],
-            $operations[3]->getChangeLog()
+            $operations[5]->getChangeLog()
         );
 
         $this->assertEqualsCanonicalizing(
@@ -585,7 +616,14 @@ class DolibarrSyncServiceTest extends TestCase
                 'array_options.options_2iopen_person_id : (new) => `200`',
                 'socid : (new) => `2`'
             ],
-            $operations[4]->getChangeLog()
+            $operations[6]->getChangeLog()
+        );
+
+        $this->assertEqualsCanonicalizing(
+            [
+                '[DELETE /thirdparties/2/categories/67]'
+            ],
+            $operations[7]->getChangeLog()
         );
 
         $this->assertCount(0, $progressionCallbackExpectedCalls);
@@ -1247,6 +1285,63 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    public function testGetExpectedTagsFor()
+    {
+        $dolibarrSyncService = $this->getMockForMethod('getExpectedTagsFor');
+
+        $organization1 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization1->method('getLegalStatus')->willReturn(LegalEnum::ASSOCIATION_LAW_1901);
+        $settings1 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings1->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL);
+        $organization1->method('getSettings')->willReturn($settings1);
+
+        $organization2 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization2->method('getLegalStatus')->willReturn(LegalEnum::ASSOCIATION_LAW_1901);
+        $settings2 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings2->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL_PREMIUM);
+        $organization2->method('getSettings')->willReturn($settings2);
+
+        $organization3 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization3->method('getLegalStatus')->willReturn(LegalEnum::LOCAL_AUTHORITY);
+        $settings3 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings3->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL);
+        $organization3->method('getSettings')->willReturn($settings3);
+
+        $organization4 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization4->method('getLegalStatus')->willReturn(LegalEnum::LOCAL_AUTHORITY);
+        $settings4 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings4->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL_PREMIUM);
+        $organization4->method('getSettings')->willReturn($settings4);
+
+        $organization5 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization5->method('getLegalStatus')->willReturn(LegalEnum::LOCAL_AUTHORITY);
+        $settings5 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings5->method('getProduct')->willReturn(SettingsProductEnum::ARTIST);
+        $organization5->method('getSettings')->willReturn($settings5);
+
+        $this->assertEquals(
+            $dolibarrSyncService->getExpectedTagsFor($organization1),
+            [67]
+        );
+        $this->assertEquals(
+            $dolibarrSyncService->getExpectedTagsFor($organization2),
+            [69]
+        );
+        $this->assertEquals(
+            $dolibarrSyncService->getExpectedTagsFor($organization3),
+            [68]
+        );
+        $this->assertEquals(
+            $dolibarrSyncService->getExpectedTagsFor($organization4),
+            [70]
+        );
+        $this->assertEquals(
+            $dolibarrSyncService->getExpectedTagsFor($organization5),
+            []
+        );
+
+    }
+
     /**
      * @see DolibarrSyncService::validateResponse()
      */