1 | <?php |
||
2 | |||
3 | /* |
||
4 | * @copyright 2014 Mautic Contributors. All rights reserved |
||
5 | * @author Mautic |
||
6 | * |
||
7 | * @link http://mautic.org |
||
8 | * |
||
9 | * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html |
||
10 | */ |
||
11 | |||
12 | namespace MauticPlugin\MauticCrmBundle\Integration; |
||
13 | |||
14 | use Mautic\FormBundle\Entity\Form; |
||
15 | use Mautic\LeadBundle\Entity\Company; |
||
16 | use Mautic\LeadBundle\Entity\Lead; |
||
17 | use Mautic\LeadBundle\Helper\IdentifyCompanyHelper; |
||
18 | use Mautic\PluginBundle\Entity\IntegrationEntity; |
||
19 | use Mautic\PluginBundle\Entity\IntegrationEntityRepository; |
||
20 | use Mautic\PluginBundle\Exception\ApiErrorException; |
||
21 | use MauticPlugin\MauticCrmBundle\Api\Zoho\Mapper; |
||
22 | use MauticPlugin\MauticCrmBundle\Api\ZohoApi; |
||
23 | use Symfony\Component\Console\Helper\ProgressBar; |
||
24 | use Symfony\Component\Console\Output\ConsoleOutput; |
||
25 | use Symfony\Component\Console\Output\OutputInterface; |
||
26 | use Symfony\Component\Form\Extension\Core\Type\ChoiceType; |
||
27 | use Symfony\Component\Form\FormBuilder; |
||
28 | |||
29 | /** |
||
30 | * @method ZohoApi getApiHelper |
||
31 | */ |
||
32 | class ZohoIntegration extends CrmAbstractIntegration |
||
33 | { |
||
34 | /** |
||
35 | * Returns the name of the social integration that must match the name of the file. |
||
36 | * |
||
37 | * @return string |
||
38 | */ |
||
39 | public function getName() |
||
40 | { |
||
41 | return 'Zoho'; |
||
42 | } |
||
43 | |||
44 | /** |
||
45 | * {@inheritdoc} |
||
46 | * |
||
47 | * @return string |
||
48 | */ |
||
49 | public function getAuthenticationType() |
||
50 | { |
||
51 | return 'oauth2'; |
||
52 | } |
||
53 | |||
54 | /** |
||
55 | * {@inheritdoc} |
||
56 | * |
||
57 | * @return array |
||
58 | */ |
||
59 | public function getRequiredKeyFields() |
||
60 | { |
||
61 | return [ |
||
62 | $this->getClientIdKey() => 'mautic.zoho.form.client_id', |
||
63 | $this->getClientSecretKey() => 'mautic.zoho.form.client_secret', |
||
64 | ]; |
||
65 | } |
||
66 | |||
67 | /** |
||
68 | * @return string |
||
69 | */ |
||
70 | public function getClientIdKey() |
||
71 | { |
||
72 | return 'client_id'; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * @return string |
||
77 | */ |
||
78 | public function getClientSecretKey() |
||
79 | { |
||
80 | return 'client_secret'; |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * @return string |
||
85 | */ |
||
86 | public function getAuthTokenKey() |
||
87 | { |
||
88 | return 'access_token'; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * @return string |
||
93 | */ |
||
94 | public function getAuthScope() |
||
95 | { |
||
96 | return 'ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.bulk.all,ZohoCRM.users.all,ZohoCRM.org.all'; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * @return string |
||
101 | */ |
||
102 | public function getDatacenter() |
||
103 | { |
||
104 | $featureSettings = $this->getKeys(); |
||
105 | |||
106 | return !empty($featureSettings['datacenter']) ? $featureSettings['datacenter'] : 'zoho.com'; |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * @return string |
||
111 | */ |
||
112 | public function getApiUrl() |
||
113 | { |
||
114 | return sprintf('https://accounts.%s', $this->getDatacenter()); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * {@inheritdoc} |
||
119 | * |
||
120 | * @return string |
||
121 | */ |
||
122 | public function getAccessTokenUrl() |
||
123 | { |
||
124 | return $this->getApiUrl().'/oauth/v2/token'; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * {@inheritdoc} |
||
129 | * |
||
130 | * @return string |
||
131 | */ |
||
132 | public function getAuthenticationUrl() |
||
133 | { |
||
134 | return $this->getApiUrl().'/oauth/v2/auth'; |
||
135 | } |
||
136 | |||
137 | /** |
||
138 | * @return array |
||
139 | */ |
||
140 | public function getSupportedFeatures() |
||
141 | { |
||
142 | return ['push_lead', 'get_leads', 'push_leads']; |
||
143 | } |
||
144 | |||
145 | /** |
||
146 | * Refresh tokens. |
||
147 | */ |
||
148 | public function getRefreshTokenKeys() |
||
149 | { |
||
150 | return [ |
||
151 | 'refresh_token', |
||
152 | 'expires', |
||
153 | ]; |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * {@inheritdoc} |
||
158 | * |
||
159 | * @param $data |
||
160 | */ |
||
161 | public function prepareResponseForExtraction($data) |
||
162 | { |
||
163 | // Extract expiry and set expires for zoho |
||
164 | if (is_array($data) && isset($data['expires_in'])) { |
||
165 | $data['expires'] = $data['expires_in'] + time(); |
||
166 | } |
||
167 | |||
168 | return $data; |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Amend mapped lead data before creating to Mautic. |
||
173 | * |
||
174 | * @param array $data |
||
175 | * @param string $object |
||
176 | * |
||
177 | * @return array |
||
178 | */ |
||
179 | public function amendLeadDataBeforeMauticPopulate($data, $object = null) |
||
180 | { |
||
181 | if ('company' === $object) { |
||
182 | $object = 'Accounts'; |
||
183 | } elseif ('Lead' === $object || 'Contact' === $object) { |
||
184 | $object .= 's'; // pluralize object name for Zoho |
||
185 | } |
||
186 | |||
187 | $config = $this->mergeConfigToFeatureSettings([]); |
||
188 | |||
189 | $result = []; |
||
190 | if (isset($data['data'])) { |
||
191 | $entity = null; |
||
192 | /** @var IntegrationEntityRepository $integrationEntityRepo */ |
||
193 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||
194 | $objects = $data['data']; |
||
195 | /** @var array $rows */ |
||
196 | // foreach ($rows as $row) { |
||
197 | $integrationEntities = []; |
||
198 | /** @var array $objects */ |
||
199 | foreach ($objects as $recordId => $entityData) { |
||
200 | $isModified = false; |
||
201 | if ('Accounts' === $object) { |
||
202 | $recordId = $entityData['id']; |
||
203 | // first try to find integration entity |
||
204 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
205 | 'Zoho', |
||
206 | $object, |
||
207 | 'company', |
||
208 | null, |
||
209 | null, |
||
210 | null, |
||
211 | false, |
||
212 | 0, |
||
213 | 0, |
||
214 | [$recordId] |
||
215 | ); |
||
216 | if (count($integrationId)) { // company exists, then update local fields |
||
217 | /** @var Company $entity */ |
||
218 | $entity = $this->companyModel->getEntity($integrationId[0]['internal_entity_id']); |
||
219 | $matchedFields = $this->populateMauticLeadData($entityData, $config, 'company'); |
||
220 | |||
221 | // Match that data with mapped lead fields |
||
222 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic_company'); |
||
223 | if (!empty($fieldsToUpdateInMautic)) { |
||
224 | $fieldsToUpdateInMautic = array_intersect_key($config['companyFields'], $fieldsToUpdateInMautic); |
||
225 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||
226 | } else { |
||
227 | $newMatchedFields = $matchedFields; |
||
228 | } |
||
229 | if (!isset($newMatchedFields['companyname'])) { |
||
230 | if (isset($newMatchedFields['companywebsite'])) { |
||
231 | $newMatchedFields['companyname'] = $newMatchedFields['companywebsite']; |
||
232 | } |
||
233 | } |
||
234 | |||
235 | // update values if already empty |
||
236 | foreach ($matchedFields as $field => $value) { |
||
237 | if (empty($entity->getFieldValue($field))) { |
||
238 | $newMatchedFields[$field] = $value; |
||
239 | } |
||
240 | } |
||
241 | |||
242 | // remove unchanged fields |
||
243 | foreach ($newMatchedFields as $k => $v) { |
||
244 | if ($entity->getFieldValue($k) === $v) { |
||
245 | unset($newMatchedFields[$k]); |
||
246 | } |
||
247 | } |
||
248 | |||
249 | if (count($newMatchedFields)) { |
||
250 | $this->companyModel->setFieldValues($entity, $newMatchedFields, false); |
||
251 | $this->companyModel->saveEntity($entity, false); |
||
252 | $isModified = true; |
||
253 | } |
||
254 | } else { |
||
255 | $entity = $this->getMauticCompany($entityData, 'Accounts'); |
||
256 | } |
||
257 | if ($entity) { |
||
258 | $result[] = $entity->getName(); |
||
259 | } |
||
260 | $mauticObjectReference = 'company'; |
||
261 | } elseif ('Leads' === $object) { |
||
262 | $recordId = $entityData['id']; |
||
263 | // first try to find integration entity |
||
264 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
265 | 'Zoho', |
||
266 | $object, |
||
267 | 'lead', |
||
268 | null, |
||
269 | null, |
||
270 | null, |
||
271 | false, |
||
272 | 0, |
||
273 | 0, |
||
274 | [$recordId] |
||
275 | ); |
||
276 | |||
277 | if (count($integrationId)) { // lead exists, then update |
||
278 | /** @var Lead $entity */ |
||
279 | $entity = $this->leadModel->getEntity($integrationId[0]['internal_entity_id']); |
||
280 | $matchedFields = $this->populateMauticLeadData($entityData, $config, $object); |
||
281 | |||
282 | // Match that data with mapped lead fields |
||
283 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic'); |
||
284 | |||
285 | if (!empty($fieldsToUpdateInMautic)) { |
||
286 | $fieldsToUpdateInMautic = array_intersect_key($config['leadFields'], $fieldsToUpdateInMautic); |
||
287 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||
288 | } else { |
||
289 | $newMatchedFields = $matchedFields; |
||
290 | } |
||
291 | |||
292 | // update values if already empty |
||
293 | foreach ($matchedFields as $field => $value) { |
||
294 | if (empty($entity->getFieldValue($field))) { |
||
295 | $newMatchedFields[$field] = $value; |
||
296 | } |
||
297 | } |
||
298 | // remove unchanged fields |
||
299 | foreach ($newMatchedFields as $k => $v) { |
||
300 | if ($entity->getFieldValue($k) === $v) { |
||
301 | unset($newMatchedFields[$k]); |
||
302 | } |
||
303 | } |
||
304 | if (count($newMatchedFields)) { |
||
305 | $this->leadModel->setFieldValues($entity, $newMatchedFields, false, false); |
||
306 | $this->leadModel->saveEntity($entity, false); |
||
307 | $isModified = true; |
||
308 | } |
||
309 | } else { |
||
310 | /** @var Lead $entity */ |
||
311 | $entity = $this->getMauticLead($entityData, true, null, null, $object); |
||
312 | } |
||
313 | |||
314 | if ($entity) { |
||
315 | $result[] = $entity->getEmail(); |
||
316 | } |
||
317 | |||
318 | // Associate lead company |
||
319 | if (!empty($entityData['Company']) |
||
320 | && $entityData['Company'] !== $this->translator->trans( |
||
321 | 'mautic.integration.form.lead.unknown' |
||
322 | ) |
||
323 | ) { |
||
324 | $company = IdentifyCompanyHelper::identifyLeadsCompany( |
||
325 | ['company' => $entityData['Company']], |
||
326 | null, |
||
327 | $this->companyModel |
||
328 | ); |
||
329 | |||
330 | if (!empty($company[2])) { |
||
331 | $syncLead = $this->companyModel->addLeadToCompany($company[2], $entity); |
||
332 | $this->em->detach($company[2]); |
||
333 | } |
||
334 | } |
||
335 | |||
336 | $mauticObjectReference = 'lead'; |
||
337 | } elseif ('Contacts' === $object) { |
||
338 | $recordId = $entityData['id']; |
||
339 | |||
340 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
341 | 'Zoho', |
||
342 | $object, |
||
343 | 'lead', |
||
344 | null, |
||
345 | null, |
||
346 | null, |
||
347 | false, |
||
348 | 0, |
||
349 | 0, |
||
350 | [$recordId] |
||
351 | ); |
||
352 | if (count($integrationId)) { // contact exists, then update |
||
353 | /** @var Lead $entity */ |
||
354 | $entity = $this->leadModel->getEntity($integrationId[0]['internal_entity_id']); |
||
355 | $matchedFields = $this->populateMauticLeadData($entityData, $config, $object); |
||
356 | |||
357 | // Match that data with mapped lead fields |
||
358 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic'); |
||
359 | if (!empty($fieldsToUpdateInMautic)) { |
||
360 | $fieldsToUpdateInMautic = array_intersect_key($config['leadFields'], $fieldsToUpdateInMautic); |
||
361 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||
362 | } else { |
||
363 | $newMatchedFields = $matchedFields; |
||
364 | } |
||
365 | |||
366 | // update values if already empty |
||
367 | foreach ($matchedFields as $field => $value) { |
||
368 | if (empty($entity->getFieldValue($field))) { |
||
369 | $newMatchedFields[$field] = $value; |
||
370 | } |
||
371 | } |
||
372 | |||
373 | // remove unchanged fields |
||
374 | foreach ($newMatchedFields as $k => $v) { |
||
375 | if ($entity->getFieldValue($k) === $v) { |
||
376 | unset($newMatchedFields[$k]); |
||
377 | } |
||
378 | } |
||
379 | |||
380 | if (count($newMatchedFields)) { |
||
381 | $this->leadModel->setFieldValues($entity, $newMatchedFields, false, false); |
||
382 | $this->leadModel->saveEntity($entity, false); |
||
383 | $isModified = true; |
||
384 | } |
||
385 | } else { |
||
386 | /** @var Lead $entity */ |
||
387 | $entity = $this->getMauticLead($entityData, true, null, null, $object); |
||
388 | } |
||
389 | |||
390 | if ($entity) { |
||
391 | $result[] = $entity->getEmail(); |
||
392 | |||
393 | // Associate lead company |
||
394 | if (!empty($entityData['AccountName']) |
||
395 | && $entityData['AccountName'] !== $this->translator->trans( |
||
396 | 'mautic.integration.form.lead.unknown' |
||
397 | ) |
||
398 | ) { |
||
399 | $company = IdentifyCompanyHelper::identifyLeadsCompany( |
||
400 | ['company' => $entityData['AccountName']], |
||
401 | null, |
||
402 | $this->companyModel |
||
403 | ); |
||
404 | |||
405 | if (!empty($company[2])) { |
||
406 | $syncLead = $this->companyModel->addLeadToCompany($company[2], $entity); |
||
407 | $this->em->detach($company[2]); |
||
408 | } |
||
409 | } |
||
410 | } |
||
411 | |||
412 | $mauticObjectReference = 'lead'; |
||
413 | } else { |
||
414 | $this->logIntegrationError( |
||
415 | new \Exception( |
||
416 | sprintf('Received an unexpected object "%s"', $object) |
||
417 | ) |
||
418 | ); |
||
419 | continue; |
||
420 | } |
||
421 | |||
422 | if ($entity) { |
||
423 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
424 | 'Zoho', |
||
425 | $object, |
||
426 | $mauticObjectReference, |
||
427 | $entity->getId() |
||
428 | ); |
||
429 | |||
430 | if (0 === count($integrationId)) { |
||
431 | $integrationEntity = new IntegrationEntity(); |
||
432 | $integrationEntity->setDateAdded(new \DateTime()); |
||
433 | $integrationEntity->setIntegration('Zoho'); |
||
434 | $integrationEntity->setIntegrationEntity($object); |
||
435 | $integrationEntity->setIntegrationEntityId($recordId); |
||
436 | $integrationEntity->setInternalEntity($mauticObjectReference); |
||
437 | $integrationEntity->setInternalEntityId($entity->getId()); |
||
438 | $integrationEntities[] = $integrationEntity; |
||
439 | } else { |
||
440 | $integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']); |
||
441 | if ($isModified) { |
||
442 | $integrationEntity->setLastSyncDate(new \DateTime()); |
||
443 | $integrationEntities[] = $integrationEntity; |
||
444 | } |
||
445 | } |
||
446 | $this->em->detach($entity); |
||
447 | unset($entity); |
||
448 | } else { |
||
449 | continue; |
||
450 | } |
||
451 | } |
||
452 | |||
453 | $this->em->getRepository('MauticPluginBundle:IntegrationEntity')->saveEntities($integrationEntities); |
||
454 | $this->em->clear('Mautic\PluginBundle\Entity\IntegrationEntity'); |
||
455 | //} |
||
456 | unset($integrationEntities); |
||
457 | } |
||
458 | |||
459 | return $result; |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * @param array $params |
||
464 | * @param string $query |
||
465 | * @param $executed |
||
466 | * @param array $result |
||
467 | * @param string $object |
||
468 | * |
||
469 | * @return int |
||
470 | */ |
||
471 | public function getLeads($params, $query, &$executed, $result = [], $object = 'Lead') |
||
472 | { |
||
473 | if ('Lead' === $object || 'Contact' === $object) { |
||
474 | $object .= 's'; // pluralize object name for Zoho |
||
475 | } |
||
476 | |||
477 | $executed = 0; |
||
478 | |||
479 | try { |
||
480 | if ($this->isAuthorized()) { |
||
481 | $config = $this->mergeConfigToFeatureSettings(); |
||
482 | $fields = $config['leadFields']; |
||
483 | $config['object'] = $object; |
||
484 | $aFields = $this->getAvailableLeadFields($config); |
||
485 | $mappedData = []; |
||
486 | |||
487 | foreach (array_keys($fields) as $k) { |
||
488 | if (isset($aFields[$object][$k])) { |
||
489 | $mappedData[] = $aFields[$object][$k]['api_name']; |
||
490 | } |
||
491 | } |
||
492 | |||
493 | $maxRecords = 200; |
||
494 | $fields = implode(',', $mappedData); |
||
495 | $oparams['fields'] = $fields; |
||
496 | $oparams['per_page'] = $maxRecords; // maximum number of records |
||
497 | if (isset($params['fetchAll'], $params['start']) && !$params['fetchAll']) { |
||
498 | $oparams['lastModifiedTime'] = date('c', strtotime($params['start'])); |
||
499 | } |
||
500 | |||
501 | if (!array_key_exists('page', $oparams)) { |
||
502 | $oparams['page'] = 1; |
||
503 | } |
||
504 | |||
505 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||
506 | $progress = new ProgressBar($params['output']); |
||
507 | $progress->start(); |
||
508 | } |
||
509 | |||
510 | while (true) { |
||
511 | $data = $this->getApiHelper()->getLeads($oparams, $object); |
||
512 | |||
513 | if (!isset($data['data'])) { |
||
514 | break; // no more data, exit loop |
||
515 | } |
||
516 | $result = $this->amendLeadDataBeforeMauticPopulate($data, $object); |
||
517 | $executed += count($result); |
||
518 | if (isset($params['output'])) { |
||
519 | if ($params['output']->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { |
||
520 | $params['output']->writeln($result); |
||
521 | } else { |
||
522 | $progress->advance(); |
||
523 | } |
||
524 | } |
||
525 | |||
526 | // prepare next loop |
||
527 | ++$oparams['page']; |
||
528 | } |
||
529 | |||
530 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||
531 | $progress->finish(); |
||
532 | } |
||
533 | } |
||
534 | } catch (\Exception $e) { |
||
535 | $this->logIntegrationError($e); |
||
536 | } |
||
537 | |||
538 | return $executed; |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * @param array $params |
||
543 | * @param null $query |
||
544 | * @param null $executed |
||
545 | * @param array $result |
||
546 | * |
||
547 | * @return int|null |
||
548 | */ |
||
549 | public function getCompanies($params = [], $query = null, &$executed = null, &$result = []) |
||
550 | { |
||
551 | $executed = 0; |
||
552 | $object = 'company'; |
||
553 | |||
554 | try { |
||
555 | if ($this->isAuthorized()) { |
||
556 | $config = $this->mergeConfigToFeatureSettings(); |
||
557 | $fields = $config['companyFields']; |
||
558 | $config['object'] = $object; |
||
559 | $aFields = $this->getAvailableLeadFields($config); |
||
560 | $mappedData = []; |
||
561 | |||
562 | foreach (array_keys($fields) as $k) { |
||
563 | if (isset($aFields[$object][$k])) { |
||
564 | $mappedData[] = $aFields[$object][$k]['api_name']; |
||
565 | } |
||
566 | } |
||
567 | |||
568 | $maxRecords = 200; |
||
569 | $fields = implode(',', $mappedData); |
||
570 | $oparams['fields'] = $fields; |
||
571 | $oparams['per_page'] = $maxRecords; // maximum number of records |
||
572 | if (isset($params['fetchAll'], $params['start']) && !$params['fetchAll']) { |
||
573 | $oparams['lastModifiedTime'] = date('c', strtotime($params['start'])); |
||
574 | } |
||
575 | |||
576 | if (!array_key_exists('page', $oparams)) { |
||
577 | $oparams['page'] = 1; |
||
578 | } |
||
579 | |||
580 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||
581 | $progress = new ProgressBar($params['output']); |
||
582 | $progress->start(); |
||
583 | } |
||
584 | |||
585 | while (true) { |
||
586 | $data = $this->getApiHelper()->getCompanies($oparams); |
||
587 | if (!isset($data['data'])) { |
||
588 | break; // no more data, exit loop |
||
589 | } |
||
590 | $result = $this->amendLeadDataBeforeMauticPopulate($data, $object); |
||
591 | $executed += count($result); |
||
592 | if (isset($params['output'])) { |
||
593 | if ($params['output']->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { |
||
594 | $params['output']->writeln($result); |
||
595 | } else { |
||
596 | $progress->advance(); |
||
597 | } |
||
598 | } |
||
599 | |||
600 | // prepare next loop |
||
601 | ++$oparams['page']; |
||
602 | } |
||
603 | |||
604 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||
605 | $progress->finish(); |
||
606 | } |
||
607 | } |
||
608 | } catch (\Exception $e) { |
||
609 | $this->logIntegrationError($e); |
||
610 | } |
||
611 | |||
612 | return $executed; |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * {@inheritdoc} |
||
617 | * |
||
618 | * @param array $data |
||
619 | * @param array $config |
||
620 | * @param null $object |
||
621 | * |
||
622 | * @return array |
||
623 | */ |
||
624 | public function populateMauticLeadData($data, $config = [], $object = 'Leads') |
||
625 | { |
||
626 | // Match that data with mapped lead fields |
||
627 | $aFields = $this->getAvailableLeadFields($config); |
||
628 | $matchedFields = []; |
||
629 | |||
630 | $fieldsName = ('company' === $object) ? 'companyFields' : 'leadFields'; |
||
631 | |||
632 | if (isset($aFields[$object])) { |
||
633 | $aFields = $aFields[$object]; |
||
634 | } |
||
635 | foreach ($aFields as $k => $v) { |
||
636 | foreach ($data as $dk => $dv) { |
||
637 | if ($dk === $v['api_name']) { |
||
638 | $matchedFields[$config[$fieldsName][$k]] = $dv; |
||
639 | } |
||
640 | } |
||
641 | } |
||
642 | |||
643 | return $matchedFields; |
||
644 | } |
||
645 | |||
646 | /** |
||
647 | * Generate the auth login URL. Note that if oauth2, response_type=code is assumed. If this is not the case, |
||
648 | * override this function. |
||
649 | * |
||
650 | * @return string |
||
651 | */ |
||
652 | public function getAuthLoginUrl() |
||
653 | { |
||
654 | $authType = $this->getAuthenticationType(); |
||
655 | |||
656 | if ('oauth2' == $authType) { |
||
657 | $callback = $this->getAuthCallbackUrl(); |
||
658 | $clientIdKey = $this->getClientIdKey(); |
||
659 | $state = $this->getAuthLoginState(); |
||
660 | $url = $this->getAuthenticationUrl() |
||
661 | .'?client_id='.$this->keys[$clientIdKey] |
||
662 | .'&response_type=code' |
||
663 | .'&redirect_uri='.urlencode($callback) |
||
664 | .'&state='.$state.'&prompt=consent&access_type=offline'; |
||
665 | |||
666 | if ($scope = $this->getAuthScope()) { |
||
667 | $url .= '&scope='.urlencode($scope); |
||
668 | } |
||
669 | |||
670 | if ($this->session) { |
||
671 | $this->session->set($this->getName().'_csrf_token', $state); |
||
672 | } |
||
673 | |||
674 | return $url; |
||
675 | } else { |
||
676 | return $this->router->generate( |
||
677 | 'mautic_integration_auth_callback', |
||
678 | ['integration' => $this->getName()] |
||
679 | ); |
||
680 | } |
||
681 | } |
||
682 | |||
683 | /** |
||
684 | * @param Form|FormBuilder $builder |
||
685 | * @param array $data |
||
686 | * @param string $formArea |
||
687 | * |
||
688 | * @throws \InvalidArgumentException |
||
689 | */ |
||
690 | public function appendToForm(&$builder, $data, $formArea) |
||
691 | { |
||
692 | if ('features' == $formArea) { |
||
693 | $builder->add( |
||
694 | 'updateBlanks', |
||
695 | ChoiceType::class, |
||
696 | [ |
||
697 | 'choices' => [ |
||
698 | 'mautic.integrations.blanks' => 'updateBlanks', |
||
699 | ], |
||
700 | 'expanded' => true, |
||
701 | 'multiple' => true, |
||
702 | 'label' => 'mautic.integrations.form.blanks', |
||
703 | 'label_attr' => ['class' => 'control-label'], |
||
704 | 'placeholder' => false, |
||
705 | 'required' => false, |
||
706 | ] |
||
707 | ); |
||
708 | } |
||
709 | if ('keys' === $formArea) { |
||
710 | $builder->add( |
||
711 | 'datacenter', |
||
712 | ChoiceType::class, |
||
713 | [ |
||
714 | 'choices' => [ |
||
715 | 'mautic.plugin.zoho.zone_us' => 'zoho.com', |
||
716 | 'mautic.plugin.zoho.zone_europe' => 'zoho.eu', |
||
717 | 'mautic.plugin.zoho.zone_japan' => 'zoho.co.jp', |
||
718 | 'mautic.plugin.zoho.zone_china' => 'zoho.com.cn', |
||
719 | ], |
||
720 | 'label' => 'mautic.plugin.zoho.zone_select', |
||
721 | 'placeholder' => false, |
||
722 | 'required' => true, |
||
723 | 'attr' => [ |
||
724 | 'tooltip' => 'mautic.plugin.zoho.zone.tooltip', |
||
725 | ], |
||
726 | ] |
||
727 | ); |
||
728 | } elseif ('features' === $formArea) { |
||
729 | $builder->add( |
||
730 | 'objects', |
||
731 | ChoiceType::class, |
||
732 | [ |
||
733 | 'choices' => [ |
||
734 | 'mautic.zoho.object.lead' => 'Leads', |
||
735 | 'mautic.zoho.object.contact' => 'Contacts', |
||
736 | 'mautic.zoho.object.account' => 'company', |
||
737 | ], |
||
738 | 'expanded' => true, |
||
739 | 'multiple' => true, |
||
740 | 'label' => $this->getTranslator()->trans('mautic.crm.form.objects_to_pull_from', ['%crm%' => 'Zoho']), |
||
741 | 'label_attr' => ['class' => ''], |
||
742 | 'placeholder' => false, |
||
743 | 'required' => false, |
||
744 | ] |
||
745 | ); |
||
746 | } |
||
747 | } |
||
748 | |||
749 | /** |
||
750 | * Get available company fields for choices in the config UI. |
||
751 | * |
||
752 | * @param array $settings |
||
753 | * |
||
754 | * @return array |
||
755 | */ |
||
756 | public function getFormCompanyFields($settings = []) |
||
757 | { |
||
758 | return $this->getFormFieldsByObject('Accounts', $settings); |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * @param array $settings |
||
763 | * |
||
764 | * @return array|mixed |
||
765 | */ |
||
766 | public function getFormLeadFields($settings = []) |
||
767 | { |
||
768 | $leadFields = $this->getFormFieldsByObject('Leads', $settings); |
||
769 | $contactFields = $this->getFormFieldsByObject('Contacts', $settings); |
||
770 | |||
771 | return array_merge($leadFields, $contactFields); |
||
772 | } |
||
773 | |||
774 | /** |
||
775 | * @param array $settings |
||
776 | * |
||
777 | * @return array|bool |
||
778 | * |
||
779 | * @throws ApiErrorException |
||
780 | */ |
||
781 | public function getAvailableLeadFields($settings = []) |
||
782 | { |
||
783 | $zohoFields = []; |
||
784 | $silenceExceptions = isset($settings['silence_exceptions']) ? $settings['silence_exceptions'] : true; |
||
785 | |||
786 | if (isset($settings['feature_settings']['objects'])) { |
||
787 | $zohoObjects = $settings['feature_settings']['objects']; |
||
788 | } else { |
||
789 | $settings = $this->settings->getFeatureSettings(); |
||
790 | $zohoObjects = isset($settings['objects']) ? $settings['objects'] : ['Leads']; |
||
791 | } |
||
792 | |||
793 | try { |
||
794 | if ($this->isAuthorized()) { |
||
795 | if (!empty($zohoObjects) && is_array($zohoObjects)) { |
||
796 | foreach ($zohoObjects as $zohoObject) { |
||
797 | // Check the cache first |
||
798 | $settings['cache_suffix'] = $cacheSuffix = '.'.$zohoObject; |
||
799 | if ($fields = parent::getAvailableLeadFields($settings)) { |
||
800 | $zohoFields[$zohoObject] = $fields; |
||
801 | continue; |
||
802 | } |
||
803 | $leadObject = $this->getApiHelper()->getLeadFields($zohoObject); |
||
804 | |||
805 | if (null === $leadObject || (isset($leadObject['status']) && 'error' === $leadObject['status'])) { |
||
806 | return []; |
||
807 | } |
||
808 | |||
809 | /** @var array $opts */ |
||
810 | $opts = $leadObject['fields']; |
||
811 | foreach ($opts as $field) { |
||
812 | if (true == $field['read_only']) { |
||
813 | continue; |
||
814 | } |
||
815 | |||
816 | $is_required = false; |
||
817 | if (true == $field['system_mandatory']) { |
||
818 | $is_required = true; |
||
819 | } |
||
820 | |||
821 | $zohoFields[$zohoObject][$field['api_name']] = [ |
||
822 | 'type' => 'string', |
||
823 | 'label' => $field['display_label'], |
||
824 | 'api_name' => $field['api_name'], |
||
825 | 'required' => $is_required, |
||
826 | ]; |
||
827 | } |
||
828 | if (empty($settings['ignore_field_cache'])) { |
||
829 | $this->cache->set('leadFields'.$cacheSuffix, $zohoFields[$zohoObject]); |
||
830 | } |
||
831 | } |
||
832 | } |
||
833 | } |
||
834 | } catch (ApiErrorException $exception) { |
||
835 | $this->logIntegrationError($exception); |
||
836 | |||
837 | if (!$silenceExceptions) { |
||
838 | if (false !== strpos($exception->getMessage(), 'Invalid Ticket Id')) { |
||
839 | // Use a bit more friendly message |
||
840 | $exception = new ApiErrorException('There was an issue with communicating with Zoho. Please try to reauthorize.'); |
||
841 | } |
||
842 | |||
843 | throw $exception; |
||
844 | } |
||
845 | |||
846 | return false; |
||
847 | } |
||
848 | |||
849 | return $zohoFields; |
||
850 | } |
||
851 | |||
852 | /** |
||
853 | * @param array $params |
||
854 | * |
||
855 | * @return mixed |
||
856 | */ |
||
857 | public function pushLeads($params = []) |
||
858 | { |
||
859 | $maxRecords = (isset($params['limit']) && $params['limit'] < 100) ? $params['limit'] : 100; |
||
860 | if (isset($params['fetchAll']) && $params['fetchAll']) { |
||
861 | $params['start'] = null; |
||
862 | $params['end'] = null; |
||
863 | } |
||
864 | $config = $this->mergeConfigToFeatureSettings(); |
||
865 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||
866 | $fieldsToUpdateInZoho = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : []; |
||
867 | $leadFields = array_unique(array_values($config['leadFields'])); |
||
868 | $totalUpdated = $totalCreated = $totalErrors = 0; |
||
869 | if ($key = array_search('mauticContactTimelineLink', $leadFields)) { |
||
870 | unset($leadFields[$key]); |
||
871 | } |
||
872 | if ($key = array_search('mauticContactIsContactableByEmail', $leadFields)) { |
||
873 | unset($leadFields[$key]); |
||
874 | } |
||
875 | if (empty($leadFields)) { |
||
876 | return [0, 0, 0]; |
||
877 | } |
||
878 | |||
879 | $fields = implode(', l.', $leadFields); |
||
880 | $fields = 'l.'.$fields; |
||
881 | |||
882 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||
883 | $fieldsToUpdate['Leads'] = array_values(array_intersect(array_keys($availableFields['Leads']), $fieldsToUpdateInZoho)); |
||
884 | $fieldsToUpdate['Contacts'] = array_values(array_intersect(array_keys($availableFields['Contacts']), $fieldsToUpdateInZoho)); |
||
885 | $fieldsToUpdate['Leads'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Leads'])); |
||
886 | $fieldsToUpdate['Contacts'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Contacts'])); |
||
887 | |||
888 | $progress = false; |
||
889 | $totalToUpdate = array_sum( |
||
890 | $integrationEntityRepo->findLeadsToUpdate('Zoho', 'lead', $fields, false, $params['start'], $params['end'], ['Contacts', 'Leads']) |
||
891 | ); |
||
892 | $totalToCreate = $integrationEntityRepo->findLeadsToCreate('Zoho', $fields, false, $params['start'], $params['end']); |
||
893 | $totalCount = $totalToCreate + $totalToUpdate; |
||
894 | |||
895 | if (defined('IN_MAUTIC_CONSOLE')) { |
||
896 | // start with update |
||
897 | if ($totalToUpdate + $totalToCreate) { |
||
898 | $output = new ConsoleOutput(); |
||
899 | $output->writeln("About $totalToUpdate to update and about $totalToCreate to create/update"); |
||
900 | $progress = new ProgressBar($output, $totalCount); |
||
901 | } |
||
902 | } |
||
903 | |||
904 | // Start with contacts so we know who is a contact when we go to process converted leads |
||
905 | $leadsToCreateInZ = []; |
||
906 | $leadsToUpdateInZ = []; |
||
907 | $isContact = []; |
||
908 | $integrationEntities = []; |
||
909 | |||
910 | // Fetch them separately so we can determine which oneas are already there |
||
911 | $toUpdate = $integrationEntityRepo->findLeadsToUpdate( |
||
912 | 'Zoho', |
||
913 | 'lead', |
||
914 | $fields, |
||
915 | $totalToUpdate, |
||
916 | $params['start'], |
||
917 | $params['end'], |
||
918 | 'Contacts', |
||
919 | [] |
||
920 | )['Contacts']; |
||
921 | |||
922 | if (is_array($toUpdate)) { |
||
923 | foreach ($toUpdate as $lead) { |
||
924 | if (isset($lead['email']) && !empty($lead['email'])) { |
||
925 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||
926 | $lead['integration_entity'] = 'Contacts'; |
||
927 | $leadsToUpdateInZ[$key] = $lead; |
||
928 | $isContact[$key] = $lead; |
||
929 | } |
||
930 | } |
||
931 | } |
||
932 | |||
933 | // Switch to Lead |
||
934 | $toUpdate = $integrationEntityRepo->findLeadsToUpdate( |
||
935 | 'Zoho', |
||
936 | 'lead', |
||
937 | $fields, |
||
938 | $totalToUpdate, |
||
939 | $params['start'], |
||
940 | $params['end'], |
||
941 | 'Leads', |
||
942 | [] |
||
943 | )['Leads']; |
||
944 | |||
945 | if (is_array($toUpdate)) { |
||
946 | foreach ($toUpdate as $lead) { |
||
947 | if (isset($lead['email']) && !empty($lead['email'])) { |
||
948 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||
949 | $lead = $this->getCompoundMauticFields($lead); |
||
950 | if (isset($isContact[$key])) { |
||
951 | $isContact[$key] = $lead; // lead-converted |
||
952 | } else { |
||
953 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
954 | 'Zoho', |
||
955 | 'Leads', |
||
956 | 'lead', |
||
957 | $lead['internal_entity_id'] |
||
958 | ); |
||
959 | |||
960 | $lead['integration_entity'] = 'Leads'; |
||
961 | $leadsToUpdateInZ[$key] = $lead; |
||
962 | $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationId[0]['id']); |
||
963 | $integrationEntities[] = $integrationEntity->setLastSyncDate(new \DateTime()); |
||
964 | } |
||
965 | } |
||
966 | } |
||
967 | } |
||
968 | unset($toUpdate); |
||
969 | |||
970 | // convert ignored contacts |
||
971 | foreach ($isContact as $email => $lead) { |
||
972 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||
973 | 'Zoho', |
||
974 | 'Leads', |
||
975 | 'lead', |
||
976 | $lead['internal_entity_id'] |
||
977 | ); |
||
978 | if (count($integrationId)) { // lead exists, then update |
||
979 | $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationId[0]['id']); |
||
980 | $integrationEntity->setLastSyncDate(new \DateTime()); |
||
981 | $integrationEntity->setInternalEntity('lead-converted'); |
||
982 | $integrationEntities[] = $integrationEntity; |
||
983 | unset($leadsToUpdateInZ[$email]); |
||
984 | } |
||
985 | } |
||
986 | |||
987 | //create lead records, including deleted on Zoho side (last_sync = null) |
||
988 | /** @var array $leadsToCreate */ |
||
989 | $leadsToCreate = $integrationEntityRepo->findLeadsToCreate('Zoho', $fields, $totalToCreate, $params['start'], $params['end']); |
||
990 | |||
991 | if (is_array($leadsToCreate)) { |
||
992 | foreach ($leadsToCreate as $lead) { |
||
993 | if (isset($lead['email']) && !empty($lead['email'])) { |
||
994 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||
995 | $lead = $this->getCompoundMauticFields($lead); |
||
996 | $lead['integration_entity'] = 'Leads'; |
||
997 | $leadsToCreateInZ[$key] = $lead; |
||
998 | } |
||
999 | } |
||
1000 | } |
||
1001 | unset($leadsToCreate); |
||
1002 | |||
1003 | if (count($integrationEntities)) { |
||
1004 | // Persist updated entities if applicable |
||
1005 | $integrationEntityRepo->saveEntities($integrationEntities); |
||
1006 | $this->em->clear(IntegrationEntity::class); |
||
1007 | } |
||
1008 | |||
1009 | // update leads and contacts |
||
1010 | $mapper = new Mapper($availableFields); |
||
1011 | foreach (['Leads', 'Contacts'] as $zObject) { |
||
1012 | $counter = 1; |
||
1013 | $mapper->setObject($zObject); |
||
1014 | foreach ($leadsToUpdateInZ as $lead) { |
||
1015 | if ($zObject !== $lead['integration_entity']) { |
||
1016 | continue; |
||
1017 | } |
||
1018 | |||
1019 | if ($progress) { |
||
1020 | $progress->advance(); |
||
1021 | } |
||
1022 | |||
1023 | $existingPerson = $this->getExistingRecord('Email', $lead['email'], $zObject); |
||
1024 | $objectFields = $this->prepareFieldsForPush($availableFields[$zObject]); |
||
1025 | $fieldsToUpdate[$zObject] = $this->getBlankFieldsToUpdate($fieldsToUpdate[$zObject], $existingPerson, $objectFields, $config); |
||
1026 | |||
1027 | $totalUpdated += $mapper |
||
1028 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||
1029 | ->setContact($lead) |
||
1030 | ->map($lead['internal_entity_id'], $lead['integration_entity_id']); |
||
1031 | ++$counter; |
||
1032 | |||
1033 | // ONLY 100 RECORDS CAN BE SENT AT A TIME |
||
1034 | if ($maxRecords === $counter) { |
||
1035 | $this->updateContactInZoho($mapper, $zObject, $totalUpdated, $totalErrors); |
||
1036 | $counter = 1; |
||
1037 | } |
||
1038 | } |
||
1039 | |||
1040 | if ($counter > 1) { |
||
1041 | $this->updateContactInZoho($mapper, $zObject, $totalUpdated, $totalErrors); |
||
1042 | } |
||
1043 | } |
||
1044 | |||
1045 | // create leads and contacts |
||
1046 | foreach (['Leads', 'Contacts'] as $zObject) { |
||
1047 | $counter = 1; |
||
1048 | $mapper->setObject($zObject); |
||
1049 | foreach ($leadsToCreateInZ as $lead) { |
||
1050 | if ($zObject !== $lead['integration_entity']) { |
||
1051 | continue; |
||
1052 | } |
||
1053 | if ($progress) { |
||
1054 | $progress->advance(); |
||
1055 | } |
||
1056 | |||
1057 | $totalCreated += $mapper |
||
1058 | ->setMappedFields($config['leadFields']) |
||
1059 | ->setContact($lead) |
||
1060 | ->map($lead['internal_entity_id']); |
||
1061 | ++$counter; |
||
1062 | |||
1063 | // ONLY 100 RECORDS CAN BE SENT AT A TIME |
||
1064 | if ($maxRecords === $counter) { |
||
1065 | $this->createContactInZoho($mapper, $zObject, $totalCreated, $totalErrors); |
||
1066 | $counter = 1; |
||
1067 | } |
||
1068 | } |
||
1069 | |||
1070 | if ($counter > 1) { |
||
1071 | $this->createContactInZoho($mapper, $zObject, $totalCreated, $totalErrors); |
||
1072 | } |
||
1073 | } |
||
1074 | |||
1075 | if ($progress) { |
||
1076 | $progress->finish(); |
||
1077 | $output->writeln(''); |
||
1078 | } |
||
1079 | |||
1080 | return [$totalUpdated, $totalCreated, $totalErrors, $totalCount - ($totalCreated + $totalUpdated + $totalErrors)]; |
||
1081 | } |
||
1082 | |||
1083 | /** |
||
1084 | * @param Lead|array $lead |
||
1085 | * @param array $config |
||
1086 | * |
||
1087 | * @return array|bool |
||
1088 | */ |
||
1089 | public function pushLead($lead, $config = []) |
||
1090 | { |
||
1091 | $config = $this->mergeConfigToFeatureSettings($config); |
||
1092 | $zObject = 'Leads'; |
||
1093 | |||
1094 | $fieldsToUpdateInZoho = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : []; |
||
1095 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||
1096 | $fieldsToUpdate['Leads'] = array_values(array_intersect(array_keys($availableFields['Leads']), $fieldsToUpdateInZoho)); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
1097 | $fieldsToUpdate['Contacts'] = array_values(array_intersect(array_keys($availableFields['Contacts']), $fieldsToUpdateInZoho)); |
||
1098 | $fieldsToUpdate['Leads'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Leads'])); |
||
1099 | $fieldsToUpdate['Contacts'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Contacts'])); |
||
1100 | $objectFields = $this->prepareFieldsForPush($availableFields[$zObject]); |
||
1101 | $existingPerson = $this->getExistingRecord('Email', $lead->getEmail(), $zObject); |
||
1102 | $fieldsToUpdate[$zObject] = $this->getBlankFieldsToUpdate($fieldsToUpdate[$zObject], $existingPerson, $objectFields, $config); |
||
1103 | |||
1104 | if (empty($config['leadFields'])) { |
||
1105 | return []; |
||
1106 | } |
||
1107 | |||
1108 | $mapper = new Mapper($availableFields); |
||
1109 | $mapper->setObject($zObject); |
||
1110 | |||
1111 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||
1112 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId('Zoho', $zObject, 'lead', $lead->getId()); |
||
1113 | |||
1114 | $counter = 0; |
||
1115 | $errorCounter = 0; |
||
1116 | |||
1117 | try { |
||
1118 | if ($this->isAuthorized()) { |
||
1119 | if (!empty($existingPerson) && empty($integrationId)) { |
||
1120 | $this->createIntegrationEntity($zObject, $existingPerson['id'], 'lead', $lead->getId()); |
||
1121 | |||
1122 | $mapper |
||
1123 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||
1124 | ->setContact($lead->getProfileFields()) |
||
1125 | ->map($lead->getId(), $existingPerson['id']); |
||
1126 | $this->updateContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||
1127 | } elseif (!empty($existingPerson) && !empty($integrationId)) { // contact exists, then update |
||
1128 | $mapper |
||
1129 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||
1130 | ->setContact($lead->getProfileFields()) |
||
1131 | ->map($lead->getId(), $existingPerson['id']); |
||
1132 | $this->updateContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||
1133 | } else { |
||
1134 | $mapper |
||
1135 | ->setMappedFields($config['leadFields']) |
||
1136 | ->setContact($lead->getProfileFields()) |
||
1137 | ->map($lead->getId()); |
||
1138 | $this->createContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||
1139 | } |
||
1140 | |||
1141 | return true; |
||
1142 | } |
||
1143 | } catch (\Exception $e) { |
||
1144 | $this->logIntegrationError($e); |
||
1145 | } |
||
1146 | |||
1147 | return false; |
||
1148 | } |
||
1149 | |||
1150 | /** |
||
1151 | * @param $fields |
||
1152 | * @param $sfRecord |
||
1153 | * @param $config |
||
1154 | * @param $objectFields |
||
1155 | */ |
||
1156 | public function getBlankFieldsToUpdate($fields, $sfRecord, $objectFields, $config) |
||
1157 | { |
||
1158 | //check if update blank fields is selected |
||
1159 | if (isset($config['updateBlanks']) && isset($config['updateBlanks'][0]) && 'updateBlanks' == $config['updateBlanks'][0]) { |
||
1160 | foreach ($sfRecord as $fieldName => $sfField) { |
||
1161 | if (array_key_exists($fieldName, $objectFields['required']['fields'])) { |
||
1162 | continue; // this will be treated differently |
||
1163 | } |
||
1164 | if ('null' === $sfField && array_key_exists($fieldName, $objectFields['create']) && !array_key_exists($fieldName, $fields)) { |
||
1165 | //map to mautic field |
||
1166 | $fields[$fieldName] = $objectFields['create'][$fieldName]; |
||
1167 | } |
||
1168 | } |
||
1169 | } |
||
1170 | |||
1171 | return $fields; |
||
1172 | } |
||
1173 | |||
1174 | /** |
||
1175 | * Get if data priority is enabled in the integration or not default is false. |
||
1176 | * |
||
1177 | * @return string |
||
1178 | */ |
||
1179 | public function getDataPriority() |
||
1180 | { |
||
1181 | return true; |
||
1182 | } |
||
1183 | |||
1184 | /** |
||
1185 | * @param array $response |
||
1186 | * @param string $zObject |
||
1187 | * @param bool $createIntegrationEntity |
||
1188 | * |
||
1189 | * @return int |
||
1190 | * |
||
1191 | * @throws \MauticPlugin\MauticCrmBundle\Api\Zoho\Exception\MatchingKeyNotFoundException |
||
1192 | */ |
||
1193 | private function consumeResponse($response, $zObject, $createIntegrationEntity = false, Mapper $mapper = null) |
||
1194 | { |
||
1195 | $rows = $response; |
||
1196 | if (isset($rows['data'][0])) { |
||
1197 | $rows = $rows['data']; |
||
1198 | } |
||
1199 | |||
1200 | $failed = 0; |
||
1201 | foreach ($rows as $key => $row) { |
||
1202 | $mauticId = $mapper->getContactIdByKey($key); |
||
1203 | |||
1204 | if ('SUCCESS' === $row['code'] && $createIntegrationEntity) { |
||
1205 | $zohoId = $row['details']['id']; |
||
1206 | $this->logger->debug('CREATE INTEGRATION ENTITY: '.$zohoId); |
||
1207 | $integrationId = $this->getIntegrationEntityRepository()->getIntegrationsEntityId( |
||
1208 | 'Zoho', |
||
1209 | $zObject, |
||
1210 | 'lead', |
||
1211 | null, |
||
1212 | null, |
||
1213 | null, |
||
1214 | false, |
||
1215 | 0, |
||
1216 | 0, |
||
1217 | $zohoId |
||
1218 | ); |
||
1219 | |||
1220 | if (0 === count($integrationId)) { |
||
1221 | $this->createIntegrationEntity($zObject, $zohoId, 'lead', $mauticId); |
||
1222 | } |
||
1223 | } elseif (isset($row['status']) && 'error' === $row['status']) { |
||
1224 | ++$failed; |
||
1225 | $exception = new ApiErrorException($row['message']); |
||
1226 | $exception->setContactId($mauticId); |
||
1227 | $this->logIntegrationError($exception); |
||
1228 | } |
||
1229 | } |
||
1230 | |||
1231 | return $failed; |
||
1232 | } |
||
1233 | |||
1234 | /** |
||
1235 | * @param string $seachColumn |
||
1236 | * @param string $searchValue |
||
1237 | * @param string $object |
||
1238 | * |
||
1239 | * @return array |
||
1240 | */ |
||
1241 | private function getExistingRecord($seachColumn, $searchValue, $object = 'Leads') |
||
1242 | { |
||
1243 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||
1244 | $records = $this->getApiHelper()->getSearchRecords($seachColumn, $searchValue, $object); |
||
1245 | $idField = [ |
||
1246 | 'id' => [ |
||
1247 | 'type' => 'string', |
||
1248 | 'label' => 'ID', |
||
1249 | 'api_name' => 'id', |
||
1250 | 'required' => true, |
||
1251 | ], |
||
1252 | ]; |
||
1253 | |||
1254 | return $this->parseZohoRecord($records, array_merge($availableFields[$object], $idField)); |
||
1255 | } |
||
1256 | |||
1257 | /** |
||
1258 | * @param $data |
||
1259 | * @param $fields |
||
1260 | * |
||
1261 | * @return array |
||
1262 | */ |
||
1263 | private function parseZohoRecord($data, $fields) |
||
1264 | { |
||
1265 | $parsedData = []; |
||
1266 | if (empty($data['data'])) { |
||
1267 | return $parsedData; |
||
1268 | } |
||
1269 | |||
1270 | $records = $data['data'][0]; |
||
1271 | foreach ($fields as $field) { |
||
1272 | foreach ($records as $recordKey => $recordValue) { |
||
1273 | if ($recordKey === $field['api_name']) { |
||
1274 | $parsedData[$recordKey] = $recordValue; |
||
1275 | continue; |
||
1276 | } |
||
1277 | } |
||
1278 | } |
||
1279 | |||
1280 | return $parsedData; |
||
1281 | } |
||
1282 | |||
1283 | /** |
||
1284 | * @param string $object |
||
1285 | * @param int $counter |
||
1286 | * @param int $errorCounter |
||
1287 | */ |
||
1288 | private function updateContactInZoho(Mapper $mapper, $object, &$counter, &$errorCounter) |
||
1289 | { |
||
1290 | $response = $this->getApiHelper()->updateLead($mapper->getArray(), $object); |
||
1291 | $failed = $this->consumeResponse($response, $object, false, $mapper); |
||
1292 | $counter -= $failed; |
||
1293 | $errorCounter += $failed; |
||
1294 | } |
||
1295 | |||
1296 | /** |
||
1297 | * @param string $object |
||
1298 | * @param int $counter |
||
1299 | * @param int $errorCounter |
||
1300 | */ |
||
1301 | private function createContactInZoho(Mapper $mapper, $object, &$counter, &$errorCounter) |
||
1302 | { |
||
1303 | $response = $this->getApiHelper()->createLead($mapper->getArray(), $object); |
||
1304 | $failed = $this->consumeResponse($response, $object, true, $mapper); |
||
1305 | $counter -= $failed; |
||
1306 | $errorCounter += $failed; |
||
1307 | } |
||
1308 | |||
1309 | /** |
||
1310 | * @param $fieldsToUpdate |
||
1311 | * @param array $objects |
||
1312 | * |
||
1313 | * @return array |
||
1314 | */ |
||
1315 | protected function cleanPriorityFields($fieldsToUpdate, $objects = null) |
||
1316 | { |
||
1317 | if (null === $objects) { |
||
1318 | $objects = ['Leads', 'Contacts']; |
||
1319 | } |
||
1320 | |||
1321 | if (isset($fieldsToUpdate['leadFields'])) { |
||
1322 | // Pass in the whole config |
||
1323 | $fields = $fieldsToUpdate; |
||
1324 | } else { |
||
1325 | $fields = array_flip($fieldsToUpdate); |
||
1326 | } |
||
1327 | |||
1328 | return $this->prepareFieldsForSync($fields, $fieldsToUpdate, $objects); |
||
1329 | } |
||
1330 | |||
1331 | /** |
||
1332 | * @param array $fields |
||
1333 | * @param array $keys |
||
1334 | * @param mixed $object |
||
1335 | * |
||
1336 | * @return array |
||
1337 | */ |
||
1338 | public function prepareFieldsForSync($fields, $keys, $object = null) |
||
1339 | { |
||
1340 | $leadFields = []; |
||
1341 | if (null === $object) { |
||
1342 | $object = 'Leads'; |
||
1343 | } |
||
1344 | |||
1345 | $objects = (!is_array($object)) ? [$object] : $object; |
||
1346 | if (is_string($object) && 'Accounts' === $object) { |
||
1347 | return isset($fields['companyFields']) ? $fields['companyFields'] : $fields; |
||
1348 | } |
||
1349 | |||
1350 | if (isset($fields['leadFields'])) { |
||
1351 | $fields = $fields['leadFields']; |
||
1352 | $keys = array_keys($fields); |
||
1353 | } |
||
1354 | |||
1355 | foreach ($objects as $obj) { |
||
1356 | if (!isset($leadFields[$obj])) { |
||
1357 | $leadFields[$obj] = []; |
||
1358 | } |
||
1359 | |||
1360 | foreach ($keys as $key) { |
||
1361 | $leadFields[$obj][$key] = $fields[$key]; |
||
1362 | } |
||
1363 | } |
||
1364 | |||
1365 | return (is_array($object)) ? $leadFields : $leadFields[$object]; |
||
1366 | } |
||
1367 | } |
||
1368 |