DolibarrSyncServiceTest.php 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  1. <?php
  2. namespace App\Tests\Service\Dolibarr;
  3. use App\Entity\Access\Access;
  4. use App\Entity\Access\FunctionType;
  5. use App\Entity\Core\AddressPostal;
  6. use App\Entity\Core\ContactPoint;
  7. use App\Entity\Network\Network;
  8. use App\Entity\Network\NetworkOrganization;
  9. use App\Entity\Organization\Organization;
  10. use App\Entity\Organization\OrganizationAddressPostal;
  11. use App\Entity\Organization\Settings;
  12. use App\Entity\Person\Person;
  13. use App\Enum\Access\FunctionEnum;
  14. use App\Enum\Access\RoleEnum;
  15. use App\Enum\Core\ContactPointTypeEnum;
  16. use App\Enum\Network\NetworkEnum;
  17. use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
  18. use App\Enum\Organization\SettingsProductEnum;
  19. use App\Repository\Access\AccessRepository;
  20. use App\Repository\Access\FunctionTypeRepository;
  21. use App\Repository\Organization\OrganizationRepository;
  22. use App\Service\Core\AddressPostalUtils;
  23. use App\Service\Dolibarr\DolibarrApiService;
  24. use App\Service\Dolibarr\DolibarrSyncService;
  25. use App\Service\Rest\Operation\CreateOperation;
  26. use App\Service\Rest\Operation\UpdateOperation;
  27. use App\Service\Utils\ArrayUtils;
  28. use Doctrine\Common\Collections\ArrayCollection;
  29. use libphonenumber\PhoneNumber;
  30. use libphonenumber\PhoneNumberUtil;
  31. use PHPUnit\Framework\MockObject\MockObject;
  32. use PHPUnit\Framework\TestCase;
  33. use Psr\Log\LoggerInterface;
  34. use Symfony\Contracts\HttpClient\ResponseInterface;
  35. use Symfony\Contracts\Translation\TranslatorInterface;
  36. class TestableDolibarrSyncService extends DolibarrSyncService {
  37. public function getDolibarrSocietiesIndex(): array { return parent::getDolibarrSocietiesIndex(); }
  38. public function findDolibarrContactFor(array $dolibarrContacts, Person $person): ?array { return parent::findDolibarrContactFor($dolibarrContacts, $person); }
  39. public function getActiveMembersIndex(): array { return parent::getActiveMembersIndex(); }
  40. public function sanitizeDolibarrData(?array $data): ?array { return parent::sanitizeDolibarrData($data); }
  41. public function getOrganizationPostalAddress(Organization $organization): ?AddressPostal { return parent::getOrganizationPostalAddress($organization); }
  42. public function getOrganizationPhone(Organization $organization): ?string { return parent::getOrganizationPhone($organization); }
  43. public function getOrganizationEmail(Organization $organization): ?string { return parent::getOrganizationEmail($organization); }
  44. public function getOrganizationNetworkId(Organization $organization): ?int { return parent::getOrganizationNetworkId($organization); }
  45. public function countWithMission(array $missions, array $members): int { return parent::countWithMission($missions, $members); }
  46. public function getPersonContact(Person $person): ?ContactPoint { return parent::getPersonContact($person); }
  47. public function formatContactPosition(array $missions, ?string $gender = 'X'): string { return parent::formatContactPosition($missions, $gender); }
  48. public function formatPhoneNumber(PhoneNumber $phoneNumber): string { return parent::formatPhoneNumber($phoneNumber); }
  49. }
  50. class DolibarrSyncServiceTest extends TestCase
  51. {
  52. private MockObject | OrganizationRepository $organizationRepository;
  53. private MockObject | AccessRepository $accessRepository;
  54. private MockObject | FunctionTypeRepository $functionTypeRepository;
  55. private MockObject | DolibarrApiService $dolibarrApiService;
  56. private MockObject | AddressPostalUtils $addressPostalUtils;
  57. private MockObject | ArrayUtils $arrayUtils;
  58. private MockObject | TranslatorInterface $translator;
  59. private MockObject | LoggerInterface $logger;
  60. public function setUp(): void {
  61. $this->organizationRepository = $this->getMockBuilder(OrganizationRepository::class)
  62. ->disableOriginalConstructor()
  63. ->getMock();
  64. $this->accessRepository = $this->getMockBuilder(AccessRepository::class)
  65. ->disableOriginalConstructor()
  66. ->getMock();
  67. $this->functionTypeRepository = $this->getMockBuilder(FunctionTypeRepository::class)
  68. ->disableOriginalConstructor()
  69. ->getMock();
  70. $this->dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
  71. ->disableOriginalConstructor()
  72. ->getMock();
  73. $this->addressPostalUtils = $this->getMockBuilder(AddressPostalUtils::class)
  74. ->disableOriginalConstructor()
  75. ->getMock();
  76. $this->arrayUtils = $this->getMockBuilder(ArrayUtils::class)
  77. ->disableOriginalConstructor()
  78. ->getMock();
  79. $this->translator = $this->getMockBuilder(TranslatorInterface::class)
  80. ->disableOriginalConstructor()
  81. ->getMock();
  82. $this->logger = $this->getMockBuilder(LoggerInterface::class)
  83. ->disableOriginalConstructor()
  84. ->getMock();
  85. $this->logger->method('info')->willReturnSelf();
  86. $this->logger->method('debug')->willReturnSelf();
  87. $this->logger->method('warning')->willReturnSelf();
  88. $this->logger->method('error')->willReturnSelf();
  89. }
  90. public function testScan(): void
  91. {
  92. $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
  93. ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
  94. $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
  95. ->setMethodsExcept(['scan'])
  96. ->getMock();
  97. // ----- Opentalent Organizations -----
  98. $orgId1 = 10;
  99. $organization1 = $this->getMockBuilder(Organization::class)->getMock();
  100. $organization1->method('getId')->willReturn($orgId1);
  101. $organization1->method('getName')->willReturn('Organization 10');
  102. $organizationData1 = [
  103. 'fullAddress' => '1 Rue Azerty',
  104. 'addressOwner' => 'Mr Keyboard',
  105. 'postalCode' => '01110',
  106. 'city' => 'ByteCity',
  107. 'email' => 'foo@bar.net',
  108. 'phone' => '0102030405',
  109. 'networkId' => NetworkEnum::CMF()->getValue(),
  110. 'product' => SettingsProductEnum::SCHOOL()->getValue()
  111. ];
  112. $orgId2 = 20;
  113. $organization2 = $this->getMockBuilder(Organization::class)->getMock();
  114. $organization2->method('getId')->willReturn($orgId2);
  115. $organization2->method('getName')->willReturn('Organization 20');
  116. $organizationData2 = [
  117. 'email' => null,
  118. 'phone' => null,
  119. 'networkId' => null,
  120. 'product' => SettingsProductEnum::ARTIST()->getValue()
  121. ];
  122. $orgId3 = 30;
  123. $organization3 = null; // This organization does not exist
  124. // Persons
  125. $accessId1 = 11; // Person 1
  126. $accessId2 = 21; // Person 2
  127. $accessId3 = 12; // Shall be ignored, because not an office member
  128. // access 4 does not exist
  129. $accessId5 = 13; // Invalid Person
  130. $personId1 = 100;
  131. $personData1 = [
  132. 'name' => 'Dupont',
  133. 'givenName' => 'Hercules',
  134. 'gender' => 'Mr',
  135. 'email' => 'an@email.net',
  136. 'phone' => '0102030405',
  137. 'mobilePhone' => '0607080910',
  138. ];
  139. $personId2 = 200;
  140. $personData2 = [
  141. 'name' => 'Simpson',
  142. 'givenName' => 'Lisa',
  143. 'gender' => null,
  144. ];
  145. $personId3 = 300; // Obsolete contact, does not exist anymore in the Opentalent DB
  146. $personId4 = 400; // Obsolete contact, does not exist anymore in the Opentalent DB
  147. $personId5 = 900; // Invalid contact with no firstname and no lastname
  148. $personData5 = [
  149. 'name' => '',
  150. 'givenName' => '',
  151. 'gender' => null,
  152. ];
  153. $activeMembers1 = [
  154. $accessId1 => [FunctionEnum::PRESIDENT()->getValue()],
  155. $accessId3 => [FunctionEnum::STUDENT()->getValue()],
  156. $accessId5 => [FunctionEnum::TREASURER()->getValue()]
  157. ];
  158. $activeMembers2 = [
  159. $accessId2 => [FunctionEnum::PRESIDENT()->getValue()]
  160. ];
  161. // ----- Opentalent : other vars -----
  162. $cmfId = 12097;
  163. $cmfDolibarrId = 12098;
  164. $ffecId = 91295;
  165. $ffecDolibarrId = 91296;
  166. // ----- Dolibarr societies -----
  167. // Existing society about to be updated
  168. $socId1 = 1;
  169. $dolibarrSociety1 = [
  170. 'id' => $socId1,
  171. 'name' => 'Organization 10',
  172. 'address' => '1 Rue Qwerty',
  173. 'zip' => '01024',
  174. 'town' => 'Ram',
  175. 'email' => 'some@email.com',
  176. 'phone' => null,
  177. 'status' => 0,
  178. 'parent' => '0',
  179. 'array_options' => [
  180. 'options_2iopeninfoopentalent' => '',
  181. 'options_2iopen_software_opentalent' => 'Opentalent Artist'
  182. ]
  183. ];
  184. // Existing society with no data
  185. $socId2 = 2;
  186. $dolibarrSociety2 = [
  187. 'id' => $socId2,
  188. "array_options" => []
  189. ];
  190. // This organization does not exist in the opentalent DB
  191. $socId3 = 3;
  192. $dolibarrSociety3 = null;
  193. // Dolibarr contacts
  194. $contactData1 = [
  195. 'id' => '1',
  196. 'civility_code' => '',
  197. 'lastname' => 'Dupond',
  198. 'firstname' => 'Bob',
  199. 'email' => 'abcd@mail.com',
  200. 'phone_pro' => '+33478570000',
  201. 'phone_mobile' => '+33682980000',
  202. 'poste' => 'Secrétaire',
  203. 'statut' => '1',
  204. 'array_options' => [
  205. 'options_2iopen_person_id' => ''
  206. ]
  207. ];
  208. // An obsolete contact that should be disabled
  209. $obsoleteContactData = [
  210. 'id' => '4',
  211. 'lastname' => 'Doe',
  212. 'firstname' => 'John',
  213. 'statut' => '1',
  214. 'array_options' => [
  215. 'options_2iopen_person_id' => $personId3
  216. ]
  217. ];
  218. // An obsolete contact that should is already disabled
  219. $obsoleteContactData2 = [
  220. 'id' => '5',
  221. 'lastname' => 'Foo',
  222. 'firstname' => 'John',
  223. 'statut' => '0',
  224. 'array_options' => [
  225. 'options_2iopen_person_id' => $personId4
  226. ]
  227. ];
  228. $dolibarrSocietyContacts1 = [$contactData1, $obsoleteContactData, $obsoleteContactData2];
  229. $dolibarrSocietyContacts2 = [];
  230. // ----- Setup Mocks -----
  231. $this->translator->method('trans')->willReturnMap([
  232. ['STUDENTS_COUNT', [], null, null, "Nombre d'élèves"],
  233. ['ADHERENTS_COUNT', [], null, null, "Nombre d'adhérents"],
  234. ['ADMIN_ACCESS_COUNT', [], null, null, "Nombre d'accès admin"],
  235. ['school', [], null, null, 'Opentalent School'],
  236. ['artist', [], null, null, 'Opentalent Artist'],
  237. ['Mr', [], null, null, 'MR'],
  238. ]);
  239. // Get societies
  240. $this->organizationRepository->method('find')
  241. ->willReturnMap([
  242. [$orgId1, null, null, $organization1],
  243. [$orgId2, null, null, $organization2],
  244. [$orgId3, null, null, $organization3]
  245. ]);
  246. $dolibarrSyncService
  247. ->expects(self::once())
  248. ->method('getDolibarrSocietiesIndex')
  249. ->willReturn([
  250. $orgId1 => $dolibarrSociety1,
  251. $orgId2 => $dolibarrSociety2,
  252. $orgId3 => $dolibarrSociety3,
  253. ]);
  254. $dolibarrSyncService
  255. ->expects(self::once())
  256. ->method('getActiveMembersIndex')
  257. ->willReturn([
  258. $orgId1 => $activeMembers1,
  259. $orgId2 => $activeMembers2,
  260. $orgId3 => []
  261. ]);
  262. // Function types
  263. $functionType1 = $this->getMockBuilder(FunctionType::class)->getMock();
  264. $functionType1->method('getMission')->willReturn(FunctionEnum::DIRECTOR()->getValue());
  265. $functionType2 = $this->getMockBuilder(FunctionType::class)->getMock();
  266. $functionType2->method('getMission')->willReturn(FunctionEnum::PRESIDENT()->getValue());
  267. $this->functionTypeRepository
  268. ->expects($this->once())
  269. ->method('findBy')
  270. ->with(['roleByDefault' => RoleEnum::ROLE_ADMIN()->getValue()])
  271. ->willReturn([$functionType1, $functionType2]);
  272. // Get CMF and FFEC ids
  273. $this->dolibarrApiService->method('getSociety')->willReturnMap(
  274. [
  275. [$cmfId, ['id' => $cmfDolibarrId]],
  276. [$ffecId, ['id' => $ffecDolibarrId]]
  277. ]
  278. );
  279. // -- Loop over organizations --
  280. $dolibarrSyncService
  281. ->expects(self::exactly(5)) // 3 organizations and 2 contacts
  282. ->method('sanitizeDolibarrData')
  283. ->willReturnCallback(function ($args) { return $args; });
  284. $addressPostal = $this->getMockBuilder(AddressPostal::class)->getMock();
  285. $dolibarrSyncService
  286. ->expects(self::exactly(2))
  287. ->method('getOrganizationPostalAddress')
  288. ->willReturnMap([
  289. [$organization1, $addressPostal],
  290. [$organization2, null],
  291. ]);
  292. $this->addressPostalUtils
  293. ->method('getFullStreetAddress')
  294. ->with($addressPostal)
  295. ->willReturn($organizationData1['fullAddress']);
  296. $addressPostal->method('getAddressOwner')->willReturn($organizationData1['addressOwner']);
  297. $addressPostal->method('getPostalCode')->willReturn($organizationData1['postalCode']);
  298. $addressPostal->method('getAddressCity')->willReturn($organizationData1['city']);
  299. $dolibarrSyncService
  300. ->expects(self::exactly(2))
  301. ->method('getOrganizationEmail')
  302. ->willReturnMap([
  303. [$organization1, $organizationData1['email']],
  304. [$organization2, $organizationData2['email']],
  305. ]);
  306. $dolibarrSyncService
  307. ->expects(self::exactly(2))
  308. ->method('getOrganizationPhone')
  309. ->willReturnMap([
  310. [$organization1, $organizationData1['phone']],
  311. [$organization2, $organizationData2['phone']],
  312. ]);
  313. $dolibarrSyncService
  314. ->expects(self::exactly(2))
  315. ->method('getOrganizationNetworkId')
  316. ->willReturnMap([
  317. [$organization1, $organizationData1['networkId']],
  318. [$organization2, $organizationData2['networkId']],
  319. ]);
  320. $settings1 = $this->getMockBuilder(Settings::class)->getMock();
  321. $settings1->method('getProduct')->willReturn($organizationData1['product']);
  322. $organization1->method('getSettings')->willReturn($settings1);
  323. $settings2 = $this->getMockBuilder(Settings::class)->getMock();
  324. $settings1->method('getProduct')->willReturn($organizationData2['product']);
  325. $organization2->method('getSettings')->willReturn($settings2);
  326. $dolibarrSyncService->method('countWithMission')->willReturnMap([
  327. [[FunctionEnum::STUDENT()->getValue()], $activeMembers1, 1],
  328. [[FunctionEnum::ADHERENT()->getValue()], $activeMembers1, 2],
  329. [[FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::PRESIDENT()->getValue()], $activeMembers1, 1],
  330. [[FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::PRESIDENT()->getValue()], $activeMembers2, 2]
  331. ]);
  332. $this->arrayUtils
  333. ->expects(self::exactly(3))
  334. ->method('getChanges')
  335. ->willReturnCallback(
  336. function (array $initialArray, array $newArray, bool $recursive = false, ?callable $callback = null) {
  337. if (in_array('name', $newArray, true)) {
  338. // Organization 1 name is already defined and has not been changed
  339. unset($newArray['name']);
  340. }
  341. if (in_array('statut', $newArray, true)) {
  342. // Contact 1 statut is already defined and has not been changed
  343. unset($newArray['statut']);
  344. }
  345. return $newArray;
  346. }
  347. );
  348. $this->dolibarrApiService->method('getContacts')->willReturnMap([
  349. [$socId1, $dolibarrSocietyContacts1],
  350. [$socId2, $dolibarrSocietyContacts2]
  351. ]);
  352. // Loop over contacts
  353. // NB: Student will be skipped since it has no office role
  354. $person1 = $this->getMockBuilder(Person::class)->getMock();
  355. $person1->method('getId')->willReturn($personId1);
  356. $person1->method('getName')->willReturn($personData1['name']);
  357. $person1->method('getGivenName')->willReturn($personData1['givenName']);
  358. $person1->method('getGender')->willReturn($personData1['gender']);
  359. $person2 = $this->getMockBuilder(Person::class)->getMock();
  360. $person2->method('getId')->willReturn($personId2);
  361. $person2->method('getName')->willReturn($personData2['name']);
  362. $person2->method('getGivenName')->willReturn($personData2['givenName']);
  363. $person2->method('getGender')->willReturn($personData2['gender']);
  364. // An invalid person that should be ignored
  365. $person5 = $this->getMockBuilder(Person::class)->getMock();
  366. $person5->method('getId')->willReturn($personId5);
  367. $person5->method('getName')->willReturn($personData5['name']);
  368. $person5->method('getGivenName')->willReturn($personData5['givenName']);
  369. $access1 = $this->getMockBuilder(Access::class)->getMock();
  370. $access1->method('getPerson')->willReturn($person1);
  371. $access2 = $this->getMockBuilder(Access::class)->getMock();
  372. $access2->method('getPerson')->willReturn($person2);
  373. $access5 = $this->getMockBuilder(Access::class)->getMock();
  374. $access5->method('getPerson')->willReturn($person5);
  375. $this->accessRepository->method('find')->willReturnMap([
  376. [$accessId1, null, null, $access1],
  377. [$accessId2, null, null, $access2],
  378. [$accessId5, null, null, $access5],
  379. ]);
  380. $dolibarrSyncService->method('findDolibarrContactFor')->willReturnMap([
  381. [$dolibarrSocietyContacts1, $person1, $contactData1],
  382. [$dolibarrSocietyContacts2, $person2, null]
  383. ]);
  384. $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
  385. $contactPoint1->method('getEmail')->willReturn($personData1['email']);
  386. $phone = $this->getMockBuilder(PhoneNumber::class)->getMock();
  387. $mobilePhone = $this->getMockBuilder(PhoneNumber::class)->getMock();
  388. $dolibarrSyncService->method('formatPhoneNumber')->willReturnMap([
  389. [$phone, $personData1['phone']],
  390. [$mobilePhone, $personData1['mobilePhone']],
  391. ]);
  392. $contactPoint1->method('getTelphone')->willReturn($phone);
  393. $contactPoint1->method('getMobilPhone')->willReturn($mobilePhone);
  394. $dolibarrSyncService->method('getPersonContact')->willReturnMap([
  395. [$person1, $contactPoint1],
  396. [$person2, null],
  397. ]);
  398. $dolibarrSyncService->method('formatContactPosition')->willReturnMap([
  399. [[FunctionEnum::PRESIDENT()->getValue()], 'Mr', 'Président'],
  400. [[FunctionEnum::PRESIDENT()->getValue()], null, 'Président(e)'],
  401. ]);
  402. // Expected logged error messages
  403. $this->logger->expects(self::exactly(2))->method('error')->withConsecutive(
  404. ["Person 900 miss a lastname and/or a firstname, ignored."],
  405. ["Organization 30 not found in the Opentalent DB"],
  406. );
  407. // Expected progression callback triggers
  408. $progressionCallbackExpectedCalls = [[1, 3], [2, 3], [3, 3]];
  409. $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
  410. [$expectedI, $expectedTotal] = array_shift($progressionCallbackExpectedCalls);
  411. if ($i !== $expectedI || $total !== $expectedTotal) {
  412. throw new \AssertionError(
  413. 'Progression callback error, expected parameters are (' . $expectedI . ',' . $expectedTotal . ')' .
  414. ', got (' . $i . ', ' . $total . ')'
  415. );
  416. }
  417. };
  418. $operations = $dolibarrSyncService->scan($progressionCallback);
  419. $this->assertCount(5, $operations);
  420. $this->assertEqualsCanonicalizing(
  421. [
  422. '[PUT thirdparties/1]',
  423. "address : `1 Rue Qwerty` => `Mr Keyboard\n1 Rue Azerty`",
  424. 'zip : `01024` => `01110`',
  425. 'town : `Ram` => `ByteCity`',
  426. 'email : `some@email.com` => `foo@bar.net`',
  427. 'phone : `` => `0102030405`',
  428. 'parent : `0` => `12098`',
  429. "array_options.options_2iopen_software_opentalent : `Opentalent Artist` => `Opentalent School`",
  430. "array_options.options_2iopeninfoopentalent : `` => `Nombre d'élèves : 1\nNombre d'adhérents : 2\nNombre d'accès admin : 1`",
  431. 'status : `0` => `1`'
  432. ],
  433. $operations[0]->getChangeLog()
  434. );
  435. $this->assertEqualsCanonicalizing(
  436. [
  437. '[PUT contacts/1]',
  438. 'civility_code : `` => `MR`',
  439. 'lastname : `Dupond` => `Dupont`',
  440. 'firstname : `Bob` => `Hercules`',
  441. 'email : `abcd@mail.com` => `an@email.net`',
  442. 'phone_pro : `+33478570000` => `0102030405`',
  443. 'phone_mobile : `+33682980000` => `0607080910`',
  444. 'poste : `Secrétaire` => `Président`',
  445. 'array_options.options_2iopen_person_id : `` => `100`'
  446. ],
  447. $operations[1]->getChangeLog()
  448. );
  449. $this->assertEqualsCanonicalizing(
  450. ['[PUT contacts/4]', 'statut : `1` => `0`'],
  451. $operations[2]->getChangeLog()
  452. );
  453. $this->assertEqualsCanonicalizing(
  454. [
  455. '[PUT thirdparties/2]',
  456. 'address : (new) => ``',
  457. 'email : (new) => ``',
  458. 'name : (new) => `Organization 20`',
  459. 'parent : (new) => ``',
  460. 'phone : (new) => ``',
  461. 'status : (new) => `1`',
  462. 'town : (new) => ``',
  463. 'zip : (new) => ``',
  464. 'array_options.options_2iopeninfoopentalent : (new) => `Nombre d\'accès admin : 2`',
  465. ],
  466. $operations[3]->getChangeLog()
  467. );
  468. $this->assertEqualsCanonicalizing(
  469. [
  470. '[POST contacts]',
  471. 'civility_code : (new) => ``',
  472. 'lastname : (new) => `Simpson`',
  473. 'firstname : (new) => `Lisa`',
  474. 'email : (new) => ``',
  475. 'phone_pro : (new) => ``',
  476. 'phone_mobile : (new) => ``',
  477. 'poste : (new) => `Président(e)`',
  478. 'statut : (new) => `1`',
  479. 'array_options.options_2iopen_person_id : (new) => `200`',
  480. 'socid : (new) => `2`'
  481. ],
  482. $operations[4]->getChangeLog()
  483. );
  484. $this->assertCount(0, $progressionCallbackExpectedCalls);
  485. }
  486. public function testExecuteError()
  487. {
  488. $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
  489. ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
  490. $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
  491. ->setMethodsExcept(['execute'])
  492. ->getMock();
  493. $operation = new CreateOperation('operation 1', 'thirdparty', ['data' => 1]);
  494. $this->assertEquals($operation->getStatus(), $operation::STATUS_READY);
  495. $responseError = $this->getMockBuilder(ResponseInterface::class)->getMock();
  496. $responseError->method('getStatusCode')->willReturn(500);
  497. $this->dolibarrApiService->method('request')->willReturn($responseError);
  498. // POST operation will returned a server error
  499. $operation = $dolibarrSyncService->execute([$operation])[0];
  500. $this->assertEquals($operation::STATUS_ERROR, $operation->getStatus());
  501. }
  502. public function testExecuteInvalid()
  503. {
  504. $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
  505. ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
  506. $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
  507. ->setMethodsExcept(['execute'])
  508. ->getMock();
  509. $operation = new UpdateOperation('operation 1', 'thirdparty', 1, ['data' => 1]);
  510. $responseInvalid = $this->getMockBuilder(ResponseInterface::class)->getMock();
  511. $responseInvalid->method('getStatusCode')->willReturn(200);
  512. $responseInvalid->method('toArray')->willReturn(['data' => 0]);
  513. $this->dolibarrApiService->method('request')->willReturn($responseInvalid);
  514. // POST operation will return a different content that the one which were posted, this should log a warning
  515. $this->logger->expects($this->once())->method('warning');
  516. $operation = $dolibarrSyncService->execute([$operation])[0];
  517. $this->assertEquals($operation::STATUS_DONE, $operation->getStatus());
  518. }
  519. public function testExecuteOk() {
  520. $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
  521. ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
  522. $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
  523. ->setMethodsExcept(['execute'])
  524. ->getMock();
  525. $progressionCallbackExpectedCalls = [[1, 1]];
  526. $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
  527. [$expectedI, $expectedTotal] = array_shift($progressionCallbackExpectedCalls);
  528. if ($i !== $expectedI || $total !== $expectedTotal) {
  529. throw new \AssertionError(
  530. 'Progression callback error, expected parameters are (' . $expectedI . ',' . $expectedTotal . ')' .
  531. ', got (' . $i . ', ' . $total . ')'
  532. );
  533. }
  534. };
  535. $operation = new CreateOperation('operation 1', 'thirdparty', ['data' => 1]);
  536. $responseOk = $this->getMockBuilder(ResponseInterface::class)->getMock();
  537. $responseOk->method('getStatusCode')->willReturn(200);
  538. $responseOk->method('toArray')->willReturn(['data' => 1]);
  539. $this->dolibarrApiService->method('request')->willReturn($responseOk);
  540. $operation = $dolibarrSyncService->execute([$operation])[0];
  541. $this->assertEquals($operation::STATUS_DONE, $operation->getStatus());
  542. }
  543. public function testGetDolibarrSocietiesIndex() {
  544. $this->dolibarrApiService
  545. ->expects($this->once())
  546. ->method('getAllClients')
  547. ->willReturn(
  548. $this->getJsonContentFromFixture('thirdparties.json')
  549. );
  550. $syncService = $this->newDolibarrSyncService();
  551. $index = $syncService->getDolibarrSocietiesIndex();
  552. $this->assertEquals("13930", $index[13930]['array_options']['options_2iopen_organization_id']);
  553. }
  554. public function testFindDolibarrContactFor() {
  555. $contacts = $this->getJsonContentFromFixture('contacts.json');
  556. // Find by id
  557. $person1 = $this->getMockBuilder(Person::class)->getMock();
  558. $person1->method('getId')->willReturn(108939);
  559. $contact1 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person1);
  560. $this->assertEquals("5868", $contact1['id']);
  561. // Find by full name (contact already has another person id, it should not be returned)
  562. $person2 = $this->getMockBuilder(Person::class)->getMock();
  563. $person2->method('getId')->willReturn(-1);
  564. $person2->method('getName')->willReturn('dupont');
  565. $person2->method('getGivenName')->willReturn('Valerie');
  566. $contact2 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person2);
  567. $this->assertEquals(null, $contact2);
  568. // Find by full name (contact has no person id, it should be returned)
  569. $person3 = $this->getMockBuilder(Person::class)->getMock();
  570. $person3->method('getId')->willReturn(-1);
  571. $person3->method('getName')->willReturn('ZORRO');
  572. $person3->method('getGivenName')->willReturn('Fabrice');
  573. $contact3 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person3);
  574. $this->assertEquals("5872", $contact3['id']);
  575. // Do not find
  576. $person4 = $this->getMockBuilder(Person::class)->getMock();
  577. $person4->method('getId')->willReturn(-1);
  578. $contact4 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person4);
  579. $this->assertEquals(null, $contact4);
  580. }
  581. public function testActiveMembersIndex() {
  582. $this->accessRepository
  583. ->expects($this->once())
  584. ->method('getAllActiveMembersAndMissions')
  585. ->willReturn(
  586. [
  587. ['id' => 123, 'organization_id' => 1, 'mission' => FunctionEnum::PRESIDENT()->getValue()],
  588. ['id' => 123, 'organization_id' => 1, 'mission' => FunctionEnum::TEACHER()->getValue()],
  589. ['id' => 124, 'organization_id' => 1, 'mission' => FunctionEnum::ADMINISTRATIVE_STAFF()->getValue()],
  590. ['id' => 125, 'organization_id' => 2, 'mission' => FunctionEnum::ADHERENT()->getValue()],
  591. ]
  592. );
  593. $syncService = $this->newDolibarrSyncService();
  594. $this->assertEquals(
  595. [
  596. 1 => [
  597. 123 => [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::TEACHER()->getValue()],
  598. 124 => [FunctionEnum::ADMINISTRATIVE_STAFF()->getValue()]
  599. ],
  600. 2 => [
  601. 125 => [FunctionEnum::ADHERENT()->getValue()]
  602. ]
  603. ],
  604. $syncService->getActiveMembersIndex()
  605. );
  606. }
  607. public function testSanitizeDolibarrData() {
  608. $this->assertEquals(null, TestableDolibarrSyncService::sanitizeDolibarrData(null));
  609. $this->assertEquals(
  610. ['a' => 'A', 'b' => null, 'c' => ['d' => 'D', 'e' => null]],
  611. TestableDolibarrSyncService::sanitizeDolibarrData(
  612. ['a' => 'A', 'b' => '', 'c' => ['d' => 'D', 'e' => '']]
  613. )
  614. );
  615. }
  616. public function testGetOrganizationPostalAddress() {
  617. $organization = $this->getMockBuilder(Organization::class)->getMock();
  618. $organizationAddressPostal1 = $this->getMockBuilder(OrganizationAddressPostal::class)->getMock();
  619. $organizationAddressPostal2 = $this->getMockBuilder(OrganizationAddressPostal::class)->getMock();
  620. $organizationAddressPostal3 = $this->getMockBuilder(OrganizationAddressPostal::class)->getMock();
  621. $addressPostal = $this->getMockBuilder(AddressPostal::class)->getMock();
  622. $organizationAddressPostal1->method('getType')->willReturn(AddressPostalOrganizationTypeEnum::ADDRESS_PRACTICE()->getValue());
  623. $organizationAddressPostal2->method('getType')->willReturn(AddressPostalOrganizationTypeEnum::ADDRESS_BILL()->getValue());
  624. $organizationAddressPostal3->method('getType')->willReturn(AddressPostalOrganizationTypeEnum::ADDRESS_OTHER()->getValue());
  625. $organizationAddressPostal2->method('getAddressPostal')->willReturn($addressPostal);
  626. $organization->expects($this->once())
  627. ->method('getOrganizationAddressPostals')
  628. ->willReturn(
  629. new ArrayCollection([$organizationAddressPostal1, $organizationAddressPostal2, $organizationAddressPostal3])
  630. );
  631. $syncService = $this->newDolibarrSyncService($organization);
  632. $this->assertEquals(
  633. $addressPostal,
  634. $syncService->getOrganizationPostalAddress($organization)
  635. );
  636. $organization2 = $this->getMockBuilder(Organization::class)->getMock();
  637. $organization2->expects($this->once())
  638. ->method('getOrganizationAddressPostals')
  639. ->willReturn(new ArrayCollection([]));
  640. $this->assertEquals(
  641. null,
  642. $syncService->getOrganizationPostalAddress($organization2)
  643. );
  644. }
  645. public function testGetOrganizationPhoneWithExistingPhone()
  646. {
  647. $organization = $this->getMockBuilder(Organization::class)->getMock();
  648. $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
  649. $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
  650. $contactPoint3 = $this->getMockBuilder(ContactPoint::class)->getMock();
  651. $contactPoint1->method('getContactType')->willReturn(ContactPointTypeEnum::OTHER()->getValue());
  652. $contactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::BILL()->getValue());
  653. $contactPoint3->method('getContactType')->willReturn(ContactPointTypeEnum::PRINCIPAL()->getValue());
  654. $phoneUtil = PhoneNumberUtil::getInstance();
  655. $phoneNumber = $phoneUtil->parse('0161626365', "FR");
  656. $contactPoint2->method('getTelphone')->willReturn($phoneNumber);
  657. $organization
  658. ->expects($this->once())
  659. ->method('getContactPoints')
  660. ->willReturn(
  661. new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
  662. );
  663. $syncService = $this->newDolibarrSyncService();
  664. $this->assertEquals(
  665. '+33161626365',
  666. $syncService->getOrganizationPhone($organization)
  667. );
  668. }
  669. public function testGetOrganizationPhoneWithMobilePhone() {
  670. $organization = $this->getMockBuilder(Organization::class)->getMock();
  671. $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
  672. $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
  673. $contactPoint3 = $this->getMockBuilder(ContactPoint::class)->getMock();
  674. $contactPoint1->method('getContactType')->willReturn(ContactPointTypeEnum::OTHER()->getValue());
  675. $contactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::BILL()->getValue());
  676. $contactPoint3->method('getContactType')->willReturn(ContactPointTypeEnum::PRINCIPAL()->getValue());
  677. $contactPoint2->expects($this->once())->method('getTelphone')->willReturn(null);
  678. $phoneUtil = PhoneNumberUtil::getInstance();
  679. $phoneNumber = $phoneUtil->parse('0661626365', "FR");
  680. $contactPoint2->method('getMobilPhone')->willReturn($phoneNumber);
  681. $organization
  682. ->expects($this->once())
  683. ->method('getContactPoints')
  684. ->willReturn(
  685. new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
  686. );
  687. $syncService = $this->newDolibarrSyncService();
  688. $this->assertEquals(
  689. '+33661626365',
  690. $syncService->getOrganizationPhone($organization)
  691. );
  692. }
  693. public function testGetOrganizationPhoneWithNoPhone() {
  694. $organization = $this->getMockBuilder(Organization::class)->getMock();
  695. $organization
  696. ->expects($this->once())
  697. ->method('getContactPoints')
  698. ->willReturn(new ArrayCollection([]));
  699. $syncService = $this->newDolibarrSyncService();
  700. $this->assertEquals(
  701. null,
  702. $syncService->getOrganizationPhone($organization)
  703. );
  704. }
  705. public function testGetOrganizationEmailWithExistingEmail() {
  706. $organization = $this->getMockBuilder(Organization::class)->getMock();
  707. $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
  708. $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
  709. $contactPoint3 = $this->getMockBuilder(ContactPoint::class)->getMock();
  710. $contactPoint1->method('getContactType')->willReturn(ContactPointTypeEnum::OTHER()->getValue());
  711. $contactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::BILL()->getValue());
  712. $contactPoint3->method('getContactType')->willReturn(ContactPointTypeEnum::PRINCIPAL()->getValue());
  713. $contactPoint2->method('getEmail')->willReturn('email@email.com');
  714. $organization
  715. ->expects($this->once())
  716. ->method('getContactPoints')
  717. ->willReturn(
  718. new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
  719. );
  720. $syncService = $this->newDolibarrSyncService();
  721. $this->assertEquals(
  722. 'email@email.com',
  723. $syncService->getOrganizationEmail($organization)
  724. );
  725. }
  726. public function testGetOrganizationEmailWithNoEmail() {
  727. $organization = $this->getMockBuilder(Organization::class)->getMock();
  728. $organization
  729. ->expects($this->once())
  730. ->method('getContactPoints')
  731. ->willReturn(new ArrayCollection([]));
  732. $syncService = $this->newDolibarrSyncService();
  733. $this->assertEquals(
  734. null,
  735. $syncService->getOrganizationEmail($organization)
  736. );
  737. }
  738. public function testGetOrganizationNetworkId() {
  739. $organization = $this->getMockBuilder(Organization::class)->getMock();
  740. $network = $this->getMockBuilder(Network::class)->getMock();
  741. $network->method('getId')->willReturn(3);
  742. $networkOrganization = $this->getMockBuilder(NetworkOrganization::class)->getMock();
  743. $networkOrganization->method('getNetwork')->willReturn($network);
  744. $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization]));
  745. $syncService = $this->newDolibarrSyncService();
  746. $this->assertEquals(
  747. 3,
  748. $syncService->getOrganizationNetworkId($organization)
  749. );
  750. }
  751. public function testGetOrganizationNetworkIdWithMultipleResult() {
  752. $organization = $this->getMockBuilder(Organization::class)->getMock();
  753. $network1 = $this->getMockBuilder(Network::class)->getMock();
  754. $network1->method('getId')->willReturn(3);
  755. $networkOrganization1 = $this->getMockBuilder(NetworkOrganization::class)->getMock();
  756. $networkOrganization1->method('getNetwork')->willReturn($network1);
  757. $networkOrganization1->method('getEndDate')->willReturn(new \DateTime('2000-01-01'));
  758. $network2 = $this->getMockBuilder(Network::class)->getMock();
  759. $network2->method('getId')->willReturn(4);
  760. $networkOrganization2 = $this->getMockBuilder(NetworkOrganization::class)->getMock();
  761. $networkOrganization2->method('getNetwork')->willReturn($network2);
  762. $networkOrganization2->method('getEndDate')->willReturn(null);
  763. $organization->method('getNetworkOrganizations')->willReturn(
  764. new ArrayCollection([$networkOrganization1, $networkOrganization2])
  765. );
  766. $syncService = $this->newDolibarrSyncService();
  767. $this->assertEquals(
  768. 4,
  769. $syncService->getOrganizationNetworkId($organization)
  770. );
  771. }
  772. public function testGetOrganizationNetworkIdWithNoResult() {
  773. $organization = $this->getMockBuilder(Organization::class)->getMock();
  774. $network = $this->getMockBuilder(Network::class)->getMock();
  775. $network->method('getId')->willReturn(3);
  776. $networkOrganization = $this->getMockBuilder(NetworkOrganization::class)->getMock();
  777. $networkOrganization->method('getNetwork')->willReturn($network);
  778. $networkOrganization->method('getEndDate')->willReturn(new \DateTime('2000-01-01'));
  779. $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization]));
  780. $syncService = $this->newDolibarrSyncService();
  781. $this->assertEquals(
  782. null,
  783. $syncService->getOrganizationNetworkId($organization)
  784. );
  785. }
  786. public function testCountWithMission() {
  787. $members = [
  788. 123 => [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::TEACHER()->getValue()],
  789. 124 => [FunctionEnum::TEACHER()->getValue()],
  790. 125 => [FunctionEnum::STUDENT()->getValue()],
  791. 126 => [FunctionEnum::TREASURER()->getValue()],
  792. ];
  793. $this->assertEquals(
  794. 2,
  795. TestableDolibarrSyncService::countWithMission([FunctionEnum::TEACHER()->getValue()], $members)
  796. );
  797. $this->assertEquals(
  798. 3,
  799. TestableDolibarrSyncService::countWithMission(
  800. [FunctionEnum::TEACHER()->getValue(), FunctionEnum::TREASURER()->getValue()],
  801. $members
  802. )
  803. );
  804. $this->assertEquals(
  805. 1,
  806. TestableDolibarrSyncService::countWithMission([FunctionEnum::STUDENT()->getValue()], $members)
  807. );
  808. $this->assertEquals(
  809. 0,
  810. TestableDolibarrSyncService::countWithMission([FunctionEnum::ARCHIVIST()->getValue()], $members)
  811. );
  812. }
  813. public function testGetPersonContact() {
  814. $person = $this->getMockBuilder(Person::class)->getMock();
  815. $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
  816. $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
  817. $contactPoint1->method('getContactType')->willReturn(ContactPointTypeEnum::OTHER()->getValue());
  818. $contactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::PRINCIPAL()->getValue());
  819. $person->expects($this->once())->method('getContactPoints')->willReturn(new ArrayCollection([$contactPoint1, $contactPoint2]));
  820. $syncService = $this->newDolibarrSyncService();
  821. $this->assertEquals(
  822. $contactPoint2,
  823. $syncService->getPersonContact($person)
  824. );
  825. $person2 = $this->getMockBuilder(Person::class)->getMock();
  826. $person2->expects($this->once())->method('getContactPoints')->willReturn(new ArrayCollection([]));
  827. $this->assertEquals(null, $syncService->getPersonContact($person2));
  828. }
  829. public function testFormatContactPosition() {
  830. $this->translator->method('trans')->willReturnMap(
  831. [
  832. [FunctionEnum::PRESIDENT()->getValue(), ['gender' => 'X'], null, null, 'Président(e)'],
  833. [FunctionEnum::PRESIDENT()->getValue(), ['gender' => 'M'], null, null, 'Président'],
  834. [FunctionEnum::PRESIDENT()->getValue(), ['gender' => 'F'], null, null, 'Présidente'],
  835. [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'X'], null, null, 'Directeur(ice)'],
  836. [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'M'], null, null, 'Directeur'],
  837. [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'F'], null, null, 'Directrice'],
  838. ]
  839. );
  840. $syncService = $this->newDolibarrSyncService();
  841. $this->assertEquals(
  842. 'Président(e)',
  843. $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()])
  844. );
  845. $this->assertEquals(
  846. 'Président',
  847. $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISTER')
  848. );
  849. $this->assertEquals(
  850. 'Présidente',
  851. $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISS')
  852. );
  853. $this->assertEquals(
  854. 'Présidente, Directrice',
  855. $syncService->formatContactPosition(
  856. [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::DIRECTOR()->getValue()],
  857. 'MISS'
  858. )
  859. );
  860. $this->assertEquals(
  861. 'Président, Directeur',
  862. $syncService->formatContactPosition(
  863. [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::ADHERENT()->getValue()],
  864. 'MISTER'
  865. )
  866. );
  867. }
  868. public function testFormatPhoneNumber() {
  869. $phoneUtil = PhoneNumberUtil::getInstance();
  870. $phoneNumber = $phoneUtil->parse('01 02 03 04 05', "FR");
  871. $this->assertEquals(
  872. '+33102030405',
  873. TestableDolibarrSyncService::formatPhoneNumber($phoneNumber)
  874. );
  875. }
  876. }