mautic / plugins /MauticCrmBundle /Integration /SugarcrmIntegration.php
chrisbryan17's picture
Upload folder using huggingface_hub
d2897cd verified
raw
history blame contribute delete
75.9 kB
<?php
namespace MauticPlugin\MauticCrmBundle\Integration;
use Doctrine\ORM\EntityManager;
use Mautic\CoreBundle\Form\Type\ButtonGroupType;
use Mautic\CoreBundle\Helper\CacheStorageHelper;
use Mautic\CoreBundle\Helper\EncryptionHelper;
use Mautic\CoreBundle\Helper\PathsHelper;
use Mautic\CoreBundle\Model\NotificationModel;
use Mautic\LeadBundle\Entity\Company;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Model\CompanyModel;
use Mautic\LeadBundle\Model\DoNotContact;
use Mautic\LeadBundle\Model\FieldModel;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\PluginBundle\Entity\IntegrationEntity;
use Mautic\PluginBundle\Entity\IntegrationEntityRepository;
use Mautic\PluginBundle\Exception\ApiErrorException;
use Mautic\PluginBundle\Model\IntegrationEntityModel;
use Mautic\UserBundle\Model\UserModel;
use Monolog\Logger;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\Router;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Contracts\Translation\TranslatorInterface;
class SugarcrmIntegration extends CrmAbstractIntegration
{
/**
* @var string[]
*/
private array $objects = [
'Leads',
'Contacts',
'Accounts',
];
/**
* @var string[]
*/
private array $sugarDncKeys = ['email_opt_out', 'invalid_email'];
private $authorizationError;
public function __construct(
EventDispatcherInterface $eventDispatcher,
CacheStorageHelper $cacheStorageHelper,
EntityManager $entityManager,
Session $session,
RequestStack $requestStack,
Router $router,
TranslatorInterface $translator,
Logger $logger,
EncryptionHelper $encryptionHelper,
LeadModel $leadModel,
CompanyModel $companyModel,
PathsHelper $pathsHelper,
NotificationModel $notificationModel,
FieldModel $fieldModel,
IntegrationEntityModel $integrationEntityModel,
protected DoNotContact $doNotContactModel,
private UserModel $userModel
) {
parent::__construct(
$eventDispatcher,
$cacheStorageHelper,
$entityManager,
$session,
$requestStack,
$router,
$translator,
$logger,
$encryptionHelper,
$leadModel,
$companyModel,
$pathsHelper,
$notificationModel,
$fieldModel,
$integrationEntityModel,
$doNotContactModel
);
}
/**
* Returns the name of the social integration that must match the name of the file.
*/
public function getName(): string
{
return 'Sugarcrm';
}
public function getSupportedFeatures(): array
{
// Only push_lead is currently supported for version 7
return ['push_lead', 'get_leads', 'push_leads'];
}
/**
* Get the array key for clientId.
*/
public function getClientIdKey(): string
{
return 'client_id';
}
/**
* Get the array key for client secret.
*/
public function getClientSecretKey(): string
{
return 'client_secret';
}
public function getSecretKeys(): array
{
return [
'client_secret',
'password',
];
}
/**
* Get the array key for the auth token.
*/
public function getAuthTokenKey(): string
{
return (isset($this->keys['version']) && '6' == $this->keys['version']) ? 'id' : 'access_token';
}
/**
* SugarCRM 7 refresh tokens.
*/
public function getRefreshTokenKeys(): array
{
return [
'refresh_token',
'expires',
];
}
public function getAccessTokenUrl(): string
{
$apiUrl = ('6' == $this->keys['version']) ? 'service/v4_1/rest.php' : 'rest/v10/oauth2/token';
return sprintf('%s/%s', $this->keys['sugarcrm_url'], $apiUrl);
}
/**
* @return string
*/
public function getAuthLoginUrl()
{
return $this->router->generate('mautic_integration_auth_callback', ['integration' => $this->getName()]);
}
/**
* Retrieves and stores tokens returned from oAuthLogin.
*
* @param array $settings
* @param array $parameters
*
* @return array
*/
public function authCallback($settings = [], $parameters = [])
{
if (isset($this->keys['version']) && '6' == $this->keys['version']) {
$success = $this->isAuthorized();
if (!$success) {
return $this->authorizationError;
} else {
return false;
}
} else {
$settings = [
'grant_type' => 'password',
'ignore_redirecturi' => true,
];
$parameters = [
'username' => $this->keys['username'],
'password' => $this->keys['password'],
'platform' => 'base',
];
return parent::authCallback($settings, $parameters);
}
}
/**
* @return array<string, string>
*/
public function getRequiredKeyFields(): array
{
return [
'sugarcrm_url' => 'mautic.sugarcrm.form.url',
'client_id' => 'mautic.sugarcrm.form.clientkey',
'client_secret' => 'mautic.sugarcrm.form.clientsecret',
'username' => 'mautic.sugarcrm.form.username',
'password' => 'mautic.sugarcrm.form.password',
];
}
/**
* Get available company fields for choices in the config UI.
*
* @param array $settings
*
* @return array
*/
public function getFormCompanyFields($settings = [])
{
return $this->getFormFieldsByObject('company', $settings);
}
/**
* Get available fields for choices in the config UI.
*
* @param array $settings
*/
public function getFormLeadFields($settings = []): array
{
if (!$this->isAuthorized()) {
return [];
}
if (isset($settings['feature_settings']['objects'])) {
// combine keys with values
$settings['feature_settings']['objects'] = array_combine(
array_values($settings['feature_settings']['objects']),
$settings['feature_settings']['objects']
);
}
// unset company object
if (isset($settings['feature_settings']['objects']['company'])) {
unset($settings['feature_settings']['objects']['company']);
}
if (empty($settings['feature_settings']['objects'])) {
// BC force add Leads and Contacts from Integration
$settings['feature_settings']['objects']['Leads'] = 'Leads';
$settings['feature_settings']['objects']['Contacts'] = 'Contacts';
}
$fields = [];
// merge all arrays from level 1
$fieldsromObjects = $this->getAvailableLeadFields($settings);
foreach ($fieldsromObjects as $fieldsFromObject) {
$fields = array_merge($fields, $fieldsFromObject);
}
return $fields;
}
/**
* @param array $settings
*
* @throws \Exception
*/
public function getAvailableLeadFields($settings = []): array
{
$sugarFields = [];
$silenceExceptions = $settings['silence_exceptions'] ?? true;
$sugarObjects = [];
if (!empty($settings['feature_settings']['objects'])) {
$sugarObjects = $settings['feature_settings']['objects'];
} else {
$sugarObjects['Leads'] = 'Leads';
$sugarObjects['Contacts'] = 'Contacts';
$settings['feature_settings']['objects'] = $sugarObjects;
}
$isRequired = fn (array $field, $object) => match (true) {
'Leads' === $object && ('webtolead_email1' === $field['name'] || 'email1' === $field['name']), 'Contacts' === $object && 'email1' === $field['name'], 'id' !== $field['name'] && !empty($field['required']) => true,
default => false,
};
try {
if (!empty($sugarObjects) and is_array($sugarObjects)) {
foreach ($sugarObjects as $sObject) {
if ('Accounts' === $sObject) {
// Match Sugar object to Mautic's
$sObject = 'company';
}
$sObject = trim($sObject);
if ($this->isAuthorized()) {
// Check the cache first
$settings['cache_suffix'] = $cacheSuffix = '.'.$sObject;
if ($fields = parent::getAvailableLeadFields($settings)) {
if (('company' === $sObject && isset($fields['id'])) || isset($fields['id__'.$sObject])) {
$sugarFields[$sObject] = $fields;
continue;
}
}
if (!isset($sugarFields[$sObject])) {
$fields = $this->getApiHelper()->getLeadFields($sObject);
if (null != $fields && !empty($fields)) {
if (isset($fields['module_fields']) && !empty($fields['module_fields'])) {
// 6.x/community
foreach ($fields['module_fields'] as $fieldInfo) {
if (isset($fieldInfo['name']) && (!in_array($fieldInfo['type'], ['id', 'assigned_user_name', 'link', 'relate']) || ('id' == $fieldInfo['type'] && 'id' == $fieldInfo['name'])
)
) {
$type = 'string';
$fieldName = (!str_contains($fieldInfo['name'],
'webtolead_email')) ? $fieldInfo['name'] : str_replace('webtolead_',
'', $fieldInfo['name']);
// make these congruent as some come in with colons and some do not
$label = str_replace(':', '', $fieldInfo['label']);
if ('company' !== $sObject) {
$sugarFields[$sObject][$fieldName.'__'.$sObject] = [
'type' => $type,
'label' => $sObject.'-'.$label,
'required' => $isRequired($fieldInfo, $sObject),
'group' => $sObject,
'optionLabel' => $fieldInfo['label'],
];
} else {
$sugarFields[$sObject][$fieldName] = [
'type' => $type,
'label' => $label,
'required' => $isRequired($fieldInfo, $sObject),
];
}
}
}
} elseif (isset($fields['fields']) && !empty($fields['fields'])) {
// 7.x
foreach ($fields['fields'] as $fieldInfo) {
if (isset($fieldInfo['name']) && empty($fieldInfo['readonly'])
&& (!in_array(
$fieldInfo['type'],
['id', 'team_list', 'link', 'relate']
)
|| ('id' == $fieldInfo['type'] && 'id' == $fieldInfo['name'])
)
) {
if (!empty($fieldInfo['comment'])) {
$label = $fieldInfo['comment'];
} elseif (!empty($fieldInfo['help'])) {
$label = $fieldInfo['help'];
} else {
$label = ucfirst(str_replace('_', ' ', $fieldInfo['name']));
}
// make these congruent as some come in with colons and some do not
$label = str_replace(':', '', $label);
$fieldName = (!str_contains($fieldInfo['name'], 'webtolead_email'))
? $fieldInfo['name']
: str_replace(
'webtolead_',
'',
$fieldInfo['name']
);
$type = 'string';
if ('company' !== $sObject) {
$sugarFields[$sObject][$fieldName.'__'.$sObject] = [
'type' => $type,
'label' => $sObject.'-'.$label,
'required' => $isRequired($fieldInfo, $sObject),
'group' => $sObject,
'optionLabel' => $label,
];
} else {
$sugarFields[$sObject][$fieldName] = [
'type' => $type,
'label' => $label,
'required' => $isRequired($fieldInfo, $sObject),
];
}
}
}
}
}
$this->cache->set('leadFields'.$cacheSuffix, $sugarFields[$sObject]);
}
} else {
throw new ApiErrorException($this->authorizationError);
}
}
}
} catch (\Exception $e) {
$this->logIntegrationError($e);
if (!$silenceExceptions) {
throw $e;
}
}
return $sugarFields;
}
/**
* @return mixed
*/
public function getFetchQuery($params)
{
return $params;
}
/**
* @param array $params
* @param array|null $query
*
* @return int|null
*/
public function getCompanies($params = [], $query = null, $executed = null)
{
$executed = null;
$sugarObject = 'Accounts';
$params['max_results'] = 100;
if (!isset($params['offset'])) {
// First call
$params['offset'] = 0;
}
$query = $params;
try {
if ($this->isAuthorized()) {
$result = $this->getApiHelper()->getLeads($query, $sugarObject);
$params['offset'] = $result['next_offset'];
$executed += $this->amendLeadDataBeforeMauticPopulate($result, $sugarObject);
if (
(isset($result['total_count']) && $result['total_count'] > $params['offset']) // Sugar 6
|| (!isset($result['total_count']) && $params['offset'] > -1)) { // Sugar 7
$result = null;
$executed += $this->getCompanies($params, null, $executed);
}
return $executed;
}
} catch (\Exception $e) {
$this->logIntegrationError($e);
}
return $executed;
}
/**
* @param array $params
*
* @return int|null
*
* @throws \Exception
* To be modified
*/
public function pushLeadActivity($params = [])
{
$executed = null;
$query = $this->getFetchQuery($params);
$config = $this->mergeConfigToFeatureSettings([]);
/** @var SugarApi $apiHelper */
$apiHelper = $this->getApiHelper();
$sugarObjects[] = 'Leads';
if (isset($config['objects']) && !empty($config['objects'])) {
$sugarObjects = $config['objects'];
}
/** @var IntegrationEntityRepository $integrationEntityRepo */
$integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class);
$startDate = new \DateTime($query['start']);
$endDate = new \DateTime($query['end']);
$limit = 100;
foreach ($sugarObjects as $object) {
try {
if ($this->isAuthorized()) {
// Get first batch
$start = 0;
$sugarIds = $integrationEntityRepo->getIntegrationsEntityId(
'Sugarcrm',
$object,
'lead',
null,
$startDate->format('Y-m-d H:i:s'),
$endDate->format('Y-m-d H:i:s'),
true,
$start,
$limit
);
while (!empty($sugarIds)) {
$executed += count($sugarIds);
// Extract a list of lead Ids
$leadIds = [];
foreach ($sugarIds as $ids) {
$leadIds[] = $ids['internal_entity_id'];
}
// Collect lead activity for this batch
$leadActivity = $this->getLeadData(
$startDate,
$endDate,
$leadIds
);
$sugarLeadData = [];
foreach ($sugarIds as $ids) {
$leadId = $ids['internal_entity_id'];
if (isset($leadActivity[$leadId])) {
$sugarId = $ids['integration_entity_id'];
$sugarLeadData[$sugarId] = $leadActivity[$leadId];
$sugarLeadData[$sugarId]['id'] = $ids['integration_entity_id'];
$sugarLeadData[$sugarId]['leadId'] = $ids['internal_entity_id'];
$sugarLeadData[$sugarId]['leadUrl'] = $this->router->generate(
'mautic_plugin_timeline_view',
['integration' => 'Sugarcrm', 'leadId' => $leadId],
UrlGeneratorInterface::ABSOLUTE_URL
);
}
}
if (!empty($sugarLeadData)) {
$apiHelper->createLeadActivity($sugarLeadData, $object);
}
// Get the next batch
$start += $limit;
$sugarIds = $integrationEntityRepo->getIntegrationsEntityId(
'Sugarcrm',
$object,
'lead',
null,
$startDate->format('Y-m-d H:i:s'),
$endDate->format('Y-m-d H:i:s'),
true,
$start,
$limit
);
}
}
} catch (\Exception $e) {
$this->logIntegrationError($e);
}
}
return $executed;
}
/**
* @param array $params
* @param array|null $query
*
* @return int|null
*/
public function getLeads($params = [], $query = null, &$executed = null, $result = [], $object = 'Leads')
{
$params['max_results'] = 100;
if (!isset($params['offset'])) {
// First call
$params['offset'] = 0;
}
$query = $params;
try {
if ($this->isAuthorized()) {
if ('Activity' !== $object and 'company' !== $object) {
$result = $this->getApiHelper()->getLeads($query, $object);
$params['offset'] = $result['next_offset'];
$executed += $this->amendLeadDataBeforeMauticPopulate($result, $object);
if (
(isset($result['total_count']) && $result['total_count'] > $params['offset']) // Sugar 6
|| (!isset($result['total_count']) && $params['offset'] > -1)) { // Sugar 7
$params['object'] = $object;
$executed += $this->getLeads($params, null, $executed, [], $object);
}
}
return $executed;
}
} catch (\Exception $e) {
$this->logIntegrationError($e);
}
return $executed;
}
/**
* @return string
*/
public function getErrorsFromResponse($response)
{
if ('6' == $this->keys['version']) {
if (!empty($response['name'])) {
return $response['description'];
} else {
return $this->translator->trans('mautic.integration.error.genericerror', [], 'flashes');
}
} else {
return parent::getErrorsFromResponse($response);
}
}
public function getAuthenticationType(): string
{
return (isset($this->keys['version']) && '6' == $this->keys['version']) ? 'rest' : 'oauth2';
}
public function getDataPriority(): bool
{
return true;
}
/**
* @return array
*/
public function prepareRequest($url, $parameters, $method, $settings, $authType)
{
if ('oauth2' == $authType && empty($settings['authorize_session']) && isset($this->keys['access_token'])) {
// Append the access token as the oauth-token header
$headers = [
"oauth-token: {$this->keys['access_token']}",
];
return [$parameters, $headers];
} else {
return parent::prepareRequest($url, $parameters, $method, $settings, $authType);
}
}
/**
* @return bool
*/
public function isAuthorized()
{
if (!$this->isConfigured()) {
return false;
}
if (!isset($this->keys['version'])) {
return false;
}
if ('6' == $this->keys['version']) {
$loginParams = [
'user_auth' => [
'user_name' => $this->keys['username'],
'password' => md5($this->keys['password']),
'version' => '1',
],
'application_name' => 'Mautic',
'name_value_list' => [],
'method' => 'login',
'input_type' => 'JSON',
'response_type' => 'JSON',
];
$parameters = [
'method' => 'login',
'input_type' => 'JSON',
'response_type' => 'JSON',
'rest_data' => json_encode($loginParams),
];
$settings['auth_type'] = 'rest';
$settings['authorize_session'] = true;
$response = $this->makeRequest($this->getAccessTokenUrl(), $parameters, 'GET', $settings);
unset($response['module'], $response['name_value_list']);
$error = $this->extractAuthKeys($response, 'id');
$this->authorizationError = $error;
return empty($error);
} else {
// SugarCRM 7 uses password grant type so login each time to ensure session is valid
$this->authCallback();
return parent::isAuthorized();
}
}
public function prepareResponseForExtraction($data)
{
// Extract expiry and set expires for 7.x
if (is_array($data) && isset($data['expires_in'])) {
$data['expires'] = $data['expires_in'] + time();
}
return $data;
}
/**
* Amend mapped lead data before creating to Mautic.
*
* @param array $data
* @param string $object
*/
public function amendLeadDataBeforeMauticPopulate($data, $object): int
{
$settings['feature_settings']['objects'][] = $object;
$fields = array_keys($this->getAvailableLeadFields($settings));
$params['fields'] = implode(',', $fields);
$count = 0;
$entity = null;
/** @var IntegrationEntityRepository $integrationEntityRepo */
$integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class);
$companyRepo = $this->em->getRepository(Company::class);
$sugarRejectedLeads = [];
if (isset($data['entry_list'])) {
$SUGAR_VERSION = '6';
$RECORDS_LIST_NAME = 'entry_list';
$MODULE_FIELD_NAME = 'module_name';
}
if (isset($data['records'])) {
$SUGAR_VERSION = '7';
$RECORDS_LIST_NAME = 'records';
$MODULE_FIELD_NAME = '_module';
}
if (isset($data[$RECORDS_LIST_NAME]) and 'Activity' !== $object) {
// Get assigned user ids
$assignedUserIds = [];
$onwerEmailByAssignedUserId = [];
if ('Leads' == $object || 'Contacts' == $object || 'Accounts' == $object) {
foreach ($data[$RECORDS_LIST_NAME] as $record) {
if ('6' == $SUGAR_VERSION) {
foreach ($record['name_value_list'] as $item) {
if ('assigned_user_id' == $item['name'] && $item['value'] && '' != $item['value']) {
$assignedUserIds[] = $item['value'];
}
}
} else {
if (isset($record['assigned_user_id']) && '' != $record['assigned_user_id']) {
$assignedUserIds[] = $record['assigned_user_id'];
}
}
}
}
if (!empty($assignedUserIds)) {
$assignedUserIds = array_unique($assignedUserIds);
$onwerEmailByAssignedUserId = $this->getApiHelper()->getEmailBySugarUserId(['ids' => $assignedUserIds]);
}
// Get all leads emails
$checkEmailsInSugar = [];
if ('Leads' == $object) {
if ('6' == $SUGAR_VERSION) {
foreach ($data[$RECORDS_LIST_NAME] as $record) {
foreach ($record['name_value_list'] as $item) {
if ('email1' == $item['name'] && $item['value'] && '' != $item['value']) {
$checkEmailsInSugar[] = $item['value'];
}
}
}
} else {
if (isset($record['email1']) && '' != $record['email1']) {
$checkEmailsInSugar[] = $record['email1'];
}
}
}
if (!empty($checkEmailsInSugar)) {
$sugarLeads = $this->getApiHelper()->getLeads(['checkemail_contacts' => $checkEmailsInSugar, 'offset' => 0, 'max_results' => 1000], 'Contacts');
if (isset($sugarLeads[$RECORDS_LIST_NAME])) {
foreach ($sugarLeads[$RECORDS_LIST_NAME] as $record) {
$sugarLeadRecord = [];
if ('6' == $SUGAR_VERSION) {
foreach ($record['name_value_list'] as $item) {
if ('email1' == $item['name'] && $item['value'] && '' != $item['value']) {
$sugarRejectedLeads[] = $item['value'];
}
}
} else {
if (isset($record['email1']) && '' != $record['email1']) {
$sugarRejectedLeads[] = $record['email1'];
}
}
}
}
}
foreach ($data[$RECORDS_LIST_NAME] as $record) {
$integrationEntities = [];
$dataObject = [];
if (isset($record[$MODULE_FIELD_NAME]) && 'Accounts' == $record[$MODULE_FIELD_NAME]) {
$newName = '';
} else {
$newName = '__'.$object;
}
if ('6' == $SUGAR_VERSION) {
foreach ($record['name_value_list'] as $item) {
if ('Activity' !== $object) {
if ($this->checkIfSugarCrmMultiSelectString($item['value'])) {
$convertedMultiSelectString = $this->convertSuiteCrmToMauticMultiSelect($item['value']);
$dataObject[$item['name'].$newName] = $convertedMultiSelectString;
} else {
$dataObject[$item['name'].$newName] = $item['value'];
}
if ('date_entered' == $item['name']) {
$itemDateEntered = new \DateTime($item['value']);
}
if ('date_modified' == $item['name']) {
$itemDateModified = new \DateTime($item['value']);
}
}
}
} else {
if ('Activity' !== $object) {
if (isset($record['date_entered']) && '' != $record['date_entered']) {
$itemDateEntered = new \DateTime($record['date_entered']);
}
if (isset($record['date_modified']) && '' != $record['date_modified']) {
$itemDateEntered = new \DateTime($record['date_modified']);
}
foreach ($record as $k => $item) {
$dataObject[$k.$newName] = $item;
}
}
}
if ('Leads' == $object && isset($dataObject['email1__Leads']) && null != $dataObject['email1__Leads']
&& '' != $dataObject['email1__Leads'] && in_array($dataObject['email1__Leads'], $sugarRejectedLeads)) {
continue; // Lead email is already in Sugar Contacts. Do not carry on
}
if (!empty($dataObject)) {
if ('Leads' == $object or 'Contacts' == $object) {
if (isset($dataObject['assigned_user_id__'.$object])) {
$auid = $dataObject['assigned_user_id__'.$object];
if (isset($onwerEmailByAssignedUserId[$auid])) {
$dataObject['owner_email'] = $onwerEmailByAssignedUserId[$auid];
}
}
$mauticObjectReference = 'lead';
$entity = $this->getMauticLead($dataObject, true, null, null, $object);
$detachClass = Lead::class;
$company = null;
$this->fetchDncToMautic($entity, $data);
if ($entity && isset($dataObject['account_id'.$newName]) && '' != trim($dataObject['account_id'.$newName])) {
$integrationCompanyEntity = $integrationEntityRepo->findOneBy(
[
'integration' => 'Sugarcrm',
'integrationEntity' => 'Accounts',
'internalEntity' => 'company',
'integrationEntityId' => $dataObject['account_id'.$newName],
]
);
if (isset($integrationCompanyEntity)) {
$companyId = $integrationCompanyEntity->getInternalEntityId();
$company = $companyRepo->find($companyId);
$this->companyModel->addLeadToCompany($company, $entity);
$companyRepo->detachEntity($company);
$this->em->detach($entity);
}
}
} elseif ('Accounts' === $object) {
$entity = $this->getMauticCompany($dataObject, $object);
$detachClass = Company::class;
$mauticObjectReference = 'company';
} else {
$this->logIntegrationError(
new \Exception(
sprintf('Received an unexpected object without an internalObjectReference "%s"', $object)
)
);
continue;
}
if ($entity) {
$integrationId = $integrationEntityRepo->getIntegrationsEntityId(
'Sugarcrm',
$object,
$mauticObjectReference,
$entity->getId()
);
if (null == $integrationId) {
$integrationEntity = new IntegrationEntity();
$integrationEntity->setDateAdded(new \DateTime());
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntity->setIntegration('Sugarcrm');
$integrationEntity->setIntegrationEntity($object);
$integrationEntity->setIntegrationEntityId($record['id']);
$integrationEntity->setInternalEntity($mauticObjectReference);
$integrationEntity->setInternalEntityId($entity->getId());
$integrationEntities[] = $integrationEntity;
} else {
$integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntities[] = $integrationEntity;
}
$this->em->detach($entity);
$entityRepository = $this->em->getRepository($detachClass);
$entityRepository->detachEntity($entity);
unset($entity);
} else {
continue;
}
++$count;
}
$this->em->getRepository(IntegrationEntity::class)->saveEntities($integrationEntities);
$this->integrationEntityModel->getRepository()->detachEntities($integrationEntities);
}
unset($data);
unset($integrationEntities);
unset($dataObject);
}
return $count;
}
/**
* @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder
* @param array $data
* @param string $formArea
*/
public function appendToForm(&$builder, $data, $formArea): void
{
if ('keys' == $formArea) {
$builder->add('version', ButtonGroupType::class, [
'choices' => [
'6.x/community' => '6',
'7.x' => '7',
],
'label' => 'mautic.sugarcrm.form.version',
'constraints' => [
new NotBlank([
'message' => 'mautic.core.value.required',
]),
],
'required' => true,
]);
}
if ('features' == $formArea) {
$builder->add(
'updateOwner',
ChoiceType::class,
[
'choices' => [
'mautic.sugarcrm.updateOwner' => 'updateOwner',
],
'expanded' => true,
'multiple' => true,
'label' => 'mautic.sugarcrm.form.updateOwner',
'label_attr' => ['class' => 'control-label'],
'placeholder' => false,
'required' => false,
'attr' => [
'onclick' => 'Mautic.postForm(mQuery(\'form[name="integration_details"]\'),\'\');',
],
]
);
$builder->add(
'updateDnc',
ChoiceType::class,
[
'choices' => [
'mautic.sugarcrm.updateDnc' => 'updateDnc',
],
'expanded' => true,
'multiple' => true,
'label' => 'mautic.sugarcrm.form.updateDnc',
'label_attr' => ['class' => 'control-label'],
'placeholder' => false,
'required' => false,
'attr' => [
'onclick' => 'Mautic.postForm(mQuery(\'form[name="integration_details"]\'),\'\');',
],
]
);
$builder->add(
'updateBlanks',
ChoiceType::class,
[
'choices' => [
'mautic.integrations.blanks' => 'updateBlanks',
],
'expanded' => true,
'multiple' => true,
'label' => 'mautic.integrations.form.blanks',
'label_attr' => ['class' => 'control-label'],
'placeholder' => false,
'required' => false,
]
);
$builder->add(
'objects',
ChoiceType::class,
[
'choices' => [
'mautic.sugarcrm.object.lead' => 'Leads',
'mautic.sugarcrm.object.contact' => 'Contacts',
'mautic.sugarcrm.object.company' => 'company',
],
'expanded' => true,
'multiple' => true,
'label' => 'mautic.sugarcrm.form.objects_to_pull_from',
'label_attr' => ['class' => ''],
'placeholder' => false,
'required' => false,
]
);
$builder->add(
'activityEvents',
ChoiceType::class,
[
'choices' => array_flip($this->leadModel->getEngagementTypes()), // Choice type expects labels as keys
'label' => 'mautic.salesforce.form.activity_included_events',
'label_attr' => [
'class' => 'control-label',
'data-toggle' => 'tooltip',
'title' => $this->translator->trans('mautic.salesforce.form.activity.events.tooltip'),
],
'multiple' => true,
'empty_data' => ['point.gained', 'form.submitted', 'email.read'], // BC with pre 2.11.0
'required' => false,
]
);
}
}
/**
* @param Lead $lead
* @param array $config
*
* @return array|bool
*/
public function pushLead($lead, $config = [])
{
$config = $this->mergeConfigToFeatureSettings($config);
if (empty($config['leadFields'])) {
return [];
}
$object = 'Leads'; // Sugar objects, default is Leads
// Check if lead has alredy been synched
/** @var IntegrationEntityRepository $integrationEntityRepo */
$integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class);
// Check if it is a sugar CRM alredy synched lead
$integrationId = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', $object, 'lead', $lead->getId());
if (empty($integrationId)) {
// Check if it is a sugar CRM alredy synched lead
$integrationId = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', 'Contacts', 'lead', $lead->getId());
if (!empty($integrationId)) {
$object = 'Contacts';
}
}
if (!empty($integrationId)) {
$integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
$lastSyncDate = $integrationEntity->getLastSyncDate();
$addedSyncDate = $integrationEntity->getDateAdded();
if ($addedSyncDate > $lastSyncDate) {
$lastSyncDate = $addedSyncDate;
}
$leadDateModified = $lead->getDateModified();
$leadDateAdded = $lead->getDateAdded();
$leadLastDate = $leadDateModified;
if ($leadDateAdded > $leadDateModified) {
$leadLastDate = $leadDateAdded;
}
if ($lastSyncDate >= $leadLastDate) {
return false;
} // Do not push lead if it was already synched
}
$fieldsToUpdateInSugar = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : [];
$leadSugarFieldsToCreate = $this->cleanSugarData($config, array_keys($config['leadFields']), $object);
$fieldsToUpdateInLeadsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, $object);
$leadFields = array_intersect_key($leadSugarFieldsToCreate, $fieldsToUpdateInLeadsSugar);
$mappedData[$object] = $this->populateLeadData($lead, ['leadFields' => $leadFields, 'object' => $object]);
$this->amendLeadDataBeforePush($mappedData[$object]);
if (empty($mappedData[$object])) {
return false;
}
if (!empty($integrationId)) {
$integrationEntity = $integrationEntityRepo->findOneBy(
[
'integration' => 'Sugarcrm',
'integrationEntity' => $object,
'internalEntity' => 'lead',
'internalEntityId' => $lead->getId(),
]
);
$mappedData[$object]['id'] = $integrationEntity->getIntegrationEntityId();
}
try {
if ($this->isAuthorized()) {
if (!is_null($lead->getOwner())) {
$sugarOwnerId = $this->getApiHelper()->getIdBySugarEmail(['emails' => [$lead->getOwner()->getEmail()]]);
if (!empty($sugarOwnerId)) {
$mappedData[$object]['assigned_user_id'] = array_values($sugarOwnerId)[0];
}
}
$createdLeadData = $this->getApiHelper()->createLead($mappedData[$object], $lead);
if (isset($createdLeadData['id'])) {
if (empty($integrationId)) {
$integrationEntity = new IntegrationEntity();
$integrationEntity->setDateAdded(new \DateTime());
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntity->setIntegration('Sugarcrm');
$integrationEntity->setIntegrationEntity($object);
$integrationEntity->setIntegrationEntityId($createdLeadData['id']);
$integrationEntity->setInternalEntity('lead');
$integrationEntity->setInternalEntityId($lead->getId());
} else {
$integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
}
$integrationEntity->setLastSyncDate(new \DateTime());
$this->em->persist($integrationEntity);
$this->em->flush($integrationEntity);
}
return true;
}
} catch (\Exception $e) {
$this->logIntegrationError($e);
}
return false;
}
/**
* Return key recognized by integration.
*/
public function convertLeadFieldKey(string $key, $field): string
{
$search = [];
foreach ($this->objects as $object) {
$search[] = '__'.$object;
}
return str_replace($search, '', $key);
}
/**
* @param array $fields
* @param array $keys
* @param string $object
*/
public function cleanSugarData($fields, $keys, $object): array
{
$leadFields = [];
foreach ($keys as $key) {
if (strstr($key, '__'.$object)) {
$newKey = str_replace('__'.$object, '', $key);
// $leadFields[$object][$newKey] = $fields['leadFields'][$key];
$leadFields[$newKey] = $fields['leadFields'][$key];
}
}
return $leadFields;
}
/**
* @param array $params
*
* @return mixed[]
*/
public function pushLeads($params = []): array
{
[$fromDate, $toDate] = $this->getSyncTimeframeDates($params);
$limit = $params['limit'];
$config = $this->mergeConfigToFeatureSettings();
$integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class);
$mauticData = $leadsToUpdate = $fields = [];
$fieldsToUpdateInSugar = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : [];
$leadFields = $config['leadFields'];
if (!empty($leadFields)) {
if ($keys = array_keys($leadFields, 'mauticContactTimelineLink')) {
foreach ($keys as $key) {
unset($leadFields[$key]);
}
}
if ($keys = array_keys($leadFields, 'mauticContactIsContactableByEmail')) {
foreach ($keys as $key) {
unset($leadFields[$key]);
}
}
$fields = implode(', l.', $leadFields);
$fields = 'l.owner_id,l.'.$fields;
$result = 0;
// Leads fields
$leadSugarFieldsToCreate = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Leads');
$fieldsToUpdateInLeadsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, 'Leads');
$leadSugarFields = array_intersect_key($leadSugarFieldsToCreate, $fieldsToUpdateInLeadsSugar);
// Contacts fields
$contactSugarFields = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Contacts');
$fieldsToUpdateInContactsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, 'Contacts');
$contactSugarFields = array_intersect_key($contactSugarFields, $fieldsToUpdateInContactsSugar);
$availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]);
// update lead/contact records
$leadsToUpdate = $integrationEntityRepo->findLeadsToUpdate($this->getName(), 'lead', $fields, $limit, $fromDate, $toDate, ['Contacts', 'Leads']);
}
$checkEmailsInSugar = [];
$deletedSugarLeads = [];
foreach ($leadsToUpdate as $object => $records) {
foreach ($records as $lead) {
if (isset($lead['email']) && !empty($lead['email'])) {
$lead = $this->getCompoundMauticFields($lead);
$checkEmailsInSugar[$object][mb_strtolower($lead['email'])] = $lead;
}
}
}
// Only get the max limit
if ($limit) {
$limit -= count($leadsToUpdate);
}
// create lead records
if (null === $limit || $limit && !empty($fields)) {
$leadsToCreate = $integrationEntityRepo->findLeadsToCreate('Sugarcrm', $fields, $limit, $fromDate, $toDate);
foreach ($leadsToCreate as $lead) {
if (isset($lead['email'])) {
$lead = $this->getCompoundMauticFields($lead);
$checkEmailsInSugar['Leads'][mb_strtolower($lead['email'])] = $lead;
}
}
}
foreach ($checkEmailsInSugar as $object => $checkObjectEmailsInSugar) {
[$checkEmailsUpdatedInSugar, $deletedRedords] = $this->getObjectDataToUpdate($checkObjectEmailsInSugar, $mauticData, $availableFields, $contactSugarFields, $leadSugarFields, $object);
// recheck synced records that might have been deleted in Sugar (deleted records don't come back in the query)
foreach ($checkEmailsUpdatedInSugar as $key => $deletedSugarRedords) {
if (isset($deletedSugarRedords['integration_entity_id']) && !empty($deletedSugarRedords['integration_entity_id'])) {
$deletedSugarLeads[$key] = $deletedSugarRedords['integration_entity_id'];
}
unset($checkEmailsUpdatedInSugar[$key]);
}
}
if (!empty($checkEmailsUpdatedInSugar)) {
$checkEmailsInSugar = array_merge($checkEmailsUpdatedInSugar, $checkEmailsInSugar);
}
// If there are any deleted, mark it as so to prevent them from being queried over and over or recreated
if ($deletedSugarLeads) {
$integrationEntityRepo->markAsDeleted($deletedSugarLeads, $this->getName(), 'lead');
}
// Create any left over
if ($checkEmailsInSugar && isset($checkEmailsInSugar['Leads'])) {
[$checkEmailsInSugar, $deletedSugarLeads] = $this->getObjectDataToUpdate($checkEmailsInSugar['Leads'], $mauticData, $availableFields, $contactSugarFields, $leadSugarFields, 'Leads');
$ownerAssignedUserIdByEmail = null;
foreach ($checkEmailsInSugar as $lead) {
if (isset($lead['email'])) {
$lead['owner_email'] = $this->getOwnerEmail($lead);
if ($lead['owner_email']) {
$ownerAssignedUserIdByEmail = $this->getApiHelper()->getIdBySugarEmail(['emails' => [$lead['owner_email']]]);
}
$this->buildCompositeBody(
$mauticData,
$availableFields,
$leadSugarFieldsToCreate, // use all matched fields when creating new records in Sugar
'Leads',
$lead,
$ownerAssignedUserIdByEmail
);
}
}
}
/** @var SugarcrmApi $apiHelper */
$apiHelper = $this->getApiHelper();
if (!empty($mauticData)) {
$result = $apiHelper->syncLeadsToSugar($mauticData);
}
return $this->processCompositeResponse($result);
}
/**
* Update body to sync.
*/
private function pushDncToSugar(array $lead, array &$body): void
{
$features = $this->settings->getFeatureSettings();
// update DNC sync disabled
if (empty($features['updateDnc'])) {
return;
}
$leadEntity = $this->leadModel->getEntity($lead['internal_entity_id']);
/** @var \Mautic\LeadBundle\Entity\DoNotContact[] $dncEntries */
$dncEntries = $this->doNotContactModel->getDncRepo()->getEntriesByLeadAndChannel($leadEntity, 'email');
$sugarDncKeys = array_combine(array_values($this->sugarDncKeys), $this->sugarDncKeys);
foreach ($dncEntries as $dncEntry) {
if (empty($sugarDncKeys)) {
continue;
}
// If DNC exists set to 1
switch ($dncEntry->getReason()) {
case 1:
case 3:
$body[] = ['name' => 'email_opt_out', 'value' => 1];
unset($sugarDncKeys['email_opt_out']);
break;
case 2:
$body[] = ['name' => 'invalid_email', 'value' => 1];
unset($sugarDncKeys['invalid_email']);
break;
}
}
// uncheck
// If DNC doesn't exist set to 1
foreach ($sugarDncKeys as $sugarDncKey) {
$body[] = ['name' => $sugarDncKey, 'value' => 0];
}
}
private function fetchDncToMautic(Lead $lead = null, array $data): void
{
if (is_null($lead)) {
return;
}
$features = $this->settings->getFeatureSettings();
if (empty($features['updateDnc'])) {
return;
}
// try find opt_out value for lead
$isContactable = true;
foreach ($data['relationship_list'] as $relationshipList) {
foreach ($relationshipList['link_list'] as $links) {
if ('email_addresses' == $links['name']) {
foreach ($links['records'] as $records) {
if (!empty($records['link_value']['email_address']['value']) && $records['link_value']['email_address']['value'] == $lead->getEmail() && !empty($records['link_value']['opt_out']['value'])) {
$isContactable = false;
break 3;
}
}
}
}
}
$reason = \Mautic\LeadBundle\Entity\DoNotContact::UNSUBSCRIBED;
if (!$isContactable) {
$this->doNotContactModel->addDncForContact($lead->getId(), 'email', $reason, $this->getName());
} else {
$this->doNotContactModel->removeDncForContact($lead->getId(), 'email', true, $reason);
}
}
/**
* @param string $object
*
* @return array The first element is made up of records that exist in Mautic, but which no longer have a match in CRM.
* We therefore assume that they've been deleted in CRM and will mark them as deleted in the pushLeads function (~line 1320).
* The second element contains Ids of records that were explicitly marked as deleted in CRM. ATM, nothing is done with this data.
*/
public function getObjectDataToUpdate($checkEmailsInSugar, &$mauticData, $availableFields, $contactSugarFields, $leadSugarFields, $object = 'Leads'): array
{
$config = $this->mergeConfigToFeatureSettings([]);
$queryParam = ('Leads' == $object) ? 'checkemail' : 'checkemail_contacts';
$sugarLead = $this->getApiHelper()->getLeads([$queryParam => array_keys($checkEmailsInSugar), 'offset' => 0, 'max_results' => 1000], $object);
$deletedSugarLeads = $sugarLeadRecords = [];
if (isset($sugarLead['entry_list'])) {
// Sugar 6.X
$sugarLeadRecords = [];
foreach ($sugarLead['entry_list'] as $k => $record) {
$sugarLeadRecord = [];
$sugarLeadRecord['id'] = $record['id'];
$sugarLeadRecord['module_name'] = $record['module_name'];
foreach ($record['name_value_list'] as $item) {
$sugarLeadRecord[$item['name']] = $item['value'];
}
if (!isset($sugarLeadRecord['email1'])) {
foreach ($sugarLead['relationship_list'][$k]['link_list'] as $links) {
if ('email_addresses' == $links['name']) {
foreach ($links['records'] as $records) {
foreach ($records as $contactEmails) {
foreach ($contactEmails as $anyAddress) {
if ('email_address' == $anyAddress['name'] && !empty($anyAddress['value'])) {
$sugarLeadRecord['email1'] = $anyAddress['value'];
break;
}
}
}
}
}
}
}
$sugarLeadRecords[] = $sugarLeadRecord;
}
} elseif (isset($sugarLead['records'])) { // Sugar 7
$sugarLeadRecords = $sugarLead['records'];
}
foreach ($sugarLeadRecords as $sugarLeadRecord) {
if (isset($sugarLeadRecord) && $sugarLeadRecord) {
$email = $sugarLeadRecord['email1'];
$key = mb_strtolower($email);
$leadOwnerEmails = [];
if (!empty($checkEmailsInSugar)) {
foreach ($checkEmailsInSugar as $emailKey => $mauticRecord) {
if ($key == $emailKey) {
$isConverted = (isset($sugarLeadRecord['contact_id'])
&& null != $sugarLeadRecord['contact_id']
&& '' != $sugarLeadRecord['contact_id']);
$sugarIdMapping[$checkEmailsInSugar[$key]['internal_entity_id']] = ($isConverted) ? $sugarLeadRecord['contact_id'] : $sugarLeadRecord['id'];
$lead['owner_email'] = $this->getOwnerEmail($mauticRecord);
if ($lead['owner_email']) {
$leadOwnerEmails[] = $lead['owner_email'];
}
$ownerAssignedUserIdByEmail = $this->getApiHelper()->getIdBySugarEmail(['emails' => array_unique($leadOwnerEmails)]);
if (empty($sugarLeadRecord['deleted']) || 0 == $sugarLeadRecord['deleted']) {
$sugarFieldMappings = $this->prepareFieldsForPush($availableFields);
if (isset($sugarFieldMappings['Contacts']) && !empty($sugarFieldMappings['Contacts'])) {
$contactSugarFields = $this->getBlankFieldsToUpdate($contactSugarFields, $sugarLeadRecord, $sugarFieldMappings['Contacts'], $config);
}
if (isset($sugarFieldMappings['Leads']) && !empty($sugarFieldMappings['Leads'])) {
$leadSugarFields = $this->getBlankFieldsToUpdate($leadSugarFields, $sugarLeadRecord, $sugarFieldMappings['Leads'], $config);
}
$this->buildCompositeBody(
$mauticData,
$availableFields,
$isConverted || ('Contacts' == $object) ? $contactSugarFields : $leadSugarFields,
$isConverted || ('Contacts' == $object) ? 'Contacts' : 'Leads',
$checkEmailsInSugar[$key],
$ownerAssignedUserIdByEmail,
$isConverted ? $sugarLeadRecord['contact_id'] : $sugarLeadRecord['id']
);
} else {
// @todo - Should return also deleted contacts from Sugar
$deletedSugarLeads[] = $sugarLeadRecord['id'];
if (!empty($sugarLeadRecord['contact_id']) || '' != $sugarLeadRecord['contact_id']) {
$deletedSugarLeads[] = $sugarLeadRecord['contact_id'];
}
}
unset($checkEmailsInSugar[$key]);
}
}
}
}
}
return [$checkEmailsInSugar, $deletedSugarLeads];
}
/**
* @return array
*/
public function getSugarLeadId($lead)
{
/** @var IntegrationEntityRepository $integrationEntityRepo */
$integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class);
// try searching for lead as this has been changed before in updated done to the plugin
$result = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', null, 'lead', $lead->getId());
return $result;
}
/**
* @param array $lead
*/
protected function getOwnerEmail($lead)
{
if (isset($lead['owner_id']) && !empty($lead['owner_id'])) {
/** @var \Mautic\UserBundle\Entity\User $user */
$user = $this->userModel->getEntity($lead['owner_id']);
return $user->getEmail();
}
return null;
}
protected function buildCompositeBody(&$mauticData, $availableFields, $fieldsToUpdateInSugarUpdate, $object, $lead, $onwerAssignedUserIdByEmail = null, $objectId = null)
{
$body = [];
if (isset($lead['email']) && !empty($lead['email'])) {
// update and create (one query) every 200 records
foreach ($fieldsToUpdateInSugarUpdate as $sugarField => $mauticField) {
$required = !empty($availableFields[$object][$sugarField.'__'.$object]['required']);
if (isset($lead[$mauticField])) {
if (str_contains($lead[$mauticField], '|')) {
// Transform Mautic Multi Select into SugarCRM/SuiteCRM Multi Select format
$value = $this->convertMauticToSuiteCrmMultiSelect($lead[$mauticField]);
} else {
$value = $lead[$mauticField];
}
$body[] = ['name' => $sugarField, 'value' => $value];
} elseif ($required) {
$value = $this->translator->trans('mautic.integration.form.lead.unknown');
$body[] = ['name' => $sugarField, 'value' => $value];
}
}
if (!empty($body)) {
$id = $lead['internal_entity_id'].'-'.$object.(!empty($lead['id']) ? '-'.$lead['id'] : '');
$body[] = ['name' => 'reference_id', 'value' => $id];
if ($objectId) {
$body[] = ['name' => 'id', 'value' => $objectId];
}
if (isset($onwerAssignedUserIdByEmail) && isset($lead['owner_email']) && isset($onwerAssignedUserIdByEmail[$lead['owner_email']])) {
$body[] = ['name' => 'assigned_user_id', 'value' => $onwerAssignedUserIdByEmail[$lead['owner_email']]];
}
// pushd DNC to Sugar CRM
$this->pushDncToSugar($lead, $body);
$mauticData[$object][] = $body;
}
}
}
/**
* @param array $response
*/
protected function processCompositeResponse($response): array
{
$created = 0;
$errored = 0;
$updated = 0;
$object = 'Lead';
$persistEntities = [];
if (is_array($response)) {
foreach ($response as $item) {
$contactId = $integrationEntityId = null;
if (!empty($item['reference_id'])) {
$reference = explode('-', $item['reference_id']);
if (3 === count($reference)) {
[$contactId, $object, $integrationEntityId] = $reference;
} else {
[$contactId, $object] = $reference;
}
}
if (isset($item['ko']) && $item['ko']) {
$this->logIntegrationError(new \Exception($item['error']));
if ($integrationEntityId) {
$integrationEntity = $this->em->getReference(IntegrationEntity::class, $integrationEntityId);
$integrationEntity->setLastSyncDate(new \DateTime());
$persistEntities[] = $integrationEntity;
} elseif ($contactId) {
$integrationEntity = new IntegrationEntity();
$integrationEntity->setDateAdded(new \DateTime());
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntity->setIntegration($this->getName());
$integrationEntity->setIntegrationEntity($object);
$integrationEntity->setInternalEntity('lead-error');
$integrationEntity->setInternal(['error' => $item['error']]);
$integrationEntity->setInternalEntityId($contactId);
$persistEntities[] = $integrationEntity;
++$errored;
}
} elseif (!$item['ko']) {
if ($item['new']) {
// New object created
$integrationEntity = new IntegrationEntity();
$integrationEntity->setDateAdded(new \DateTime());
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntity->setIntegration($this->getName());
$integrationEntity->setIntegrationEntity($object);
$integrationEntity->setIntegrationEntityId($item['id']);
$integrationEntity->setInternalEntity('lead');
$integrationEntity->setInternalEntityId($contactId);
$persistEntities[] = $integrationEntity;
++$created;
} else {
// Record was updated
if ($integrationEntityId) {
$integrationEntity = $this->em->getReference(IntegrationEntity::class, $integrationEntityId);
$integrationEntity->setLastSyncDate(new \DateTime());
} else {
// Found in Sugarcrm so create a new record for it
$integrationEntity = new IntegrationEntity();
$integrationEntity->setDateAdded(new \DateTime());
$integrationEntity->setLastSyncDate(new \DateTime());
$integrationEntity->setIntegration($this->getName());
$integrationEntity->setIntegrationEntity($object);
$integrationEntity->setIntegrationEntityId($item['id']);
$integrationEntity->setInternalEntity('lead');
$integrationEntity->setInternalEntityId($contactId);
}
$persistEntities[] = $integrationEntity;
++$updated;
}
} else {
$error = 'Unknown status code '.$item['httpStatusCode'];
$this->logIntegrationError(new \Exception($error.' ('.$item['reference_id'].')'));
}
}
if ($persistEntities) {
$this->em->getRepository(IntegrationEntity::class)->saveEntities($persistEntities);
$this->integrationEntityModel->getRepository()->detachEntities($persistEntities);
unset($persistEntities);
}
}
return [$updated, $created, $errored];
}
/**
* @param array $objects
*
* @return array
*/
protected function cleanPriorityFields($fieldsToUpdate, $objects = null)
{
if (null === $objects) {
$objects = ['Leads', 'Contacts'];
}
if (isset($fieldsToUpdate['leadFields'])) {
// Pass in the whole config
$fields = $fieldsToUpdate;
} else {
$fields = array_flip($fieldsToUpdate);
}
return $this->prepareFieldsForSync($fields, $fieldsToUpdate, $objects);
}
/**
* @param array $fields
* @param array $keys
* @param mixed $object
*
* @return array
*/
public function prepareFieldsForSync($fields, $keys, $object = null)
{
$leadFields = [];
if (null === $object) {
$object = 'Lead';
}
$objects = (!is_array($object)) ? [$object] : $object;
if (is_string($object) && 'Accounts' === $object) {
return $fields['companyFields'] ?? $fields;
}
if (isset($fields['leadFields'])) {
$fields = $fields['leadFields'];
$keys = array_keys($fields);
}
foreach ($objects as $obj) {
if (!isset($leadFields[$obj])) {
$leadFields[$obj] = [];
}
foreach ($keys as $key) {
if (strpos($key, '__'.$obj)) {
$newKey = str_replace('__'.$obj, '', $key);
if ('Id' === $newKey) {
// Don't map Id for push
continue;
}
$leadFields[$obj][$newKey] = $fields[$key];
}
}
}
return (is_array($object)) ? $leadFields : $leadFields[$object];
}
/**
* @param string $priorityObject
*
* @return mixed
*/
protected function getPriorityFieldsForMautic($config, $object = null, $priorityObject = 'mautic')
{
$fields = parent::getPriorityFieldsForMautic($config, $object, $priorityObject);
return ($object && isset($fields[$object])) ? $fields[$object] : $fields;
}
protected function prepareFieldsForPush($fields): array
{
$fieldMappings = [];
$required = [];
$config = $this->mergeConfigToFeatureSettings();
$contactFields = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Contacts');
$leadFields = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Leads');
if (!empty($contactFields)) {
foreach ($fields['Contacts'] as $key => $field) {
if ($field['required']) {
$required[$key] = $field;
}
}
$fieldMappings['Contacts']['required'] = [
'fields' => $required,
];
$fieldMappings['Contacts']['create'] = $contactFields;
}
if (!empty($leadFields)) {
foreach ($fields['Leads'] as $key => $field) {
if ($field['required']) {
$required[$key] = $field;
}
}
$fieldMappings['Leads']['required'] = [
'fields' => $required,
];
$fieldMappings['Leads']['create'] = $leadFields;
}
return $fieldMappings;
}
/**
* Converts Mautic Multi-Select String into the format used to store Multi-Select values used by SuiteCRM / SugarCRM 6.x.
*/
public function convertMauticToSuiteCrmMultiSelect($mauticMultiSelectStringToConvert): string
{
// $mauticMultiSelectStringToConvert = 'test|enhancedapi|dataservices';
$multiSelectArrayValues = explode('|', $mauticMultiSelectStringToConvert);
$convertedSugarCrmMultiSelectString = '';
foreach ($multiSelectArrayValues as $item) {
$convertedSugarCrmMultiSelectString = $convertedSugarCrmMultiSelectString.'^'.$item.'^,';
}
return substr($convertedSugarCrmMultiSelectString, 0, -1);
}
/**
* Checks if a string contains SuiteCRM / SugarCRM 6.x Multi-Select values.
*
* @param string $stringToCheck
*/
public function checkIfSugarCrmMultiSelectString($stringToCheck): bool
{
// Regular Express to check SugarCRM/SuiteCRM Multi-Select format below
// example format: '^choice1^,^choice2^,^choice_3^'
$regex = '/(\^)(?:([A-Za-z0-9\-\_]+))(\^)/';
if (preg_match($regex, $stringToCheck)) {
return true;
} else {
return false;
}
}
/**
* Converts a SuiteCRM / SugarCRM 6.x Multi-Select String into the format used to store Multi-Select values used by Mautic.
*/
public function convertSuiteCrmToMauticMultiSelect($suiteCrmMultiSelectStringToConvert): string
{
// Mautic Multi Select format - 'choice1|choice2|choice_3'
$regexString = '/(\^)(?:([A-Za-z0-9\-\_]+))(\^)/';
preg_match_all($regexString, $suiteCrmMultiSelectStringToConvert, $matches, PREG_SET_ORDER, 0);
$convertedString = '';
foreach ($matches as $innerArray) {
$convertedString = $convertedString.$innerArray[2].'|';
}
return substr($convertedString, 0, -1);
}
}