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,88 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Pipedrive\Scheduler\Adapter;
use ArrayObject;
use Xentral\Components\Logger\LoggerAwareTrait;
use Xentral\Modules\Pipedrive\Exception\PipedriveSchedulerAdapterBadMethodException;
use Xentral\Modules\Pipedrive\Scheduler\PipedriveSchedulerTaskInterface;
final class PipedriveSchedulerAdapter
{
use LoggerAwareTrait;
/** @var PipedriveSchedulerTaskInterface $schedulerTask */
private $schedulerTask;
/** @var bool $debugMode */
public $debugMode = false;
/**
* PipedriveSchedulerAdapter constructor.
*
* @param PipedriveSchedulerTaskInterface $schedulerTask
*/
public function __construct(PipedriveSchedulerTaskInterface $schedulerTask)
{
$this->schedulerTask = $schedulerTask;
}
/**
* @param $method
* @param $args
*
* @throws PipedriveSchedulerAdapterBadMethodException
*
* @return void
*/
public function __call($method, $args)
{
if (!method_exists($this->schedulerTask, $method)) {
$class = get_class($this->schedulerTask);
throw new PipedriveSchedulerAdapterBadMethodException(
sprintf('Method %s at %s class is missing', $method, $class)
);
}
if (!is_callable([$this->schedulerTask, $method])) {
$class = get_class($this->schedulerTask);
throw new PipedriveSchedulerAdapterBadMethodException(
sprintf('No callable method %s at %s class', $method, $class)
);
}
$this->debug(json_encode(new ArrayObject($args)));
if ($method === 'execute' && empty($args) === true) {
$this->schedulerTask->beforeScheduleAction(new ArrayObject($args));
}
if ($this->debugMode === true) {
$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));
}
}
/**
* @param string $message
*
* @return void
*/
public function debug(string $message): void
{
if ($this->debugMode === false) {
return;
}
$this->logger->debug(date('Y-m-d H:i:s') . '- ' . $message);
}
}
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Pipedrive\Scheduler;
use ArrayObject;
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
use Xentral\Modules\Pipedrive\Exception\PipedriveEventException;
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
use Xentral\Modules\Pipedrive\Exception\PipedriveRequestQueuesException;
use Xentral\Modules\Pipedrive\RequestQueues\PipedriveRequestQueuesService;
final class PipedriveProcessSchedulerTask implements PipedriveSchedulerTaskInterface
{
/** @var string */
public const CALL_TYPE = 'pipedrive';
/** @var PipedriveRequestQueuesService $service */
private $service;
/**
* @param PipedriveRequestQueuesService $service
*/
public function __construct(PipedriveRequestQueuesService $service)
{
$this->service = $service;
}
/**
* @throws PipedriveConfigurationException
* @throws PipedriveEventException
* @throws PipedriveMetaException
* @throws PipedriveRequestQueuesException
*
* @return void
*/
public function execute(): void
{
$this->service->execute(self::CALL_TYPE);
}
/**
* @return void
*/
public function cleanup(): void
{
$this->service->cleanup();
}
// @codeCoverageIgnoreStart
/**
* @inheritDoc
*/
public function beforeScheduleAction(ArrayObject $data)
{
// TODO: Implement beforeScheduleAction() method.
}
/**
* @inheritDoc
*/
public function afterScheduleAction(ArrayObject $data)
{
// TODO: Implement afterScheduleAction() method.
}
// @codeCoverageIgnoreEnd
}
@@ -0,0 +1,284 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Pipedrive\Scheduler;
use ArrayObject;
use Exception;
use Xentral\Components\Database\Database;
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
use Xentral\Modules\Pipedrive\Exception\PipedriveDealServiceException;
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
use Xentral\Modules\Pipedrive\Gateway\PipedriveDealGateway;
use Xentral\Modules\Pipedrive\Service\PipedriveConfigurationService;
use Xentral\Modules\Pipedrive\Service\PipedriveDealService;
use Xentral\Modules\Pipedrive\Service\PipedriveEventService;
use Xentral\Modules\Pipedrive\Service\PipedriveMetaReaderService;
use Xentral\Modules\Pipedrive\Service\PipedriveMetaWriterService;
use Xentral\Modules\Pipedrive\Wrapper\PipedriveResubmissionWrapper;
final class PipedrivePullDealsTask implements PipedriveSchedulerTaskInterface
{
/** @var Database $db */
private $db;
/** @var PipedriveMetaWriterService $metaWrite */
private $metaWrite;
/** @var PipedriveDealGateway $gateway */
private $gateway;
/** @var PipedriveDealService $dealService */
private $dealService;
/** @var PipedriveConfigurationService $configuration */
private $configuration;
/** @var PipedriveEventService $eventService */
private $eventService;
/** @var PipedriveMetaReaderService $metaReaderService */
private $metaReaderService;
/** @var PipedriveResubmissionWrapper $resubmissionWrapper */
private $resubmissionWrapper;
/**
* @param PipedriveDealService $dealService
* @param Database $db
* @param PipedriveMetaWriterService $metaWriterService
* @param PipedriveDealGateway $gateway
* @param PipedriveConfigurationService $configuration
* @param PipedriveEventService $eventService
* @param PipedriveMetaReaderService $metaReaderService
* @param PipedriveResubmissionWrapper $resubmissionWrapper
*/
public function __construct(
PipedriveDealService $dealService,
Database $db,
PipedriveMetaWriterService $metaWriterService,
PipedriveDealGateway $gateway,
PipedriveConfigurationService $configuration,
PipedriveEventService $eventService,
PipedriveMetaReaderService $metaReaderService,
PipedriveResubmissionWrapper $resubmissionWrapper
) {
$this->db = $db;
$this->dealService = $dealService;
$this->metaWrite = $metaWriterService;
$this->gateway = $gateway;
$this->configuration = $configuration;
$this->eventService = $eventService;
$this->metaReaderService = $metaReaderService;
$this->resubmissionWrapper = $resubmissionWrapper;
}
/**
* @param array $option
* @param string|null $type
* @param bool $recursiveMode
*
* @throws PipedriveConfigurationException
* @throws PipedriveDealServiceException
* @throws PipedriveMetaException
*
* @return void
*/
public function execute(array $option = [], ?string $type = null, bool $recursiveMode = false): void
{
$settings = $this->configuration->getSettings();
if ($settings['pd_sync_deals'] !== true) {
return;
}
$type = $type ?? 'pipedrive_recently_updated_deals';
if ($type !== 'pipedrive_recently_updated_deals' && empty($recursiveMode) &&
$this->db->fetchValue('SELECT COUNT(id) FROM `pipedrive_deals` WHERE `hidden` = 0') > 0) {
$type = 'pipedrive_recently_updated_deals';
}
$ret = $this->pull($type, $option);
if (array_key_exists('has_more', $ret) && $ret['has_more'] === true) {
$this->execute($option, $type, true);
}
}
/**
* @return void
*/
public function cleanup(): void
{
// TODO: Implement cleanup() method.
}
/**
* @param string $type
* @param array $options
*
* @throws PipedriveDealServiceException
* @throws PipedriveMetaException
* @throws Exception
*
* @return array
*/
protected function pull(string $type = 'pipedrive_recently_updated_deals', array $options = []): array
{
$response = $this->dealService->pullDeals($type, $options);
if ($response->getStatusCode() !== 200) {
return [];
}
$deals = $response->getData();
$pagination = $response->getPagination();
$metaFile = sprintf('%s.json', $type);
$metaOption = $this->metaReaderService->readFromFile($metaFile);
$hasMore = is_array($pagination) && array_key_exists(
'more_items_in_collection',
$pagination
) ? $pagination['more_items_in_collection'] : false;
$startOffset = 0;
if (empty($metaOption)) {
$timeOffset = '1970-01-01 23:59:59';
$this->metaWrite->save($metaFile, ['timeOffset' => $timeOffset]);
} elseif (array_key_exists('has_more', $options) && $options['has_more'] === true) {
$timeOffset = $options['previous_timeOffset'] ?? '1970-01-01 23:59:59';
$startOffset += 100;
} else {
$timeOffset = $metaOption['timeOffset'];
}
if (is_array($deals) && count($deals) > 0) {
foreach ($deals as $deal) {
$dealId = $deal['id'];
$hDeal = $deal['data'];
if ($hDeal['deleted'] === true) {
// DELETE IT HERE
if ($mapping = $this->gateway->getDealByPipedriveId($dealId)) {
$this->db->perform(
'DELETE FROM `pipedrive_deals` WHERE `pd_deal_id` = :id',
['id' => $dealId]
);
$this->db->perform(
'DELETE FROM `wiedervorlage` WHERE id = :id',
['id' => $mapping['wiedervorlage_id']]
);
}
continue;
}
if ($mapping = $this->gateway->getDealByPipedriveId($dealId)) {
$this->updateXTDeal($hDeal, $mapping['wiedervorlage_id']);
} elseif ($pipelineId = $this->addXTDeal($hDeal)) {
$this->db->perform(
'INSERT INTO `pipedrive_deals` (`pd_deal_id`, `created_at`, `wiedervorlage_id`)
VALUES (:id,NOW(), :pipelineId)',
['id' => (int)$dealId, 'pipelineId' => $pipelineId]
);
}
}
}
if (!empty($metaOption) && !array_key_exists('previous_timeOffset', $metaOption)) {
$this->metaWrite->save($metaFile, ['timeOffset' => gmdate('Y-m-d H:i:s')]);
}
return [
'has_more' => $hasMore,
'previous_timeOffset' => $timeOffset,
'startOffset' => $startOffset,
];
}
/**
* @param ArrayObject $data
*
* @throws PipedriveConfigurationException
*
* @return mixed|void
*/
public function beforeScheduleAction(ArrayObject $data)
{
if (empty($this->configuration->tryGetConfiguration('pipedrive_settings'))) {
return;
}
try {
$leadsFields = $this->configuration->matchSelectedAddressFreeField();
} catch (PipedriveConfigurationException $exception) {
return;
}
if (empty($leadsFields)) {
return;
}
}
/**
* @param ArrayObject $data
*/
public function afterScheduleAction(ArrayObject $data)
{
}
/**
* @param array $deal
*
* @throws Exception
*
* @return int
*/
private function addXTDeal(array $deal): int
{
$internalDeal = $this->configuration->formatDealToInternal($deal);
if (!$internalDeal) {
return 0;
}
$latestId = $this->resubmissionWrapper->addResubmission($internalDeal);
if ($data = $this->gateway->getMappingStageByResubmissionStageId($internalDeal['stages'])) {
$viewId = $data['wiedervorlage_view_id'];
$eventMsg = sprintf(
'Neues Deal (<a href="/index.php?module=wiedervorlage&action=list&view=%d">%s</a>) vom Pipedrive hinzugef&uuml;gt ins Xentral importiert',
$viewId,
$internalDeal['bezeichnung']
);
$this->eventService->add($eventMsg);
}
return $latestId;
}
/**
* @param array $deal
* @param int $wvId
*
* @throws Exception
*
* @return void
*/
private function updateXTDeal(array $deal, int $wvId): void
{
$internalDeal = $this->configuration->formatDealToInternal($deal);
if (!$internalDeal) {
return;
}
$this->resubmissionWrapper->updateResubmission($wvId, $internalDeal);
if ($data = $this->gateway->getMappingStageByResubmissionStageId($internalDeal['stages'])) {
$viewId = $data['wiedervorlage_view_id'];
$eventMsg = sprintf(
'Deal (<a href="/index.php?module=wiedervorlage&action=list&view=%d">%s</a>)
vom Pipedrive ge&auml;ndert und ins Xentral importiert',
$viewId,
$internalDeal['bezeichnung']
);
$this->eventService->add($eventMsg);
}
}
}
@@ -0,0 +1,305 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Pipedrive\Scheduler;
use Xentral\Components\Database\Database;
use ArrayObject;
use Xentral\Components\Database\Exception\EscapingException;
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
use Xentral\Modules\Pipedrive\Exception\PipedriveEventException;
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
use Xentral\Modules\Pipedrive\Exception\PipedrivePersonServiceException;
use Xentral\Modules\Pipedrive\Gateway\PipedriveContactGateway;
use Xentral\Modules\Pipedrive\Service\PipedriveConfigurationService;
use Xentral\Modules\Pipedrive\Service\PipedriveEventService;
use Xentral\Modules\Pipedrive\Service\PipedriveMetaReaderService;
use Xentral\Modules\Pipedrive\Service\PipedriveMetaWriterService;
use Xentral\Modules\Pipedrive\Service\PipedrivePersonService;
final class PipedrivePullPersonsTask implements PipedriveSchedulerTaskInterface
{
/** @var PipedrivePersonService $contactService */
private $contactService;
/** @var Database $db */
private $db;
/** @var PipedriveMetaWriterService $metaWriterService */
private $metaWriterService;
/** @var PipedriveContactGateway $gateway */
private $gateway;
/** @var PipedriveConfigurationService $configuration */
private $configuration;
/** @var PipedriveEventService $eventService */
private $eventService;
/** @var PipedriveMetaReaderService $metaReaderService */
private $metaReaderService;
/**
* @param PipedrivePersonService $contactService
* @param Database $db
* @param PipedriveMetaWriterService $metaService
* @param PipedriveContactGateway $gateway
* @param PipedriveConfigurationService $configuration
* @param PipedriveEventService $eventService
* @param PipedriveMetaReaderService $metaReaderService
*/
public function __construct(
PipedrivePersonService $contactService,
Database $db,
PipedriveMetaWriterService $metaService,
PipedriveContactGateway $gateway,
PipedriveConfigurationService $configuration,
PipedriveEventService $eventService,
PipedriveMetaReaderService $metaReaderService
) {
$this->db = $db;
$this->contactService = $contactService;
$this->metaWriterService = $metaService;
$this->gateway = $gateway;
$this->configuration = $configuration;
$this->eventService = $eventService;
$this->metaReaderService = $metaReaderService;
}
/**
* @param array $option
* @param string|null $type
* @param bool $recursiveMode
*
* @throws EscapingException
* @throws PipedriveConfigurationException
* @throws PipedriveEventException
* @throws PipedriveMetaException
* @throws PipedrivePersonServiceException
*
* @return void
*/
public function execute(array $option = [], ?string $type = null, bool $recursiveMode = false): void
{
$settings = $this->configuration->getSettings();
if ($settings['pd_sync_addresses'] !== true) {
return;
}
$type = $type ?? 'pipedrive_recently_updated';
if (empty($recursiveMode) && $this->db->fetchValue(
'SELECT COUNT(id) FROM `pipedrive_contacts` WHERE `hidden` = 0'
) > 0) {
$type = $type === 'all' ? 'pipedrive_recently_updated' : $type;
}
$ret = $this->pull($type, $option);
if (!empty($ret['has_more'])) {
$this->execute($option, $type, true);
}
}
/**
* @return void
*/
public function cleanup(): void
{
}
/**
* @param string $type
* @param array $options
*
* @throws EscapingException
* @throws PipedriveConfigurationException
* @throws PipedriveEventException
* @throws PipedriveMetaException
* @throws PipedrivePersonServiceException
*
* @return array
*/
protected function pull(string $type = 'pipedrive_recently_updated', array $options = []): array
{
$response = $this->contactService->pullPersons($type, $options);
if ($response->getStatusCode() !== 200) {
return [];
}
$persons = $response->getData();
$pagination = $response->getPagination();
$metaFile = sprintf('%s.json', $type);
$metaOption = $this->metaReaderService->readFromFile($metaFile);
$hasMore = is_array($pagination) && array_key_exists(
'more_items_in_collection',
$pagination
) ? $pagination['more_items_in_collection'] : false;
$startOffset = 0;
if (empty($metaOption)) {
$timeOffset = '1970-01-01 23:59:59';
$this->metaWriterService->save($metaFile, ['timeOffset' => date('Y-m-d H:i:s')]);
} elseif (array_key_exists('has_more', $options) && $options['has_more'] === true) {
$timeOffset = $options['previous_timeOffset'] ?? '1970-01-01 23:59:59';
$startOffset += 100;
} else {
$timeOffset = $metaOption['timeOffset'];
}
if (is_array($persons) && count($persons) > 0) {
foreach ($persons as $contact) {
$contactData = $contact['data'];
$contactId = $contact['id'];
if ($this->gateway->getMappingByPipedriveId($contactId)) {
// UPDATE
$this->updateXTContact($contactData);
} else {
$this->addXTContact($contactData);
}
}
}
if (!empty($metaOption) && !array_key_exists('previous_timeOffset', $metaOption)) {
$this->metaWriterService->save($metaFile, ['timeOffset' => date('Y-m-d H:i:s')]);
}
return [
'has_more' => $hasMore,
'previous_timeOffset' => $timeOffset,
'startOffset' => $startOffset,
];
}
/**
* @param array $contact
*
* @throws PipedriveConfigurationException
* @throws PipedriveMetaException
* @throws EscapingException
* @throws PipedriveEventException
*
* @return void
*/
private function addXTContact(array $contact): void
{
$internalContact = $this->configuration->formatAddressByResponse($contact);
if (!$internalContact) {
return;
}
$paramValues = array_map(
function ($value) {
if (empty($value)) {
return "''";
}
return is_string($value) ? $this->db->escapeString($value) : $value;
},
array_values($internalContact)
);
$placeHolders = implode(',', array_fill(0, count($paramValues), '%s'));
$insertSql = sprintf(
'INSERT INTO `adresse` (%s) VALUES(%s)',
implode(',', array_keys($internalContact)),
vsprintf($placeHolders, $paramValues)
);
$this->db->perform($insertSql);
if ($addressId = $this->db->lastInsertId()) {
$this->db->perform(
'INSERT INTO `pipedrive_contacts`
(`pd_contact_id`, `created_at`, `address_id`) VALUES (:id, NOW(), :aid)',
['id' => (int)$contact['id'], 'aid' => $addressId]
);
$personName = !empty($internalContact['email'])? $internalContact['email'] : $internalContact['name'];
$eventMsg = sprintf(
'Neuen Kontakt (<a href="/index.php?module=adresse&action=edit&id=%d">%s</a>) hinzugef&uuml;gt.',
$addressId,
$personName
);
$this->eventService->add($eventMsg);
// ADD to group
$this->configuration->addContactToGroup($addressId);
}
}
/**
* @param array $contact
*
* @throws PipedriveConfigurationException
* @throws PipedriveEventException
*
* @return void
*/
private function updateXTContact(array $contact): void
{
$internalContact = $this->configuration->formatAddressByResponse($contact);
if (!$internalContact) {
return;
}
$excludeVars = ['lead', 'typ', 'sprache', 'waehrung', 'kundenfreigabe'];
foreach ($excludeVars as $excludeVar) {
unset($internalContact[$excludeVar]);
}
if ($hPdContact = $this->gateway->getMappingByPipedriveId($contact['id'])) {
$asPlaceHolders = array_map(
static function ($val) {
return vsprintf('%s=:%s', [$val, $val]);
},
array_keys($internalContact)
);
$placeHolders = implode(',', $asPlaceHolders);
$affected = $this->db->fetchAffected(
'UPDATE `adresse` SET ' . $placeHolders . ' WHERE `id` = ' . $hPdContact['address_id'],
$internalContact
);
if ($affected > 0) {
$personName = !empty($internalContact['email'])? $internalContact['email'] : $internalContact['name'];
$eventMsg = sprintf(
'Kontakt (<a href="/index.php?module=adresse&action=edit&id=%d">%s</a>) ge&auml;ndert.',
$hPdContact['address_id'],
$personName
);
$this->eventService->add($eventMsg);
}
}
}
/**
* @param ArrayObject $data
*
* @throws PipedriveConfigurationException
*
* @return void
*/
public function beforeScheduleAction(ArrayObject $data): void
{
if (empty($this->configuration->tryGetConfiguration('pipedrive_settings'))) {
return;
}
try {
$leadsFields = $this->configuration->matchSelectedAddressFreeField();
} catch (PipedriveConfigurationException $exception) {
return;
}
if (empty($leadsFields)) {
return;
}
}
/**
* @param ArrayObject $data
*
* @return void
*/
public function afterScheduleAction(ArrayObject $data): void
{
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Xentral\Modules\Pipedrive\Scheduler;
use ArrayObject;
interface PipedriveSchedulerTaskInterface
{
/**
* @return void
*/
public function execute(): void;
/**
* @return void
*/
public function cleanup(): void;
/**
* @param ArrayObject $data
*
* @return mixed
*/
public function beforeScheduleAction(ArrayObject $data);
/**
* @param ArrayObject $data
*
* @return mixed
*/
public function afterScheduleAction(ArrayObject $data);
}