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); |
||||
0 ignored issues
–
show
Unused Code
introduced
by
Loading history...
|
|||||
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 = []) |
||||
0 ignored issues
–
show
The parameter
$query is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.
Loading history...
|
|||||
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)); |
||||
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 |