Initial xentral_oss_20.3.c9ffacf

This commit is contained in:
Alex
2021-05-21 08:49:41 +02:00
parent 5406e4a551
commit 34e5ac43d9
18884 changed files with 2109867 additions and 0 deletions
@@ -0,0 +1,65 @@
<?php
namespace Xentral\Modules\Hubspot\Scheduler\Adapter;
use ArrayObject;
use Xentral\Modules\Hubspot\Exception\SchedulerAdapterBadMethodException;
use Xentral\Modules\Hubspot\Scheduler\HubspotSchedulerTaskInterface;
final class SchedulerAdapter
{
/** @var HubspotSchedulerTaskInterface $schedulerTask */
private $schedulerTask;
/** @var bool $debugMode */
public $debugMode = false;
public function __construct(HubspotSchedulerTaskInterface $schedulerTask)
{
$this->schedulerTask = $schedulerTask;
}
public function __call($method, $args)
{
if (!method_exists($this->schedulerTask, $method)) {
$class = get_class($this->schedulerTask);
throw new SchedulerAdapterBadMethodException(sprintf('Method %s at %s class is missing', $method, $class));
}
if (is_callable([$this->schedulerTask, $method])) {
if ($method === 'execute' && empty($args) === true) {
$this->schedulerTask->beforeScheduleAction(new ArrayObject($args));
}
if ($this->debugMode === true) {
$this->debug(json_encode(new ArrayObject($args)));
$message = 'Call ' . get_class($this->schedulerTask) . '::' . $method . ' with args ' . json_encode(
$args
);
$this->debug($message);
}
call_user_func([$this->schedulerTask, $method], $args);
if ($method === 'execute' && empty($args) === true) {
$this->schedulerTask->afterScheduleAction(new ArrayObject($args));
}
} else {
$class = get_class($this->schedulerTask);
throw new SchedulerAdapterBadMethodException(sprintf('No callable method %s at %s class', $method, $class));
}
}
/**
* @param string $message
* @param null|string $debuggerFile
*
* @return null|void
*/
public function debug($message, $debuggerFile = null)
{
if ($this->debugMode === false) {
return null;
}
$logFile = null === $debuggerFile ? sys_get_temp_dir() . '/pull.log' : $debuggerFile;
file_put_contents($logFile, date('Y-m-d H:i:s') . '- ' . $message . "\n", FILE_APPEND | LOCK_EX);
}
}
@@ -0,0 +1,69 @@
<?php
namespace Xentral\Modules\Hubspot\Scheduler;
use ArrayObject;
use Xentral\Modules\Hubspot\RequestQueues\HubspotRequestQueuesService;
use Xentral\Modules\SubscriptionCycle\Scheduler\TaskMutexServiceInterface;
final class HubspotProcessSchedulerTask implements HubspotSchedulerTaskInterface
{
const CALL_TYPE = 'hubspot';
/** @var HubspotRequestQueuesService $gateway */
private $service;
/** @var TaskMutexServiceInterface $taskMutexService */
private $taskMutexService;
/**
* @param HubspotRequestQueuesService $service
* @param TaskMutexServiceInterface $taskMutexService
*/
public function __construct(HubspotRequestQueuesService $service, TaskMutexServiceInterface $taskMutexService)
{
$this->service = $service;
$this->taskMutexService = $taskMutexService;
}
/**
* @return void
*/
public function execute()
{
if ($this->taskMutexService->isTaskInstanceRunning('hubspot_process')) {
return;
}
$this->taskMutexService->setMutex('hubspot_process');
$this->service->execute(static::CALL_TYPE);
}
/**
* @return void
*/
public function cleanup()
{
$this->taskMutexService->setMutex('hubspot_process', false);
$this->service->cleanup();
}
/**
* @param ArrayObject $args
*
* @return void
*/
public function beforeScheduleAction(ArrayObject $args)
{
// TODO: Implement beforeScheduleAction() method.
}
/**
* @param ArrayObject $args
*
* @return void
*/
public function afterScheduleAction(ArrayObject $args)
{
// TODO: Implement afterScheduleAction() method.
}
}
@@ -0,0 +1,866 @@
<?php
namespace Xentral\Modules\Hubspot\Scheduler;
use JsonException;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Modules\Country\Gateway\CountryGateway;
use Xentral\Modules\Hubspot\Exception\HubspotConfigurationServiceException;
use Xentral\Modules\Hubspot\Exception\HubspotException;
use Xentral\Modules\Hubspot\Exception\MetaException;
use Xentral\Modules\Hubspot\HubspotConfigurationService;
use Xentral\Modules\Hubspot\HubspotContactService;
use Xentral\Modules\Hubspot\HubspotEventService;
use Xentral\Modules\Hubspot\HubspotContactGateway;
use Xentral\Modules\Hubspot\HubspotMetaService;
use ArrayObject;
use Xentral\Modules\SubscriptionCycle\Scheduler\TaskMutexServiceInterface;
final class HubspotPullContactsTask implements HubspotSchedulerTaskInterface
{
/** @var HubspotContactService $contactService */
private $contactService;
/** @var Database $db */
private $db;
/** @var HubspotMetaService $meta */
private $meta;
/** @var HubspotContactGateway $gateway */
private $gateway;
/** @var HubspotConfigurationService $configuration */
private $configuration;
/** @var HubspotEventService $eventService */
private $eventService;
/** @var CountryGateway $countryGateway */
private $countryGateway;
/** @var TaskMutexServiceInterface $taskMutexService */
private $taskMutexService;
/** @var bool $mutexOn */
private $mutexOn = false;
/**
* @param HubspotContactService $contactService
* @param Database $db
* @param HubspotMetaService $metaService
* @param HubspotContactGateway $gateway
* @param HubspotConfigurationService $configuration
* @param HubspotEventService $eventService
* @param CountryGateway $countryGateway
* @param TaskMutexServiceInterface $taskMutexService
*/
public function __construct(
HubspotContactService $contactService,
Database $db,
HubspotMetaService $metaService,
HubspotContactGateway $gateway,
HubspotConfigurationService $configuration,
HubspotEventService $eventService,
CountryGateway $countryGateway,
TaskMutexServiceInterface $taskMutexService
) {
$this->db = $db;
$this->contactService = $contactService;
$this->meta = $metaService;
$this->gateway = $gateway;
$this->configuration = $configuration;
$this->eventService = $eventService;
$this->countryGateway = $countryGateway;
$this->taskMutexService = $taskMutexService;
}
/**
* @param array $option
* @param null $type
* @param false $recursiveMode
*
* @throws EscapingException
* @throws HubspotConfigurationServiceException
* @throws HubspotException
* @throws JsonException
* @throws MetaException
*
* @return void
*/
public function execute($option = [], $type = null, $recursiveMode = false): void
{
if ($recursiveMode === false && $this->mutexOn === false) {
if ($this->taskMutexService->isTaskInstanceRunning('hubspot_pull_contacts')) {
return;
}
$this->taskMutexService->setMutex('hubspot_pull_contacts');
$this->mutexOn = true;
}
$settings = $this->configuration->getSettings();
if ($settings['hs_sync_addresses'] !== true) {
return;
}
$contactType = $type ?? 'all';
if (empty($recursiveMode) && $this->hasHsContacts() === true) {
$contactType = $contactType === 'all' ? 'recently_updated' : $contactType;
}
if ($contactType === 'company') {
$contactType = 'companies';
}
$ret = $this->pull($contactType, $option);
if (!empty($ret['has_more'])) {
$option = ['vidOffset' => $ret['vidOffset'], 'timeOffset' => $ret['timeOffset']];
usleep(5000000);
$this->execute($option, $contactType, true);
}
}
/**
* @param array $option
* @param null $type
* @param false $recursiveMode
*
* @throws EscapingException
* @throws HubspotConfigurationServiceException
* @throws HubspotException
* @throws JsonException
* @throws MetaException
*
* @return void
*/
protected function executeForCompany($option = [], $type = null, $recursiveMode = false): void
{
if ($recursiveMode === false && $this->mutexOn === false) {
if ($this->taskMutexService->isTaskInstanceRunning('hubspot_pull_contacts')) {
return;
}
$this->taskMutexService->setMutex('hubspot_pull_contacts');
$this->mutexOn = true;
}
$settings = $this->configuration->getSettings();
if ($settings['hs_sync_addresses'] !== true) {
return;
}
$contactType = $type ?? 'company';
if (empty($recursiveMode) && $this->hasHsContacts() === true) {
$contactType = $contactType === 'company' ? 'recent_companies' : $contactType;
}
$ret = $this->pull($contactType, $option);
if (!empty($ret['has_more'])) {
$option = ['vidOffset' => $ret['vidOffset'], 'timeOffset' => $ret['timeOffset']];
usleep(5000000);
$this->executeForCompany($option, $contactType, true);
}
}
/**
* @param string $type
*
* @return bool
*/
private function hasHsContacts(string $type = 'address'): bool
{
return $this->db->fetchValue(
'SELECT COUNT(`id`) FROM `hubspot_contacts` WHERE `hidden` = 0 AND `type` = :type',
['type' => $type]
) > 0;
}
/**
* @param string $email
* @param int $addressId
*
* @return bool
*/
private function contactPersonExists(string $email, int $addressId): bool
{
return $this->db->fetchValue(
'SELECT `id` FROM `ansprechpartner` WHERE `email` = :email AND `adresse` = :addressId',
['email' => $email, 'addressId' => $addressId]
) > 0;
}
/**
* @param string $name
*
* @return int
*/
private function getAddressIdByCompanyName(string $name): int
{
return $this->db->fetchValue(
"SELECT ad.id FROM `adresse` AS `ad` JOIN `hubspot_contacts` AS `hs`
ON(ad.id = hs.address_id)
WHERE ad.firma = 1 AND
ad.typ = 'firma' AND
hs.type = 'company' AND
ad.name = :name LIMIT 1",
['name' => $name]
);
}
/**
* @return void
*/
public function cleanup(): void
{
$this->taskMutexService->setMutex('hubspot_pull_contacts', false);
}
/**
* @param array $companies
*
* @throws HubspotException
* @throws EscapingException
*
* @return void
*/
private function importCompany(array $companies): void
{
$settings = $this->configuration->getSettings();
foreach ($companies as $company) {
$companyId = $company['companyId'];
if ($this->gateway->getMappingByHubspotId($companyId, 'company')) {
$this->updateXTContact($companyId, 'company');
continue;
}
$properties = $company['properties'];
$companyData = array_combine(array_keys($properties), array_column($properties, 'value'));
$companyNameTag = $properties['name'];
$contactSourceIds = array_combine(array_keys($properties), array_column($properties, 'sourceId'));
$hsLeadStatus = array_key_exists('hs_lead_status', $companyData) ? $companyData['hs_lead_status'] : '';
$createdAt = $companyNameTag['timestamp'];
$sourceEmail = $companyNameTag['sourceId'];
if (empty($sourceEmail)) {
$sourceEmail = $contactSourceIds['name'];
}
$country = empty($companyData['country']) ? 'DE' : $companyData['country'];
if ($country !== 'DE') {
$countryDb = $this->countryGateway->findByName($country);
if (!empty($countryDb)) {
$country = $countryDb['iso2_code'];
}
}
$address = [
'typ' => 'firma',
'sprache' => 'deutsch',
'name' => $companyData['name'],
'land' => $country,
'email' => $sourceEmail,
'kundenfreigabe' => 1,
'firma' => 1,
'waehrung' => 'EUR',
'internetseite' => empty($companyData['website']) ? '' : $companyData['website'],
'ort' => empty($companyData['city']) ? '' : $companyData['city'],
'plz' => empty($companyData['zip']) ? '' : $companyData['zip'],
'strasse' => empty($companyData['address']) ? '' : $companyData['address'],
];
try {
$leadFields = $this->configuration->matchSelectedAddressFreeField();
$lrField = $leadFields['hubspot_lr_field'];
$lsField = $leadFields['hubspot_ls_field'];
$address[$lsField] = $hsLeadStatus;
$address[$lrField] = empty($companyData['lifecyclestage']) ? '' : $companyData['lifecyclestage'];
} catch (HubspotException $exception) {
$this->eventService->add($exception->getMessage());
}
$numberOfEmployeesField = $this->configuration->tryGetConfiguration('hubspot_numberofemployees_field');
if (!empty($numberOfEmployeesField)) {
$fieldName = str_replace('adresse', '', $numberOfEmployeesField);
$numberOfEmployees = empty($companyData['numberofemployees']) ? 0 : $companyData['numberofemployees'];
$address[$fieldName] = $numberOfEmployees;
}
$defaultCustomFields = array_key_exists('hubspot_address_free_fields', $settings) ?
$settings['hubspot_address_free_fields'] : [];
if (!empty($defaultCustomFields)) {
foreach ($defaultCustomFields as $defaultCustomField => $systemField) {
$fieldName = str_replace('adresse', '', $systemField);
$fieldValue = empty($companyData[$defaultCustomField]) ? '' : $companyData[$defaultCustomField];
$address[$fieldName] = $fieldValue;
}
}
if (!empty($createdAt) &&
array_key_exists('hs_sync_addresses_from', $settings) &&
!empty($settings['hs_sync_addresses_from'])
) {
$addedTime = $createdAt / 1000;
if ($addedTime < $settings['hs_sync_addresses_from']) {
continue;
}
}
// Status
if (array_key_exists('hs_sync_address_status', $settings) &&
!empty($settings['hs_sync_address_status']) &&
$hsLeadStatus !== $settings['hs_sync_address_status']
) {
continue;
}
$hubspotOwnerId = (int)array_key_exists(
'hubspot_owner_id',
$companyData
) ? $companyData['hubspot_owner_id'] : 0;
if ($hubspotOwnerId !== 0) {
$staffId = $this->manageSaleStaffPerson($companyId, $hubspotOwnerId);
if ($staffId !== 0) {
$address['vertrieb'] = $staffId;
}
}
$this->addXTContact($companyId, 'company', $address);
}
}
/**
* @param string $type
* @param array $options
*
* @throws EscapingException
* @throws HubspotConfigurationServiceException
* @throws HubspotException
* @throws MetaException
* @throws JsonException
*
* @return array
*/
protected function pull($type = 'recently_updated', $options = []): array
{
$response = in_array($type, ['company', 'recent_companies']) ?
$this->contactService->pullCompanies($type, $options) :
$this->contactService->pullContacts($type, $options);
if ($response->getStatusCode() !== 200) {
return [];
}
$data = $response->getJson();
$offSet = array_key_exists('vid-offset', $data) ? $data['vid-offset'] : 0;
$singleOffset = array_key_exists('offset', $data) ? $data['offset'] : -1;
$hasMore = array_key_exists('has-more', $data) ? $data['has-more'] : false;
$timeOffset = array_key_exists('time-offset', $data) ? $data['time-offset'] : 0;
if (array_key_exists('companies', $data) || $type === 'recent_companies') {
$companyResponse = $type !== 'recent_companies' ? $data['companies'] : $data['results'];
$this->importCompany($companyResponse);
}
if (array_key_exists('contacts', $data)) {
$settings = $this->configuration->getSettings();
$contacts = $data['contacts'];
if (count($contacts) > 0) {
foreach ($contacts as $contact) {
$contactId = $contact['vid'];
$properties = $contact['properties'];
$createdAt = $contact['addedAt'];
$contactData = array_combine(array_keys($properties), array_column($properties, 'value'));
$hsLeadStatus = array_key_exists('hs_lead_status', $contactData) ?
$contactData['hs_lead_status'] : '';
$email = '';
$identityProfile = $contact['identity-profiles'];
if (!empty($identityProfile)) {
$identities = array_column($identityProfile, 'identities');
foreach ($identities as $identity) {
foreach ($identity as $item) {
if ($item['type'] === 'EMAIL') {
$email = $item['value'];
break;
}
}
}
}
$contactCompany = array_key_exists('company', $contactData) ? $contactData['company'] : '';
if (!empty($contactCompany) && ($addressId = $this->getAddressIdByCompanyName(
$contactCompany
))) {
$contactPerson = [
'type' => 'herr',
'name' => sprintf(
'%s %s',
empty($contactData['firstname']) ? 'Hubspot - ' : $contactData['firstname'],
empty($contactData['lastname']) ? '' : $contactData['lastname']
),
'adresse' => $addressId,
'email' => $email,
'land' => 'DE',
'phone' => $contactData['phone'],
];
if ($this->contactPersonExists($email, $addressId) === true) {
continue;
}
$this->addContactPerson($contactPerson, $contactId);
continue;
}
if ($this->gateway->getMappingByHubspotId($contactId)) {
// UPDATE
if ($type === 'recently_updated') {
$this->updateXTContact($contactId);
}
continue;
}
if (!empty($createdAt) &&
array_key_exists('hs_sync_addresses_from', $settings) &&
!empty($settings['hs_sync_addresses_from'])
) {
$addedTime = $createdAt / 1000;
if ($addedTime < $settings['hs_sync_addresses_from']) {
continue;
}
}
// Status
if (array_key_exists('hs_sync_address_status', $settings) &&
!empty($settings['hs_sync_address_status']) &&
$hsLeadStatus !== $settings['hs_sync_address_status']
) {
continue;
}
$this->addXTContact($contactId);
}
}
}
if ($timeOffset === 0) {
$timeOffset = time() * 1000;
}
$remainingData = ['vidOffset' => $offSet, 'timeOffset' => $timeOffset];
if ($singleOffset !== -1) {
$remainingData['offset'] = $singleOffset;
}
$this->meta->setName($type)->save($remainingData);
$remainingData['has_more'] = $hasMore;
return $remainingData;
}
/**
* @param int $contactId
* @param string $type
* @param array $contact
*
* @throws HubspotException
* @throws EscapingException
*
* @return void
*/
private function addXTContact(int $contactId = 0, string $type = 'address', array $contact = []): void
{
$addressContact = $contact;
if (empty($contact)) {
$addressContact = $this->configuration->formatAddressByResponse(
$this->contactService->getContactById($contactId)
);
}
if (empty($addressContact)) {
return;
}
$hubspotOwnerId = (int)$addressContact['hubspot_owner_id'];
unset($addressContact['hubspot_owner_id']);
if ($hubspotOwnerId !== 0) {
$staffId = $this->manageSaleStaffPerson($contactId, $hubspotOwnerId);
if ($staffId !== 0) {
$addressContact['vertrieb'] = $staffId;
}
}
$paramValues = array_map(
function ($value) {
if (empty($value)) {
return '\'\'';
}
return is_string($value) ? $this->db->escapeString($value) : $value;
},
array_values($addressContact)
);
$placeHolders = implode(',', array_fill(0, count($paramValues), '%s'));
$sql = 'INSERT INTO adresse(' . implode(',', array_keys($addressContact)) . ')
VALUES(' . vsprintf($placeHolders, $paramValues) . ')';
$this->db->perform($sql);
if ($addressId = $this->db->lastInsertId()) {
$this->db->perform(
'INSERT INTO `hubspot_contacts` (`hs_contact_id`, `created_at`, `address_id`, `type`)
VALUES (:id, NOW(), :aid, :type)',
['id' => $contactId, 'aid' => $addressId, 'type' => $type]
);
$eventItem = !empty($addressContact['email']) ? $addressContact['email'] : $addressContact['name'];
$eventMsg = sprintf(
'Neuen Kontakt (<a href="/index.php?module=adresse&action=edit&id=%d">%s</a>) vom Hubspot hinzugef&uuml;gt ins Xentral importiert.',
$addressId,
$eventItem
);
$this->eventService->add($eventMsg);
// ADD to group
$this->configuration->addContactToGroup($addressId);
// get companies contacts
if ($type === 'company') {
$this->importCompanyContactPersons($contactId, $addressId);
}
}
}
/**
* @param int $hubspotContactId
* @param string|null $type
*
* @throws HubspotException
*
* @return void
*/
private function updateXTContact(int $hubspotContactId = 0, ?string $type = 'address'): void
{
$remoteResponse = $type === 'company' ? $this->contactService->getCompanyById($hubspotContactId) :
$this->contactService->getContactById($hubspotContactId);
if ($remoteResponse->getStatusCode() !== 200) {
return;
}
try {
$xtContact = $type === 'company' ? $this->configuration->formatCompanyByResponse($remoteResponse) :
$this->configuration->formatAddressByResponse($remoteResponse);
} catch (HubspotException $exception) {
$this->eventService->add($exception->getMessage());
return;
}
$excludeVars = ['lead', 'typ', 'sprache', 'waehrung', 'kundenfreigabe'];
foreach ($excludeVars as $excludeVar) {
if (array_key_exists($excludeVar, $xtContact)) {
unset($xtContact[$excludeVar]);
}
}
$hubspotOwnerId = (int)$xtContact['hubspot_owner_id'];
unset($xtContact['hubspot_owner_id']);
if ($hubspotOwnerId !== 0) {
$staffId = $this->manageSaleStaffPerson($hubspotContactId, $hubspotOwnerId);
if ($staffId !== 0) {
$xtContact['vertrieb'] = $staffId;
}
}
$hHSContact = $this->gateway->getMappingByHubspotId($hubspotContactId, $type);
if (empty($hHSContact)) {
return;
}
$asPlaceHolders = array_map(
static function ($val) {
return vsprintf('%s=:%s', [$val, $val]);
},
array_keys($xtContact)
);
$placeHolders = implode(',', $asPlaceHolders);
$affected = $this->db->fetchAffected(
'UPDATE adresse SET ' . $placeHolders . ' WHERE id=' . $hHSContact['address_id'],
$xtContact
);
$eventItem = !empty($xtContact['email']) ? $xtContact['email'] : $xtContact['name'];
if ($affected > 0) {
$eventMsg = sprintf(
'Kontakt (<a href="/index.php?module=adresse&action=edit&id=%d">%s</a>) vom Hubspot ge&auml;ndert und ins Xentral importiert',
$hHSContact['address_id'],
$eventItem
);
$this->eventService->add($eventMsg);
}
if ($type === 'company') {
$this->importCompanyContactPersons($hubspotContactId, $hHSContact['address_id']);
}
}
/**
* @param ArrayObject $args
*
* @throws EscapingException
* @throws HubspotConfigurationServiceException
* @throws HubspotException
* @throws JsonException
* @throws MetaException
*
* @return void
*/
public function beforeScheduleAction(ArrayObject $args): void
{
if (empty($this->configuration->tryGetConfiguration(HubspotConfigurationService::HUBSPOT_SALT_CONF_NAME))) {
return;
}
try {
$leadsFields = $this->configuration->matchSelectedAddressFreeField();
} catch (HubspotException $exception) {
return;
}
if (empty($leadsFields)) {
return;
}
$this->executeForCompany();
}
/**
* @param array $contactPerson
* @param int $hubspotContactPersonId
*
* @throws HubspotConfigurationServiceException
*
* @return void
*/
private function addContactPerson(array $contactPerson, int $hubspotContactPersonId = 0): void
{
$this->db->perform(
'INSERT INTO `ansprechpartner` (
`typ`,
`name`,
`adresse`,
`email`,
`land`,
`logdatei`,
`telefon`
)
VALUES (:type, :name, :adresse, :email, :land, NOW(), :phone)',
$contactPerson
);
$companyContactPersonId = $this->db->lastInsertId();
if (empty($companyContactPersonId) || empty($hubspotContactPersonId)) {
return;
}
$contactExists = $this->gateway->hubspotContactExists($hubspotContactPersonId, ['address', 'person']);
if ($contactExists === true) {
$sql = 'UPDATE `hubspot_contacts` SET `type` = :type, `address_id` = :aid WHERE `hs_contact_id` = :id';
$this->db->perform(
$sql,
[
'id' => $hubspotContactPersonId,
'aid' => $companyContactPersonId,
'type' => 'person',
]
);
} else {
$this->db->perform(
'INSERT INTO `hubspot_contacts` (`hs_contact_id`, `created_at`, `address_id`, `type`)
VALUES (:id, NOW(), :aid, :type)',
['id' => $hubspotContactPersonId, 'aid' => $companyContactPersonId, 'type' => 'person']
);
}
if ($this->isContactPersonInHubspotGroup($companyContactPersonId) === false) {
$this->addContactPersonToHubspotGroup($companyContactPersonId);
}
}
/**
* @param int $contactPersonId
*
* @throws HubspotConfigurationServiceException
*
* @return void
*/
private function addContactPersonToHubspotGroup(int $contactPersonId): void
{
$defaultSettings = $this->configuration->getSettings();
$contactGrpId = array_key_exists('hs_contact_grp', $defaultSettings) ? $defaultSettings['hs_contact_grp'] : 0;
if (empty($contactGrpId)) {
return;
}
$sql = 'INSERT INTO `ansprechpartner_gruppen` (`ansprechpartner`, `gruppe`, `aktiv`) VALUES (:id, :group, 1)';
$this->db->perform($sql, ['id' => $contactPersonId, 'group' => $contactGrpId]);
}
/**
* @param int $contactPersonId
*
* @throws HubspotConfigurationServiceException
*
* @return bool
*/
private function isContactPersonInHubspotGroup(int $contactPersonId): bool
{
$defaultSettings = $this->configuration->getSettings();
$contactGrpId = array_key_exists('hs_contact_grp', $defaultSettings) ? $defaultSettings['hs_contact_grp'] : 0;
if (empty($contactGrpId)) {
return true;
}
$sql = 'SELECT `id` FROM `ansprechpartner_gruppen` WHERE `ansprechpartner` = :id AND `gruppe` = :group';
$result = $this->db->fetchValue($sql, ['id' => $contactPersonId, 'group' => $contactGrpId]);
return !empty($result);
}
/**
* @param int $internalContactPersonId
*
* @return void
*/
public function mapPersonToCompany(int $internalContactPersonId): void
{
$mappingData = $this->gateway->getHubspotMappingByPersonId($internalContactPersonId);
if (empty($mappingData)) {
return;
}
if (empty($mappingData['company_id']) || empty($mappingData['contact_id'])) {
return;
}
$this->contactService->addContactToCompany($mappingData['company_id'], $mappingData['contact_id']);
}
/**
* @param int $companyId
* @param int $addressId
*
* @throws HubspotConfigurationServiceException
*
* @return void
*/
private function importCompanyContactPersons(int $companyId, int $addressId): void
{
$contactPersonsResponse = $this->contactService->getCompanyContacts($companyId);
if ($contactPersonsResponse->getStatusCode() !== 200) {
return;
}
$contactPersonsData = $contactPersonsResponse->getJson();
if (empty($contactPersonsData['contacts'])) {
return;
}
$contactPersons = $contactPersonsData['contacts'];
foreach ($contactPersons as $contactPerson) {
$email = '';
$identities = $contactPerson['identities'];
$contactPersonVid = array_column($identities, 'vid');
$rawContact = [];
$contactPersonId = 0;
if (!empty($contactPersonVid)) {
$contactPersonId = $contactPersonVid[0];
$hubspotContactResponse = $this->contactService->getContactById($contactPersonId);
if ($hubspotContactResponse->getStatusCode() !== 200) {
continue;
}
$contactJs = $hubspotContactResponse->getJson();
$properties = $contactJs['properties'];
$rawContact = array_combine(
array_keys($properties),
array_column($properties, 'value')
);
$email = array_key_exists('email', $rawContact) ? $rawContact['email'] : '';
}
if (empty($rawContact)) {
$contactPersonIdentity = array_column($identities, 'identity');
foreach ($contactPersonIdentity as $identity) {
foreach ($identity as $item) {
if ($item['type'] === 'EMAIL') {
$email = $item['value'];
break;
}
}
}
$properties = $contactPerson['properties'];
$rawContact = array_combine(
array_column($properties, 'name'),
array_column($properties, 'value')
);
}
$contactPersonForDB = [
'type' => 'herr',
'name' => sprintf(
'%s %s',
empty($rawContact['firstname']) ? 'Hubspot - ' : $rawContact['firstname'],
empty($rawContact['lastname']) ? '' : $rawContact['lastname']
),
'adresse' => $addressId,
'email' => $email,
'land' => 'DE',
'phone' => empty($rawContact['phone']) ? '' : $rawContact['phone'],
];
if ($this->contactPersonExists($email, $addressId) === true) {
continue;
}
$this->addContactPerson($contactPersonForDB, $contactPersonId);
}
}
public function afterScheduleAction(ArrayObject $args): void
{
// TODO: Implement afterSchedule() method.
}
/**
* @param int $companyId
* @param int $hubspotOwnerId
*
* @throws HubspotException
*
* @return int
*/
private function manageSaleStaffPerson(int $companyId, int $hubspotOwnerId): int
{
if ($companyId === 0 || $hubspotOwnerId === 0) {
return 0;
}
$addressResponse = $this->contactService->getHubspotOwner($hubspotOwnerId);
$address = $addressResponse->getJson();
if (empty($address)) {
return 0;
}
$email = $address['email'];
$sql = 'SELECT id FROM `adresse` WHERE `typ` != :type AND `email` = :email LIMIT 1';
$addressId = $this->db->fetchValue($sql, ['type' => 'firma', 'email' => $email]);
$staffExists = $this->gateway->hubspotSaleStaffExists($companyId);
if ($staffExists === true) {
$sql = 'UPDATE `hubspot_contacts` SET `address_id` = :aid WHERE `hs_contact_id` = :id AND `data` = :company';
$this->db->perform(
$sql,
[
'id' => $hubspotOwnerId,
'aid' => $addressId,
'company' => (string)$companyId,
]
);
} elseif ($addressId !== 0) {
$this->db->perform(
'INSERT INTO `hubspot_contacts` (`hs_contact_id`, `created_at`, `address_id`, `type`, `data`)
VALUES (:id, NOW(), :aid, :type, :company)',
['id' => $hubspotOwnerId, 'aid' => $addressId, 'type' => 'sale_staff', 'company' => (string)$companyId]
);
}
return $addressId;
}
}
@@ -0,0 +1,317 @@
<?php
namespace Xentral\Modules\Hubspot\Scheduler;
use ArrayObject;
use Exception;
use Xentral\Components\Database\Database;
use Xentral\Modules\Hubspot\Exception\HubspotDealGatewayNotFoundException;
use Xentral\Modules\Hubspot\Exception\HubspotException;
use Xentral\Modules\Hubspot\HubspotEventService;
use Xentral\Modules\Hubspot\HubspotHttpResponseService as Response;
use Xentral\Modules\Hubspot\HubspotConfigurationService;
use Xentral\Modules\Hubspot\HubspotDealGateway;
use Xentral\Modules\Hubspot\HubspotDealService;
use Xentral\Modules\Hubspot\HubspotMetaService;
use Xentral\Modules\Hubspot\HubspotContactGateway;
use Xentral\Modules\SubscriptionCycle\Scheduler\TaskMutexServiceInterface;
final class HubspotPullDealsTask implements HubspotSchedulerTaskInterface
{
/** @var Database $db */
private $db;
/** @var HubspotMetaService $meta */
private $meta;
/** @var HubspotDealGateway $gateway */
private $gateway;
/** @var HubspotDealService $dealService */
private $dealService;
/** @var HubspotConfigurationService $configuration */
private $configuration;
/** @var HubspotEventService $eventService */
private $eventService;
/** @var HubspotContactGateway $contactGateway */
private $contactGateway;
/** @var TaskMutexServiceInterface $taskMutexService */
private $taskMutexService;
/**
* @param HubspotDealService $dealService
* @param Database $db
* @param HubspotMetaService $metaService
* @param HubspotDealGateway $gateway
* @param HubspotConfigurationService $configuration
* @param HubspotEventService $eventService
* @param HubspotContactGateway $contactGateway
* @param TaskMutexServiceInterface $taskMutexService
*/
public function __construct(
HubspotDealService $dealService,
Database $db,
HubspotMetaService $metaService,
HubspotDealGateway $gateway,
HubspotConfigurationService $configuration,
HubspotEventService $eventService,
HubspotContactGateway $contactGateway,
TaskMutexServiceInterface $taskMutexService
) {
$this->db = $db;
$this->dealService = $dealService;
$this->meta = $metaService;
$this->gateway = $gateway;
$this->configuration = $configuration;
$this->eventService = $eventService;
$this->contactGateway = $contactGateway;
$this->taskMutexService = $taskMutexService;
}
/**
* @param array $option
*
* @param null|string $type
*
* @param bool $recursiveMode
*
* @throws Exception
* @return void
*/
public function execute($option = [], $type = null, $recursiveMode = false): void
{
if ($recursiveMode === false) {
if ($this->taskMutexService->isTaskInstanceRunning('hubspot_pull_deals')) {
return;
}
$this->taskMutexService->setMutex('hubspot_pull_deals');
}
$settings = $this->configuration->getSettings();
if ($settings['hs_sync_deals'] !== true) {
return;
}
$type = $type ?? 'all_deals';
if ($type !== 'recently_updated_deals' && empty($recursiveMode) &&
$this->db->fetchValue('SELECT COUNT(id) FROM hubspot_deals WHERE hidden=0') > 0) {
$type = 'recently_created_deals';
}
$ret = $this->pull($type, $option);
if (array_key_exists('has_more', $ret) && $ret['has_more'] === true) {
$option = ['offset' => $ret['deal_offset']];
$this->execute($option, $type, true);
}
if ($ret['has_more'] === false && !in_array($type, ['all_deals', 'recently_updated_deals'])) {
$this->execute([], 'recently_updated_deals');
}
}
/**
* @return void
*/
public function cleanup()
{
$this->taskMutexService->setMutex('hubspot_pull_deals', false);
}
/**
* @param string $type
* @param array $options
*
* @throws Exception
* @return array
*/
protected function pull($type = 'recently_created_deals', $options = [])
{
/** @var Response $response */
$response = $this->dealService->pullDeals($type, $options);
if ($response->getStatusCode() === 200) {
$data = $response->getJson();
$offSet = array_key_exists('offset', $data) ? $data['offset'] : 0;
$since = array_key_exists('since', $data) ? $data['since'] : 0;
$hasMore = array_key_exists('hasMore', $data) ? $data['hasMore'] : false;
if (array_key_exists('deals', $data) || array_key_exists('results', $data)) {
$deals = array_key_exists('deals', $data) ? $data['deals'] : $data['results'];
if (count($deals) > 0) {
foreach ($deals as $deal) {
$dealId = $deal['dealId'];
$addressId = 0;
$associations = array_key_exists('associations', $deal) ? $deal['associations'] : [];
if (!empty($associations)) {
$dealContactIds = $associations['associatedVids'];
$companyIds = $associations['associatedCompanyIds'];
if (!empty($companyIds)) {
$dealContactIds = $companyIds;
}
if (!empty($dealContactIds)) {
$dealContactId = $dealContactIds[0];
$hsContact = $this->contactGateway->getMappingByHubspotId($dealContactId, null);
if (!empty($hsContact)) {
$addressId = $hsContact['address_id'];
}
}
}
if ($deal['isDeleted'] === true) {
// @todo delete from xentral
continue;
}
if ($mapping = $this->gateway->getByHubspotId($dealId)) {
// UPDATE
if (($type === 'recently_updated_deals')) {
$this->updateXTDeal($dealId, $mapping['wiedervorlage_id'], $addressId);
//$updated++;
}
continue;
}
if ($pipelineId = $this->addXTDeal($dealId, $addressId)) {
$this->db->perform(
'INSERT INTO `hubspot_deals` (`hs_deal_id`, `created_at`, `wiedervorlage_id`)
VALUES (:id,NOW(), :pipelineId)',
['id' => (int)$dealId, 'pipelineId' => $pipelineId]
);
}
}
}
}
if ($since === 0) {
$since = time() * 1000;
}
$this->meta->setName($type)->save(['offset' => $offSet, 'since' => $since]);
return ['deal_offset' => $offSet, 'has_more' => $hasMore];
}
return [];
}
public function beforeScheduleAction(ArrayObject $data)
{
if (empty($this->configuration->tryGetConfiguration(HubspotConfigurationService::HUBSPOT_SALT_CONF_NAME))) {
return;
}
try {
$leadsFields = $this->configuration->matchSelectedAddressFreeField();
} catch (HubspotException $exception) {
return;
}
if (empty($leadsFields)) {
return;
}
}
/**
* @param ArrayObject $data
*/
public function afterScheduleAction(ArrayObject $data)
{
// TODO: Implement afterSchedule() method.
}
/**
* @param $dealId
* @param int $addressId
*
* @throws HubspotException
* @throws HubspotDealGatewayNotFoundException
*
* @throws Exception
* @return int
*/
private function addXTDeal($dealId, $addressId = 0): int
{
if ($xtDeal = $this->configuration->formatDealByResponse($this->dealService->getDealById($dealId))) {
$xtDeal['adr'] = $addressId;
$this->db->perform(
'INSERT INTO `wiedervorlage` (
`bezeichnung`,
`datum_angelegt`,
`zeit_angelegt`,
`datum_erinnerung`,
`zeit_erinnerung`,
`stages`,
`adresse`
)
VALUES(:bezeichnung, :datum_angelegt, :zeit_angelegt, :datum_erinnerung, :zeit_erinnerung, :stages, :adr)',
$xtDeal
);
$latestId = $this->db->lastInsertId();
if ($data = $this->gateway->getMappingStageByResubmissionStageId($xtDeal['stages'])) {
$viewId = $data['wiedervorlage_view_id'];
// update
$eventMsg = sprintf(
'Neues Deal(<a href="/index.php?module=wiedervorlage&action=list&view=%d">%s</a>) vom Hubspot importiert.',
$viewId,
$xtDeal['bezeichnung']
);
$this->eventService->add($eventMsg);
}
return $latestId;
}
return 0;
}
/**
* @param int $dealId
* @param int $wvId
* @param int $addressId
*
* @throws Exception
*/
private function updateXTDeal($dealId, $wvId, $addressId = 0)
{
if ($xtDeal = $this->configuration->formatDealByResponse($this->dealService->getDealById($dealId))) {
$xtDeal['adr'] = $addressId;
$affected = $this->db->fetchAffected(
'UPDATE `wiedervorlage`
SET `bezeichnung` = :bezeichnung,
`datum_angelegt` = :datum_angelegt,
`zeit_angelegt` = :zeit_angelegt,
`datum_erinnerung` = :datum_erinnerung,
`zeit_erinnerung` = :zeit_erinnerung,
`stages` = :stages,
`adresse` = :adr
WHERE `id` =' . $wvId,
$xtDeal
);
if ($affected > 0) {
$data = $this->gateway->getMappingStageByResubmissionStageId($xtDeal['stages']);
if (empty($data)) {
return;
}
$viewId = $data['wiedervorlage_view_id'];
// update
$eventMsg = sprintf(
'Ge&auml;ndertes Deal(<a href="/index.php?module=wiedervorlage&action=list&view=%d">%s</a>) vom Hubspot importiert.',
$viewId,
$xtDeal['bezeichnung']
);
$this->eventService->add($eventMsg);
}
}
}
}
@@ -0,0 +1,289 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Hubspot\Scheduler;
use ArrayObject;
use Xentral\Components\Database\Database;
use Xentral\Modules\Hubspot\Exception\HubspotConfigurationServiceException;
use Xentral\Modules\Hubspot\Exception\HubspotException;
use Xentral\Modules\Hubspot\Exception\MetaException;
use Xentral\Modules\Hubspot\HubspotConfigurationService;
use Xentral\Modules\Hubspot\HubspotContactGateway;
use Xentral\Modules\Hubspot\HubspotEngagementService;
use Xentral\Modules\Hubspot\HubspotEventService;
use Xentral\Modules\Hubspot\HubspotMetaService;
use Xentral\Modules\SubscriptionCycle\Scheduler\TaskMutexServiceInterface;
final class HubspotPullEngagementsTask implements HubspotSchedulerTaskInterface
{
/** @var int[] $itemsCount */
private $itemsCount = ['count' => 100];
/** @var Database $db */
private $db;
/** @var HubspotEngagementService $engagementsService */
private $engagementsService;
/** @var HubspotMetaService $meta */
private $meta;
/** @var HubspotConfigurationService $configuration */
private $configuration;
/** @var HubspotEventService $event */
private $event;
/** @var HubspotContactGateway $contactGateway */
private $contactGateway;
/** @var TaskMutexServiceInterface $taskMutexService */
private $taskMutexService;
/**
* @param Database $db
* @param HubspotEngagementService $engagementService
* @param HubspotMetaService $metaService
* @param HubspotConfigurationService $configuration
* @param HubspotEventService $eventService
* @param HubspotContactGateway $contactGateway
* @param TaskMutexServiceInterface $taskMutexService
*/
public function __construct(
Database $db,
HubspotEngagementService $engagementService,
HubspotMetaService $metaService,
HubspotConfigurationService $configuration,
HubspotEventService $eventService,
HubspotContactGateway $contactGateway,
TaskMutexServiceInterface $taskMutexService
) {
$this->db = $db;
$this->engagementsService = $engagementService;
$this->configuration = $configuration;
$this->meta = $metaService;
$this->event = $eventService;
$this->contactGateway = $contactGateway;
$this->taskMutexService = $taskMutexService;
}
/**
* @throws HubspotException
*
* @return void
*/
public function execute(): void
{
if ($this->taskMutexService->isTaskInstanceRunning('hubspot_pull_engagements')) {
return;
}
$this->taskMutexService->setMutex('hubspot_pull_engagements');
try {
$settings = $this->configuration->getSettings();
} catch (HubspotConfigurationServiceException $e) {
return;
}
if (empty($settings['hs_sync_engagements'])) {
return;
}
$this->recursiveExecute();
}
/**
* @param array $option
*
* @throws HubspotException
*
* @return void
*/
private function recursiveExecute(array $option = []): void
{
$remainingData = $this->pull($option);
if (!empty($remainingData['has_more'])) {
unset($remainingData['has_more']);
$this->recursiveExecute($remainingData);
}
}
/**
* @param array $options
*
* @throws HubspotException
*
* @return array|null
*/
private function pull(array $options = []): ?array
{
$options = array_merge($options, $this->itemsCount);
$metaInfo = $this->meta->setName('recent_engagements')->get();
$requestData = [];
if (array_key_exists('since', $metaInfo)) {
$requestData['since'] = $metaInfo['since'];
}
if (array_key_exists('offset', $metaInfo)) {
$requestData['offset'] = $metaInfo['offset'];
}
$options = array_merge($options, $requestData);
$response = $this->engagementsService->getRecentEngagements($options);
if ($response->getStatusCode() !== 200) {
return null;
}
$data = $response->getJson();
$offSet = array_key_exists('offset', $data) ? $data['offset'] : 0;
$hasMore = array_key_exists('hasMore', $data) ? $data['hasMore'] : false;
$total = array_key_exists('total', $data) ? $data['total'] : 0;
$since = array_key_exists('since', $data) ? $data['since'] : time() * 1000;
if ($total === 0 || !array_key_exists('results', $data)) {
return null;
}
$engagements = $data['results'];
foreach ($engagements as $engagement) {
$engagementData = $engagement['engagement'];
// ONLY TYPE NOTE IS CURRENTLY ALLOWED
if ($engagementData['type'] !== 'NOTE') {
continue;
}
$engagementId = $engagementData['id'];
$engagementAssociations = $engagement['associations'];
$metadata = $engagement['metadata'];
$body = $metadata['body'];
$companyIds = null;
$contactIds = null;
if ($this->engagementExists($engagementId)) {
continue;
}
foreach ($engagementAssociations as $intended => $values) {
if (!is_string($intended)) {
continue;
}
if ($intended === 'companyIds') {
$companyIds = $values;
}
if ($intended === 'contactIds') {
$contactIds = $values;
}
}
if (empty($companyIds) && empty($contactIds)) {
continue;
}
$intendedIds = array_merge($companyIds, $contactIds);
foreach ($intendedIds as $intendedId) {
$noteId = $this->addNote($intendedId, $body);
if (empty($noteId)) {
continue;
}
$mapping = $this->contactGateway->getMappingByHubspotId($intendedId, null);
if (empty($mapping)) {
continue;
}
$addressId = $mapping['address_id'];
$this->db->perform(
'INSERT INTO `hubspot_contacts` (`hs_contact_id`, `created_at`, `address_id`, `type`)
VALUES (:id, NOW(), :aid, :type)',
['id' => $engagementId, 'aid' => $noteId, 'type' => 'note']
);
$eventItem = 'Adresse';
$eventMsg = sprintf(
'Neue Notiz vom Hubspot hinzugef&uuml;gt ins Xentral f&uuml;r (<a href="/index.php?module=adresse&action=edit&id=%d">%s</a>) importiert.',
$addressId,
$eventItem
);
$this->event->add($eventMsg);
}
}
$remainingData = ['offset' => $offSet, 'since' => $since];
try {
$this->meta->setName('recent_engagements')->save($remainingData);
} catch (MetaException $e) {
$this->event->add($e->getMessage());
}
$remainingData['has_more'] = $hasMore;
return $remainingData;
}
public function cleanup()
{
$this->taskMutexService->setMutex('hubspot_pull_engagements', false);
}
public function beforeScheduleAction(ArrayObject $args)
{
}
public function afterScheduleAction(ArrayObject $args)
{
}
/**
* @param int $hubspotContactId
* @param string $body
*
* @return int
*/
private function addNote(int $hubspotContactId, string $body): int
{
$mapping = $this->contactGateway->getMappingByHubspotId($hubspotContactId, null);
if (empty($mapping)) {
return 0;
}
$sql = 'INSERT INTO `dokumente` (
`adresse_to`,
`adresse_from`,
`typ`,
`betreff`,
`content`,
`datum`,
`uhrzeit`,
`created`,
`bearbeiter`)
VALUES (:address, 1, :type, :object, :body, :date, :time, NOW(), :editor)';
$this->db->perform(
$sql,
[
'address' => $mapping['address_id'],
'type' => 'notiz',
'object' => 'Hubspot note',
'body' => $body,
'date' => date('Y-m-d'),
'time' => date('H:i:s'),
'editor' => 'HubspotModule',
]
);
return $this->db->lastInsertId();
}
/**
* @param int $engagementId
*
* @return bool
*/
private function engagementExists(int $engagementId) : bool
{
return $this->contactGateway->hubspotContactExists($engagementId, ['note']);
}
}
@@ -0,0 +1,16 @@
<?php
namespace Xentral\Modules\Hubspot\Scheduler;
use ArrayObject;
interface HubspotSchedulerTaskInterface
{
public function execute();
public function cleanup();
public function beforeScheduleAction(ArrayObject $args);
public function afterScheduleAction(ArrayObject $args);
}