Browse Source

add tests for Service/Dolibarr/* (ongoing) and minor fixes

Olivier Massot 3 năm trước cách đây
mục cha
commit
06cd4271e7

+ 1 - 1
src/ApiResources/Dolibarr/DolibarrContractLine.php

@@ -108,7 +108,7 @@ class DolibarrContractLine implements ApiResourcesInterface
         return $this;
     }
 
-    public function isDateEnd(): ?\DateTimeInterface
+    public function getDateEnd(): ?\DateTimeInterface
     {
         return $this->dateEnd;
     }

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

@@ -8,6 +8,7 @@ use MyCLabs\Enum\Enum;
 /**
  * Type de produit disponible pour une organisation
  * @method static SCHOOL()
+ * @method static ARTIST()
  */
 class SettingsProductEnum extends Enum
 {

+ 1 - 1
src/Service/Core/AddressPostalUtils.php

@@ -13,7 +13,7 @@ class AddressPostalUtils
      * @param string $separator
      * @return string
      */
-    public static function getFullStreetAddress(AddressPostal $addressPostal, string $separator = "\n"): string {
+    public function getFullStreetAddress(AddressPostal $addressPostal, string $separator = "\n"): string {
         return implode($separator, array_filter([
             trim($addressPostal->getStreetAddress()),
             trim($addressPostal->getStreetAddressSecond()),

+ 3 - 3
src/Service/Dolibarr/DolibarrAccountCreator.php

@@ -83,8 +83,8 @@ class DolibarrAccountCreator
                     ->setContractId((int)$lineData['fk_contrat'])
                     ->setServiceRef($lineData['product_ref'])
                     ->setServiceLabel($lineData['product_label'])
-                    ->setDateStart(new \DateTime(date('c', $lineData['date_start'])))
-                    ->setDateEnd(new \DateTime(date('c', $lineData['date_end'])));
+                    ->setDateStart(new \DateTime(date('c', (int)$lineData['date_start'])))
+                    ->setDateEnd(new \DateTime(date('c', (int)$lineData['date_end'])));
     }
 
     public function createDolibarrBill(array $billData): DolibarrBill {
@@ -93,7 +93,7 @@ class DolibarrAccountCreator
                     ->setRef($billData['ref'])
                     ->setTaxExcludedAmount((float)$billData['total_ht'])
                     ->setTaxIncludedAmount((float)$billData['total_ttc'])
-                    ->setDate(new \DateTime(date('c', $billData['date'])))
+                    ->setDate(new \DateTime(date('c', (int)$billData['date'])))
                     ->setPaid((bool)$billData['paye']);
     }
 }

+ 1 - 1
src/Service/Dolibarr/DolibarrApiService.php

@@ -80,7 +80,7 @@ class DolibarrApiService extends ApiRequestService
      * Get all the societies which are Opentalent client
      * @throws HttpException
      */
-    public function getAllClients(bool $withContract = false): array
+    public function getAllClients(): array
     {
         return $this->getJsonContent(
             "thirdparties",

+ 34 - 32
src/Service/Dolibarr/DolibarrSyncService.php

@@ -51,6 +51,8 @@ class DolibarrSyncService
         private AccessRepository $accessRepository,
         private FunctionTypeRepository $functionTypeRepository,
         private DolibarrApiService $dolibarrApiService,
+        private AddressPostalUtils $addressPostalUtils,
+        private ArrayUtils$arrayUtils,
         private TranslatorInterface $translator,
         private LoggerInterface $logger
     ) {}
@@ -63,10 +65,12 @@ class DolibarrSyncService
      *
      * Returns an array of DolibarrSyncOperations
      *
-     * @var callable | null $progressionCallback A callback method for indicating the current progression of the process;
-     *                                           Shall accept two integer arguments: current progression, and total.
      * @return array<BaseRestOperation>
      * @throws Exception
+     * @var callable | null $progressionCallback A callback method for indicating the current progression of the process;
+     *                                           Shall accept two integer arguments: current progression, and total.
+     *
+     * @noinspection NullPointerExceptionInspection
      */
     public function scan(?callable $progressionCallback = null): array {
         $this->logger->info("-- Scan started --");
@@ -92,11 +96,15 @@ class DolibarrSyncService
         $operations = [];
         $i = 0; $total = count($dolibarrClientsIndex);
         foreach ($dolibarrClientsIndex as $organizationId => $dolibarrSociety) {
-            $dolibarrSociety = self::sanitizeDolibarrData($dolibarrSociety);
+            $dolibarrSociety = $this->sanitizeDolibarrData($dolibarrSociety);
 
             $organization = $this->organizationRepository->find($organizationId);
             if ($organization === null) {
                 $this->logger->error("Organization " . $organizationId . " not found in the Opentalent DB");
+                $i++;
+                if ($progressionCallback !== null) {
+                    $progressionCallback($i, $total);
+                }
                 continue;
             }
 
@@ -112,7 +120,7 @@ class DolibarrSyncService
             // Sync contact data of the client
             $mainAddress = $this->getOrganizationPostalAddress($organization);
             if ($mainAddress !== null) {
-                $streetAddress = AddressPostalUtils::getFullStreetAddress($mainAddress);
+                $streetAddress = $this->addressPostalUtils->getFullStreetAddress($mainAddress);
                 if (trim($mainAddress->getAddressOwner() ?? '') !== '') {
                     $streetAddress = $mainAddress->getAddressOwner() . "\n" . $streetAddress;
                 }
@@ -145,14 +153,14 @@ class DolibarrSyncService
             $product = $organization->getSettings()->getProduct();
             if (SettingsProductEnum::isSchool($product)) {
                 $infos[] = $this->translator->trans('STUDENTS_COUNT') . " : " .
-                    self::countWithMission([FunctionEnum::STUDENT()->getValue()], $organizationMembers);
+                    $this->countWithMission([FunctionEnum::STUDENT()->getValue()], $organizationMembers);
             }
             if (SettingsProductEnum::isSchool($product) || SettingsProductEnum::isArtist($product)) {
                 $infos[] = $this->translator->trans('ADHERENTS_COUNT') . " : " .
-                    self::countWithMission([FunctionEnum::ADHERENT()->getValue()], $organizationMembers);
+                    $this->countWithMission([FunctionEnum::ADHERENT()->getValue()], $organizationMembers);
             }
             $infos[] = $this->translator->trans('ADMIN_ACCESS_COUNT') . " : " .
-                self::countWithMission($adminMissions, $organizationMembers);
+                $this->countWithMission($adminMissions, $organizationMembers);
 
             // /!\ On est forcé de passer la sub-array entière pour mettre à jour le champ modifié, sinon
             //     tous les autres champs seront passés à null...
@@ -166,7 +174,7 @@ class DolibarrSyncService
             $newSocietyData['status'] = '1';
 
             // Only update the fields that are different (it's important to let it non-recursive, the subarray have to be passed entirely)
-            $newSocietyData = ArrayUtils::getChanges(
+            $newSocietyData = $this->arrayUtils->getChanges(
                 $dolibarrSociety,
                 $newSocietyData,
                 false,
@@ -197,24 +205,18 @@ class DolibarrSyncService
                 $access = $this->accessRepository->find($accessId);
                 $person = $access?->getPerson();
 
-                if ($person === null) // this should not happen, but is expected by code inspection...
-                { throw new \Exception('Access or person not found'); }
-
                 // Keep track of the contacts seen
-                if (in_array($person->getId(), $contactsProcessed, true)) {
-                    // already updated from another mission
-                    continue;
-                }
                 $contactsProcessed[] = $person->getId();
 
-                // special: if the contact has no name, ignore it
-                if (!$person->getName() || !$person->getGivenName()) {
+                // special: if the contact hasn't a firstname and a lastname, ignore it
+                if (empty($person->getName()) || empty($person->getGivenName())) {
+                    $this->logger->error("Person " . $person->getId() . " miss a lastname and/or a firstname, ignored.");
                     continue;
                 }
 
                 // Get the matching dolibarr contact
-                $dolibarrContact = self::findDolibarrContactFor($dolibarrSocietyContacts, $person);
-                $dolibarrContact = self::sanitizeDolibarrData($dolibarrContact);
+                $dolibarrContact = $this->findDolibarrContactFor($dolibarrSocietyContacts, $person);
+                $dolibarrContact = $this->sanitizeDolibarrData($dolibarrContact);
 
                 $contact = $this->getPersonContact($person);
 
@@ -224,8 +226,8 @@ class DolibarrSyncService
                     'lastname' => trim($person->getName()),
                     'firstname' => trim($person->getGivenName()),
                     'email' => $contact?->getEmail(),
-                    'phone_pro' => $contact?->getTelphone() ? self::formatPhoneNumber($contact?->getTelphone()) : null,
-                    'phone_mobile' => $contact?->getMobilPhone() ? self::formatPhoneNumber($contact?->getMobilPhone()): null,
+                    'phone_pro' => $contact?->getTelphone() ? $this->formatPhoneNumber($contact?->getTelphone()) : null,
+                    'phone_mobile' => $contact?->getMobilPhone() ? $this->formatPhoneNumber($contact?->getMobilPhone()): null,
                     'poste' => $this->formatContactPosition($missions, $person->getGender()),
                     'statut' => '1'
                 ];
@@ -249,7 +251,7 @@ class DolibarrSyncService
                     );
                 } else {
                     // Only update the fields that are different (it's important to let it non-recursive, the subarray have to be passed entirely)
-                    $newContactData = ArrayUtils::getChanges(
+                    $newContactData = $this->arrayUtils->getChanges(
                         $dolibarrContact,
                         $newContactData,
                         false,
@@ -457,7 +459,7 @@ class DolibarrSyncService
      * @param Person $person
      * @return array|null
      */
-    protected static function findDolibarrContactFor(array $dolibarrContacts, Person $person): ?array {
+    protected function findDolibarrContactFor(array $dolibarrContacts, Person $person): ?array {
         foreach ($dolibarrContacts as $contactData) {
             if (!empty($contactData["array_options"]["options_2iopen_person_id"])) {
                 $id = (int)$contactData["array_options"]["options_2iopen_person_id"];
@@ -492,14 +494,14 @@ class DolibarrSyncService
      * @param array|null $data
      * @return array|null
      */
-    protected static function sanitizeDolibarrData(?array $data): ?array {
+    protected function sanitizeDolibarrData(?array $data): ?array {
         if ($data === null) {
             return null;
         }
 
         foreach ($data as $field => $value) {
             if (is_array($value)) {
-                $data[$field] = self::sanitizeDolibarrData($value);
+                $data[$field] = $this->sanitizeDolibarrData($value);
             } else if ($value === '') {
                 $data[$field] = null;
             }
@@ -556,10 +558,10 @@ class DolibarrSyncService
                 if ($contactPoint->getContactType() === $contactType) {
                     if ($contactPoint->getTelphone() !== null) {
 
-                        return self::formatPhoneNumber($contactPoint->getTelphone());
+                        return $this->formatPhoneNumber($contactPoint->getTelphone());
                     }
                     if ($contactPoint->getMobilPhone() !== null) {
-                        return self::formatPhoneNumber($contactPoint->getMobilPhone());
+                        return $this->formatPhoneNumber($contactPoint->getMobilPhone());
                     }
                 }
             }
@@ -616,7 +618,7 @@ class DolibarrSyncService
      * @param array $members An organization members as returned by getActiveMembersIndex: [$accessID => [$missions...]]
      * @return int
      */
-    protected static function countWithMission(array $missions, array $members): int {
+    protected function countWithMission(array $missions, array $members): int {
         return count(array_filter(
             $members,
             static function ($actualMissions) use ($missions) { return !empty(array_intersect($actualMissions, $missions)); }
@@ -694,7 +696,7 @@ class DolibarrSyncService
      * @param PhoneNumber $phoneNumber
      * @return mixed
      */
-    protected static function formatPhoneNumber(PhoneNumber $phoneNumber): string {
+    protected function formatPhoneNumber(PhoneNumber $phoneNumber): string {
         $phoneUtil = PhoneNumberUtil::getInstance();
         return str_replace(
             ' ',
@@ -728,10 +730,10 @@ class DolibarrSyncService
         }
 
         // Sanitize to get rid of the null / empty strings transformations of the API
-        $updated = self::sanitizeDolibarrData($updated);
-        $responseData = self::sanitizeDolibarrData($responseData);
+        $updated = $this->sanitizeDolibarrData($updated);
+        $responseData = $this->sanitizeDolibarrData($responseData);
 
-        $diffs = ArrayUtils::getChanges($responseData, $updated, true);
+        $diffs = $this->arrayUtils->getChanges($responseData, $updated, true);
 
         if (!empty($diffs)) {
             /** @noinspection JsonEncodingApiUsageInspection */

+ 2 - 2
src/Service/Utils/ArrayUtils.php

@@ -19,7 +19,7 @@ class ArrayUtils
      *                                accept two parameters (the values) and return true if the values are equals.
      * @return array
      */
-    public static function getChanges(array $initialArray, array $newArray, bool $recursive = false, ?callable $callback = null): array
+    public function getChanges(array $initialArray, array $newArray, bool $recursive = false, ?callable $callback = null): array
     {
         $changes = [];
         foreach ($newArray as $field => $value) {
@@ -27,7 +27,7 @@ class ArrayUtils
                 $changes[$field] = $value;
             }
             elseif ($recursive && is_array($initialArray[$field]) && is_array($value)) {
-                $newVal = self::getChanges($initialArray[$field], $value, $recursive, $callback);
+                $newVal = $this->getChanges($initialArray[$field], $value, $recursive, $callback);
                 if (!empty($newVal)) {
                     $changes[$field] = $newVal;
                 }

+ 0 - 2
tests/Service/Core/ContactPointUtilsTest.php

@@ -57,8 +57,6 @@ class ContactPointUtilsTest extends TestCase
         $access = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
         $access->method('getPerson')->willReturn($person);
 
-        $contactPoint = $this->getMockBuilder(ContactPoint::class)->disableOriginalConstructor()->getMock();
-
         $this->contactPointRepository
             ->method('getByTypeAndPerson')
             ->with(ContactPointTypeEnum::PRINCIPAL()->getValue(), $access->getPerson())

+ 0 - 1
tests/Service/Cotisation/UtilsTest.php

@@ -267,7 +267,6 @@ class UtilsTest extends TestCase
         $this->assertEquals(AlertStateEnum::AFFILIATION()->getValue(), $cotisationUtils->getAlertState($organization, $year) );
         $this->assertEquals(AlertStateEnum::AFFILIATION()->getValue(), $cotisationUtils->getAlertState($organization, $year) );
         $this->assertEquals(AlertStateEnum::INVOICE()->getValue(), $cotisationUtils->getAlertState($organization, $year) );
-
     }
 
     /**

+ 152 - 82
tests/Service/Dolibarr/DolibarrAccountCreatorTest.php

@@ -1,7 +1,10 @@
-<?php
+<?php /** @noinspection PhpUnhandledExceptionInspection */
 
 namespace App\Tests\Service\Dolibarr;
 
+use App\ApiResources\Dolibarr\DolibarrAccount;
+use App\ApiResources\Dolibarr\DolibarrBill;
+use App\ApiResources\Dolibarr\DolibarrContract;
 use App\Service\Dolibarr\DolibarrAccountCreator;
 use App\Service\Dolibarr\DolibarrApiService;
 use PHPUnit\Framework\TestCase;
@@ -9,110 +12,177 @@ use PHPUnit\Framework\TestCase;
 class DolibarrAccountCreatorTest extends TestCase
 {
     private DolibarrApiService $dolibarrApiService;
-    private DolibarrAccountCreator $dolibarrAccountCreator;
 
     public function setUp(): void {
         $this->dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
             ->disableOriginalConstructor()
             ->getMock();
-
-        $this->dolibarrAccountCreator = new DolibarrAccountCreator($this->dolibarrApiService);
     }
 
-    private function getJsonContentFromFixture(string $filename): array {
-        $filepath = dirname(__FILE__) . '/fixtures/' . $filename;
-        return json_decode(file_get_contents($filepath), true);
+    public function testGetDolibarrAccount(): void
+    {
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['getDolibarrAccount'])
+            ->getMock();
+
+        $organizationId = 1;
+        $socId = 123;
+
+        // Create dolibarr account
+        $accountData = [1]; // dummy non-null data
+        $this->dolibarrApiService->expects(self::once())->method('getSociety')->with($organizationId)->willReturn($accountData);
+
+        $dolibarrAccount = $this->getMockBuilder(DolibarrAccount::class)->getMock();
+        $dolibarrAccount->method('getSocId')->willReturn($socId);
+
+        $dolibarrAccountCreator
+            ->expects(self::once())
+            ->method('createDolibarrAccount')
+            ->with($organizationId, $accountData)
+            ->willReturn($dolibarrAccount);
+
+        // Get active contract and services
+        $contractData = [1]; // dummy non-null data
+        $this->dolibarrApiService->expects(self::once())->method('getActiveContract')->with($socId)->willReturn($contractData);
+
+        $dolibarrContract = $this->getMockBuilder(DolibarrContract::class)->getMock();
+
+        $dolibarrAccountCreator
+            ->expects(self::once())
+            ->method('createDolibarrContract')
+            ->with($contractData)
+            ->willReturn($dolibarrContract);
+
+        $dolibarrAccount->expects(self::once())->method('setContract')->with($dolibarrContract);
+
+        // get bills
+        $billsData = [[10], [20]]; // dummy non-empty data
+
+        $dolibarrBill1 = $this->getMockBuilder(DolibarrBill::class)->getMock();
+        $dolibarrBill2 = $this->getMockBuilder(DolibarrBill::class)->getMock();
+
+        $this->dolibarrApiService->expects(self::once())->method('getBills')->with($socId)->willReturn($billsData);
+
+        $dolibarrAccountCreator
+            ->expects(self::exactly(2))
+            ->method('createDolibarrBill')
+            ->willReturnMap([
+                [[10], $dolibarrBill1],
+                [[20], $dolibarrBill2]
+            ]);
+
+        $dolibarrAccount->expects(self::exactly(2))->method('addBill')->withConsecutive([$dolibarrBill1], [$dolibarrBill2]);
+
+        $result = $dolibarrAccountCreator->getDolibarrAccount(1);
+
+        $this->assertEquals($dolibarrAccount, $result);
     }
 
-    public function testGetDolibarrAccount() {
-        $this->dolibarrApiService
-            ->expects($this->once())
-            ->method('getSociety')
-            ->with(1)
-            ->willReturn(
-                $this->getJsonContentFromFixture('thirdparty.json')[0]
-            );
-
-        $this->dolibarrApiService
-            ->expects($this->once())
-            ->method('getActiveContract')
-            ->with(1726)
-            ->willReturn(
-                $this->getJsonContentFromFixture('contract.json')[0]
-            );
-
-        $this->dolibarrApiService
-            ->expects($this->once())
-            ->method('getBills')
-            ->with(1726)
-            ->willReturn(
-                $this->getJsonContentFromFixture('bills.json')
-            );
-
-        $dolibarrAccount = $this->dolibarrAccountCreator->getDolibarrAccount(1);
-
-        $this->assertEquals(
-            $dolibarrAccount->getClientNumber(),
-            "001855"
-        );
-        $this->assertEquals(
-            $dolibarrAccount->getContract()->getRef(),
-            "0000037-C1_1"
-        );
-        $this->assertEquals(
-            $dolibarrAccount->getContract()->getLines()[0]->getServiceRef(),
-            "ABO_ART_PRE_CMF"
-        );
-        $this->assertEquals(
-            $dolibarrAccount->getBills()[2]->getRef(),
-            "FA2105-03135"
-        );
+    public function testCreateDolibarrAccount(): void
+    {
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['createDolibarrAccount'])
+            ->getMock();
+
+        $organizationId = 1;
+        $accountData = ['id' => '2', 'code_client' => 'C2', 'array_options' => []];
+
+        $dolibarrAccount = $dolibarrAccountCreator->createDolibarrAccount($organizationId, $accountData);
+
+        $this->assertEquals(1, $dolibarrAccount->getOrganizationId());
+        $this->assertEquals(2, $dolibarrAccount->getSocId());
+        $this->assertEquals("C2", $dolibarrAccount->getClientNumber());
+        $this->assertEquals(null, $dolibarrAccount->getProduct());
     }
 
-    public function testCreateDolibarrAccount() {
-        $accountData = $this->getJsonContentFromFixture('thirdparty.json')[0];
+    public function testCreateDolibarrAccountWithProduct(): void
+    {
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['createDolibarrAccount'])
+            ->getMock();
+
+        $organizationId = 1;
+        $accountData = ['id' => '2', 'code_client' => 'C2', 'array_options' => ['options_2iopen_software_used' => 1]];
+
+        $dolibarrAccount = $dolibarrAccountCreator->createDolibarrAccount($organizationId, $accountData);
 
-        $dolibarrAccount = $this->dolibarrAccountCreator->createDolibarrAccount(1, $accountData);
-        $this->assertEquals(
-            $dolibarrAccount->getClientNumber(),
-            "001855"
-        );
+        $this->assertEquals('PRODUCT_ARTIST', $dolibarrAccount->getProduct());
     }
 
-    public function testCreateDolibarrContract() {
-        $contractData = $this->getJsonContentFromFixture('contract.json')[0];
+    public function testCreateDolibarrContract(): void
+    {
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['createDolibarrContract'])
+            ->getMock();
+
+        $contractData = [
+            'ref' => 'foo',
+            'socid' => 123,
+            'lines' => [[1], [2]]
+        ];
 
-        $dolibarrContract = $this->dolibarrAccountCreator->createDolibarrContract($contractData);
+        $dolibarrAccountCreator
+            ->expects(self::exactly(2))
+            ->method('createDolibarrContractLine')
+            ->withConsecutive([[1]], [[2]]);
 
-        $this->assertEquals(
-            $dolibarrContract->getRef(),
-            "0000037-C1_1"
-        );
-        $this->assertEquals(
-            $dolibarrContract->getLines()[0]->getServiceRef(),
-            "ABO_ART_PRE_CMF"
-        );
+        $dolibarrContract = $dolibarrAccountCreator->createDolibarrContract($contractData);
+
+        $this->assertEquals('foo', $dolibarrContract->getRef());
+        $this->assertEquals(123, $dolibarrContract->getSocId());
     }
 
     public function testCreateDolibarrContractLine() {
-        $lineData = $this->getJsonContentFromFixture('contract.json')[0]['lines'][0];
-
-        $dolibarrContractLine = $this->dolibarrAccountCreator->createDolibarrContractLine($lineData);
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['createDolibarrContractLine'])
+            ->getMock();
 
-        $this->assertEquals(
-            $dolibarrContractLine->getServiceRef(),
-            "ABO_ART_PRE_CMF"
-        );
+        $lineData = [
+            'id'=> '1',
+            'fk_contrat' => '123',
+            'product_ref' => 'foo',
+            'product_label' => 'bar',
+            'date_start' => '1577836800',
+            'date_end' => '1609372800'
+        ];
+
+        $dolibarrContractLine = $dolibarrAccountCreator->createDolibarrContractLine($lineData);
+
+        $this->assertEquals(1, $dolibarrContractLine->getId());
+        $this->assertEquals(123, $dolibarrContractLine->getContractId());
+        $this->assertEquals('foo', $dolibarrContractLine->getServiceRef());
+        $this->assertEquals('bar', $dolibarrContractLine->getServiceLabel());
+        $this->assertEquals('2020-01-01T00:00:00+00:00', $dolibarrContractLine->getDateStart()->format('c'));
+        $this->assertEquals('2020-12-31T00:00:00+00:00', $dolibarrContractLine->getDateEnd()->format('c'));
     }
 
     public function testCreateDolibarrBill() {
-        $billData = $this->getJsonContentFromFixture('bills.json')[0];
 
-        $dolibarrBill = $this->dolibarrAccountCreator->createDolibarrBill($billData);
-        $this->assertEquals(
-            $dolibarrBill->getRef(),
-            "AV2106-02992"
-        );
-    }
+        $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
+            ->setConstructorArgs([$this->dolibarrApiService])
+            ->setMethodsExcept(['createDolibarrBill'])
+            ->getMock();
 
+        $billData = [
+            'id' => '1',
+            'ref' => 'foo',
+            'total_ht' => '100.0',
+            'total_ttc' => '120.0',
+            'date' => '1577836800',
+            'paye' => '0'
+        ];
+
+        $dolibarrBill = $dolibarrAccountCreator->createDolibarrBill($billData);
+
+        $this->assertEquals(1, $dolibarrBill->getId());
+        $this->assertEquals('foo', $dolibarrBill->getRef());
+        $this->assertEquals(100.0, $dolibarrBill->getTaxExcludedAmount());
+        $this->assertEquals(120.0, $dolibarrBill->getTaxIncludedAmount());
+        $this->assertEquals(false, $dolibarrBill->getPaid());
+    }
 }

+ 165 - 55
tests/Service/Dolibarr/DolibarrApiServiceTest.php

@@ -3,96 +3,206 @@
 namespace App\Tests\Service\Dolibarr;
 
 use App\Service\Dolibarr\DolibarrApiService;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
-use Symfony\Contracts\HttpClient\ResponseInterface;
 
 class DolibarrApiServiceTest extends TestCase
 {
-    private HttpClientInterface $client;
-    private DolibarrApiService $dolibarrApiService;
+    private MockObject | HttpClientInterface $client;
 
     public function setUp(): void {
         $this->client = $this->getMockBuilder(HttpClientInterface::class)
             ->disableOriginalConstructor()
             ->getMock();
-
-        $this->dolibarrApiService = new DolibarrApiService($this->client);
-    }
-
-    private function getContentFromFixture(string $filename): string {
-        $filepath = dirname(__FILE__) . '/fixtures/' . $filename;
-        return file_get_contents($filepath);
     }
 
     /**
      * @see DolibarrApiService::getSociety()
      */
     public function testGetSociety(): void {
-        $responseContent = $this->getContentFromFixture('thirdparty.json');
-
-        $response = $this->getMockBuilder(ResponseInterface::class)
-            ->disableOriginalConstructor()
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getSociety'])
             ->getMock();
-        $response->method('getContent')->willReturn($responseContent);
 
-        $this->client
-            ->expects($this->once())
-            ->method('request')
-            ->with("GET", "thirdparties?limit=1&sqlfilters=ref_int%3D1")
-            ->willReturn($response);
+        $organizationId = 123;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("thirdparties", [ "limit" => "1", "sqlfilters" => "ref_int=" . $organizationId])
+            ->willReturn([['id' => 1]]); // dummy non-empty data
 
-        $data = $this->dolibarrApiService->getSociety(1);
+        $society = $dolibarrApiService->getSociety(1);
 
-        $this->assertEquals(
-            $data['id'],
-            1726
-        );
+        $this->assertEquals(['id' => 1], $society);
     }
 
     /**
      * @see DolibarrApiService::getActiveContract()
      */
     public function testGetActiveContract(): void {
-        $responseContent = $this->getContentFromFixture('contract.json');
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getActiveContract'])
+            ->getMock();
 
-        $response = $this->getMockBuilder(ResponseInterface::class)
-            ->disableOriginalConstructor()
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contracts", ["limit" => "1", "sqlfilters" => "statut=1", "thirdparty_ids" => $socId])
+            ->willReturn([['id' => 1]]); // dummy non-empty data
+
+        $this->assertEquals(['id' => 1], $dolibarrApiService->getActiveContract($socId));
+    }
+
+    /**
+     * @see DolibarrApiService::getActiveContract()
+     */
+    public function testGetActiveContractMissing(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getActiveContract'])
             ->getMock();
-        $response->method('getContent')->willReturn($responseContent);
-
-        $this->client
-            ->expects($this->once())
-            ->method('request')
-            ->with("GET", "contracts?limit=1&sqlfilters=statut%3D1&thirdparty_ids=1")
-            ->willReturn($response);
-
-        $this->assertEquals(
-            $this->dolibarrApiService->getActiveContract(1)['socid'],
-            43
-        );
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contracts", ["limit" => "1", "sqlfilters" => "statut=1", "thirdparty_ids" => $socId])
+            ->willThrowException(new HttpException(404));
+
+        $this->assertEquals(null, $dolibarrApiService->getActiveContract($socId));
     }
 
     /**
      * @see DolibarrApiService::getBills()
      */
     public function testGetBills(): void {
-        $responseContent = $this->getContentFromFixture('bills.json');
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getBills'])
+            ->getMock();
 
-        $response = $this->getMockBuilder(ResponseInterface::class)
-            ->disableOriginalConstructor()
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("invoices", ["sortfield" => "datef", "sortorder" => "DESC", "limit" => 5, "sqlfilters" => "fk_soc=" . $socId])
+            ->willReturn([['id' => 10], ['id' => 20]]);
+
+        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getBills($socId));
+    }
+
+    /**
+     * @see DolibarrApiService::getBills()
+     */
+    public function testGetBillsMissing(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getBills'])
             ->getMock();
-        $response->method('getContent')->willReturn($responseContent);
-
-        $this->client
-            ->expects($this->once())
-            ->method('request')
-            ->with("GET", "invoices?sortfield=datef&sortorder=DESC&limit=5&sqlfilters=fk_soc%3D1")
-            ->willReturn($response);
-
-        $this->assertEquals(
-            $this->dolibarrApiService->getBills(1)[2]['socid'],
-            284
-        );
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("invoices", ["sortfield" => "datef", "sortorder" => "DESC", "limit" => 5, "sqlfilters" => "fk_soc=" . $socId])
+            ->willThrowException(new HttpException(404));
+
+        $this->assertEquals([], $dolibarrApiService->getBills($socId));
     }
+
+    public function testGetAllClients(): void
+    {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getAllClients'])
+            ->getMock();
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("thirdparties", ["limit" => "1000000", "sqlfilters" => "client=1"])
+            ->willReturn([['id' => 10], ['id' => 20]]);
+
+        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getAllClients());
+    }
+
+    public function testGetContacts() {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getContacts'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contacts", ['limit' => 1000, 'thirdparty_ids' => $socId])
+            ->willReturn([['id' => 10], ['id' => 20]]);
+
+        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getContacts($socId));
+    }
+
+    public function testGetContactsMissing(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getContacts'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contacts", ['limit' => 1000, 'thirdparty_ids' => $socId])
+            ->willThrowException(new HttpException(404));
+
+        $this->assertEquals([], $dolibarrApiService->getContacts($socId));
+    }
+
+    public function testGetActiveOpentalentContacts() {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getActiveOpentalentContacts'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contacts?limit=1000&t.statut=1&thirdparty_ids=" . $socId . "&sqlfilters=(te.2iopen_person_id%3A%3E%3A0)")
+            ->willReturn([['id' => 10], ['id' => 20]]);
+
+        $this->assertEquals([['id' => 10], ['id' => 20]], $dolibarrApiService->getActiveOpentalentContacts($socId));
+    }
+
+    public function testGetActiveOpentalentContactsMissing(): void {
+        $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
+            ->setConstructorArgs([$this->client])
+            ->setMethodsExcept(['getActiveOpentalentContacts'])
+            ->getMock();
+
+        $socId = 1;
+
+        $dolibarrApiService
+            ->expects(self::once())
+            ->method('getJsonContent')
+            ->with("contacts?limit=1000&t.statut=1&thirdparty_ids=" . $socId . "&sqlfilters=(te.2iopen_person_id%3A%3E%3A0)")
+            ->willThrowException(new HttpException(404));
+
+        $this->assertEquals([], $dolibarrApiService->getActiveOpentalentContacts($socId));
+    }
+
+
 }

+ 345 - 194
tests/Service/Dolibarr/DolibarrSyncServiceTest.php

@@ -15,19 +15,22 @@ use App\Entity\Person\Person;
 use App\Enum\Access\FunctionEnum;
 use App\Enum\Access\RoleEnum;
 use App\Enum\Core\ContactPointTypeEnum;
+use App\Enum\Network\NetworkEnum;
 use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\Repository\Access\AccessRepository;
 use App\Repository\Access\FunctionTypeRepository;
 use App\Repository\Organization\OrganizationRepository;
+use App\Service\Core\AddressPostalUtils;
 use App\Service\Dolibarr\DolibarrApiService;
 use App\Service\Dolibarr\DolibarrSyncService;
 use App\Service\Rest\Operation\CreateOperation;
 use App\Service\Rest\Operation\UpdateOperation;
+use App\Service\Utils\ArrayUtils;
 use Doctrine\Common\Collections\ArrayCollection;
-use JetBrains\PhpStorm\Pure;
 use libphonenumber\PhoneNumber;
 use libphonenumber\PhoneNumberUtil;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -35,27 +38,29 @@ use Symfony\Contracts\Translation\TranslatorInterface;
 
 class TestableDolibarrSyncService extends DolibarrSyncService {
     public function getDolibarrSocietiesIndex(): array { return parent::getDolibarrSocietiesIndex(); }
-    public static function findDolibarrContactFor(array $dolibarrContacts, Person $person): ?array { return parent::findDolibarrContactFor($dolibarrContacts, $person); }
+    public function findDolibarrContactFor(array $dolibarrContacts, Person $person): ?array { return parent::findDolibarrContactFor($dolibarrContacts, $person); }
     public function getActiveMembersIndex(): array { return parent::getActiveMembersIndex(); }
-    public static function sanitizeDolibarrData(?array $data): ?array { return parent::sanitizeDolibarrData($data); }
+    public function sanitizeDolibarrData(?array $data): ?array { return parent::sanitizeDolibarrData($data); }
     public function getOrganizationPostalAddress(Organization $organization): ?AddressPostal { return parent::getOrganizationPostalAddress($organization); }
     public function getOrganizationPhone(Organization $organization): ?string { return parent::getOrganizationPhone($organization); }
     public function getOrganizationEmail(Organization $organization): ?string { return parent::getOrganizationEmail($organization); }
     public function getOrganizationNetworkId(Organization $organization): ?int { return parent::getOrganizationNetworkId($organization); }
-    public static function countWithMission(array $missions, array $members): int { return parent::countWithMission($missions, $members); }
+    public function countWithMission(array $missions, array $members): int { return parent::countWithMission($missions, $members); }
     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 static function formatPhoneNumber(PhoneNumber $phoneNumber): string { return parent::formatPhoneNumber($phoneNumber); }
+    public function formatPhoneNumber(PhoneNumber $phoneNumber): string { return parent::formatPhoneNumber($phoneNumber); }
 }
 
 class DolibarrSyncServiceTest extends TestCase
 {
-    private OrganizationRepository $organizationRepository;
-    private AccessRepository $accessRepository;
-    private FunctionTypeRepository $functionTypeRepository;
-    private DolibarrApiService $dolibarrApiService;
-    private TranslatorInterface $translator;
-    private LoggerInterface $logger;
+    private MockObject | OrganizationRepository $organizationRepository;
+    private MockObject | AccessRepository $accessRepository;
+    private MockObject | FunctionTypeRepository $functionTypeRepository;
+    private MockObject | DolibarrApiService $dolibarrApiService;
+    private MockObject | AddressPostalUtils $addressPostalUtils;
+    private MockObject | ArrayUtils $arrayUtils;
+    private MockObject | TranslatorInterface $translator;
+    private MockObject | LoggerInterface $logger;
 
     public function setUp(): void {
         $this->organizationRepository = $this->getMockBuilder(OrganizationRepository::class)
@@ -70,6 +75,12 @@ class DolibarrSyncServiceTest extends TestCase
         $this->dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
             ->disableOriginalConstructor()
             ->getMock();
+        $this->addressPostalUtils = $this->getMockBuilder(AddressPostalUtils::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->arrayUtils = $this->getMockBuilder(ArrayUtils::class)
+            ->disableOriginalConstructor()
+            ->getMock();
         $this->translator = $this->getMockBuilder(TranslatorInterface::class)
             ->disableOriginalConstructor()
             ->getMock();
@@ -83,53 +94,54 @@ class DolibarrSyncServiceTest extends TestCase
         $this->logger->method('error')->willReturnSelf();
     }
 
-    #[Pure]
-    private function newDolibarrSyncService(): TestableDolibarrSyncService
+    public function testScan(): void
     {
-        return new TestableDolibarrSyncService(
-             $this->organizationRepository,
-             $this->accessRepository,
-             $this->functionTypeRepository,
-             $this->dolibarrApiService,
-             $this->translator,
-             $this->logger
-        );
-    }
-
-    private function getJsonContentFromFixture(string $filename): array {
-        $filepath = __DIR__ . '/fixtures/' . $filename;
-        return json_decode(file_get_contents($filepath), true);
-    }
-
-    public function testScan() {
-
-        // mock services and special methods from repos
-        $this->dolibarrApiService
-            ->expects($this->once())
-            ->method('getAllClients')
-            ->willReturn(
-                $this->getJsonContentFromFixture('thirdparty.json')
-            );
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['scan'])
+            ->getMock();
 
-        $this->dolibarrApiService->method('getSociety')->willReturnMap(
-            [
-                [12097, ['id' => 711]],
-                [91295, ['id' => 5086]]
+        $dolibarrSociety1 = [
+            'id' => 1,
+            'name' => 'Organization 10',
+            'address' => '1 Rue Qwerty',
+            'zip' => '01024',
+            'town' => 'Ram',
+            'email' => 'some@email.com',
+            'phone' => null,
+            'status' => 0,
+            'parent' => '0',
+            'array_options' => [
+                'options_2iopeninfoopentalent' => '',
+                'options_2iopen_software_opentalent' => 'Opentalent Artist'
             ]
-        );
-        $this->accessRepository
-            ->expects($this->once())
-            ->method('getAllActiveMembersAndMissions')
-            ->willReturn(
-                [
-                    ['id' => 1, 'organization_id' => 37306, 'mission' => FunctionEnum::PRESIDENT()->getValue()],
-                    ['id' => 1, 'organization_id' => 37306, 'mission' => FunctionEnum::ADHERENT()->getValue()],
-                    ['id' => 2, 'organization_id' => 37306, 'mission' => FunctionEnum::TREASURER()->getValue()],
-                    ['id' => 2, 'organization_id' => 37306, 'mission' => FunctionEnum::ADHERENT()->getValue()],
-                    ['id' => 3, 'organization_id' => 37306, 'mission' => FunctionEnum::STUDENT()->getValue()],
-                    ['id' => 3, 'organization_id' => 37306, 'mission' => FunctionEnum::ADHERENT()->getValue()],
-                ]
-            );
+        ];
+        $dolibarrSociety2 = ['id' => 2, "array_options" => []];
+        $dolibarrSociety3 = ['id' => 3, "array_options" => []];
+
+        // Get societies
+        $dolibarrSyncService
+            ->expects(self::once())
+            ->method('getDolibarrSocietiesIndex')
+            ->willReturn([
+                10 => $dolibarrSociety1,
+                20 => $dolibarrSociety2,
+                30 => $dolibarrSociety3
+            ]);
+
+        // Get members
+        $activeMembers1 = [11 => [FunctionEnum::PRESIDENT()->getValue()], 12 => [FunctionEnum::STUDENT()->getValue()], 13 => [FunctionEnum::TREASURER()->getValue()]];
+        $activeMembers2 = [21 => [FunctionEnum::PRESIDENT()->getValue()]];
+
+        $dolibarrSyncService
+            ->expects(self::once())
+            ->method('getActiveMembersIndex')
+            ->willReturn([
+                10 => $activeMembers1,
+                20 => $activeMembers2,
+                30 => [],
+            ]);
 
         // Function types
         $functionType1 = $this->getMockBuilder(FunctionType::class)->getMock();
@@ -142,182 +154,321 @@ class DolibarrSyncServiceTest extends TestCase
             ->with(['roleByDefault' => RoleEnum::ROLE_ADMIN()->getValue()])
             ->willReturn([$functionType1, $functionType2]);
 
-        // Organization's name
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
-        $organization->method('getId')->willReturn(37306);
-        $organization->method('getName')->willReturn("Etablissement d'Enseignement Artistique");
+        // Get CMF and FFEC ids
+        $this->dolibarrApiService->method('getSociety')->willReturnMap(
+            [
+                [12097, ['id' => 10]],
+                [91295, ['id' => 20]]
+            ]
+        );
+
+        // -- Loop over organizations --
+        $dolibarrSyncService
+            ->expects(self::exactly(5))  // 3 organizations and 2 contacts
+            ->method('sanitizeDolibarrData')
+            ->willReturnCallback(function ($args) { return $args; });
+
+        $organization1 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization1->method('getId')->willReturn(10);
+        $organization1->method('getName')->willReturn('Organization 10');
+
+        $organization2 = $this->getMockBuilder(Organization::class)->getMock();
+        $organization2->method('getId')->willReturn(20);
+        $organization2->method('getName')->willReturn('Organization 20');
+
+        $this->organizationRepository->method('find')
+            ->willReturnMap([
+                [10, null, null, $organization1],
+                [20, null, null, $organization2],
+                [30, null, null, null]
+            ]);
 
-        // Postal address
-        $organizationAddressPostal = $this->getMockBuilder(OrganizationAddressPostal::class)->getMock();
         $addressPostal = $this->getMockBuilder(AddressPostal::class)->getMock();
-        $addressPostal->method('getStreetAddress')->willReturn('21b baker street');
-        $addressPostal->method('getStreetAddressSecond')->willReturn('');
-        $addressPostal->method('getStreetAddressThird')->willReturn('');
-        $addressPostal->method('getAddressOwner')->willReturn('');
-        $addressPostal->method('getPostalCode')->willReturn('250 329');
-        $addressPostal->method('getAddressCity')->willReturn('Londres');
-        $organizationAddressPostal->method('getType')->willReturn(AddressPostalOrganizationTypeEnum::ADDRESS_CONTACT()->getValue());
-        $organizationAddressPostal->method('getAddressPostal')->willReturn($addressPostal);
-        $organization->method('getOrganizationAddressPostals')->willReturn(
-            new ArrayCollection([$organizationAddressPostal])
-        );
-
-        // Email and phone
-        $phoneUtil = PhoneNumberUtil::getInstance();
 
-        $contactPoint = $this->getMockBuilder(ContactPoint::class)->getMock();
-        $contactPoint->method('getContactType')->willReturn(ContactPointTypeEnum::CONTACT()->getValue());
-        $contactPoint->method('getEmail')->willReturn('email@email.com');
-        $phoneNumber = $phoneUtil->parse('01 02 03 04 05', 'FR');
-        $contactPoint->method('getTelphone')->willReturn($phoneNumber);
-        $organization->method('getContactPoints')->willReturn(
-            new ArrayCollection([$contactPoint])
-        );
+        $dolibarrSyncService
+            ->expects(self::exactly(2))
+            ->method('getOrganizationPostalAddress')
+            ->willReturnMap([
+               [$organization1, $addressPostal],
+               [$organization2, null],
+            ]);
+
+        $this->addressPostalUtils
+            ->method('getFullStreetAddress')
+            ->with($addressPostal)
+            ->willReturn('1 Rue Azerty');
+
+        $addressPostal->method('getAddressOwner')->willReturn('Mr Keyboard');
+        $addressPostal->method('getPostalCode')->willReturn('01110');
+        $addressPostal->method('getAddressCity')->willReturn('ByteCity');
+
+        $dolibarrSyncService
+            ->expects(self::exactly(2))
+            ->method('getOrganizationEmail')
+            ->willReturnMap([
+                [$organization1, 'foo@bar.net'],
+                [$organization2, null],
+            ]);
+
+        $dolibarrSyncService
+            ->expects(self::exactly(2))
+            ->method('getOrganizationPhone')
+            ->willReturnMap([
+                [$organization1, '0102030405'],
+                [$organization2, null],
+            ]);
+
+        $dolibarrSyncService
+            ->expects(self::exactly(2))
+            ->method('getOrganizationNetworkId')
+            ->willReturnMap([
+                [$organization1, NetworkEnum::CMF()->getValue()],
+                [$organization2, null],
+            ]);
+
+        $settings1 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings1->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL()->getValue());
+        $organization1->method('getSettings')->willReturn($settings1);
+
+        $settings2 = $this->getMockBuilder(Settings::class)->getMock();
+        $settings1->method('getProduct')->willReturn(SettingsProductEnum::ARTIST()->getValue());
+        $organization2->method('getSettings')->willReturn($settings2);
+
+        $dolibarrSyncService->method('countWithMission')->willReturnMap([
+            [[FunctionEnum::STUDENT()->getValue()], $activeMembers1, 1],
+            [[FunctionEnum::ADHERENT()->getValue()], $activeMembers1, 2],
+            [[FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::PRESIDENT()->getValue()], $activeMembers1, 1],
+            [[FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::PRESIDENT()->getValue()], $activeMembers2, 2]
+        ]);
+
+        $this->translator->method('trans')->willReturnMap([
+           ['STUDENTS_COUNT', [], null, null, "Nombre d'élèves"],
+           ['ADHERENTS_COUNT', [], null, null, "Nombre d'adhérents"],
+           ['ADMIN_ACCESS_COUNT', [], null, null, "Nombre d'accès admin"],
+           ['school', [], null, null, 'Opentalent School'],
+           ['artist', [], null, null, 'Opentalent Artist'],
+           ['Mr', [], null, null, 'MR'],
+        ]);
+
+        $this->arrayUtils
+            ->expects(self::exactly(3))
+            ->method('getChanges')
+            ->willReturnCallback(
+                function (array $initialArray, array $newArray, bool $recursive = false, ?callable $callback = null) {
+                    if (in_array('name', $newArray, true)) {
+                        // Organization 1 name is already defined and has not been changed
+                        unset($newArray['name']);
+                    }
+                    if (in_array('statut', $newArray, true)) {
+                        // Contact 1 statut is already defined and has not been changed
+                        unset($newArray['statut']);
+                    }
+                    return $newArray;
+                }
+            );
 
-        // Network
-        $network = $this->getMockBuilder(Network::class)->getMock();
-        $network->method('getId')->willReturn(3);
-        $networkOrganization = $this->getMockBuilder(NetworkOrganization::class)->getMock();
-        $networkOrganization->method('getNetwork')->willReturn($network);
-        $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization]));
+        $contactData1 = [
+            'id' => '1',
+            'civility_code' => '',
+            'lastname' => 'Dupond',
+            'firstname' => 'Bob',
+            'email' => 'abcd@mail.com',
+            'phone_pro' => '+33478570000',
+            'phone_mobile' => '+33682980000',
+            'poste' => 'Secrétaire',
+            'statut' => '1',
+            'array_options' => [
+                'options_2iopen_person_id' => ''
+            ]
+        ];
 
-        // Product
-        $settings = $this->getMockBuilder(Settings::class)->getMock();
-        $settings->method('getProduct')->willReturn(SettingsProductEnum::SCHOOL()->getValue());
-        $organization->method('getSettings')->willReturn($settings);
+        // An obsolete contact that should be disabled
+        $obsoleteContactData = [
+            'id' => '4',
+            'lastname' => 'Doe',
+            'firstname' => 'John',
+            'statut' => '1',
+            'array_options' => [
+                'options_2iopen_person_id' => 300
+            ]
+        ];
 
-        // Get dolibarr contacts
-        $this->dolibarrApiService
-        ->method('getContacts')
-        ->with(1726)
-        ->willReturn(
-            array_filter(
-                $this->getJsonContentFromFixture('contacts.json'),
-                static function ($c) {
-                    return in_array(
-                        (int)$c["array_options"]["options_2iopen_person_id"],
-                        [
-                            108939, // existing person, to be updated
-                            156252  // no matching person, to be deleted
-                                   // a new contact shall be created too
-                        ]
-                    ); }
-            )
-        );
+        // An obsolete contact that should is already disabled
+        $obsoleteContactData2 = [
+            'id' => '5',
+            'lastname' => 'Foo',
+            'firstname' => 'John',
+            'statut' => '0',
+            'array_options' => [
+                'options_2iopen_person_id' => 400
+            ]
+        ];
 
-        $this->organizationRepository->method('find')->willReturn($organization);
+        $dolibarrSocietyContacts1 = [$contactData1, $obsoleteContactData, $obsoleteContactData2];
+        $dolibarrSocietyContacts2 = [];
 
-        $access1 = $this->getMockBuilder(Access::class)->getMock();
-        $access1->method('getId')->willReturn(1);
+        $this->dolibarrApiService->method('getContacts')->willReturnMap([
+            [1, $dolibarrSocietyContacts1],
+            [2, $dolibarrSocietyContacts2]
+        ]);
+
+        // Loop over contacts
+        // NB: Student will be skipped since it has no office role
 
         $person1 = $this->getMockBuilder(Person::class)->getMock();
-        $person1->method('getId')->willReturn(108939);
-        $person1->method('getName')->willReturn('Holmes');
-        $person1->method('getGender')->willReturn('MISTER');
-        $person1->method('getGivenName')->willReturn('Sherlock');
-        $person1->method('getGivenName')->willReturn('Sherlock');
-
-        $personContactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
-        $personContactPoint1->method('getContactType')->willReturn(ContactPointTypeEnum::CONTACT()->getValue());
-        $personContactPoint1->method('getEmail')->willReturn('sherlock@holmes.com');
-        $phoneNumber1 = $phoneUtil->parse('02 98 76 54 32', 'FR');
-        $personContactPoint1->method('getTelphone')->willReturn($phoneNumber1);
-        $personContactPoint1->method('getMobilPhone')->willReturn(null);
-        $person1->method('getContactPoints')->willReturn(
-            new ArrayCollection([$personContactPoint1])
-        );
+        $person1->method('getId')->willReturn(100);
+        $person1->method('getName')->willReturn('Dupont');
+        $person1->method('getGivenName')->willReturn('Hercules');
+        $person1->method('getGender')->willReturn('Mr');
+
+        $person2 = $this->getMockBuilder(Person::class)->getMock();
+        $person2->method('getId')->willReturn(200);
+        $person2->method('getName')->willReturn('Simpson');
+        $person2->method('getGivenName')->willReturn('Lisa');
+        $person2->method('getGender')->willReturn(null);
+
+        // An invalid person that should be ignored
+        $invalidPerson = $this->getMockBuilder(Person::class)->getMock();
+        $invalidPerson->method('getId')->willReturn(900);
+        $invalidPerson->method('getName')->willReturn('');
+        $invalidPerson->method('getGivenName')->willReturn('');
+
+
+        $access1 = $this->getMockBuilder(Access::class)->getMock();
         $access1->method('getPerson')->willReturn($person1);
 
         $access2 = $this->getMockBuilder(Access::class)->getMock();
-        $access2->method('getId')->willReturn(1);
-
-        $person2 = $this->getMockBuilder(Person::class)->getMock();
-        $person2->method('getId')->willReturn(1000);
-        $person2->method('getName')->willReturn('Watson');
-        $person2->method('getGender')->willReturn('MISTER');
-        $person2->method('getGivenName')->willReturn('John');
-        $person2->method('getGivenName')->willReturn('John');
-
-        $personContactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
-        $personContactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::CONTACT()->getValue());
-        $personContactPoint2->method('getEmail')->willReturn('docteur@watson.com');
-        $phoneNumber2 = $phoneUtil->parse('02 10 11 12 13', 'FR');
-        $personContactPoint2->method('getTelphone')->willReturn($phoneNumber2);
-        $personContactPoint2->method('getMobilPhone')->willReturn(null);
-        $person2->method('getContactPoints')->willReturn(
-            new ArrayCollection([$personContactPoint2])
-        );
         $access2->method('getPerson')->willReturn($person2);
 
-        $this->accessRepository->method('find')->willReturnMap(
-            [
-                [1, null, null, $access1],
-                [2, null, null, $access2],
-            ]
-        );
+        $access3 = $this->getMockBuilder(Access::class)->getMock();
+        $access3->method('getPerson')->willReturn($invalidPerson);
 
-        $this->translator->method('trans')->willReturnMap(
-            [
-                ['STUDENTS_COUNT', [], null, null, "Nombre d'élèves"],
-                ['ADHERENTS_COUNT', [], null, null, "Nombre d'adhérents"],
-                ['ADMIN_ACCESS_COUNT', [], null, null, "Nombre d'accès admin"],
-                ['school', [], null, null, "Opentalent School"],
-            ]
+        $this->accessRepository->method('find')->willReturnMap([
+            [11, null, null, $access1],
+            [21, null, null, $access2],
+            [13, null, null, $access3],
+        ]);
+
+        $dolibarrSyncService->method('findDolibarrContactFor')->willReturnMap([
+           [$dolibarrSocietyContacts1, $person1, $contactData1],
+           [$dolibarrSocietyContacts2, $person2, null]
+        ]);
+
+        $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
+        $contactPoint1->method('getEmail')->willReturn('mail@domain.net');
+
+        $phone = $this->getMockBuilder(PhoneNumber::class)->getMock();
+        $contactPoint1->method('getTelphone')->willReturn($phone);
+
+        $mobilePhone = $this->getMockBuilder(PhoneNumber::class)->getMock();
+        $contactPoint1->method('getMobilPhone')->willReturn($mobilePhone);
+
+        $dolibarrSyncService->method('getPersonContact')->willReturnMap([
+           [$person1, $contactPoint1],
+           [$person2, null],
+        ]);
+
+        $dolibarrSyncService->method('formatPhoneNumber')->willReturnMap([
+            [$phone, '0102030405'],
+            [$mobilePhone, '0607080910'],
+        ]);
+
+        $dolibarrSyncService->method('formatContactPosition')->willReturnMap([
+            [[FunctionEnum::PRESIDENT()->getValue()], 'Mr', 'Président'],
+            [[FunctionEnum::PRESIDENT()->getValue()], null, 'Président(e)'],
+        ]);
+
+        $this->logger->expects(self::exactly(2))->method('error')->withConsecutive(
+            ["Person 900 miss a lastname and/or a firstname, ignored."],
+            ["Organization 30 not found in the Opentalent DB"],
         );
 
-        $syncService = $this->newDolibarrSyncService();
+        $progressionCallbackExpectedCalls = [[1, 3], [2, 3], [3, 3]];
 
-        $operations = $syncService->scan();
+        $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
+            [$expectedI, $expectedTotal] = array_shift($progressionCallbackExpectedCalls);
+            if ($i !== $expectedI || $total !== $expectedTotal) {
+                throw new \AssertionError(
+                    'Progression callback error, expected parameters are (' . $expectedI . ',' . $expectedTotal . ')' .
+                    ', got (' . $i . ', ' . $total . ')'
+                );
+            }
+        };
 
-        $this->assertCount(4, $operations);
-        $this->assertEquals(
-            [
-                '[PUT thirdparties/1726]',
-                "address : `\n217, rue Raoul Follereau\n` => `21b baker street`",
-                'zip : `74300` => `250 329`',
-                'town : `CLUSES` => `Londres`',
-                'email : `` => `email@email.com`',
-                'phone : `+33678403010` => `+33102030405`',
-                'parent : `` => `711`',
-                "array_options.options_2iopen_software_opentalent : `` => `Opentalent School`",
-                "array_options.options_2iopeninfoopentalent : `` => `Nombre d'élèves : 1\nNombre d'adhérents : 3\nNombre d'accès admin : 1`"
+        $operations = $dolibarrSyncService->scan($progressionCallback);
+
+        $this->assertCount(5, $operations);
 
+        $this->assertEqualsCanonicalizing(
+            [
+                '[PUT thirdparties/1]',
+                "address : `1 Rue Qwerty` => `Mr Keyboard\n1 Rue Azerty`",
+                'zip : `01024` => `01110`',
+                'town : `Ram` => `ByteCity`',
+                'email : `some@email.com` => `foo@bar.net`',
+                'phone : `` => `0102030405`',
+                'parent : `0` => `10`',
+                "array_options.options_2iopen_software_opentalent : `Opentalent Artist` => `Opentalent School`",
+                "array_options.options_2iopeninfoopentalent : `` => `Nombre d'élèves : 1\nNombre d'adhérents : 2\nNombre d'accès admin : 1`",
+                'status : `0` => `1`'
             ],
             $operations[0]->getChangeLog()
         );
-        $this->assertEquals(
+
+        $this->assertEqualsCanonicalizing(
             [
-                '[PUT contacts/5868]',
-                'civility_code : `MME` => ``',
-                'lastname : `DUPONT` => `Holmes`',
-                'firstname : `Valerie` => `Sherlock`',
-                'email : `abcd@hotmail.com` => ``',
-                'phone_pro : `+33478570000` => ``',
-                'phone_mobile : `+33682980000` => ``',
-                'poste : `Secrétaire` => ``'
+                '[PUT contacts/1]',
+                'civility_code : `` => `MR`',
+                'lastname : `Dupond` => `Dupont`',
+                'firstname : `Bob` => `Hercules`',
+                'email : `abcd@mail.com` => `mail@domain.net`',
+                'phone_pro : `+33478570000` => `0102030405`',
+                'phone_mobile : `+33682980000` => `0607080910`',
+                'poste : `Secrétaire` => `Président`',
+                'array_options.options_2iopen_person_id : `` => `100`'
             ],
             $operations[1]->getChangeLog()
         );
-        $this->assertEquals(
+
+        $this->assertEqualsCanonicalizing(
+            ['[PUT contacts/4]', 'statut : `1` => `0`'],
+            $operations[2]->getChangeLog()
+        );
+
+        $this->assertEqualsCanonicalizing(
+            [
+                '[PUT thirdparties/2]',
+                'address : (new) => ``',
+                'email : (new) => ``',
+                'name : (new) => `Organization 20`',
+                'parent : (new) => ``',
+                'phone : (new) => ``',
+                'status : (new) => `1`',
+                'town : (new) => ``',
+                'zip : (new) => ``',
+                'array_options.options_2iopeninfoopentalent : (new) => `Nombre d\'accès admin : 2`',
+            ],
+            $operations[3]->getChangeLog()
+        );
+
+        $this->assertEqualsCanonicalizing(
             [
                 '[POST contacts]',
                 'civility_code : (new) => ``',
-                'lastname : (new) => `Watson`',
-                'firstname : (new) => `John`',
+                'lastname : (new) => `Simpson`',
+                'firstname : (new) => `Lisa`',
                 'email : (new) => ``',
                 'phone_pro : (new) => ``',
                 'phone_mobile : (new) => ``',
-                'poste : (new) => ``',
+                'poste : (new) => `Président(e)`',
                 'statut : (new) => `1`',
-                'array_options.options_2iopen_person_id : (new) => `1000`',
-                'socid : (new) => `1726`'
+                'array_options.options_2iopen_person_id : (new) => `200`',
+                'socid : (new) => `2`'
             ],
-            $operations[2]->getChangeLog()
-        );
-        $this->assertEquals(
-            ['[PUT contacts/5869]', 'statut : `1` => `0`'],
-            $operations[3]->getChangeLog()
+            $operations[4]->getChangeLog()
         );
+
+        $this->assertCount(0, $progressionCallbackExpectedCalls);
     }
 
     public function testExecuteError()

+ 0 - 542
tests/Service/Dolibarr/fixtures/bills.json

@@ -1,542 +0,0 @@
-[
-  {
-    "socid": "284",
-    "fk_user_author": null,
-    "fk_user_valid": null,
-    "date": 1624831200,
-    "datem": 1626091849,
-    "ref_client": null,
-    "type": "2",
-    "remise_absolue": null,
-    "remise_percent": null,
-    "total_ht": "-731.00000000",
-    "total_tva": "-146.20000000",
-    "total_localtax1": "0.00000000",
-    "total_localtax2": "0.00000000",
-    "total_ttc": "-877.20000000",
-    "revenuestamp": "0.00000000",
-    "close_code": null,
-    "close_note": null,
-    "paye": "1",
-    "module_source": null,
-    "pos_source": null,
-    "fk_fac_rec_source": null,
-    "fk_facture_source": "256",
-    "linked_objects": [],
-    "date_lim_reglement": 1624831200,
-    "cond_reglement_code": null,
-    "mode_reglement_code": "VIR",
-    "fk_bank": null,
-    "products": [],
-    "lines": [
-      {
-        "fk_facture": "310",
-        "fk_parent_line": null,
-        "label": null,
-        "desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "localtax1_type": "0",
-        "localtax2_type": "0",
-        "fk_remise_except": null,
-        "rang": "0",
-        "fk_fournprice": null,
-        "pa_ht": "731.00000000",
-        "marge_tx": -0,
-        "marque_tx": 0,
-        "special_code": "0",
-        "origin": null,
-        "origin_id": null,
-        "fk_code_ventilation": 0,
-        "date_start": 1624053600,
-        "date_end": 1655503200,
-        "ref": "ABO_SCH_PRE_200-399_CMF",
-        "product_ref": "ABO_SCH_PRE_200-399_CMF",
-        "libelle": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_label": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "situation_percent": "100",
-        "fk_prev_id": null,
-        "multicurrency_subprice": "-731.00000000",
-        "multicurrency_total_ht": "-731.00000000",
-        "multicurrency_total_tva": "-146.20000000",
-        "multicurrency_total_ttc": "-877.20000000",
-        "qty": "1",
-        "subprice": "-731.00000000",
-        "product_type": "1",
-        "fk_product": "327",
-        "vat_src_code": "",
-        "tva_tx": "20.000",
-        "localtax1_tx": "0.000",
-        "localtax2_tx": "0.000",
-        "remise_percent": "0",
-        "total_ht": "-731.00000000",
-        "total_tva": "-146.20000000",
-        "total_localtax1": "0.00000000",
-        "total_localtax2": "0.00000000",
-        "total_ttc": "-877.20000000",
-        "info_bits": "0",
-        "id": "1240",
-        "rowid": "1240",
-        "fk_unit": null,
-        "import_key": null,
-        "array_options": {
-          "options_show_total_ht": null,
-          "options_show_reduc": null
-        },
-        "linkedObjectsIds": null,
-        "canvas": null,
-        "ref_ext": null,
-        "statut": null,
-        "state": null,
-        "state_id": null,
-        "state_code": null,
-        "transport_mode_id": null,
-        "last_main_doc": null,
-        "fk_account": null,
-        "lines": null,
-        "date_creation": null,
-        "date_validation": null,
-        "date_modification": null,
-        "description": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "fk_product_type": "1",
-        "code_ventilation": "100105976",
-        "fk_accounting_account": "100105976"
-      }
-    ],
-    "line": null,
-    "extraparams": [],
-    "specimen": null,
-    "fac_rec": null,
-    "fk_multicurrency": "0",
-    "multicurrency_code": "EUR",
-    "multicurrency_tx": "1.00000000",
-    "multicurrency_total_ht": "-731.00000000",
-    "multicurrency_total_tva": "-146.20000000",
-    "multicurrency_total_ttc": "-877.20000000",
-    "situation_cycle_ref": null,
-    "situation_counter": null,
-    "situation_final": "0",
-    "tab_previous_situation_invoice": [],
-    "tab_next_situation_invoice": [],
-    "id": "310",
-    "import_key": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_origvente": "2",
-      "options_logicielfact": "2",
-      "options_versionfact": "2",
-      "options_ec_beg_bill_period_date": "",
-      "options_ec_end_bill_period_date": ""
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "thirdparty": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "AV2106-02992",
-    "ref_ext": null,
-    "statut": "2",
-    "country": null,
-    "country_id": null,
-    "country_code": null,
-    "state": null,
-    "state_id": null,
-    "state_code": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "0",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": "InfraSPlus_F",
-    "last_main_doc": "facture/AV2106-02992/AV2106-02992.pdf",
-    "fk_account": "1",
-    "note_public": "Annule la facture FA2105-03135 suite geste commerciale exceptionnel de 10% sur le montant de l'abonnement au logiciel Opentalent School Premium",
-    "note_private": null,
-    "fk_incoterms": "0",
-    "libelle_incoterms": null,
-    "location_incoterms": "",
-    "name": null,
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_creation": 1624893979,
-    "date_validation": 1624831200,
-    "date_modification": 1626091849,
-    "entity": "1",
-    "date_pointoftax": "",
-    "mode_reglement": "Virement",
-    "cond_reglement_doc": null,
-    "user_author": "5",
-    "user_valid": "5",
-    "totalpaid": null,
-    "totalcreditnotes": null,
-    "totaldeposits": null,
-    "remaintopay": "-877.20",
-    "contacts_ids": [
-      "29845"
-    ]
-  },
-  {
-    "socid": "284",
-    "fk_user_author": null,
-    "fk_user_valid": null,
-    "date": 1624831200,
-    "datem": 1625552613,
-    "ref_client": null,
-    "type": "0",
-    "remise_absolue": null,
-    "remise_percent": null,
-    "total_ht": "657.90000000",
-    "total_tva": "131.58000000",
-    "total_localtax1": "0.00000000",
-    "total_localtax2": "0.00000000",
-    "total_ttc": "789.48000000",
-    "revenuestamp": "0.00000000",
-    "close_code": null,
-    "close_note": null,
-    "paye": "1",
-    "module_source": null,
-    "pos_source": null,
-    "fk_fac_rec_source": null,
-    "fk_facture_source": null,
-    "linked_objects": [],
-    "date_lim_reglement": 1624917600,
-    "cond_reglement_code": "RECEP",
-    "mode_reglement_code": "VIR",
-    "fk_bank": null,
-    "products": [],
-    "lines": [
-      {
-        "fk_facture": "311",
-        "fk_parent_line": null,
-        "label": null,
-        "desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p><span style=\"color:#e74c3c\"><strong>Geste commercial exceptionnel de 10 %</strong></span></p>",
-        "localtax1_type": "0",
-        "localtax2_type": "0",
-        "fk_remise_except": null,
-        "rang": "0",
-        "fk_fournprice": null,
-        "pa_ht": "731.00000000",
-        "marge_tx": -10.000000000000002,
-        "marque_tx": -11.111111111111114,
-        "special_code": "0",
-        "origin": null,
-        "origin_id": null,
-        "fk_code_ventilation": 0,
-        "date_start": 1624053600,
-        "date_end": 1655503200,
-        "ref": "ABO_SCH_PRE_200-399_CMF",
-        "product_ref": "ABO_SCH_PRE_200-399_CMF",
-        "libelle": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_label": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "situation_percent": "100",
-        "fk_prev_id": null,
-        "multicurrency_subprice": "731.00000000",
-        "multicurrency_total_ht": "657.90000000",
-        "multicurrency_total_tva": "131.58000000",
-        "multicurrency_total_ttc": "789.48000000",
-        "qty": "1",
-        "subprice": "731.00000000",
-        "product_type": "1",
-        "fk_product": "327",
-        "vat_src_code": "",
-        "tva_tx": "20.000",
-        "localtax1_tx": "0.000",
-        "localtax2_tx": "0.000",
-        "remise_percent": "10",
-        "total_ht": "657.90000000",
-        "total_tva": "131.58000000",
-        "total_localtax1": "0.00000000",
-        "total_localtax2": "0.00000000",
-        "total_ttc": "789.48000000",
-        "info_bits": "0",
-        "id": "1241",
-        "rowid": "1241",
-        "fk_unit": null,
-        "import_key": null,
-        "array_options": {
-          "options_show_total_ht": null,
-          "options_show_reduc": null
-        },
-        "linkedObjectsIds": null,
-        "canvas": null,
-        "ref_ext": null,
-        "statut": null,
-        "state": null,
-        "state_id": null,
-        "state_code": null,
-        "transport_mode_id": null,
-        "last_main_doc": null,
-        "fk_account": null,
-        "lines": null,
-        "date_creation": null,
-        "date_validation": null,
-        "date_modification": null,
-        "description": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p><span style=\"color:#e74c3c\"><strong>Geste commercial exceptionnel de 10 %</strong></span></p>",
-        "fk_product_type": "1",
-        "code_ventilation": "100105976",
-        "fk_accounting_account": "100105976"
-      }
-    ],
-    "line": null,
-    "extraparams": [],
-    "specimen": null,
-    "fac_rec": null,
-    "fk_multicurrency": "0",
-    "multicurrency_code": "EUR",
-    "multicurrency_tx": "1.00000000",
-    "multicurrency_total_ht": "657.90000000",
-    "multicurrency_total_tva": "131.58000000",
-    "multicurrency_total_ttc": "789.48000000",
-    "situation_cycle_ref": null,
-    "situation_counter": null,
-    "situation_final": "0",
-    "tab_previous_situation_invoice": [],
-    "tab_next_situation_invoice": [],
-    "id": "311",
-    "import_key": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_origvente": "2",
-      "options_logicielfact": "2",
-      "options_versionfact": "2",
-      "options_ec_beg_bill_period_date": 1624053600,
-      "options_ec_end_bill_period_date": 1655503200
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "thirdparty": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "FA2106-03183",
-    "ref_ext": null,
-    "statut": "2",
-    "country": null,
-    "country_id": null,
-    "country_code": null,
-    "state": null,
-    "state_id": null,
-    "state_code": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": "Due upon receipt",
-    "shipping_method_id": null,
-    "modelpdf": "InfraSPlus_F",
-    "last_main_doc": "facture/FA2106-03183/FA2106-03183.pdf",
-    "fk_account": "1",
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": "0",
-    "libelle_incoterms": null,
-    "location_incoterms": "",
-    "name": null,
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_creation": 1624895968,
-    "date_validation": 1624831200,
-    "date_modification": 1625552613,
-    "entity": "1",
-    "date_pointoftax": "",
-    "mode_reglement": "Virement",
-    "cond_reglement_doc": "Due upon receipt",
-    "user_author": "5",
-    "user_valid": "5",
-    "totalpaid": "789.48000000",
-    "totalcreditnotes": null,
-    "totaldeposits": null,
-    "remaintopay": "0",
-    "contacts_ids": [
-      "29845"
-    ]
-  },
-  {
-    "socid": "284",
-    "fk_user_author": null,
-    "fk_user_valid": null,
-    "date": 1621980000,
-    "datem": 1626091889,
-    "ref_client": null,
-    "type": "0",
-    "remise_absolue": null,
-    "remise_percent": null,
-    "total_ht": "731.00000000",
-    "total_tva": "146.20000000",
-    "total_localtax1": "0.00000000",
-    "total_localtax2": "0.00000000",
-    "total_ttc": "877.20000000",
-    "revenuestamp": "0.00000000",
-    "close_code": null,
-    "close_note": null,
-    "paye": "1",
-    "module_source": null,
-    "pos_source": null,
-    "fk_fac_rec_source": null,
-    "fk_facture_source": null,
-    "linked_objects": [],
-    "date_lim_reglement": 1622066400,
-    "cond_reglement_code": "RECEP",
-    "mode_reglement_code": "VIR",
-    "fk_bank": null,
-    "products": [],
-    "lines": [
-      {
-        "fk_facture": "256",
-        "fk_parent_line": null,
-        "label": null,
-        "desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "localtax1_type": "0",
-        "localtax2_type": "0",
-        "fk_remise_except": null,
-        "rang": "0",
-        "fk_fournprice": null,
-        "pa_ht": "731.00000000",
-        "marge_tx": 0,
-        "marque_tx": 0,
-        "special_code": "0",
-        "origin": null,
-        "origin_id": null,
-        "fk_code_ventilation": 0,
-        "date_start": 1624053600,
-        "date_end": 1655503200,
-        "ref": "ABO_SCH_PRE_200-399_CMF",
-        "product_ref": "ABO_SCH_PRE_200-399_CMF",
-        "libelle": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_label": "Abonnement Opentalent School premium Tranche  200-399 Adhérent CMF",
-        "product_desc": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "situation_percent": "100",
-        "fk_prev_id": null,
-        "multicurrency_subprice": "731.00000000",
-        "multicurrency_total_ht": "731.00000000",
-        "multicurrency_total_tva": "146.20000000",
-        "multicurrency_total_ttc": "877.20000000",
-        "qty": "1",
-        "subprice": "731.00000000",
-        "product_type": "1",
-        "fk_product": "327",
-        "vat_src_code": "",
-        "tva_tx": "20.000",
-        "localtax1_tx": "0.000",
-        "localtax2_tx": "0.000",
-        "remise_percent": "0",
-        "total_ht": "731.00000000",
-        "total_tva": "146.20000000",
-        "total_localtax1": "0.00000000",
-        "total_localtax2": "0.00000000",
-        "total_ttc": "877.20000000",
-        "info_bits": "0",
-        "id": "952",
-        "rowid": "952",
-        "fk_unit": null,
-        "import_key": null,
-        "array_options": {
-          "options_show_total_ht": null,
-          "options_show_reduc": null
-        },
-        "linkedObjectsIds": null,
-        "canvas": null,
-        "ref_ext": null,
-        "statut": null,
-        "state": null,
-        "state_id": null,
-        "state_code": null,
-        "transport_mode_id": null,
-        "last_main_doc": null,
-        "fk_account": null,
-        "lines": null,
-        "date_creation": null,
-        "date_validation": null,
-        "date_modification": null,
-        "description": "<p>Logiciel de gestion et de communication pour les &eacute;tablissements d&#39;enseignement artistique comprend :</p>\r\n\r\n<ul>\r\n\t<li>3 comptes administratifs</li>\r\n\t<li>comptes utilisateurs (&eacute;l&egrave;ves, familles et professeurs)</li>\r\n\t<li>site internet complet</li>\r\n</ul>\r\n<br />\r\n<em><strong>Conditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. </strong>.En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</em>\r\n\r\n<p>&nbsp;</p>",
-        "fk_product_type": "1",
-        "code_ventilation": "100105976",
-        "fk_accounting_account": "100105976"
-      }
-    ],
-    "line": null,
-    "extraparams": [],
-    "specimen": null,
-    "fac_rec": null,
-    "fk_multicurrency": "0",
-    "multicurrency_code": "EUR",
-    "multicurrency_tx": "1.00000000",
-    "multicurrency_total_ht": "731.00000000",
-    "multicurrency_total_tva": "146.20000000",
-    "multicurrency_total_ttc": "877.20000000",
-    "situation_cycle_ref": null,
-    "situation_counter": null,
-    "situation_final": "0",
-    "tab_previous_situation_invoice": [],
-    "tab_next_situation_invoice": [],
-    "id": "256",
-    "import_key": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_origvente": "2",
-      "options_logicielfact": "2",
-      "options_versionfact": "2",
-      "options_ec_beg_bill_period_date": 1624053600,
-      "options_ec_end_bill_period_date": 1655503200
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "thirdparty": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "FA2105-03135",
-    "ref_ext": null,
-    "statut": "2",
-    "country": null,
-    "country_id": null,
-    "country_code": null,
-    "state": null,
-    "state_id": null,
-    "state_code": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": "Due upon receipt",
-    "shipping_method_id": null,
-    "modelpdf": "InfraSPlus_F",
-    "last_main_doc": "facture/FA2105-03135/FA2105-03135.pdf",
-    "fk_account": "1",
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": "0",
-    "libelle_incoterms": null,
-    "location_incoterms": "",
-    "name": null,
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_creation": 1622017153,
-    "date_validation": 1621980000,
-    "date_modification": 1626091889,
-    "entity": "1",
-    "date_pointoftax": "",
-    "mode_reglement": "Virement",
-    "cond_reglement_doc": "Due upon receipt",
-    "user_author": "5",
-    "user_valid": "5",
-    "totalpaid": null,
-    "totalcreditnotes": "877.20000000",
-    "totaldeposits": null,
-    "remaintopay": "0",
-    "contacts_ids": [
-      "29845"
-    ]
-  }
-]

+ 0 - 442
tests/Service/Dolibarr/fixtures/contacts.json

@@ -1,442 +0,0 @@
-[
-  {
-    "civility_id": null,
-    "civility_code": "MME",
-    "civility": "Madame",
-    "address": null,
-    "zip": null,
-    "town": null,
-    "state_id": null,
-    "state_code": null,
-    "state": null,
-    "poste": "Secrétaire",
-    "socid": "8",
-    "statut": "1",
-    "code": null,
-    "email": "abcd@hotmail.com",
-    "no_email": null,
-    "skype": null,
-    "photo": null,
-    "jabberid": null,
-    "phone_pro": "+33478570000",
-    "phone_perso": "",
-    "phone_mobile": "+33682980000",
-    "fax": "",
-    "priv": "0",
-    "birthday": "",
-    "default_lang": null,
-    "ref_facturation": null,
-    "ref_contrat": null,
-    "ref_commande": null,
-    "ref_propal": null,
-    "user_id": null,
-    "user_login": null,
-    "roles": null,
-    "cacheprospectstatus": [],
-    "fk_prospectlevel": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Jamais contacté",
-    "stcomm_picto": null,
-    "id": "5868",
-    "import_key": "crm",
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_person_id": "108939"
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "5868",
-    "ref_ext": null,
-    "country": "",
-    "country_id": "0",
-    "country_code": "",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": "DUPONT",
-    "firstname": "Valerie",
-    "date_creation": "",
-    "date_validation": null,
-    "date_modification": 1612541370,
-    "entity": "1",
-    "socname": "Société Musicale l'Abeille",
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "mail": "abcd@hotmail.com",
-    "gender": "woman"
-  },
-  {
-    "civility_id": null,
-    "civility_code": "MME",
-    "civility": "Madame",
-    "address": null,
-    "zip": null,
-    "town": null,
-    "state_id": null,
-    "state_code": null,
-    "state": null,
-    "poste": "Présidente",
-    "socid": "8",
-    "statut": "1",
-    "code": null,
-    "email": "xyz@sfr.fr",
-    "no_email": null,
-    "skype": null,
-    "photo": null,
-    "jabberid": null,
-    "phone_pro": "",
-    "phone_perso": "",
-    "phone_mobile": "+33600009399",
-    "fax": "",
-    "priv": "0",
-    "birthday": "",
-    "default_lang": null,
-    "ref_facturation": null,
-    "ref_contrat": null,
-    "ref_commande": null,
-    "ref_propal": null,
-    "user_id": null,
-    "user_login": null,
-    "roles": null,
-    "cacheprospectstatus": [],
-    "fk_prospectlevel": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Jamais contact&eacute;",
-    "stcomm_picto": null,
-    "id": "5869",
-    "import_key": "crm",
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_person_id": "156252"
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "5869",
-    "ref_ext": null,
-    "country": "",
-    "country_id": "0",
-    "country_code": "",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": "DURAND",
-    "firstname": "Elise",
-    "date_creation": "",
-    "date_validation": null,
-    "date_modification": 1612541370,
-    "entity": "1",
-    "socname": "Société Musicale l'Abeille",
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "mail": "xyz@sfr.fr",
-    "gender": "woman"
-  },
-  {
-    "civility_id": null,
-    "civility_code": "MME",
-    "civility": "Madame",
-    "address": null,
-    "zip": null,
-    "town": null,
-    "state_id": null,
-    "state_code": null,
-    "state": null,
-    "poste": "Trésorière",
-    "socid": "9",
-    "statut": "1",
-    "code": null,
-    "email": null,
-    "no_email": null,
-    "skype": null,
-    "photo": null,
-    "jabberid": null,
-    "phone_pro": "",
-    "phone_perso": "",
-    "phone_mobile": "",
-    "fax": "",
-    "priv": "0",
-    "birthday": "",
-    "default_lang": null,
-    "ref_facturation": null,
-    "ref_contrat": null,
-    "ref_commande": null,
-    "ref_propal": null,
-    "user_id": null,
-    "user_login": null,
-    "roles": null,
-    "cacheprospectstatus": [],
-    "fk_prospectlevel": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Jamais contact&eacute;",
-    "stcomm_picto": null,
-    "id": "5870",
-    "import_key": "crm",
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_person_id": "112775"
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "5870",
-    "ref_ext": null,
-    "country": "",
-    "country_id": "0",
-    "country_code": "",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": "LEGRAND",
-    "firstname": "Anaïs",
-    "date_creation": "",
-    "date_validation": null,
-    "date_modification": 1612541370,
-    "entity": "1",
-    "socname": "Ensemble à vents Opus 92",
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "mail": null,
-    "gender": "woman"
-  },
-  {
-    "civility_id": null,
-    "civility_code": "MME",
-    "civility": "Madame",
-    "address": null,
-    "zip": null,
-    "town": null,
-    "state_id": null,
-    "state_code": null,
-    "state": null,
-    "poste": "Secrétaire",
-    "socid": "9",
-    "statut": "1",
-    "code": null,
-    "email": null,
-    "no_email": null,
-    "skype": null,
-    "photo": null,
-    "jabberid": null,
-    "phone_pro": "",
-    "phone_perso": "",
-    "phone_mobile": "",
-    "fax": "",
-    "priv": "0",
-    "birthday": "",
-    "default_lang": null,
-    "ref_facturation": null,
-    "ref_contrat": null,
-    "ref_commande": null,
-    "ref_propal": null,
-    "user_id": null,
-    "user_login": null,
-    "roles": null,
-    "cacheprospectstatus": [],
-    "fk_prospectlevel": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Jamais contact&eacute;",
-    "stcomm_picto": null,
-    "id": "5871",
-    "import_key": "crm",
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_person_id": "302117"
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "5871",
-    "ref_ext": null,
-    "country": "",
-    "country_id": "0",
-    "country_code": "",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": "LEJEUNE",
-    "firstname": "Cassandra",
-    "date_creation": "",
-    "date_validation": null,
-    "date_modification": 1612541370,
-    "entity": "1",
-    "socname": "Ensemble à vents Opus 92",
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "mail": null,
-    "gender": "woman"
-  },
-  {
-    "civility_id": null,
-    "civility_code": "MR",
-    "civility": "Monsieur",
-    "address": null,
-    "zip": null,
-    "town": null,
-    "state_id": null,
-    "state_code": null,
-    "state": null,
-    "poste": "Président",
-    "socid": "9",
-    "statut": "1",
-    "code": null,
-    "email": "email@gmail.com",
-    "no_email": null,
-    "skype": null,
-    "photo": null,
-    "jabberid": null,
-    "phone_pro": "",
-    "phone_perso": "",
-    "phone_mobile": "",
-    "fax": "",
-    "priv": "0",
-    "birthday": "",
-    "default_lang": null,
-    "ref_facturation": null,
-    "ref_contrat": null,
-    "ref_commande": null,
-    "ref_propal": null,
-    "user_id": null,
-    "user_login": null,
-    "roles": null,
-    "cacheprospectstatus": [],
-    "fk_prospectlevel": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Jamais contact&eacute;",
-    "stcomm_picto": null,
-    "id": "5872",
-    "import_key": "crm",
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_person_id": null
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "5872",
-    "ref_ext": null,
-    "country": "",
-    "country_id": "0",
-    "country_code": "",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": "ZORRO",
-    "firstname": "Fabrice",
-    "date_creation": "",
-    "date_validation": null,
-    "date_modification": 1612541370,
-    "entity": "1",
-    "socname": "Ensemble à vents Opus 92",
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "mail": "email@gmail.com",
-    "gender": "man"
-  }
-]

+ 0 - 187
tests/Service/Dolibarr/fixtures/contract.json

@@ -1,187 +0,0 @@
-[
-  {
-    "ref_customer": null,
-    "ref_supplier": null,
-    "entity": "1",
-    "socid": "43",
-    "societe": null,
-    "statut": "1",
-    "product": null,
-    "fk_user_author": null,
-    "user_author_id": "1",
-    "user_creation": null,
-    "user_cloture": null,
-    "date_creation": 1280786400,
-    "date_modification": null,
-    "date_validation": null,
-    "date_contrat": 1280786400,
-    "commercial_signature_id": "5",
-    "commercial_suivi_id": "5",
-    "fk_projet": null,
-    "extraparams": [],
-    "lines": [
-      {
-        "id": "4",
-        "ref": "ABO_ART_PRE_CMF",
-        "tms": null,
-        "fk_contrat": "2",
-        "fk_product": "282",
-        "statut": "4",
-        "type": null,
-        "label": null,
-        "libelle": null,
-        "description": "<p>Logiciel de gestion et de communication en ligne pour les structures culturelles de type Chorale, Orchestre, Compagnie (cirque, danse, de rue, théâtre,etc...).\n<br/>Inclus 150 comptes internet pour les membres et un site internet personnalisable.\n<br/>Conditions réservées aux adhérents CMF. En cas de non renouvellement de votre adhésion, vous basculerez sur les conditions publiques.</p>",
-        "product_type": "1",
-        "product_ref": "ABO_ART_PRE_CMF",
-        "product_label": "Abonnement Opentalent Artist premium CMF",
-        "date_commande": null,
-        "date_start": 1280786400,
-        "date_start_real": 1280786400,
-        "date_end": 1659477600,
-        "date_end_real": "",
-        "tva_tx": "20.000",
-        "localtax1_tx": "0.000",
-        "localtax2_tx": "0.000",
-        "localtax1_type": null,
-        "localtax2_type": null,
-        "qty": "1",
-        "remise_percent": "0",
-        "remise": null,
-        "fk_remise_except": null,
-        "subprice": "63.33000000",
-        "price": "63.33",
-        "price_ht": "63.33",
-        "total_ht": "63.33000000",
-        "total_tva": "12.67000000",
-        "total_localtax1": "0.00000000",
-        "total_localtax2": "0.00000000",
-        "total_ttc": "76.00000000",
-        "fk_fournprice": null,
-        "pa_ht": "63.33000000",
-        "info_bits": "0",
-        "fk_user_author": "1",
-        "fk_user_ouverture": "1",
-        "fk_user_cloture": null,
-        "commentaire": null,
-        "rowid": null,
-        "fk_unit": null,
-        "import_key": null,
-        "array_options": {
-          "options_2iopen_vers_fact": null,
-          "options_2iopen_log_fact": null,
-          "options_ec_type_line": "8"
-        },
-        "linkedObjectsIds": null,
-        "canvas": null,
-        "origin": null,
-        "origin_id": null,
-        "ref_ext": null,
-        "state": null,
-        "state_id": null,
-        "state_code": null,
-        "barcode_type": null,
-        "barcode_type_code": null,
-        "barcode_type_label": null,
-        "barcode_type_coder": null,
-        "transport_mode_id": null,
-        "last_main_doc": null,
-        "fk_account": null,
-        "note": null,
-        "lines": null,
-        "date_creation": null,
-        "date_validation": null,
-        "date_modification": null,
-        "desc": "<p>Logiciel de gestion et de communication en ligne pour les structures culturelles de type Chorale, Orchestre, Compagnie (cirque, danse, de rue, théâtre,etc...).\n<br/>Inclus 150 comptes internet pour les membres et un site internet personnalisable.\n<br/>Conditions réservées aux adhérents CMF. En cas de non renouvellement de votre adhésion, vous basculerez sur les conditions publiques.</p>",
-        "vat_src_code": "",
-        "product_desc": "<p>Logiciel de gestion et de communication en ligne pour les structures culturelles de type Chorale, Orchestre, Compagnie (cirque, danse, de rue, th&eacute;&acirc;tre,etc...).<br />\r\nInclus 150 comptes internet pour les membres et un site internet personnalisable.<br />\r\nConditions r&eacute;serv&eacute;es aux adh&eacute;rents CMF. En cas de non renouvellement de votre adh&eacute;sion, vous basculerez sur les conditions publiques.</p>"
-      }
-    ],
-    "id": "2",
-    "import_key": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopen_origvente": "2",
-      "options_logicielfact": "1",
-      "options_versionfact": "2",
-      "options_ec_initial_amount": "63.33000000",
-      "options_ec_amount": "63.33000000",
-      "options_ec_initial_value_installation": null,
-      "options_ec_current_value_installation": "0.00000000",
-      "options_ec_signature_date": "",
-      "options_ec_effective_date": 1280786400,
-      "options_ec_duration_months": "12",
-      "options_ec_tacit_renewal": "1",
-      "options_ec_termination_period_months": "2",
-      "options_ec_des_term_date": "",
-      "options_ec_eff_term_date": "",
-      "options_ec_billing_due": "1",
-      "options_ec_billing_frequency": "4",
-      "options_ec_billing_begin_period": "1",
-      "options_ec_payment_condition": "1",
-      "options_ec_payment_mode": "2",
-      "options_ec_account": "1",
-      "options_ec_revaluation_index": "2",
-      "options_ec_enable_to_revaluation_date": 1609455600,
-      "options_ec_revaluation_date": "1",
-      "options_ec_month_for_new_revaluation_index": "11",
-      "options_ec_initial_revaluation_index_used": "1",
-      "options_ec_last_revaluation_index_used": "14",
-      "options_ec_revaluation_calculation_base": "2",
-      "options_ec_fixed_amount_not_revaluable": null,
-      "options_ec_unauthorized_deflation": "1",
-      "options_ec_amount_round_rule": null
-    },
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "thirdparty": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "ref": "0000037-C1_1",
-    "ref_ext": null,
-    "country": null,
-    "country_id": null,
-    "country_code": null,
-    "state": null,
-    "state_id": null,
-    "state_code": null,
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": null,
-    "cond_reglement_id": null,
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": null,
-    "note_public": null,
-    "note_private": null,
-    "note": null,
-    "total_ht": "63.33",
-    "total_tva": null,
-    "total_localtax1": null,
-    "total_localtax2": null,
-    "total_ttc": "76",
-    "fk_incoterms": null,
-    "libelle_incoterms": null,
-    "location_incoterms": null,
-    "name": null,
-    "lastname": null,
-    "firstname": null,
-    "mise_en_service": 1280786400,
-    "fin_validite": 1627941600,
-    "fk_soc": "43",
-    "nbofserviceswait": 0,
-    "nbofservicesopened": 1,
-    "nbofservicesexpired": 0,
-    "nbofservicesclosed": 0,
-    "nbofservices": 1,
-    "total_vat": "12.67"
-  }
-]

+ 0 - 798
tests/Service/Dolibarr/fixtures/thirdparties.json

@@ -1,798 +0,0 @@
-[
-  {
-    "entity": "1",
-    "name": "Etablissement d'Enseignement Artistique",
-    "name_alias": null,
-    "address": "\n217, rue Raoul Follereau\n",
-    "zip": "74300",
-    "town": "CLUSES",
-    "status": "1",
-    "state_id": "81",
-    "state_code": "74",
-    "state": "Haute-Savoie",
-    "phone": "+33678403010",
-    "fax": null,
-    "email": null,
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": null,
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "812",
-    "forme_juridique": null,
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "001855",
-    "code_fournisseur": null,
-    "code_compta": "0001855",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": null,
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": null,
-    "default_lang": null,
-    "ref": "1726",
-    "ref_ext": null,
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "12",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_used": "4",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "37306"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "1726",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  },
-  {
-    "entity": "1",
-    "name": "Société Musicale l'Abeille",
-    "name_alias": null,
-    "address": "\n4 rue Jean Moulin\n\n",
-    "zip": "69310",
-    "town": "PIERRE BENITE",
-    "status": "1",
-    "state_id": "76",
-    "state_code": "69",
-    "state": "Rhône",
-    "phone": "623936009",
-    "fax": null,
-    "email": "alexis.manasser@yahoo.fr",
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": "http://abeille.wifeo.com/",
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "92",
-    "forme_juridique": "Association loi 1901 ou assimilé",
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "000002",
-    "code_fournisseur": null,
-    "code_compta": "0000002",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": "Autres email(s): ",
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": "711",
-    "default_lang": null,
-    "ref": "8",
-    "ref_ext": "3c839cab-f46d-eddc-f2ef-543fb02978a2",
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopeninfoopentalent": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "21",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "1",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "13878"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "8",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  },
-  {
-    "entity": "1",
-    "name": "Ensemble à vents Opus 92",
-    "name_alias": null,
-    "address": "\n96 rue de la Sous Préfecture\n\n",
-    "zip": "69400",
-    "town": "VILLEFRANCHE-SUR-SAÔNE",
-    "status": "1",
-    "state_id": "76",
-    "state_code": "69",
-    "state": "Rhône",
-    "phone": "683283834",
-    "fax": null,
-    "email": "bureau@opus92.org",
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": "http://www.opus92.org",
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "92",
-    "forme_juridique": "Association loi 1901 ou assimilé",
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "000003",
-    "code_fournisseur": null,
-    "code_compta": "0000003",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": "Autres email(s): contact@opus92.org",
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": "711",
-    "default_lang": null,
-    "ref": "9",
-    "ref_ext": "7191614d-2d4c-8c25-17b9-543fb25fa130",
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopeninfoopentalent": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "19",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "1",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "13891"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "9",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  },
-  {
-    "entity": "1",
-    "name": "Association Musicale de Montanay",
-    "name_alias": null,
-    "address": "\n142 rue Centrale\n\n",
-    "zip": "69250",
-    "town": "MONTANAY",
-    "status": "1",
-    "state_id": "76",
-    "state_code": "69",
-    "state": "Rhône",
-    "phone": "04 78 00 73 64",
-    "fax": null,
-    "email": "gonnet-family@wanadoo.fr",
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": "https://assocmusicalemontanay.wordpress.com/",
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "92",
-    "forme_juridique": "Association loi 1901 ou assimilé",
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "000004",
-    "code_fournisseur": null,
-    "code_compta": "0000004",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": "Autres email(s): franckbrosse@aol.com, direction.amm@gmail.com",
-    "note_public": null,
-    "stcomm_id": "3",
-    "statut_commercial": "Contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": "711",
-    "default_lang": null,
-    "ref": "10",
-    "ref_ext": "7f92c92b-ffea-2753-c2bd-4d3a116f7d4e",
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopeninfoopentalent": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "19",
-      "options_2iopen_nombre_eleves": "2",
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "1",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "13904"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "10",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  },
-  {
-    "entity": "1",
-    "name": "Le Chant du Marmont",
-    "name_alias": null,
-    "address": "\nen Mairie\nRue des Gagères\n",
-    "zip": "01480",
-    "town": "FRANS",
-    "status": "1",
-    "state_id": "7",
-    "state_code": "01",
-    "state": "Ain",
-    "phone": "474607172",
-    "fax": null,
-    "email": "pat.mouchon@orange.fr",
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": "http:/bffrans.freeheberg.com",
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "92",
-    "forme_juridique": "Association loi 1901 ou assimilé",
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "000005",
-    "code_fournisseur": null,
-    "code_compta": "0000005",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": "Autres email(s): nuguet.roger@orange.fr",
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": "711",
-    "default_lang": null,
-    "ref": "11",
-    "ref_ext": "5001328e-ce5e-f1b2-ae9e-543fb29aea42",
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopeninfoopentalent": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "19",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "1",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "13917"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "11",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  },
-  {
-    "entity": "1",
-    "name": "Orchestre Symphonique de Villefranche",
-    "name_alias": null,
-    "address": "\n96 rue de la Sous Préfecture\n\n",
-    "zip": "69400",
-    "town": "VILLEFRANCHE-SUR-SAÔNE",
-    "status": "1",
-    "state_id": "76",
-    "state_code": "69",
-    "state": "Rhône",
-    "phone": "06.86.12.85.22",
-    "fax": null,
-    "email": "orchestre.osv@orange.fr",
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": " ",
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "92",
-    "forme_juridique": "Association loi 1901 ou assimilé",
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "000006",
-    "code_fournisseur": null,
-    "code_compta": "0000006",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": "Autres email(s): sebremont@orange.fr",
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": "711",
-    "default_lang": null,
-    "ref": "12",
-    "ref_ext": "8f83c66f-384c-98c8-94e5-543fb25fb8b6",
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_2iopeninfoopentalent": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "21",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "1",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "13930"
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "12",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  }
-]

+ 0 - 135
tests/Service/Dolibarr/fixtures/thirdparty.json

@@ -1,135 +0,0 @@
-[
-  {
-    "entity": "1",
-    "name": "Etablissement d'Enseignement Artistique",
-    "name_alias": null,
-    "address": "\n217, rue Raoul Follereau\n",
-    "zip": "74300",
-    "town": "CLUSES",
-    "status": "1",
-    "state_id": "81",
-    "state_code": "74",
-    "state": "Haute-Savoie",
-    "phone": "+33678403010",
-    "fax": null,
-    "email": null,
-    "skype": null,
-    "twitter": null,
-    "facebook": null,
-    "linkedin": null,
-    "url": null,
-    "barcode": null,
-    "idprof1": null,
-    "idprof2": null,
-    "idprof3": null,
-    "idprof4": null,
-    "idprof5": null,
-    "idprof6": null,
-    "tva_assuj": "1",
-    "tva_intra": null,
-    "localtax1_assuj": "0",
-    "localtax1_value": null,
-    "localtax2_assuj": "0",
-    "localtax2_value": null,
-    "managers": null,
-    "capital": null,
-    "typent_id": "0",
-    "typent_code": "TE_UNKNOWN",
-    "effectif": "",
-    "effectif_id": "0",
-    "forme_juridique_code": "812",
-    "forme_juridique": null,
-    "remise_percent": "",
-    "remise_supplier_percent": "0",
-    "mode_reglement_supplier_id": null,
-    "cond_reglement_supplier_id": null,
-    "transport_mode_supplier_id": null,
-    "fk_prospectlevel": null,
-    "date_modification": 1632728888,
-    "user_modification": null,
-    "date_creation": "",
-    "user_creation": null,
-    "specimen": null,
-    "client": "2",
-    "prospect": 0,
-    "fournisseur": "0",
-    "code_client": "001855",
-    "code_fournisseur": null,
-    "code_compta": "0001855",
-    "code_compta_client": null,
-    "code_compta_fournisseur": null,
-    "note_private": null,
-    "note_public": null,
-    "stcomm_id": "0",
-    "statut_commercial": "Never contacted",
-    "stcomm_picto": null,
-    "price_level": null,
-    "outstanding_limit": null,
-    "order_min_amount": null,
-    "supplier_order_min_amount": null,
-    "parent": null,
-    "default_lang": null,
-    "ref": "1726",
-    "ref_ext": null,
-    "import_key": "crm",
-    "webservices_url": null,
-    "webservices_key": null,
-    "logo": null,
-    "logo_small": null,
-    "logo_mini": null,
-    "logo_squarred": null,
-    "logo_squarred_small": null,
-    "logo_squarred_mini": null,
-    "accountancy_code_sell": null,
-    "accountancy_code_buy": null,
-    "array_options": {
-      "options_rfltr_model_id": null,
-      "options_sirene_status": null,
-      "options_b4d_spe_name": null,
-      "options_sirene_update_date": "",
-      "options_2iopen_domain": "7",
-      "options_2iopen_structure_type": "12",
-      "options_2iopen_nombre_eleves": null,
-      "options_2iopen_software_opentalent": null,
-      "options_2iopen_software_used": "4",
-      "options_2iopen_num_portable": null,
-      "options_2iopen_structure_type_cmf": null,
-      "options_2iopen_organization_id": "37306",
-      "options_2iopeninfoopentalent": ""
-    },
-    "fk_incoterms": null,
-    "location_incoterms": null,
-    "libelle_incoterms": null,
-    "fk_multicurrency": null,
-    "multicurrency_code": null,
-    "id": "1726",
-    "linkedObjectsIds": null,
-    "canvas": null,
-    "fk_project": null,
-    "contact": null,
-    "contact_id": null,
-    "user": null,
-    "origin": null,
-    "origin_id": null,
-    "statut": null,
-    "country": "France",
-    "country_id": "1",
-    "country_code": "FR",
-    "barcode_type": null,
-    "barcode_type_code": null,
-    "barcode_type_label": null,
-    "barcode_type_coder": null,
-    "mode_reglement_id": "2",
-    "cond_reglement_id": "1",
-    "transport_mode_id": null,
-    "cond_reglement": null,
-    "shipping_method_id": null,
-    "modelpdf": null,
-    "last_main_doc": null,
-    "fk_account": "0",
-    "lastname": null,
-    "firstname": null,
-    "civility_id": null,
-    "date_validation": null
-  }
-]