|
|
@@ -1,25 +1,33 @@
|
|
|
<?php
|
|
|
+declare(strict_types=1);
|
|
|
|
|
|
namespace App\Service\Dolibarr\DolibarrSync;
|
|
|
|
|
|
use App\Entity\Core\AddressPostal;
|
|
|
use App\Entity\Organization\Organization;
|
|
|
+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\OrganizationIdsEnum;
|
|
|
use App\Enum\Organization\SettingsProductEnum;
|
|
|
use App\Repository\Access\AccessRepository;
|
|
|
+use App\Repository\Access\FunctionTypeRepository;
|
|
|
use App\Repository\Core\ContactPointRepository;
|
|
|
use App\Repository\Organization\OrganizationRepository;
|
|
|
use App\Service\Core\AddressPostalUtils;
|
|
|
use App\Service\Dolibarr\DolibarrApiService;
|
|
|
+use App\Service\Dolibarr\DolibarrSync\SyncOperation\DolibarrCreateOperation;
|
|
|
+use App\Service\Dolibarr\DolibarrSync\SyncOperation\DolibarrSyncOperation;
|
|
|
+use App\Service\Dolibarr\DolibarrSync\SyncOperation\DolibarrUpdateOperation;
|
|
|
use Exception;
|
|
|
use HttpException;
|
|
|
use libphonenumber\PhoneNumber;
|
|
|
use libphonenumber\PhoneNumberFormat;
|
|
|
use libphonenumber\PhoneNumberUtil;
|
|
|
+use Symfony\Contracts\Translation\TranslatorInterface;
|
|
|
|
|
|
/**
|
|
|
* Push the data from the Opentalent DB into the Dolibarr DB, trough both applications
|
|
|
@@ -29,12 +37,13 @@ use libphonenumber\PhoneNumberUtil;
|
|
|
*/
|
|
|
class DolibarrSyncService
|
|
|
{
|
|
|
-
|
|
|
public function __construct(
|
|
|
private OrganizationRepository $organizationRepository,
|
|
|
private AccessRepository $accessRepository,
|
|
|
private ContactPointRepository $contactPointRepository,
|
|
|
+ private FunctionTypeRepository $functionTypeRepository,
|
|
|
private DolibarrApiService $dolibarrApiService,
|
|
|
+ private TranslatorInterface $translator
|
|
|
) {}
|
|
|
|
|
|
/**
|
|
|
@@ -50,12 +59,20 @@ class DolibarrSyncService
|
|
|
*/
|
|
|
public function scan(): array {
|
|
|
|
|
|
+ var_dump($this->translator->trans('HOUR_PRESIDENT', ['gender' => 'F'] )); die;
|
|
|
+
|
|
|
// Index the dolibarr clients by organization ids
|
|
|
$dolibarrClientsIndex = $this->getDolibarrSocietiesIndex();
|
|
|
|
|
|
// Get all active accesses
|
|
|
$membersIndex = $this->getActiveMembersIndex();
|
|
|
|
|
|
+ // Get all the missions with an admin default role
|
|
|
+ $adminMissions = [];
|
|
|
+ foreach ($this->functionTypeRepository->findBy(['roleByDefault' => RoleEnum::ROLE_ADMIN()]) as $functionType) {
|
|
|
+ $adminMissions[] = $functionType->getMission();
|
|
|
+ }
|
|
|
+
|
|
|
// Loop over the Opentalent organizations, and fill up the operations list
|
|
|
$operations = [];
|
|
|
foreach ($this->organizationRepository->findAll() as $organization) {
|
|
|
@@ -86,12 +103,15 @@ class DolibarrSyncService
|
|
|
$mainAddress = $this->getOrganizationPostalAddress($organization);
|
|
|
|
|
|
if ($mainAddress !== null) {
|
|
|
- $streetAddress = AddressPostalUtils::getFullStreetAddress($mainAddress);
|
|
|
+ $streetAddress = AddressPostalUtils::getFullStreetAddress($mainAddress, '\n');
|
|
|
if (trim($mainAddress->getAddressOwner() ?? '') !== '') {
|
|
|
- $streetAddress = 'Chez ' . $mainAddress->getAddressOwner() . '\n' . $streetAddress;
|
|
|
+ $streetAddress = $mainAddress->getAddressOwner() . '\n' . $streetAddress;
|
|
|
}
|
|
|
|
|
|
- if ($streetAddress !== $dolibarrSociety['address']) {
|
|
|
+ if (
|
|
|
+ preg_replace('/\s/', ' ', $streetAddress) !==
|
|
|
+ preg_replace('/\s/', ' ', $dolibarrSociety['address'])
|
|
|
+ ) {
|
|
|
$putSocietyData['address'] = $streetAddress;
|
|
|
}
|
|
|
|
|
|
@@ -128,18 +148,16 @@ class DolibarrSyncService
|
|
|
}
|
|
|
|
|
|
$parent = $this->dolibarrApiService->getSociety($parentOrganizationId);
|
|
|
- if ($parent['id'] !== $dolibarrSociety['parent']) {
|
|
|
- $putSocietyData['parent'] = $parent['id'];
|
|
|
+ if ((int)$parent['id'] !== (int)$dolibarrSociety['parent']) {
|
|
|
+ $putSocietyData['parent'] = (int)$parent['id'];
|
|
|
}
|
|
|
|
|
|
// More infos
|
|
|
$infosArray = [];
|
|
|
|
|
|
$product = $organization->getSettings()->getProduct();
|
|
|
- if (
|
|
|
- $product == SettingsProductEnum::SCHOOL()->getValue() ||
|
|
|
- $product == SettingsProductEnum::SCHOOL_PREMIUM()->getValue()
|
|
|
- ) {
|
|
|
+
|
|
|
+ if (SettingsProductEnum::isSchool($product)) {
|
|
|
$studentsCount = count(array_filter(
|
|
|
$organizationMembers,
|
|
|
function ($missions) { return in_array(FunctionEnum::STUDENT()->getValue(), $missions); }
|
|
|
@@ -148,10 +166,8 @@ class DolibarrSyncService
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
- $product == SettingsProductEnum::SCHOOL()->getValue() ||
|
|
|
- $product == SettingsProductEnum::SCHOOL_PREMIUM()->getValue() ||
|
|
|
- $product == SettingsProductEnum::ARTIST()->getValue() ||
|
|
|
- $product == SettingsProductEnum::ARTIST_PREMIUM()->getValue()
|
|
|
+ SettingsProductEnum::isSchool($product) ||
|
|
|
+ SettingsProductEnum::isArtist($product)
|
|
|
) {
|
|
|
$membersCount = count(array_filter(
|
|
|
$organizationMembers,
|
|
|
@@ -160,11 +176,9 @@ class DolibarrSyncService
|
|
|
$infosArray[] = "Nombre d'adhérents : " . $membersCount;
|
|
|
}
|
|
|
|
|
|
- $adminsCount = count($this->accessRepository->findBy(
|
|
|
- [
|
|
|
- 'organization' => $organization,
|
|
|
- 'adminAccess' => true
|
|
|
- ]
|
|
|
+ $adminsCount = count(array_filter(
|
|
|
+ $organizationMembers,
|
|
|
+ function ($missions) use ($adminMissions) { return !empty(array_intersect($adminMissions, $missions)); }
|
|
|
));
|
|
|
$infosArray[] = "Nombre d'accès admin : " . $adminsCount;
|
|
|
|
|
|
@@ -177,47 +191,16 @@ class DolibarrSyncService
|
|
|
$putSocietyData['array_options'] = $arrayOptions;
|
|
|
}
|
|
|
|
|
|
- $operations[] = new DolibarrSyncOperation(
|
|
|
- 'Update organization ' . $organization->getId() . ' - ' . $organization->getName(),
|
|
|
- 'PUT',
|
|
|
- 'thirdparties/' . $dolibarrSociety['id'],
|
|
|
- $putSocietyData,
|
|
|
- $dolibarrSociety
|
|
|
+ $operations[] = new DolibarrUpdateOperation(
|
|
|
+ 'Update society : ' . $organization->getName() . ' (' . $organization->getId() . ')',
|
|
|
+ 'thirdparties',
|
|
|
+ $dolibarrSociety,
|
|
|
+ $putSocietyData
|
|
|
);
|
|
|
|
|
|
// ** Contacts
|
|
|
- $officeMissions = [
|
|
|
- FunctionEnum::PRESIDENT()->getValue(),
|
|
|
- FunctionEnum::SECRETARY()->getValue(),
|
|
|
- FunctionEnum::TREASURER()->getValue(),
|
|
|
- FunctionEnum::ADMINISTRATIVE_OFFICER()->getValue(),
|
|
|
- FunctionEnum::ADMINISTRATIVE_SECRETARY()->getValue(),
|
|
|
- FunctionEnum::ADMINISTRATIVE_DIRECTOR()->getValue(),
|
|
|
- FunctionEnum::ADMINISTRATIVE_STAFF()->getValue()
|
|
|
- ];
|
|
|
-
|
|
|
- $rolesLabels = [
|
|
|
- 'MISS' => [
|
|
|
- FunctionEnum::PRESIDENT()->getValue() => 'Présidente',
|
|
|
- FunctionEnum::SECRETARY()->getValue() => 'Secrétaire',
|
|
|
- FunctionEnum::TREASURER()->getValue() => 'Trésorière',
|
|
|
- FunctionEnum::ADMINISTRATIVE_OFFICER()->getValue() => 'Responsable admin.',
|
|
|
- FunctionEnum::ADMINISTRATIVE_SECRETARY()->getValue() => 'Secrétaire admin.',
|
|
|
- FunctionEnum::ADMINISTRATIVE_DIRECTOR()->getValue() => 'Directrice',
|
|
|
- FunctionEnum::ADMINISTRATIVE_STAFF()->getValue() => 'Personnel administratif'
|
|
|
- ],
|
|
|
- 'MISTER' => [
|
|
|
- FunctionEnum::PRESIDENT()->getValue() => 'Président',
|
|
|
- FunctionEnum::SECRETARY()->getValue() => 'Secrétaire',
|
|
|
- FunctionEnum::TREASURER()->getValue() => 'Trésorier',
|
|
|
- FunctionEnum::ADMINISTRATIVE_OFFICER()->getValue() => 'Responsable admin.',
|
|
|
- FunctionEnum::ADMINISTRATIVE_SECRETARY()->getValue() => 'Secrétaire admin.',
|
|
|
- FunctionEnum::ADMINISTRATIVE_DIRECTOR()->getValue() => 'Directeur',
|
|
|
- FunctionEnum::ADMINISTRATIVE_STAFF()->getValue() => 'Personnel administratif'
|
|
|
- ]
|
|
|
- ];
|
|
|
-
|
|
|
- $dolibarrContactsIndex = $this->getDolibarrContactsIndex($dolibarrSociety['id']);
|
|
|
+ $officeMissions = FunctionEnum::getOfficeMissions();
|
|
|
+ $dolibarrContactsIndex = $this->getDolibarrContactsIndex((int)$dolibarrSociety['id']);
|
|
|
$contactsProcessed = [];
|
|
|
|
|
|
foreach ($organizationMembers as $accessId => $missions) {
|
|
|
@@ -226,105 +209,73 @@ class DolibarrSyncService
|
|
|
$access = $this->accessRepository->find($accessId);
|
|
|
$person = $access->getPerson();
|
|
|
|
|
|
+ // Keep track of the contacts seen
|
|
|
if (in_array($person->getId(), $contactsProcessed)) {
|
|
|
// already updated from another mission
|
|
|
continue;
|
|
|
}
|
|
|
$contactsProcessed[] = $person->getId();
|
|
|
|
|
|
- if (array_key_exists($person->getId(), $dolibarrContactsIndex)) {
|
|
|
- $dolibarrContact = $dolibarrContactsIndex[$person->getId()];
|
|
|
- } else {
|
|
|
- // new contact
|
|
|
- $dolibarrContact = null;
|
|
|
- }
|
|
|
-
|
|
|
- $putContactData = [];
|
|
|
+ // Build parameters for the query (if a query is needed
|
|
|
+ $dolibarrContact = $dolibarrContactsIndex[$person->getId()] ?? null;
|
|
|
|
|
|
- $contactRes = $this->contactPointRepository->getByTypeAndPerson(
|
|
|
- ContactPointTypeEnum::PRINCIPAL()->getValue(), $person
|
|
|
- );
|
|
|
- if (empty($contactRes)) {
|
|
|
- $contactRes = $this->contactPointRepository->getByTypeAndPerson(
|
|
|
- ContactPointTypeEnum::OTHER()->getValue(), $person
|
|
|
- );
|
|
|
- }
|
|
|
- $contact = empty($contactRes) ? null : $contactRes[0];
|
|
|
-
|
|
|
- $lastname = $person->getName();
|
|
|
- $firstname = $person->getGivenName();
|
|
|
- $email = $contact?->getEmail();
|
|
|
- $phone = null;
|
|
|
- if ($contact !== null && $contact->getTelphone() !== null) {
|
|
|
- $phone = $this->formatPhoneNumber($contact->getTelphone());
|
|
|
- }
|
|
|
- $mobilePhone = null;
|
|
|
- if ($contact !== null && $contact->getMobilPhone() !== null) {
|
|
|
- $mobilePhone = $this->formatPhoneNumber($contact->getMobilPhone());
|
|
|
+ // <-- special case: for phone numbers, dolibarr api returns empty strings even when field is null in DB
|
|
|
+ // if left like this, it would leads to unnecessary updates
|
|
|
+ if ($dolibarrContact !== null) {
|
|
|
+ if ($dolibarrContact['phone_pro'] === '')
|
|
|
+ $dolibarrContact['phone_pro'] = null;
|
|
|
+ if ($dolibarrContact['phone_mobile'] === '')
|
|
|
+ $dolibarrContact['phone_mobile'] = null;
|
|
|
}
|
|
|
- $civility = $person->getGender() === 'MISS' ? 'Mrs.' : 'Mr.';
|
|
|
- $poste = implode(
|
|
|
- ', ',
|
|
|
- array_map(
|
|
|
- function($m) use ($rolesLabels, $person) {
|
|
|
- return $rolesLabels[$person->getGender() ?? 'MISTER'][$m];
|
|
|
- },
|
|
|
- array_filter(
|
|
|
- $missions,
|
|
|
- function($m) use ($officeMissions){ return in_array($m, $officeMissions); }
|
|
|
+ // -->
|
|
|
+
|
|
|
+ $contact = $this->getPersonContact($person);
|
|
|
+
|
|
|
+ $newData = [
|
|
|
+ 'civility_code' => ['MISS' => 'MME', 'MISTER' => 'MR'][$person->getGender()] ?? null,
|
|
|
+ 'lastname' => $person->getName(),
|
|
|
+ 'firstname' => $person->getGivenName(),
|
|
|
+ 'email' => $contact?->getEmail(),
|
|
|
+ 'phone_pro' => $contact?->getTelphone() ? $this->formatPhoneNumber($contact?->getTelphone()) : null,
|
|
|
+ 'phone_mobile' => $contact?->getMobilPhone() ? $this->formatPhoneNumber($contact?->getMobilPhone()): null,
|
|
|
+ 'poste' => implode(
|
|
|
+ ', ',
|
|
|
+ array_map(
|
|
|
+ function($m) use ($person) {
|
|
|
+ return $this->translator->trans($m, ['gender' => ['MISS' => 'F', 'MISTER' => 'M'][$person->getGender()] ?? '?'] );
|
|
|
+ },
|
|
|
+ array_filter(
|
|
|
+ $missions,
|
|
|
+ function ($m) { return $m !== FunctionEnum::ADHERENT()->getValue() && $m !== FunctionEnum::STUDENT()->getValue(); }
|
|
|
+ )
|
|
|
)
|
|
|
)
|
|
|
- );
|
|
|
+ ];
|
|
|
|
|
|
if ($dolibarrContact === null) {
|
|
|
- $postContactData = [
|
|
|
- 'civility' => $civility,
|
|
|
- 'lastname' => $lastname,
|
|
|
- 'firstname' => $firstname,
|
|
|
- 'email' => $email,
|
|
|
- 'phone_pro' => $phone,
|
|
|
- 'phone_mobile' => $mobilePhone,
|
|
|
- 'poste' => $poste
|
|
|
- ];
|
|
|
-
|
|
|
- $operations[] = new DolibarrSyncOperation(
|
|
|
- 'Create person ' . $person->getId() . ' - ' . $person->getName() . ' ' . $person->getGivenName(),
|
|
|
- 'POST',
|
|
|
+ $operations[] = new DolibarrCreateOperation(
|
|
|
+ 'New contact: ' . $person->getName() . ' ' . $person->getGivenName() . ' (' . $person->getId() . ')',
|
|
|
'contacts',
|
|
|
- $postContactData
|
|
|
+ $newData
|
|
|
);
|
|
|
} else {
|
|
|
- if ($civility !== $dolibarrContact['civility']) {
|
|
|
- $putContactData['civility'] = $civility;
|
|
|
- }
|
|
|
- if ($lastname !== $dolibarrContact['lastname']) {
|
|
|
- $putContactData['lastname'] = $lastname;
|
|
|
- }
|
|
|
- if ($firstname !== $dolibarrContact['firstname']) {
|
|
|
- $putContactData['firstname'] = $firstname;
|
|
|
- }
|
|
|
- if ($email !== $dolibarrContact['email']) {
|
|
|
- $putContactData['email'] = $contact->getEmail();
|
|
|
- }
|
|
|
- // the dolibarr api return an empty string even if the field is null
|
|
|
- if ($phone !== $dolibarrContact['phone_pro'] && $dolibarrContact['phone_pro'] !== '') {
|
|
|
- $putContactData['phone_pro'] = $phone;
|
|
|
- }
|
|
|
- // the dolibarr api return an empty string even if the field is null
|
|
|
- if ($mobilePhone !== $dolibarrContact['phone_mobile'] && $dolibarrContact['phone_mobile'] !== '') {
|
|
|
- $putContactData['phone_mobile'] = $mobilePhone;
|
|
|
- }
|
|
|
- if ($poste !== $dolibarrContact['poste']) {
|
|
|
- $putContactData['poste'] = $poste;
|
|
|
- }
|
|
|
-
|
|
|
- $operations[] = new DolibarrSyncOperation(
|
|
|
- 'Update person ' . $person->getId() . ' - ' . $person->getName() . ' ' . $person->getGivenName(),
|
|
|
- 'PUT',
|
|
|
- 'contacts/' . $dolibarrContact['id'],
|
|
|
- $putContactData,
|
|
|
- $dolibarrContact
|
|
|
+ // Only update the fields that are different
|
|
|
+ $newData = array_filter(
|
|
|
+ $newData,
|
|
|
+ function($v, $k) use ($dolibarrContact) { return $v !== $dolibarrContact[$k]; },
|
|
|
+ ARRAY_FILTER_USE_BOTH
|
|
|
);
|
|
|
+
|
|
|
+ // add an update operation if some data has to be updated
|
|
|
+ if (!empty($newData)) {
|
|
|
+ $operations[] = new DolibarrUpdateOperation(
|
|
|
+ 'Update contact: ' . $person->getName() . ' ' . $person->getGivenName() . ' (' . $person->getId() . ')' .
|
|
|
+ ' in ' . $organization->getName() . ' (' . $organization->getId() . ')',
|
|
|
+ 'contacts',
|
|
|
+ $dolibarrContact,
|
|
|
+ $newData,
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// No need to test the other missions of this access
|
|
|
@@ -334,13 +285,18 @@ class DolibarrSyncService
|
|
|
}
|
|
|
|
|
|
foreach ($dolibarrContactsIndex as $personId => $contactData) {
|
|
|
+ if ($contactData['statut'] === 0) {
|
|
|
+ // contact is already disabled
|
|
|
+ continue;
|
|
|
+ }
|
|
|
if (!in_array($personId, $contactsProcessed)) {
|
|
|
// Ce personId n'existe plus dans les membres Opentalent de cette société, on delete
|
|
|
- $operations[] = new DolibarrSyncOperation(
|
|
|
- 'Delete person ' . $personId . ' - ' . $person->getName() . ' ' . $person->getGivenName() .
|
|
|
- ' from the contacts of organization ' . $organization->getId() . ' - ' . $organization->getName(),
|
|
|
- 'DELETE',
|
|
|
- 'contacts/' . $contactData['id']
|
|
|
+ $operations[] = new DolibarrUpdateOperation(
|
|
|
+ 'Disable contact: ' . $contactData['lastname'] . ' ' . $contactData['firstname'] . ' (' . $personId . ')' .
|
|
|
+ ' from ' . $organization->getName() . ' (' . $organization->getId() . ')',
|
|
|
+ 'contacts',
|
|
|
+ $contactData,
|
|
|
+ ['statut' => 0]
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
@@ -537,20 +493,31 @@ class DolibarrSyncService
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ private function getPersonContact(Person $person) {
|
|
|
+ $contactPriorities = [
|
|
|
+ ContactPointTypeEnum::PRINCIPAL()->getValue(),
|
|
|
+ ContactPointTypeEnum::OTHER()->getValue()
|
|
|
+ ];
|
|
|
+
|
|
|
+ foreach ($contactPriorities as $contactType) {
|
|
|
+ $result = $this->contactPointRepository->getByTypeAndPerson(
|
|
|
+ ContactPointTypeEnum::PRINCIPAL()->getValue(), $person
|
|
|
+ );
|
|
|
+ if (!empty($result)) {
|
|
|
+ return $result[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * Formatte un numéro de téléphone, au format français si l'indicatif national
|
|
|
- * est l'indicatif français ou s'il est manquant,
|
|
|
- * au format international sinon.
|
|
|
+ * Format a phone number into international format
|
|
|
*
|
|
|
* @param PhoneNumber $phoneNumber
|
|
|
* @return mixed
|
|
|
*/
|
|
|
private function formatPhoneNumber(PhoneNumber $phoneNumber): string {
|
|
|
$phoneUtil = PhoneNumberUtil::getInstance();
|
|
|
- if (!$phoneNumber->hasCountryCode() || $phoneNumber->getCountryCode() == 33) {
|
|
|
- return $phoneUtil->format($phoneNumber, PhoneNumberFormat::NATIONAL);
|
|
|
- } else {
|
|
|
- return $phoneUtil->format($phoneNumber, PhoneNumberFormat::INTERNATIONAL);
|
|
|
- }
|
|
|
+ return $phoneUtil->format($phoneNumber, PhoneNumberFormat::INTERNATIONAL);
|
|
|
}
|
|
|
}
|