Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive;
|
||||
|
||||
use ApplicationCore;
|
||||
use Xentral\Core\DependencyInjection\ContainerInterface;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedriveContactGateway;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedriveDealGateway;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedrivePersonPropertyGateway;
|
||||
use Xentral\Modules\Pipedrive\RequestQueues\PipedriveRequestQueuesGateway;
|
||||
use Xentral\Modules\Pipedrive\RequestQueues\PipedriveRequestQueuesService;
|
||||
use Xentral\Modules\Pipedrive\Scheduler\PipedriveProcessSchedulerTask;
|
||||
use Xentral\Modules\Pipedrive\Scheduler\PipedrivePullDealsTask;
|
||||
use Xentral\Modules\Pipedrive\Scheduler\PipedrivePullPersonsTask;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveClientService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveConfigurationService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveDealPropertyService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveDealService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveEventService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveHttpClientService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveMetaReaderService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveMetaWriterService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedrivePersonPropertyService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedrivePersonService;
|
||||
use Xentral\Modules\Pipedrive\Validator\PipedriveDealValidator;
|
||||
use Xentral\Modules\Pipedrive\Validator\PipedrivePersonValidator;
|
||||
use Xentral\Modules\Pipedrive\Wrapper\PipedriveAddAddressRoleWrapper;
|
||||
use Xentral\Modules\Pipedrive\Wrapper\PipedriveResubmissionWrapper;
|
||||
|
||||
final class Bootstrap
|
||||
{
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function registerServices(): array
|
||||
{
|
||||
return [
|
||||
'PipedriveConfigurationService' => 'onInitPipedriveConfigurationService',
|
||||
'PipedrivePersonService' => 'onInitPipedrivePersonService',
|
||||
'PipedriveClientService' => 'onInitPipedriveClientService',
|
||||
'PipedriveRequestQueuesGateway' => 'onInitPipedriveRequestQueuesGateway',
|
||||
'PipedriveRequestQueuesService' => 'onInitPipedriveRequestQueuesService',
|
||||
'PipedriveContactGateway' => 'onInitPipedriveContactGateway',
|
||||
'PipedriveDealGateway' => 'onInitPipedriveDealGateway',
|
||||
'PipedrivePersonPropertyService' => 'onInitPipedrivePersonPropertyService',
|
||||
'PipedrivePersonPropertyGateway' => 'onInitPipedrivePersonPropertyGateway',
|
||||
'PipedriveDealPropertyService' => 'onInitPipedriveDealPropertyService',
|
||||
'PipedriveProcessSchedulerTask' => 'onInitPipedriveProcessSchedulerTask',
|
||||
'PipedrivePullPersonsTask' => 'onInitPipedrivePullPersonsTask',
|
||||
'PipedrivePullDealsTask' => 'onInitPipedrivePullDealsTask',
|
||||
'PipedriveDealService' => 'onInitPipedriveDealService',
|
||||
'PipedriveEventService' => 'onInitPipedriveEventService',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveConfigurationService
|
||||
*/
|
||||
public static function onInitPipedriveConfigurationService(
|
||||
ContainerInterface $container
|
||||
): PipedriveConfigurationService {
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
$metaTmp = $app->erp->GetTMP() . 'meta' . DIRECTORY_SEPARATOR . 'pipedrive';
|
||||
|
||||
return new PipedriveConfigurationService(
|
||||
$container->get('SystemConfigModule'),
|
||||
new PipedriveMetaWriterService($metaTmp),
|
||||
$container->get('PipedrivePersonPropertyGateway'),
|
||||
$container->get('PipedriveDealGateway'),
|
||||
new PipedriveMetaReaderService($metaTmp),
|
||||
new PipedriveAddAddressRoleWrapper($app->erp)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @throws Exception\PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveClientService
|
||||
*/
|
||||
public static function onInitPipedriveClientService(ContainerInterface $container): PipedriveClientService
|
||||
{
|
||||
return new PipedriveClientService(
|
||||
new PipedriveHttpClientService($container->get('HttpClientFactory'), 30),
|
||||
$container->get('PipedriveConfigurationService')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedrivePersonService
|
||||
*/
|
||||
public static function onInitPipedrivePersonService(ContainerInterface $container): PipedrivePersonService
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
$metaTmp = $app->erp->GetTMP() . 'meta' . DIRECTORY_SEPARATOR . 'pipedrive';
|
||||
|
||||
return new PipedrivePersonService(
|
||||
$container->get('PipedriveClientService'),
|
||||
new PipedrivePersonValidator(),
|
||||
new PipedriveMetaReaderService($metaTmp)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveRequestQueuesGateway
|
||||
*/
|
||||
public static function onInitPipedriveRequestQueuesGateway(ContainerInterface $container
|
||||
): PipedriveRequestQueuesGateway {
|
||||
return new PipedriveRequestQueuesGateway($container->get('Database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveRequestQueuesService
|
||||
*/
|
||||
public static function onInitPipedriveRequestQueuesService(ContainerInterface $container
|
||||
): PipedriveRequestQueuesService {
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
|
||||
return new PipedriveRequestQueuesService(
|
||||
$container->get('PipedriveRequestQueuesGateway'),
|
||||
$container->get('Database'),
|
||||
$app,
|
||||
$container->get('PipedriveConfigurationService'),
|
||||
$container->get('PipedriveEventService')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveContactGateway
|
||||
*/
|
||||
public static function onInitPipedriveContactGateway(ContainerInterface $container): PipedriveContactGateway
|
||||
{
|
||||
return new PipedriveContactGateway(
|
||||
$container->get('Database'), $container->get('PipedriveConfigurationService')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveDealGateway
|
||||
*/
|
||||
public static function onInitPipedriveDealGateway(ContainerInterface $container): PipedriveDealGateway
|
||||
{
|
||||
return new PipedriveDealGateway($container->get('Database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedrivePersonPropertyGateway
|
||||
*/
|
||||
public static function onInitPipedrivePersonPropertyGateway(ContainerInterface $container
|
||||
): PipedrivePersonPropertyGateway {
|
||||
return new PipedrivePersonPropertyGateway($container->get('Database'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedrivePersonPropertyService
|
||||
*/
|
||||
public static function onInitPipedrivePersonPropertyService(ContainerInterface $container
|
||||
): PipedrivePersonPropertyService {
|
||||
return new PipedrivePersonPropertyService($container->get('PipedriveClientService'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedrivePullPersonsTask
|
||||
*/
|
||||
public static function onInitPipedrivePullPersonsTask(ContainerInterface $container): PipedrivePullPersonsTask
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
$metaTmp = $app->erp->GetTMP() . 'meta' . DIRECTORY_SEPARATOR . 'pipedrive';
|
||||
|
||||
return new PipedrivePullPersonsTask(
|
||||
$container->get('PipedrivePersonService'),
|
||||
$container->get('Database'),
|
||||
new PipedriveMetaWriterService($metaTmp),
|
||||
$container->get('PipedriveContactGateway'),
|
||||
$container->get('PipedriveConfigurationService'),
|
||||
$container->get('PipedriveEventService'),
|
||||
new PipedriveMetaReaderService($metaTmp)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveProcessSchedulerTask
|
||||
*/
|
||||
public static function onInitPipedriveProcessSchedulerTask(ContainerInterface $container
|
||||
): PipedriveProcessSchedulerTask {
|
||||
return new PipedriveProcessSchedulerTask($container->get('PipedriveRequestQueuesService'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveDealPropertyService
|
||||
*/
|
||||
public static function onInitPipedriveDealPropertyService(ContainerInterface $container
|
||||
): PipedriveDealPropertyService {
|
||||
return new PipedriveDealPropertyService(
|
||||
$container->get('PipedriveClientService'),
|
||||
$container->get('Database'),
|
||||
$container->get('PipedrivePersonPropertyGateway'),
|
||||
$container->get('ResubmissionGateway'),
|
||||
new PipedriveResubmissionWrapper($container->get('Database'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedrivePullDealsTask
|
||||
*/
|
||||
public static function onInitPipedrivePullDealsTask(ContainerInterface $container): PipedrivePullDealsTask
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
$metaTmp = $app->erp->GetTMP() . 'meta' . DIRECTORY_SEPARATOR . 'pipedrive';
|
||||
|
||||
return new PipedrivePullDealsTask(
|
||||
$container->get('PipedriveDealService'),
|
||||
$container->get('Database'),
|
||||
new PipedriveMetaWriterService($metaTmp),
|
||||
$container->get('PipedriveDealGateway'),
|
||||
$container->get('PipedriveConfigurationService'),
|
||||
$container->get('PipedriveEventService'),
|
||||
new PipedriveMetaReaderService($metaTmp),
|
||||
new PipedriveResubmissionWrapper($container->get('Database'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveDealService
|
||||
*/
|
||||
public static function onInitPipedriveDealService(ContainerInterface $container): PipedriveDealService
|
||||
{
|
||||
/** @var ApplicationCore $app */
|
||||
$app = $container->get('LegacyApplication');
|
||||
$metaTmp = $app->erp->GetTMP() . 'meta' . DIRECTORY_SEPARATOR . 'pipedrive';
|
||||
|
||||
return new PipedriveDealService(
|
||||
$container->get('PipedriveClientService'),
|
||||
new PipedriveDealValidator(),
|
||||
new PipedriveMetaReaderService($metaTmp)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return PipedriveEventService
|
||||
*/
|
||||
public static function onInitPipedriveEventService(ContainerInterface $container): PipedriveEventService
|
||||
{
|
||||
return new PipedriveEventService($container->get('Database'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
final class PipedriveClientException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
final class PipedriveConfigurationException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
class PipedriveContactGatewayNotFoundException extends RuntimeException
|
||||
implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveDealGatewayNotFoundException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveDealPropertyServiceException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveDealServiceException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveEventException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use Xentral\Core\Exception\ModuleExceptionInterface;
|
||||
|
||||
interface PipedriveExceptionInterface extends ModuleExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class PipedriveHttpClientException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveMetaException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedrivePersonPropertyGatewayException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedrivePersonPropertyServiceException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedrivePersonServiceException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedrivePullPersonsTaskException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class PipedriveRequestQueuesException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
final class PipedriveSchedulerAdapterBadMethodException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class PipedriveValidatorException extends RuntimeException implements PipedriveExceptionInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Gateway;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveConfigurationService;
|
||||
|
||||
final class PipedriveContactGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var PipedriveConfigurationService $configurationService */
|
||||
private $configurationService;
|
||||
|
||||
/**
|
||||
* @param Database $db
|
||||
* @param PipedriveConfigurationService $configurationService
|
||||
*/
|
||||
public function __construct(Database $db, PipedriveConfigurationService $configurationService)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->configurationService = $configurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pdContactId Pipedrive contact ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappingByPipedriveId(int $pdContactId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
p.id,
|
||||
p.created_at,
|
||||
p.data,
|
||||
p.address_id
|
||||
FROM `pipedrive_contacts` AS `p`
|
||||
WHERE p.hidden = 0 AND p.pd_contact_id = :id',
|
||||
['id' => $pdContactId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $addressId Xentral Address ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappingByAddressId(int $addressId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
p.id,
|
||||
p.created_at,
|
||||
p.data,
|
||||
p.pd_contact_id
|
||||
FROM `pipedrive_contacts` AS `p`
|
||||
WHERE p.hidden = 0 AND p.address_id = :id',
|
||||
['id' => $addressId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $addressId
|
||||
* @param bool $withStatusField
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInternalAddressById(int $addressId, bool $withStatusField = false): array
|
||||
{
|
||||
$placeHolder = '';
|
||||
if ($withStatusField === true) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$leadFields = $this->configurationService->matchSelectedAddressFreeField();
|
||||
$lsField = $leadFields['pipedrive_ls_field'];
|
||||
$placeHolder = ", a.{$lsField}";
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
$sql = 'SELECT
|
||||
a.id,
|
||||
a.lead,
|
||||
a.typ,
|
||||
a.sprache,
|
||||
a.name,
|
||||
a.vorname,
|
||||
a.nachname,
|
||||
a.land,
|
||||
a.ort,
|
||||
a.plz,
|
||||
a.bundesstaat,
|
||||
a.telefon,
|
||||
a.email %s
|
||||
FROM `adresse` AS `a`
|
||||
WHERE a.geloescht = 0 AND a.id = :id';
|
||||
|
||||
return $this->db->fetchRow(sprintf($sql, $placeHolder), ['id' => $addressId]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Gateway;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
final class PipedriveDealGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pdDealId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDealByPipedriveId(int $pdDealId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
d.id,
|
||||
d.created_at,
|
||||
d.data,
|
||||
d.wiedervorlage_id
|
||||
FROM `pipedrive_deals` AS `d`
|
||||
WHERE d.hidden = 0 AND d.pd_deal_id = :id',
|
||||
['id' => $pdDealId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDealByResubmissionId(int $resubmissionId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
d.id,
|
||||
d.created_at,
|
||||
d.data,
|
||||
d.wiedervorlage_id,
|
||||
d.pd_deal_id
|
||||
FROM `pipedrive_deals` AS `d`
|
||||
WHERE d.hidden = 0 AND d.wiedervorlage_id = :id',
|
||||
['id' => $resubmissionId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $stageId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappingStageByResubmissionStageId(int $stageId): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
pm.id,
|
||||
pm.created_at,
|
||||
pm.wiedervorlage_stage_id,
|
||||
pm.label,
|
||||
pm.value,
|
||||
pm.wiedervorlage_view_id,
|
||||
pm.type
|
||||
FROM `pipedrive_mappings` AS `pm`
|
||||
WHERE pm.wiedervorlage_stage_id=:resubmission_id AND pm.type =:type',
|
||||
['resubmission_id' => $stageId, 'type' => 'deals']
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Gateway;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
final class PipedrivePersonPropertyGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @param bool $isSystem
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLeadsByType(string $type, bool $isSystem = false): array
|
||||
{
|
||||
return $this->db->fetchAssoc(
|
||||
'SELECT
|
||||
p.id,
|
||||
p.created_at,
|
||||
p.label,
|
||||
p.value,
|
||||
p.type,
|
||||
p.wiedervorlage_stage_id
|
||||
FROM `pipedrive_mappings` AS `p` WHERE p.type = :type AND p.is_system=:system',
|
||||
['type' => $type, 'system' => (int)$isSystem]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
* @param string $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMappingByValueAndType(int $value, string $type): array
|
||||
{
|
||||
return $this->db->fetchRow(
|
||||
'SELECT
|
||||
hm.id,
|
||||
hm.created_at,
|
||||
hm.wiedervorlage_stage_id,
|
||||
hm.label,
|
||||
hm.value,
|
||||
hm.type
|
||||
FROM `pipedrive_mappings` AS `hm`
|
||||
WHERE hm.value =:value AND hm.type = :type',
|
||||
['value' => $value, 'type' => $type]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dbName
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressFreeFields(string $dbName): array
|
||||
{
|
||||
return $this->db->fetchCol(
|
||||
'SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS`
|
||||
WHERE `TABLE_SCHEMA` =:db AND `TABLE_NAME` = :table AND `COLUMN_NAME` LIKE "adressefreifeld%"',
|
||||
[
|
||||
'db' => $dbName,
|
||||
'table' => 'firmendaten',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getConfiguredFreeAddressFieldValues(): array
|
||||
{
|
||||
return $this->db->fetchCol(
|
||||
'SELECT f.wert FROM `firmendaten_werte` AS `f`
|
||||
WHERE `name` LIKE "adressetabellezusatz%" AND f.wert !="" AND f.wert IS NOT NULL'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\RequestQueues;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
final class PipedriveRequestQueuesGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* PipedriveRequestQueuesGateway constructor.
|
||||
*
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNewRequestsByCallType(string $type = 'pipedrive'): array
|
||||
{
|
||||
|
||||
$sql = 'SELECT
|
||||
rq.id,
|
||||
rq.command,
|
||||
rq.not_before,
|
||||
rq.amount_attempts,
|
||||
rq.runner,
|
||||
rq.check_sum,
|
||||
rq.on_after_done,
|
||||
rq.is_looped,
|
||||
rq.setting_name,
|
||||
rq.created_at,
|
||||
rq.modified_at
|
||||
FROM `pipedrive_request_queues` AS `rq`
|
||||
WHERE rq.call_type = :type AND rq.completed = 0 AND rq.deleted = 0
|
||||
ORDER BY rq.created_at';
|
||||
|
||||
return $this->db->fetchAll($sql, ['type' => $type]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\RequestQueues;
|
||||
|
||||
use ApplicationCore;
|
||||
use Exception;
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Database\Exception\DatabaseExceptionInterface;
|
||||
use Xentral\Core\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
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\Service\PipedriveConfigurationService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveEventService;
|
||||
use Xentral\Modules\Pipedrive\Service\PipedriveServerResponseInterface;
|
||||
use DateTime;
|
||||
use RuntimeException;
|
||||
|
||||
final class PipedriveRequestQueuesService
|
||||
{
|
||||
|
||||
/** @var int */
|
||||
private const _WAITING_TIME = 10000000;
|
||||
|
||||
/** @var int */
|
||||
private const _BATCH = 1;
|
||||
|
||||
/** @var PipedriveRequestQueuesGateway $gateway */
|
||||
private $gateway;
|
||||
|
||||
/** @var ApplicationCore $app */
|
||||
private $app;
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var array $completedIds */
|
||||
private $completedIds = [];
|
||||
|
||||
/** @var PipedriveConfigurationService $confService */
|
||||
private $confService;
|
||||
|
||||
/** @var PipedriveEventService $eventService */
|
||||
private $eventService;
|
||||
|
||||
/**
|
||||
* @param PipedriveRequestQueuesGateway $gateway
|
||||
* @param Database $database
|
||||
* @param ApplicationCore $app
|
||||
* @param PipedriveConfigurationService $confService
|
||||
* @param PipedriveEventService $eventService
|
||||
*/
|
||||
public function __construct(
|
||||
PipedriveRequestQueuesGateway $gateway,
|
||||
Database $database,
|
||||
ApplicationCore $app,
|
||||
PipedriveConfigurationService $confService,
|
||||
PipedriveEventService $eventService
|
||||
) {
|
||||
$this->gateway = $gateway;
|
||||
$this->app = $app;
|
||||
$this->db = $database;
|
||||
$this->confService = $confService;
|
||||
$this->eventService = $eventService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $option
|
||||
*
|
||||
* @throws PipedriveRequestQueuesException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function addRequest(array $option): int
|
||||
{
|
||||
$default = [
|
||||
'check_sum' => '',
|
||||
'command' => '',
|
||||
'on_after_done' => '',
|
||||
'not_before' => 0,
|
||||
'call_type' => 'pipedrive',
|
||||
'is_looped' => 0,
|
||||
'setting_name' => '',
|
||||
];
|
||||
|
||||
$hCommand = [];
|
||||
if (array_key_exists('method', $option)) {
|
||||
$hCommand['method'] = $option['method'];
|
||||
unset($option['method']);
|
||||
}
|
||||
if (array_key_exists('args', $option)) {
|
||||
$hCommand['args'] = $option['args'];
|
||||
unset($option['args']);
|
||||
}
|
||||
if (!empty($hCommand)) {
|
||||
$option['command'] = json_encode($hCommand, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
|
||||
}
|
||||
|
||||
if (!empty($option['check_sum'])) {
|
||||
$default['check_sum'] = $option['check_sum'];
|
||||
} else {
|
||||
$default['check_sum'] = array_key_exists('command', $option) ? md5($option['command']) : '';
|
||||
}
|
||||
|
||||
$option = array_merge($default, $option);
|
||||
|
||||
if (!array_key_exists('runner', $option)) {
|
||||
throw new PipedriveRequestQueuesException('Runner is missing!');
|
||||
}
|
||||
|
||||
if (!empty($option['on_after_done']) && is_array($option['on_after_done'])) {
|
||||
$option['on_after_done'] = json_encode(
|
||||
$option['on_after_done'],
|
||||
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
|
||||
);
|
||||
}
|
||||
|
||||
$check = 'SELECT EXISTS(
|
||||
SELECT prq.id FROM `pipedrive_request_queues` AS `prq`
|
||||
WHERE prq.deleted=0 AND prq.completed=0 AND prq.check_sum=:check_sum AND prq.runner=:runner
|
||||
)';
|
||||
if (empty(
|
||||
$this->db->fetchValue(
|
||||
$check,
|
||||
['runner' => $option['runner'], 'check_sum' => $option['check_sum']]
|
||||
)
|
||||
)) {
|
||||
$add = 'INSERT INTO `pipedrive_request_queues` (`command`, `on_after_done`, `runner`, `not_before`, `check_sum`, `call_type`, `is_looped`, `setting_name`, `created_at` )
|
||||
VALUES (:command, :on_after_done, :runner, :not_before, :check_sum, :call_type, :is_looped, :setting_name, NOW())';
|
||||
try {
|
||||
$this->db->perform($add, $option);
|
||||
} catch (DatabaseExceptionInterface $exception) {
|
||||
throw new PipedriveRequestQueuesException($exception->getMessage());
|
||||
}
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $callType
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveEventException
|
||||
* @throws PipedriveRequestQueuesException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws Exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function execute(?string $callType = null): void
|
||||
{
|
||||
$callType = $callType ?? 'pipedrive';
|
||||
$jobs = $this->gateway->getNewRequestsByCallType($callType);
|
||||
if (!empty($jobs)) {
|
||||
$batch_loop = self::_BATCH;
|
||||
$iCount = 0;
|
||||
$bSkipWait = count($jobs) <= 1;
|
||||
foreach ($jobs as $job) {
|
||||
$settings = $this->confService->getSettings();
|
||||
|
||||
// HANDLE CAN EXECUTE
|
||||
if (!empty($job['setting_name']) && array_key_exists(
|
||||
$job['setting_name'],
|
||||
$settings
|
||||
) && $settings[$job['setting_name']] === false) {
|
||||
// SKIP
|
||||
continue;
|
||||
}
|
||||
|
||||
// HANDLE is_looped
|
||||
if (!empty($job['is_looped']) && !empty($job['setting_name'])) {
|
||||
$settingInterval = sprintf('%s_interval', $job['setting_name']);
|
||||
if (!array_key_exists($settingInterval, $settings)) {
|
||||
// SKIP or ERROR?
|
||||
continue;
|
||||
}
|
||||
$interval = $settings[$settingInterval];
|
||||
$modifiedAt = new DateTime($job['modified_at']);
|
||||
$iModifiedAt = $modifiedAt->getTimestamp();
|
||||
if (time() < $iModifiedAt + $interval) {
|
||||
// SKIP, waiting for next running time
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($job['is_looped'])) {
|
||||
$this->db->perform(
|
||||
'UPDATE `pipedrive_request_queues` SET `modified_at` = NOW() WHERE `id` = :id',
|
||||
['id' => $job['id']]
|
||||
);
|
||||
} else {
|
||||
$this->db->perform(
|
||||
'UPDATE `pipedrive_request_queues`
|
||||
SET `amount_attempts` = `amount_attempts` +1 WHERE `id` = :id',
|
||||
['id' => $job['id']]
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$oClass = $this->app->Container->get($job['runner']);
|
||||
} catch (ServiceNotFoundException $exception) {
|
||||
throw new PipedriveRequestQueuesException($exception->getMessage());
|
||||
}
|
||||
|
||||
|
||||
$hCommand = json_decode($job['command'], true);
|
||||
if (is_array($hCommand)) {
|
||||
$xArg = $hCommand['args'];
|
||||
$sMethod = $hCommand['method'];
|
||||
try {
|
||||
/** @var PipedriveServerResponseInterface $xReturn */
|
||||
$xReturn = call_user_func_array([$oClass, $sMethod], $xArg);
|
||||
} catch (RuntimeException $exception) {
|
||||
$this->eventService->add($exception->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
$isLooped = !empty($job['is_looped']);
|
||||
|
||||
if (empty($job['on_after_done'])) {
|
||||
$this->markJobAsComplete($job['id'], $isLooped);
|
||||
} else {
|
||||
$this->onAfterDone($job['id'], $xReturn, $job['on_after_done'], $isLooped);
|
||||
}
|
||||
|
||||
//BATCH PROCESS CHECK - take a nap
|
||||
if ($bSkipWait === false) {
|
||||
if ($iCount === $batch_loop) {
|
||||
$batch_loop += self::_BATCH;
|
||||
@usleep(self::_WAITING_TIME);
|
||||
}
|
||||
echo 'Script always alive... ';
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param PipedriveServerResponseInterface|null $response
|
||||
* @param string|null $onAfter
|
||||
* @param bool $looped
|
||||
*
|
||||
* @throws PipedriveEventException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function onAfterDone(
|
||||
int $id,
|
||||
?PipedriveServerResponseInterface $response,
|
||||
?string $onAfter = null,
|
||||
bool $looped = false
|
||||
): void {
|
||||
$hOnAfter = !empty($onAfter) ? json_decode($onAfter, true) : [];
|
||||
|
||||
if (!empty($hOnAfter) && array_key_exists('runner', $hOnAfter) && array_key_exists(
|
||||
'method',
|
||||
$hOnAfter
|
||||
) && array_key_exists('args', $hOnAfter)) {
|
||||
$oClass = $this->app->Container->get($hOnAfter['runner']);
|
||||
if (!empty($hOnAfter['replace_in_args']) && in_array($response->getStatusCode(), [200, 201], true)) {
|
||||
$jsonData = $response->getData();
|
||||
foreach ($hOnAfter['args'] as &$xArg) {
|
||||
if (is_string($xArg)) {
|
||||
foreach ($hOnAfter['replace_in_args'] as $replace_with) {
|
||||
if (!empty($jsonData[$replace_with])) {
|
||||
$xArg = sprintf($xArg, $jsonData[$replace_with]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($xArg);
|
||||
try {
|
||||
call_user_func_array(
|
||||
[$oClass, $hOnAfter['method']],
|
||||
$hOnAfter['args']
|
||||
);
|
||||
|
||||
// ADD EVENT
|
||||
if (array_key_exists('event', $hOnAfter) && !empty($hOnAfter['event']) && is_string(
|
||||
$hOnAfter['event']
|
||||
) &&
|
||||
in_array($response->getStatusCode(), [200, 201], true)) {
|
||||
$this->eventService->add($hOnAfter['event']);
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
$this->eventService->add($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->markJobAsComplete($id, $looped);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param bool $looped
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function markJobAsComplete(int $id, bool $looped): void
|
||||
{
|
||||
if (is_numeric($id) && $looped === false) {
|
||||
$this->db->perform('UPDATE `pipedrive_request_queues` SET `completed` = 1 WHERE `id` = :id', ['id' => $id]);
|
||||
$this->completedIds[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function cleanup(): void
|
||||
{
|
||||
$this->db->perform('DELETE FROM `pipedrive_request_queues` WHERE `completed` = 1');
|
||||
$this->eventService->deleteByInterval();
|
||||
|
||||
unset($this->completedIds);
|
||||
}
|
||||
}
|
||||
@@ -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ü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ä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ü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ä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);
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
|
||||
final class PipedriveClientService
|
||||
{
|
||||
|
||||
/** @var string[] $endPoints */
|
||||
private $endPoints = [
|
||||
'allPersons' => '/v1/persons',
|
||||
'recentlyUpdatedPersons' => '/v1/recents',
|
||||
'deleteContact' => '/v1/persons/:id',
|
||||
'updateContact' => '/v1/persons/:id',
|
||||
'createContact' => '/v1/persons',
|
||||
'getContactById' => '/v1/persons/:id',
|
||||
'recentlyUpdatedDeals' => '/v1/recents',
|
||||
'createDeal' => '/v1/deals',
|
||||
'allDeals' => '/v1/deals',
|
||||
'deleteDeal' => '/v1/deals/:id',
|
||||
'updateDeal' => '/v1/deals/:id',
|
||||
'getDealById' => '/v1/deals/:id',
|
||||
'getPersonFields' => '/v1/personFields',
|
||||
'getOnePersonField' => '/v1/personFields/:id',
|
||||
'getStages' => '/v1/stages',
|
||||
'getPipelines' => '/v1/pipelines',
|
||||
];
|
||||
|
||||
/** @var string $authMethod */
|
||||
private $authMethod = 'key';
|
||||
|
||||
/** @var string|null $apiKey */
|
||||
private $apiKey;
|
||||
|
||||
/** @var PipedriveHttpClientService $client */
|
||||
private $client;
|
||||
|
||||
/** @var PipedriveConfigurationService $confService */
|
||||
private $confService;
|
||||
|
||||
/**
|
||||
* @param PipedriveHttpClientService $client
|
||||
* @param PipedriveConfigurationService $confService
|
||||
* @param string|null $apiKey
|
||||
*/
|
||||
public function __construct(
|
||||
PipedriveHttpClientService $client,
|
||||
PipedriveConfigurationService $confService,
|
||||
?string $apiKey = null
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->apiKey = $apiKey;
|
||||
$this->confService = $confService;
|
||||
}
|
||||
|
||||
/** @var string $apiUrl */
|
||||
private $apiUrl = 'https://api.pipedrive.com%s';
|
||||
|
||||
/**
|
||||
* @param string $resource
|
||||
* @param array $args
|
||||
* @param string|null $suffix
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEndPoint(string $resource, array $args = [], ?string $suffix = null): string
|
||||
{
|
||||
|
||||
if (!array_key_exists($resource, $this->endPoints)) {
|
||||
throw new PipedriveClientException('Undefined resource endpoint');
|
||||
}
|
||||
|
||||
$suffixUrl = $suffix ?? $this->endPoints[$resource];
|
||||
$url = sprintf($this->apiUrl, $suffixUrl);
|
||||
if ($this->authMethod === 'key') {
|
||||
$apiKey = $this->apiKey ?? $this->getConfApiKey();
|
||||
$url .= sprintf('?api_token=%s', $apiKey);
|
||||
}
|
||||
if (!empty($args)) {
|
||||
preg_match_all('/:[a-zA-Z0-9._-]+/', $url, $match);
|
||||
if (!empty($match) && !empty($match[0])) {
|
||||
$url = str_replace($match[0], $args, $url);
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $ressource
|
||||
* @param array $data
|
||||
* @param array $endPointArgs
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function read(string $ressource, array $data = [], array $endPointArgs = []): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->get($this->getEndPoint($ressource, $endPointArgs), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
protected function getConfApiKey(): ?string
|
||||
{
|
||||
return $this->confService->getDecryptedConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ressource
|
||||
* @param array $data
|
||||
* @param array $endPointArgs
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function post(string $ressource, array $data = [], array $endPointArgs = []): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->post($this->getEndPoint($ressource, $endPointArgs), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ressource
|
||||
* @param array $data
|
||||
* @param array $endPointArgs
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function delete(string $ressource, array $endPointArgs = [], array $data = []): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->delete($this->getEndPoint($ressource, $endPointArgs), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ressource
|
||||
* @param array $data
|
||||
* @param array $endPointArgs
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function put(string $ressource, array $data = [], array $endPointArgs = []): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->put($this->getEndPoint($ressource, $endPointArgs), $data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedriveDealGateway;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedrivePersonPropertyGateway;
|
||||
use Xentral\Modules\Pipedrive\Wrapper\PipedriveAddAddressRoleWrapper;
|
||||
use Xentral\Modules\SystemConfig\SystemConfigModule;
|
||||
|
||||
final class PipedriveConfigurationService
|
||||
{
|
||||
/** @var string */
|
||||
private const PIPEDRIVE_SETTINGS = 'pipedrive_settings';
|
||||
|
||||
/** @var string */
|
||||
private const PIPEDRIVE_CONF_NAME = 'pipedrive_conf.json';
|
||||
|
||||
/** @var array $_defaultSettings */
|
||||
private static $_defaultSettings = [
|
||||
'pd_sync_deals' => true,
|
||||
'pd_sync_addresses' => true,
|
||||
'pd_api_key' => null,
|
||||
];
|
||||
|
||||
/** @var SystemConfigModule $configWrapper */
|
||||
private $configWrapper;
|
||||
|
||||
/** @var PipedriveMetaWriterService $metaWriterService */
|
||||
private $metaWriterService;
|
||||
|
||||
/** @var PipedrivePersonPropertyGateway $propertyGateway */
|
||||
private $propertyGateway;
|
||||
|
||||
/** @var PipedriveDealGateway $pipedriveDealGateway */
|
||||
private $pipedriveDealGateway;
|
||||
|
||||
/** @var PipedriveMetaReaderService $metaReaderService */
|
||||
private $metaReaderService;
|
||||
|
||||
/** @var PipedriveAddAddressRoleWrapper $addAddressRoleWrapper */
|
||||
private $addAddressRoleWrapper;
|
||||
|
||||
/**
|
||||
* @param SystemConfigModule $configWrapper
|
||||
* @param PipedriveMetaWriterService $metaWriterService
|
||||
* @param PipedrivePersonPropertyGateway $propertyGateway
|
||||
* @param PipedriveDealGateway $pipedriveDealGateway
|
||||
* @param PipedriveMetaReaderService $metaReaderService
|
||||
* @param PipedriveAddAddressRoleWrapper $addAddressRoleWrapper
|
||||
*/
|
||||
public function __construct(
|
||||
SystemConfigModule $configWrapper,
|
||||
PipedriveMetaWriterService $metaWriterService,
|
||||
PipedrivePersonPropertyGateway $propertyGateway,
|
||||
PipedriveDealGateway $pipedriveDealGateway,
|
||||
PipedriveMetaReaderService $metaReaderService,
|
||||
PipedriveAddAddressRoleWrapper $addAddressRoleWrapper
|
||||
) {
|
||||
$this->configWrapper = $configWrapper;
|
||||
$this->metaWriterService = $metaWriterService;
|
||||
$this->propertyGateway = $propertyGateway;
|
||||
$this->pipedriveDealGateway = $pipedriveDealGateway;
|
||||
$this->metaReaderService = $metaReaderService;
|
||||
$this->addAddressRoleWrapper = $addAddressRoleWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function trySetConfiguration(string $name, string $value): void
|
||||
{
|
||||
if (empty($name)) {
|
||||
throw new PipedriveConfigurationException('Cannot set Configuration');
|
||||
}
|
||||
|
||||
$this->configWrapper->setValue(self::PIPEDRIVE_SETTINGS, $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function tryGetConfiguration(string $name): ?string
|
||||
{
|
||||
if (empty($name)) {
|
||||
throw new PipedriveConfigurationException('Cannot get configuration on Empty');
|
||||
}
|
||||
|
||||
return $this->configWrapper->tryGetValue(self::PIPEDRIVE_SETTINGS, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
* @param string $value
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEncryptedConfiguration(array $settings, string $value): array
|
||||
{
|
||||
if (empty($settings)) {
|
||||
throw new PipedriveConfigurationException('Cannot set Configuration');
|
||||
}
|
||||
$encValue = $this->encrypt($value);
|
||||
$settings['pd_api_key'] = $encValue;
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDecryptedConfiguration(): ?string
|
||||
{
|
||||
$settings = $this->getSettings();
|
||||
|
||||
return $settings['pd_api_key'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $plaintext
|
||||
* @param string $sCipher
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function encrypt(string $plaintext, string $sCipher = 'aes-128-gcm'): string
|
||||
{
|
||||
if (!in_array($sCipher, openssl_get_cipher_methods(), true)) {
|
||||
throw new PipedriveConfigurationException(sprintf('Cipher method %s does not exist', $sCipher));
|
||||
}
|
||||
if (null === $this->getNonceSalt()) {
|
||||
return $plaintext;
|
||||
}
|
||||
|
||||
$key = hash('sha256', $this->getNonceSalt());
|
||||
$ivLen = openssl_cipher_iv_length($sCipher);
|
||||
$iv = openssl_random_pseudo_bytes($ivLen, $crypto_strong);
|
||||
|
||||
if ($iv === false || $crypto_strong === false) {
|
||||
throw new PipedriveConfigurationException('Bad Random length');
|
||||
}
|
||||
$cipherTextRaw = openssl_encrypt($plaintext, $sCipher, $key, $options = 0, $iv, $tag);
|
||||
|
||||
return base64_encode($iv . $cipherTextRaw . '..' . $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
* @param string $sCipher
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return false|string|null
|
||||
*/
|
||||
protected function decrypt(string $string, string $sCipher = 'aes-128-gcm')
|
||||
{
|
||||
if (empty($string) || !$this->isBase64Encoded($string)) {
|
||||
return '';
|
||||
}
|
||||
if (null === $this->getNonceSalt()) {
|
||||
return $this->isBase64Encoded($string) ? null : $string;
|
||||
}
|
||||
$stringDecode = base64_decode($string);
|
||||
$encExploded = explode('..', $stringDecode);
|
||||
$enc = array_shift($encExploded);
|
||||
$tag = implode('', $encExploded);
|
||||
$key = hash('sha256', $this->getNonceSalt());
|
||||
$ivLen = openssl_cipher_iv_length($sCipher);
|
||||
$iv = substr($enc, 0, $ivLen);
|
||||
$cipherTextRaw = substr($enc, $ivLen);
|
||||
|
||||
return openssl_decrypt($cipherTextRaw, $sCipher, $key, $options = 0, $iv, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isBase64Encoded(string $string): bool
|
||||
{
|
||||
return base64_encode(base64_decode($string)) === $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string|null
|
||||
*/
|
||||
private function generateSecureSalt()
|
||||
{
|
||||
$rand = sprintf('%s', mt_rand());
|
||||
|
||||
return password_hash(uniqid($rand, true), PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
public function createSalt(bool $force = false)
|
||||
{
|
||||
if ($force === true) {
|
||||
$this->metaWriterService->delete(self::PIPEDRIVE_CONF_NAME);
|
||||
}
|
||||
if ($this->metaReaderService->exists(self::PIPEDRIVE_CONF_NAME) && $this->metaReaderService->hasKey(
|
||||
'nonce_salt',
|
||||
self::PIPEDRIVE_CONF_NAME
|
||||
)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return $this->metaWriterService->save(self::PIPEDRIVE_CONF_NAME, ['nonce_salt' => $this->generateSecureSalt()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function getNonceSalt(): ?string
|
||||
{
|
||||
$data = $this->metaReaderService->readFromFile(self::PIPEDRIVE_CONF_NAME);
|
||||
|
||||
return $data['nonce_salt'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $hContact
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatAddressByResponse(array $hContact): array
|
||||
{
|
||||
if (empty($hContact)) {
|
||||
throw new PipedriveConfigurationException('Invalid contact');
|
||||
}
|
||||
|
||||
$leadFields = $this->matchSelectedAddressFreeField();
|
||||
$lsField = $leadFields['pipedrive_ls_field'];
|
||||
|
||||
$ahEmail = array_map(
|
||||
static function ($email) {
|
||||
if (!array_key_exists('primary', $email) && $email['primary'] === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $email;
|
||||
},
|
||||
$hContact['email']
|
||||
);
|
||||
$email = array_filter($ahEmail);
|
||||
$primaryEmail = is_array($email[0]) && array_key_exists('value', $email[0]) ? $email[0]['value'] : '';
|
||||
|
||||
$ahPhone = array_map(
|
||||
static function ($phone) {
|
||||
if (!array_key_exists('primary', $phone) && $phone['primary'] === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $phone;
|
||||
},
|
||||
$hContact['phone']
|
||||
);
|
||||
|
||||
$phone = array_filter($ahPhone);
|
||||
$primaryPhone = is_array($phone[0]) && array_key_exists('value', $phone[0]) ? $phone[0]['value'] : '';
|
||||
|
||||
return [
|
||||
'lead' => 1,
|
||||
'typ' => !empty($hContact['org_name']) ? 'firma' : 'herr',
|
||||
'sprache' => 'deutsch',
|
||||
'name' => $hContact['org_name'] ?? $hContact['name'],
|
||||
'vorname' => empty($hContact['first_name']) ? 'Pipedrive - ' : $hContact['first_name'],
|
||||
'nachname' => empty($hContact['last_name']) ? $primaryEmail : $hContact['last_name'],
|
||||
'land' => empty($hContact['country']) ? 'DE' : $hContact['country'],
|
||||
'telefon' => $primaryPhone ?? '',
|
||||
'email' => $primaryEmail,
|
||||
'kundenfreigabe' => 1,
|
||||
'waehrung' => 'EUR',
|
||||
'ansprechpartner' => !empty($hContact['org_name']) ? $hContact['name'] : '',
|
||||
$lsField => empty($hContact['label']) ? '' : $hContact['label'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function matchSelectedAddressFreeField(): array
|
||||
{
|
||||
$hFields = [];
|
||||
$asAddressFreeFieldValues = $this->propertyGateway->getConfiguredFreeAddressFieldValues();
|
||||
$pdConfFields = [
|
||||
'pipedrive_ls_field' => $this->tryGetConfiguration('pipedrive_ls_field'),
|
||||
];
|
||||
|
||||
foreach ($asAddressFreeFieldValues as $fieldName) {
|
||||
$addrField = 'adresse' . $fieldName;
|
||||
if (in_array($addrField, $pdConfFields, true)) {
|
||||
$indexField = array_search($addrField, $pdConfFields, true);
|
||||
$hFields[$indexField] = $fieldName;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($hFields)) {
|
||||
throw new PipedriveConfigurationException('Pipedrive Label-Status fields cannot be matched');
|
||||
}
|
||||
|
||||
return $hFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $address
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatAddressToPipedriveContact(array $address): array
|
||||
{
|
||||
if (empty($address)) {
|
||||
throw new PipedriveConfigurationException('Address is invalid');
|
||||
}
|
||||
|
||||
$leadFields = $this->matchSelectedAddressFreeField();
|
||||
$lsField = $leadFields['pipedrive_ls_field'];
|
||||
|
||||
return [
|
||||
'email' => $address['email'],
|
||||
'first_name' => empty($address['vorname']) ? $address['name'] : $address['vorname'],
|
||||
'last_name' => empty($address['nachname']) ? $address['name'] : $address['nachname'],
|
||||
'name' => $address['name'],
|
||||
'phone' => $address['telefon'],
|
||||
'label' => $address[$lsField],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $hDeal
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatDealToInternal(array $hDeal): array
|
||||
{
|
||||
if (empty($hDeal)) {
|
||||
throw new PipedriveConfigurationException('Error! Deal cannot be formatted for Xentral');
|
||||
}
|
||||
|
||||
$dealStage = $this->propertyGateway->getMappingByValueAndType($hDeal['stage_id'], 'deals');
|
||||
$asAddTime = [];
|
||||
$addTime = $hDeal['add_time'] ?? null;
|
||||
if ($addTime !== null) {
|
||||
$asAddTime = explode(' ', $addTime);
|
||||
}
|
||||
|
||||
return [
|
||||
'bezeichnung' => $hDeal['title'],
|
||||
'datum_angelegt' => !empty($asAddTime) ? $asAddTime[0] : null,
|
||||
'zeit_angelegt' => !empty($asAddTime) ? $asAddTime[1] : null,
|
||||
'datum_erinnerung' => null,
|
||||
'zeit_erinnerung' => null,
|
||||
'betrag' => array_key_exists('value', $hDeal) ? (float)$hDeal['value'] : 0.00,
|
||||
'stages' => !empty($dealStage['wiedervorlage_stage_id']) ?
|
||||
$dealStage['wiedervorlage_stage_id'] : 0,
|
||||
'chance' => !empty($hDeal['probability']) ? $hDeal['probability'] : 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $resubmission
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function formatResubmissionToPipedriveDeal(array $resubmission): array
|
||||
{
|
||||
if (empty($resubmission)) {
|
||||
throw new PipedriveConfigurationException('Resubmission is invalid');
|
||||
}
|
||||
|
||||
$status = 'open';
|
||||
if (!empty($resubmission['abgeschlossen'])) {
|
||||
if ($resubmission['status'] === 'gewonnen') {
|
||||
$status = 'won';
|
||||
} elseif ($resubmission['status'] === 'verloren') {
|
||||
$status = 'lost';
|
||||
}
|
||||
}
|
||||
|
||||
$mapping = $this->pipedriveDealGateway->getMappingStageByResubmissionStageId($resubmission['stages']);
|
||||
|
||||
return [
|
||||
'title' => $resubmission['bezeichnung'],
|
||||
'stage_id' => !empty($mapping) ? $mapping['value'] : null,
|
||||
'value' => empty($resubmission['betrag']) ? 0.00 : $resubmission['betrag'],
|
||||
'probability' => $resubmission['chance'],
|
||||
'status' => $status,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
*/
|
||||
public function setSettings($settings = []): void
|
||||
{
|
||||
$this->trySetConfiguration(
|
||||
self::PIPEDRIVE_SETTINGS,
|
||||
json_encode($settings, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings(): array
|
||||
{
|
||||
$settingsRaw = $this->tryGetConfiguration(self::PIPEDRIVE_SETTINGS);
|
||||
if (empty($settingsRaw)) {
|
||||
return static::$_defaultSettings;
|
||||
}
|
||||
|
||||
$settings = json_decode($settingsRaw, true);
|
||||
if ($settings === null && json_last_error() === JSON_ERROR_NONE) {
|
||||
throw new PipedriveConfigurationException(json_last_error_msg());
|
||||
}
|
||||
|
||||
if (empty($settings)) {
|
||||
return static::$_defaultSettings;
|
||||
}
|
||||
|
||||
if (array_key_exists('pd_api_key', $settings) && !empty($settings['pd_api_key'])) {
|
||||
$settings['pd_api_key'] = $this->decrypt($settings['pd_api_key']);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactId
|
||||
*
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addContactToGroup(int $contactId = 0): void
|
||||
{
|
||||
$defaultSettings = $this->getSettings();
|
||||
$contactGrpId = array_key_exists('pd_contact_grp', $defaultSettings) ? $defaultSettings['pd_contact_grp'] : 0;
|
||||
if (!empty($contactGrpId)) {
|
||||
$this->addAddressRoleWrapper->add($contactId, $contactGrpId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveDealPropertyServiceException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
use Xentral\Modules\Pipedrive\Gateway\PipedrivePersonPropertyGateway;
|
||||
use Xentral\Modules\Pipedrive\Wrapper\PipedriveResubmissionWrapper;
|
||||
use Xentral\Modules\Resubmission\Exception\StageNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\ViewNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Service\ResubmissionGateway;
|
||||
|
||||
final class PipedriveDealPropertyService
|
||||
{
|
||||
|
||||
/** @var PipedriveClientService $client */
|
||||
private $client;
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var PipedrivePersonPropertyGateway $propertyGateway */
|
||||
private $propertyGateway;
|
||||
|
||||
/** @var ResubmissionGateway $resubmissionGateway */
|
||||
private $resubmissionGateway;
|
||||
|
||||
/** @var PipedriveResubmissionWrapper $resubmissionWrapper */
|
||||
private $resubmissionWrapper;
|
||||
|
||||
/**
|
||||
* @param PipedriveClientService $client
|
||||
* @param Database $db
|
||||
* @param PipedrivePersonPropertyGateway $propertyGateway
|
||||
* @param ResubmissionGateway $resubmissionGateway
|
||||
* @param PipedriveResubmissionWrapper $resubmissionWrapper
|
||||
*/
|
||||
public function __construct(
|
||||
PipedriveClientService $client,
|
||||
Database $db,
|
||||
PipedrivePersonPropertyGateway $propertyGateway,
|
||||
ResubmissionGateway $resubmissionGateway,
|
||||
PipedriveResubmissionWrapper $resubmissionWrapper
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->db = $db;
|
||||
$this->propertyGateway = $propertyGateway;
|
||||
$this->resubmissionGateway = $resubmissionGateway;
|
||||
$this->resubmissionWrapper = $resubmissionWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pipelineId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveDealPropertyServiceException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDealStages(int $pipelineId = 0): array
|
||||
{
|
||||
$data = [];
|
||||
if ($pipelineId !== 0) {
|
||||
$data = ['pipeline_id' => $pipelineId];
|
||||
}
|
||||
$response = $this->client->read('getStages', $data);
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
throw new PipedriveDealPropertyServiceException($response->getError());
|
||||
}
|
||||
|
||||
return $response->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $pipelineId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveDealPropertyServiceException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws StageNotFoundException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function installDealStages(?int $pipelineId = null): void
|
||||
{
|
||||
// Get Deal Stages
|
||||
$firstStage = 0;
|
||||
$viewId = 0;
|
||||
if ($dealStages = $this->propertyGateway->getLeadsByType('deals')) {
|
||||
$stage = array_unique(array_column($dealStages, 'wiedervorlage_stage_id'));
|
||||
$firstStage = $stage[0];
|
||||
}
|
||||
|
||||
if ($firstStage > 0) {
|
||||
$viewId = $this->resubmissionGateway->getViewIdByStage($firstStage);
|
||||
}
|
||||
|
||||
if (empty($firstStage) || empty($viewId)) {
|
||||
try {
|
||||
$viewId = $this->resubmissionGateway->getViewIdByNameAndDescription('Pipedrive', 'Pipedrive');
|
||||
} catch (ViewNotFoundException $exception) {
|
||||
$viewId = $this->resubmissionWrapper->addResubmissionView('Pipedrive', 'Pipedrive');
|
||||
}
|
||||
}
|
||||
|
||||
// Default Pipeline
|
||||
if ($pipelineId === null) {
|
||||
$pipeline = $this->getDefaultPipeline();
|
||||
$pipelineId = $pipeline['id'] ?? 0;
|
||||
}
|
||||
|
||||
if ($viewId > 0 && ($ahDealStages = $this->getDealStages($pipelineId))) {
|
||||
$position = $this->resubmissionGateway->getMaxSortByViewId($viewId) + 1;
|
||||
foreach ($ahDealStages as $hDealStage) {
|
||||
if ($this->db->fetchValue(
|
||||
'SELECT hm.id FROM `pipedrive_mappings` AS `hm` WHERE hm.value=:value AND hm.type = :type',
|
||||
[
|
||||
'value' => $hDealStage['id'],
|
||||
'type' => 'deals',
|
||||
]
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stage = [
|
||||
'desc' => $hDealStage['name'],
|
||||
'name' => $hDealStage['name'],
|
||||
'position' => $position,
|
||||
'wiedervorlage_view_id' => $viewId,
|
||||
'enabled' => 1,
|
||||
'ausblenden' => 0,
|
||||
];
|
||||
$stageId = $this->resubmissionWrapper->addResubmissionStage($stage);
|
||||
|
||||
if (!empty($stageId)) {
|
||||
$this->db->perform(
|
||||
'INSERT INTO `pipedrive_mappings` (`label`, `value`, `type`, `wiedervorlage_stage_id`,
|
||||
`is_system`, `wiedervorlage_view_id`)
|
||||
VALUES(:label, :value,:type, :wstage_id, :is_system, :view_id)',
|
||||
[
|
||||
'label' => $hDealStage['name'],
|
||||
'value' => $hDealStage['id'],
|
||||
'type' => 'deals',
|
||||
'wstage_id' => $stageId,
|
||||
'is_system' => 1,
|
||||
'view_id' => $viewId,
|
||||
]
|
||||
);
|
||||
}
|
||||
$position++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveDealPropertyServiceException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefaultPipeline(): array
|
||||
{
|
||||
$response = $this->client->read('getPipelines');
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
throw new PipedriveDealPropertyServiceException($response->getError());
|
||||
}
|
||||
|
||||
$data = $response->getData();
|
||||
|
||||
return array_shift($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveDealServiceException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveValidatorException;
|
||||
use Xentral\Modules\Pipedrive\Validator\PipedriveDealValidator;
|
||||
|
||||
final class PipedriveDealService
|
||||
{
|
||||
/** @var array $allowedSyncDealsOptions */
|
||||
private $allowedSyncDealsOptions = [
|
||||
'pipedrive_recently_updated_deals' => 'getRecentlyUpdatedDeals',
|
||||
'pipedrive_all_deals' => 'getDeals',
|
||||
];
|
||||
|
||||
/** @var array $itemOption */
|
||||
private $itemOption = [
|
||||
'limit' => 100,
|
||||
'items' => 'deal',
|
||||
'start' => 0,
|
||||
'since_timestamp' => '1970-01-01 23:59:59',
|
||||
];
|
||||
|
||||
/** @var PipedriveClientService $client */
|
||||
private $client;
|
||||
|
||||
/** @var PipedriveDealValidator $validator */
|
||||
private $validator;
|
||||
|
||||
/** @var PipedriveMetaReaderService $metaReaderService */
|
||||
private $metaReaderService;
|
||||
|
||||
/**
|
||||
* @param PipedriveClientService $client
|
||||
* @param PipedriveDealValidator $validator
|
||||
* @param PipedriveMetaReaderService $metaReaderService
|
||||
*/
|
||||
public function __construct(
|
||||
PipedriveClientService $client,
|
||||
PipedriveDealValidator $validator,
|
||||
PipedriveMetaReaderService $metaReaderService
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->validator = $validator;
|
||||
$this->metaReaderService = $metaReaderService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveDealServiceException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedriveValidatorException
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function createDeal(array $data = []): PipedriveServerResponseInterface
|
||||
{
|
||||
$default = ['status' => 'open', 'stage_id' => 1];
|
||||
$data += $default;
|
||||
|
||||
if (!$this->validator->isValid($data)) {
|
||||
throw new PipedriveDealServiceException(sprintf('%s::createDeal Invalid Deal data', get_class($this)));
|
||||
}
|
||||
|
||||
$deal = $this->validator->getData();
|
||||
|
||||
return $this->client->post('createDeal', $deal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $dealId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getDealById(int $dealId): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->read('getDealById', [], [$dealId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getDeals(array $options = []): PipedriveServerResponseInterface
|
||||
{
|
||||
$options += $this->itemOption;
|
||||
|
||||
return $this->client->read('allDeals', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getRecentlyUpdatedDeals(array $options = []): PipedriveServerResponseInterface
|
||||
{
|
||||
$options += $this->itemOption;
|
||||
|
||||
return $this->client->read('recentlyUpdatedDeals', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $dealId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function deleteDeal(int $dealId): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->delete('deleteDeal', [$dealId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $dealId
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveDealServiceException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedriveValidatorException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function updateDealById(int $dealId, array $data = []): PipedriveServerResponseInterface
|
||||
{
|
||||
if (!$this->validator->isValid($data)) {
|
||||
throw new PipedriveDealServiceException(sprintf('%s::updateDealById Invalid Deal data', get_class($this)));
|
||||
}
|
||||
|
||||
$deal = $this->validator->getData();
|
||||
|
||||
return $this->client->put('updateDeal', $deal, [$dealId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveDealServiceException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function pullDeals(
|
||||
string $type = 'pipedrive_recently_updated_deals',
|
||||
array $options = []
|
||||
): PipedriveServerResponseInterface {
|
||||
if ('pipedrive_all_deals' !== $type && !array_key_exists($type, $this->allowedSyncDealsOptions)) {
|
||||
throw new PipedriveDealServiceException(sprintf('Sync Type %s not allowed', $type));
|
||||
}
|
||||
$metaFile = sprintf('%s.json', $type);
|
||||
if ($type !== 'pipedrive_all_deals') {
|
||||
$options = $this->addMetaOption($metaFile, $options);
|
||||
}
|
||||
|
||||
/** @var PipedriveServerResponseInterface $response */
|
||||
return $this->{$this->allowedSyncDealsOptions[$type]}($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $metaFile
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function addMetaOption(string $metaFile, array $options) : array
|
||||
{
|
||||
$metaInfo = $this->metaReaderService->readFromFile($metaFile);
|
||||
|
||||
if (!empty($metaInfo)) {
|
||||
$timeOffset = $metaInfo['timeOffset'];
|
||||
$offset = strtotime($metaInfo['timeOffset']);
|
||||
if ($offset !== false) {
|
||||
$timeOffset = gmdate('Y-m-d H:i:s', $offset - 3600);
|
||||
}
|
||||
$options = array_merge($options, ['since_timestamp' => $timeOffset]);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Components\Database\Exception\DatabaseExceptionInterface;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveEventException;
|
||||
|
||||
final class PipedriveEventService
|
||||
{
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $event
|
||||
*
|
||||
* @throws PipedriveEventException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function add(string $event): int
|
||||
{
|
||||
$add = 'INSERT INTO `pipedrive_events` (`event`, `created_at`)
|
||||
VALUES (:event, NOW())';
|
||||
try {
|
||||
$this->db->perform($add, ['event' => $event]);
|
||||
} catch (DatabaseExceptionInterface $exception) {
|
||||
throw new PipedriveEventException($exception->getMessage());
|
||||
}
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteById(int $id): void
|
||||
{
|
||||
$this->db->perform('DELETE FROM `pipedrive_events` WHERE `id` = :id', ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAll(): void
|
||||
{
|
||||
$this->db->perform('DELETE FROM `pipedrive_events`');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $days
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteByInterval(int $days = 30): void
|
||||
{
|
||||
$sql = 'DELETE FROM `pipedrive_events` WHERE `created_at` < DATE_SUB(NOW(), INTERVAL :days DAY)';
|
||||
$this->db->perform($sql, ['days' => $days]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Components\HttpClient\Exception\TransferErrorExceptionInterface;
|
||||
use Xentral\Components\HttpClient\HttpClientFactory;
|
||||
use Xentral\Components\HttpClient\HttpClientInterface;
|
||||
use Xentral\Components\HttpClient\Request\ClientRequest;
|
||||
use \Xentral\Components\HttpClient\RequestOptions;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
|
||||
final class PipedriveHttpClientService
|
||||
{
|
||||
|
||||
/** @var string */
|
||||
private const GET_REQUEST = 'GET';
|
||||
/** @var string */
|
||||
private const POST_REQUEST = 'POST';
|
||||
/** @var string */
|
||||
private const DELETE_REQUEST = 'DELETE';
|
||||
/** @var string */
|
||||
private const PATCH_REQUEST = 'PATCH';
|
||||
/** @var string */
|
||||
private const PUT_REQUEST = 'PUT';
|
||||
|
||||
/** @var null|string $endpoint */
|
||||
protected $endpoint;
|
||||
|
||||
/** @var array $userAgent */
|
||||
protected $userAgent = [];
|
||||
|
||||
/** @var array $hRequestVerbs */
|
||||
protected $hRequestVerbs = [
|
||||
self::GET_REQUEST => null,
|
||||
self::POST_REQUEST => 'json',
|
||||
self::PUT_REQUEST => 'json',
|
||||
self::PATCH_REQUEST => 'json',
|
||||
self::DELETE_REQUEST => null,
|
||||
];
|
||||
|
||||
/** @var int $timeout */
|
||||
private $timeout = 10;
|
||||
|
||||
/** @var array $_headers */
|
||||
private $_headers = [];
|
||||
|
||||
/** @var HttpClientInterface $client */
|
||||
private $client;
|
||||
|
||||
/** @var null|RequestOptions $requestOption */
|
||||
private $requestOption;
|
||||
|
||||
/** @var HttpClientFactory|null $factory */
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* @param HttpClientFactory $factory
|
||||
* @param int $timeout
|
||||
* @param HttpClientInterface|null $client
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*/
|
||||
public function __construct(
|
||||
HttpClientFactory $factory,
|
||||
int $timeout = 0,
|
||||
?HttpClientInterface $client = null
|
||||
) {
|
||||
if (!empty($timeout)) {
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
$this->factory = $factory;
|
||||
$this->client = $client ?? $this->createClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param array $data
|
||||
* @param array $headers
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
protected function performRequest(
|
||||
string $url,
|
||||
string $method,
|
||||
array $data = [],
|
||||
array $headers = []
|
||||
): ?PipedriveServerResponseInterface {
|
||||
$this->setHeader($headers);
|
||||
|
||||
$keyParam = $this->hRequestVerbs[$method];
|
||||
$paramData = null;
|
||||
if ($keyParam !== null && $keyParam === 'json') {
|
||||
$paramData = json_encode($data);
|
||||
}
|
||||
|
||||
$rqHeaders = array_merge(
|
||||
['Accept' => 'application/json', 'Content-Type' => 'application/json'],
|
||||
$this->getHeaders()
|
||||
);
|
||||
|
||||
$request = new ClientRequest($method, $url, $rqHeaders);
|
||||
if (!empty($paramData)) {
|
||||
if ($this->requestOption === null) {
|
||||
$this->requestOption = new RequestOptions();
|
||||
}
|
||||
$this->requestOption->setBody($paramData);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->client->sendRequest($request, $this->requestOption);
|
||||
|
||||
return new PipedriveHttpResponseService($response);
|
||||
} catch (TransferErrorExceptionInterface $exception) {
|
||||
throw new PipedriveHttpClientException($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return HttpClientInterface
|
||||
*/
|
||||
private function createClient(): HttpClientInterface
|
||||
{
|
||||
if (!is_int($this->timeout) || $this->timeout < 0) {
|
||||
throw new PipedriveHttpClientException(
|
||||
sprintf('Connection timeout must be an int >= 0, got "%s".', gettype($this->timeout))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->factory === null) {
|
||||
throw new PipedriveHttpClientException('HttpClientFactory is missing!');
|
||||
}
|
||||
|
||||
$options = new RequestOptions();
|
||||
if ($this->timeout > 0) {
|
||||
$options->setTimeout($this->timeout);
|
||||
}
|
||||
$options->setHeader('Accept', 'application/json');
|
||||
$options->setHeader('Content-Type', 'application/json');
|
||||
|
||||
$this->requestOption = $options;
|
||||
|
||||
return $this->factory->createClient($this->requestOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $header
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
public function get(string $url, array $data = [], array $header = []): ?PipedriveServerResponseInterface
|
||||
{
|
||||
if (!empty($data)) {
|
||||
$query = parse_url($url, PHP_URL_QUERY);
|
||||
$newQuery = http_build_query($data);
|
||||
$url = $query ? $url . '&' . $newQuery : $url . '?' . $newQuery;
|
||||
}
|
||||
|
||||
return $this->performRequest($url, self::GET_REQUEST, [], $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $header
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
public function post(string $url, array $data = [], array $header = []): ?PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->performRequest($url, self::POST_REQUEST, $data, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $header
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
public function patch(string $url, array $data = [], array $header = []): ?PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->performRequest($url, self::PATCH_REQUEST, $data, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $header
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
public function delete(string $url, array $data = [], array $header = []): ?PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->performRequest($url, self::DELETE_REQUEST, $data, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $option
|
||||
*/
|
||||
protected function setHeader(array $option = []): void
|
||||
{
|
||||
$this->_headers += $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeaders(): array
|
||||
{
|
||||
$default = ['User-Agent' => 'Xentral-ERP-CRM'];
|
||||
|
||||
return $this->_headers += $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $header
|
||||
*
|
||||
* @throws PipedriveHttpClientException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface|null
|
||||
*/
|
||||
public function put(string $url, array $data = [], array $header = []): ?PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->performRequest($url, self::PUT_REQUEST, $data, $header);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Xentral\Components\HttpClient\Response\ServerResponseInterface;
|
||||
|
||||
final class PipedriveHttpResponseService implements PipedriveServerResponseInterface
|
||||
{
|
||||
/** @var ServerResponseInterface $response */
|
||||
private $response;
|
||||
|
||||
/** @var null|array $json */
|
||||
private $json;
|
||||
|
||||
/**
|
||||
* @param ServerResponseInterface $response
|
||||
*/
|
||||
public function __construct(ServerResponseInterface $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
$this->json = json_decode((string)$this->response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the json response body
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
public function getJson(): ?array
|
||||
{
|
||||
return $this->json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StreamInterface
|
||||
*/
|
||||
public function getBody(): StreamInterface
|
||||
{
|
||||
return $this->response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->response->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess(): bool
|
||||
{
|
||||
$success = false;
|
||||
if (null !== $this->json && is_array($this->json)) {
|
||||
$success = array_key_exists('success', $this->json) && $this->json['success'] === true;
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
$data = [];
|
||||
if (null !== $this->json && is_array($this->json) && array_key_exists('data', $this->json)) {
|
||||
$data = $this->json['data'] ?? [];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError(): string
|
||||
{
|
||||
$error = '';
|
||||
if (null !== $this->json && is_array($this->json) && array_key_exists('error', $this->json) &&
|
||||
!in_array($this->getStatusCode(), [200, 201])) {
|
||||
$error = $this->json['error'] ?? 'Unknown Error';
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAdditionalData(): array
|
||||
{
|
||||
$additionalData = [];
|
||||
if (null !== $this->json && is_array($this->json) && array_key_exists('additional_data', $this->json)) {
|
||||
$additionalData = $this->json['additional_data'];
|
||||
}
|
||||
|
||||
return $additionalData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getPagination(): ?array
|
||||
{
|
||||
$pagination = null;
|
||||
if (($additional_data = $this->getAdditionalData()) && array_key_exists('pagination', $additional_data)) {
|
||||
$pagination = $additional_data['pagination'];
|
||||
}
|
||||
|
||||
return $pagination;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveExceptionInterface;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
|
||||
final class PipedriveMetaReaderService
|
||||
{
|
||||
/** @var string $tmpDir directory to save the meta file */
|
||||
private $tmpDir;
|
||||
|
||||
/**
|
||||
* @param string $tmpDir
|
||||
*/
|
||||
public function __construct(string $tmpDir)
|
||||
{
|
||||
$this->tmpDir = $tmpDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function readFromFile(string $fileName): ?array
|
||||
{
|
||||
if (empty($fileName)) {
|
||||
throw new PipedriveMetaException(
|
||||
sprintf('::readFromFile() Expects Meta content to be non empty string file, %s given', $fileName)
|
||||
);
|
||||
}
|
||||
|
||||
if (!$this->exists($fileName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fullFileName = $this->getFullFileName($fileName);
|
||||
|
||||
$meta = @file_get_contents($fullFileName);
|
||||
|
||||
if (($meta = json_decode($meta, true)) === null || (json_last_error() !== JSON_ERROR_NONE)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function getFullFileName(string $fileName): ?string
|
||||
{
|
||||
$metaFile = sprintf($this->tmpDir . DIRECTORY_SEPARATOR . '%s', $fileName);
|
||||
if (!is_file($metaFile)) {
|
||||
throw new PipedriveMetaException(sprintf('File "%s" was not found', $metaFile));
|
||||
}
|
||||
|
||||
return $metaFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $fileName): bool
|
||||
{
|
||||
try {
|
||||
$fullFileName = $this->getFullFileName($fileName);
|
||||
|
||||
return file_exists($fullFileName) && is_file($fullFileName);
|
||||
} catch (PipedriveExceptionInterface $exception) {
|
||||
//
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $fileName
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasKey(string $key, string $fileName): bool
|
||||
{
|
||||
return $this->exists($fileName) && ($meta = $this->readFromFile($fileName)) && array_key_exists($key, $meta);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
|
||||
final class PipedriveMetaWriterService
|
||||
{
|
||||
/** @var string $tmpDir directory to save the meta file */
|
||||
private $tmpDir;
|
||||
|
||||
/**
|
||||
* PipedriveMetaWriterService constructor.
|
||||
*
|
||||
* @param string $tmpDir
|
||||
*/
|
||||
public function __construct(string $tmpDir)
|
||||
{
|
||||
$this->tmpDir = $tmpDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
public function save(string $fileName, array $data)
|
||||
{
|
||||
if (empty($fileName)) {
|
||||
throw new PipedriveMetaException('Name cannot be empty');
|
||||
}
|
||||
|
||||
if (!function_exists('json_encode')) {
|
||||
throw new PipedriveMetaException('Required PHP extension "json" is missing.');
|
||||
}
|
||||
|
||||
$content = json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
|
||||
|
||||
return file_put_contents(
|
||||
$this->getFullFileName($fileName),
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getFullFileName(string $fileName): string
|
||||
{
|
||||
if (!is_dir($this->tmpDir) && !@mkdir($this->tmpDir, 0777, true) && !is_dir($this->tmpDir)) {
|
||||
throw new PipedriveMetaException(sprintf('Directory "%s" was not created', $this->tmpDir));
|
||||
}
|
||||
|
||||
return sprintf($this->tmpDir . DIRECTORY_SEPARATOR . '%s', $fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileName
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(string $fileName): bool
|
||||
{
|
||||
$metaFile = $this->getFullFileName($fileName);
|
||||
|
||||
return file_exists($metaFile) && is_file($metaFile) && @unlink($metaFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedrivePersonPropertyServiceException;
|
||||
|
||||
final class PipedrivePersonPropertyService
|
||||
{
|
||||
|
||||
/** @var PipedriveClientService $client */
|
||||
private $client;
|
||||
|
||||
/**
|
||||
* @param PipedriveClientService $client
|
||||
*/
|
||||
public function __construct(PipedriveClientService $client)
|
||||
{
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getProperties(): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->read('getPersonFields');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getProperty(int $id): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->read('getOnePersonField', [], [$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedrivePersonPropertyServiceException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPdLeadStatus(): array
|
||||
{
|
||||
$response = $this->getProperty(9039);
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
throw new PipedrivePersonPropertyServiceException($response->getError());
|
||||
}
|
||||
|
||||
if (($data = $response->getData()) && array_key_exists('options', $data)) {
|
||||
return $data['options'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveConfigurationException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveHttpClientException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveMetaException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedrivePersonServiceException;
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveValidatorException;
|
||||
use Xentral\Modules\Pipedrive\Validator\PipedrivePersonValidator;
|
||||
|
||||
final class PipedrivePersonService
|
||||
{
|
||||
|
||||
/** @var array $itemOption */
|
||||
private $itemOption = [
|
||||
'limit' => 100,
|
||||
'items' => 'person',
|
||||
'start' => 0,
|
||||
'since_timestamp' => '1970-01-01 23:59:59',
|
||||
];
|
||||
|
||||
/** @var string[] $allowedSyncPeronOptions */
|
||||
private $allowedSyncPeronOptions = [
|
||||
'pipedrive_recently_updated' => 'getRecentlyUpdatedPersons',
|
||||
'pipedrive_all' => 'getAllPersons',
|
||||
];
|
||||
|
||||
/** @var PipedriveClientService $client */
|
||||
private $client;
|
||||
|
||||
/** @var PipedriveMetaReaderService $metaReaderService */
|
||||
private $metaReaderService;
|
||||
|
||||
/** @var PipedrivePersonValidator $validator */
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* PipedrivePersonService constructor.
|
||||
*
|
||||
* @param PipedriveClientService $client
|
||||
* @param PipedrivePersonValidator $validator
|
||||
* @param PipedriveMetaReaderService $metaReaderService
|
||||
*/
|
||||
public function __construct(
|
||||
PipedriveClientService $client,
|
||||
PipedrivePersonValidator $validator,
|
||||
PipedriveMetaReaderService $metaReaderService
|
||||
) {
|
||||
$this->client = $client;
|
||||
$this->validator = $validator;
|
||||
$this->metaReaderService = $metaReaderService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getAllPersons(array $options = []): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->read('allPersons', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getRecentlyUpdatedPersons(array $options = []): PipedriveServerResponseInterface
|
||||
{
|
||||
$options = array_merge($this->itemOption, $options);
|
||||
|
||||
return $this->client->read('recentlyUpdatedPersons', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function deleteContact(int $contactId = 0): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->delete('deleteContact', [$contactId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedrivePersonServiceException
|
||||
* @throws PipedriveValidatorException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function createContact(array $data = []): PipedriveServerResponseInterface
|
||||
{
|
||||
if (!$this->validator->isValid($data)) {
|
||||
throw new PipedrivePersonServiceException(
|
||||
sprintf('%s::createContact Invalid contact data', get_class($this))
|
||||
);
|
||||
}
|
||||
$identity = $this->formatContactIdentity($this->validator->getData());
|
||||
|
||||
return $this->client->post('createContact', $identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactId
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveConfigurationException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedrivePersonServiceException
|
||||
* @throws PipedriveValidatorException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function updateContactById(int $contactId, array $data = []): PipedriveServerResponseInterface
|
||||
{
|
||||
if (!$this->validator->isValid($data)) {
|
||||
throw new PipedrivePersonServiceException(
|
||||
sprintf('%s::updateContactById Invalid contact data', get_class($this))
|
||||
);
|
||||
}
|
||||
|
||||
$identity = $this->formatContactIdentity($this->validator->getData());
|
||||
|
||||
return $this->client->put('updateContact', $identity, [$contactId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function formatContactIdentity(array $data): array
|
||||
{
|
||||
$identity = [];
|
||||
foreach ($data as $property => $value) {
|
||||
if (in_array($property, ['email', 'phone'])) {
|
||||
$identity[$property][] = [
|
||||
'label' => 'other',
|
||||
'value' => $value,
|
||||
];
|
||||
} else {
|
||||
$identity[$property] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedrivePersonServiceException
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function pullPersons(string $type = 'pipedrive_all', array $options = []): PipedriveServerResponseInterface
|
||||
{
|
||||
$options = array_merge($this->itemOption, $options);
|
||||
|
||||
if ('pipedrive_all' !== $type && !array_key_exists($type, $this->allowedSyncPeronOptions)) {
|
||||
throw new PipedrivePersonServiceException(sprintf('Sync Type %s not allowed', $type));
|
||||
}
|
||||
|
||||
$metaFile = sprintf('%s.json', $type);
|
||||
|
||||
if ($type !== 'pipedrive_all') {
|
||||
$options = $this->addMetaOption($metaFile, $options);
|
||||
}
|
||||
|
||||
/** @var PipedriveServerResponseInterface $response */
|
||||
return $this->{$this->allowedSyncPeronOptions[$type]}($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactId
|
||||
*
|
||||
* @throws PipedriveClientException
|
||||
* @throws PipedriveHttpClientException
|
||||
* @throws PipedriveMetaException
|
||||
* @throws PipedriveConfigurationException
|
||||
*
|
||||
* @return PipedriveServerResponseInterface
|
||||
*/
|
||||
public function getContactById(int $contactId): PipedriveServerResponseInterface
|
||||
{
|
||||
return $this->client->read('getContactById', [], [$contactId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $metaFile
|
||||
* @param array $options
|
||||
*
|
||||
* @throws PipedriveMetaException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function addMetaOption(string $metaFile, array $options) : array
|
||||
{
|
||||
$metaInfo = $this->metaReaderService->readFromFile($metaFile);
|
||||
|
||||
if (!empty($metaInfo)) {
|
||||
$timeOffset = $metaInfo['timeOffset'];
|
||||
$offset = strtotime($metaInfo['timeOffset']);
|
||||
if ($offset !== false) {
|
||||
$timeOffset = gmdate('Y-m-d H:i:s', $offset - 3600);
|
||||
}
|
||||
$options = array_merge($options, ['since_timestamp' => $timeOffset]);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Service;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
interface PipedriveServerResponseInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the json response body
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getJson();
|
||||
|
||||
/**
|
||||
* Gets the response body.
|
||||
*
|
||||
* @return StreamInterface
|
||||
*/
|
||||
public function getBody(): StreamInterface;
|
||||
|
||||
/**
|
||||
* Gets the response status code.
|
||||
*
|
||||
* @return int Status code.
|
||||
*/
|
||||
public function getStatusCode(): int;
|
||||
|
||||
/**
|
||||
* Returns the error message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError(): string;
|
||||
|
||||
/**
|
||||
* Checks whether the call was successful or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess(): bool;
|
||||
|
||||
/**
|
||||
* Retrieves Data from the response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array;
|
||||
|
||||
/**
|
||||
* Retrieves Additional Data from the response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAdditionalData(): array;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Validator;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveValidatorException;
|
||||
|
||||
final class PipedriveDealValidator implements PipedriveValidatorInterface
|
||||
{
|
||||
/** @var string $rules */
|
||||
private $rules;
|
||||
|
||||
/** @var array $data */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* PipedriveDealValidator constructor.
|
||||
*
|
||||
* @param string $rule
|
||||
*/
|
||||
public function __construct(string $rule = 'default')
|
||||
{
|
||||
$this->rules = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isValid(array $data = []): bool
|
||||
{
|
||||
$this->data = $data;
|
||||
$validatorMethod = 'validatorRule' . ucfirst($this->rules);
|
||||
if (!method_exists($this, $validatorMethod)) {
|
||||
throw new PipedriveValidatorException(sprintf('Validator method %s is missing', $validatorMethod));
|
||||
}
|
||||
|
||||
$rules = $this->{$validatorMethod}();
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (array_key_exists('rule', $rule)) {
|
||||
$validation = call_user_func($rule['rule'], $this->data);
|
||||
if (!$validation && (!empty($this->data[$field]) || !empty($rule['required']))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function validatorRuleDefault(): array
|
||||
{
|
||||
return [
|
||||
'title' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['title']) && is_string($data['title']);
|
||||
},
|
||||
'required' => true,
|
||||
'message' => sprintf('%s should be a non empty String', 'Title'),
|
||||
],
|
||||
'value' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['value']) && is_string($data['value']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'Deal Value'),
|
||||
],
|
||||
'currency' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['currency']) && is_string($data['currency']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'currency'),
|
||||
],
|
||||
|
||||
'user_id' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['user_id']) && is_numeric($data['user_id']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%d should be a non empty String', 'user_id'),
|
||||
],
|
||||
|
||||
'person_id' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['person_id']) && is_numeric($data['person_id']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%d should be a non empty String', 'person_id'),
|
||||
],
|
||||
|
||||
'stage_id' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['stage_id']) && is_numeric($data['stage_id']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%d should be a non empty String', 'stage_id'),
|
||||
],
|
||||
|
||||
'probability' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['probability']) && is_numeric($data['probability']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'probability'),
|
||||
],
|
||||
|
||||
'status' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['status']) && in_array($data['status'], ['open', 'won', 'lost', 'deleted']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'status'),
|
||||
],
|
||||
|
||||
'lost_reason' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['lost_reason']) && is_string($data['lost_reason']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'lost reason'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->data,
|
||||
static function ($value) {
|
||||
return is_numeric($value) || (is_string($value) && trim($value) !== '');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Validator;
|
||||
|
||||
class PipedrivePersonPropertyValidator implements PipedriveValidatorInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isValid(array $data = []): bool
|
||||
{
|
||||
// TODO: Implement isValid() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function validatorRuleDefault(): array
|
||||
{
|
||||
// TODO: Implement validatorRuleDefault() method.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Validator;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveValidatorException;
|
||||
|
||||
final class PipedrivePersonValidator implements PipedriveValidatorInterface
|
||||
{
|
||||
/** @var string $rules */
|
||||
private $rules;
|
||||
|
||||
/** @var array $data */
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* PipedrivePersonValidator constructor.
|
||||
*
|
||||
* @param string $rules
|
||||
*/
|
||||
public function __construct(string $rules = 'default')
|
||||
{
|
||||
$this->rules = $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isValid(array $data = []): bool
|
||||
{
|
||||
$this->data = $data;
|
||||
$validatorMethod = 'validatorRule' . ucfirst($this->rules);
|
||||
if (!method_exists($this, $validatorMethod)) {
|
||||
throw new PipedriveValidatorException(sprintf('Validator method %s is missing', $validatorMethod));
|
||||
}
|
||||
|
||||
$rules = $this->{$validatorMethod}();
|
||||
foreach ($rules as $field => $rule) {
|
||||
if (array_key_exists('rule', $rule)) {
|
||||
$validation = call_user_func($rule['rule'], $this->data);
|
||||
if (!$validation && (!empty($this->data[$field]) || !empty($rule['required']))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function validatorRuleDefault(): array
|
||||
{
|
||||
return [
|
||||
'email' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['email']) && is_string($data['email']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'Email'),
|
||||
],
|
||||
'name' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['name']) && is_string($data['name']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'Name'),
|
||||
],
|
||||
'first_name' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['first_name']) && is_string($data['first_name']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'First name'),
|
||||
],
|
||||
'last_name' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['last_name']) && is_string($data['last_name']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'Last name'),
|
||||
],
|
||||
|
||||
'phone' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['phone']) && is_string($data['phone']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty String', 'phone'),
|
||||
],
|
||||
|
||||
'label' => [
|
||||
'rule' => static function ($data) {
|
||||
return !empty($data['label']) && is_numeric($data['label']);
|
||||
},
|
||||
'required' => false,
|
||||
'message' => sprintf('%s should be a non empty numeric', 'Pipedrive-Label:'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->data,
|
||||
static function ($value) {
|
||||
return is_numeric($value) || (is_string($value) && trim($value) !== '');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Validator;
|
||||
|
||||
use Xentral\Modules\Pipedrive\Exception\PipedriveValidatorException;
|
||||
|
||||
interface PipedriveValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Checks whether the given data is valid or not
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @throws PipedriveValidatorException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(array $data = []): bool;
|
||||
|
||||
/**
|
||||
* Defines the default validator data object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validatorRuleDefault(): array;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Wrapper;
|
||||
|
||||
use erpAPI;
|
||||
|
||||
final class PipedriveAddAddressRoleWrapper
|
||||
{
|
||||
|
||||
/** @var erpAPI $erp */
|
||||
private $erp;
|
||||
|
||||
/**
|
||||
* @param erpAPI $erp
|
||||
*/
|
||||
public function __construct(erpAPI $erp)
|
||||
{
|
||||
$this->erp = $erp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $contactId
|
||||
* @param int $groupId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add(int $contactId, int $groupId): void
|
||||
{
|
||||
$this->erp->AddRolleZuAdresse($contactId, 'Mitglied', 'von', 'Gruppe', $groupId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Xentral\Modules\Pipedrive\Wrapper;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
|
||||
final class PipedriveResubmissionWrapper
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $deal
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function addResubmission(array $deal): int
|
||||
{
|
||||
$this->db->perform(
|
||||
'INSERT INTO `wiedervorlage` (`bezeichnung`,
|
||||
`datum_angelegt`,
|
||||
`zeit_angelegt`,
|
||||
`datum_erinnerung`,
|
||||
`zeit_erinnerung`,
|
||||
`stages`,
|
||||
`chance`,
|
||||
`beschreibung`,
|
||||
`ergebnis`)
|
||||
VALUES(:bezeichnung,
|
||||
:datum_angelegt,
|
||||
:zeit_angelegt,
|
||||
:datum_erinnerung,
|
||||
:zeit_erinnerung,
|
||||
:stages,
|
||||
:chance, \'\', \'\'
|
||||
)',
|
||||
$deal
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
* @param array $deal
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function updateResubmission(int $resubmissionId, array $deal): void
|
||||
{
|
||||
$sql = sprintf(
|
||||
'UPDATE `wiedervorlage`
|
||||
SET `chance` = :chance, `bezeichnung` = :bezeichnung, `datum_angelegt` = :datum_angelegt,
|
||||
`zeit_angelegt` = :zeit_angelegt, `datum_erinnerung` = :datum_erinnerung,
|
||||
`zeit_erinnerung` = :zeit_erinnerung, `stages` = :stages
|
||||
WHERE id = %d',
|
||||
$resubmissionId
|
||||
);
|
||||
|
||||
$this->db->perform($sql, $deal);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $shortName
|
||||
* @param int $project
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function addResubmissionView(string $name, string $shortName, int $project = 0): int
|
||||
{
|
||||
$this->db->perform(
|
||||
'INSERT INTO `wiedervorlage_view` (`name`, `shortname`, `project`, `active`)
|
||||
VALUES (:name, :desc_short, :project, 1)',
|
||||
[
|
||||
'name' => $name,
|
||||
'desc_short' => $shortName,
|
||||
'project' => $project,
|
||||
]
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $stage
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function addResubmissionStage(array $stage): int
|
||||
{
|
||||
$this->db->perform(
|
||||
'INSERT INTO `wiedervorlage_stages` (`kurzbezeichnung`, `name`,
|
||||
`stageausblenden`, `sort`, `view`, `ausblenden`)
|
||||
VALUES(:desc, :name,:enabled, :position,:wiedervorlage_view_id,:ausblenden)',
|
||||
$stage
|
||||
);
|
||||
|
||||
return $this->db->lastInsertId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
var PipedriveModule = function ($) {
|
||||
'use strict';
|
||||
|
||||
var me = {
|
||||
|
||||
isInitialized: false,
|
||||
|
||||
storage: {},
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
init: function () {
|
||||
if (me.isInitialized === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
me.registerEvents();
|
||||
|
||||
me.isInitialized = true;
|
||||
},
|
||||
|
||||
registerEvents: function () {
|
||||
$('#sync_pd_xt').on('click', function (event) {
|
||||
me.syncAccount();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('#no-matching').on('click', function (event) {
|
||||
$(this).prop('checked', true);
|
||||
$('#do-matching').prop('checked', false);
|
||||
me.hideMatchingSetting();
|
||||
});
|
||||
|
||||
$('#do-matching').on('click', function (event) {
|
||||
$(this).prop('checked', true);
|
||||
$('#no-matching').prop('checked', false);
|
||||
me.showMatchingSetting();
|
||||
});
|
||||
|
||||
$('#addresses_interval-checkbox').add('#deals_interval-checkbox').on('change', function () {
|
||||
me.setReadOnly(this);
|
||||
})
|
||||
},
|
||||
|
||||
setReadOnly: function(src) {
|
||||
|
||||
var id = $(src).attr('id');
|
||||
var idExploded = id.split('-');
|
||||
$('#'+ idExploded[0]).prop('readonly',!$('#'+ idExploded[0]).prop('readonly'));
|
||||
},
|
||||
|
||||
showMatchingSetting: function () {
|
||||
$('.deal-system').removeClass('hd-invisible').find('select').prop('disabled', false);
|
||||
},
|
||||
|
||||
hideMatchingSetting: function () {
|
||||
$('.deal-system').addClass('hd-invisible').find('select').prop('disabled', true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
addApiKey: function () {
|
||||
if (me.isInitialized === false) {
|
||||
me.init();
|
||||
}
|
||||
|
||||
me.resetAdd();
|
||||
me.storage.$createItemDialog.dialog('open');
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @return {void}
|
||||
*/
|
||||
syncAccount: function () {
|
||||
var $form = $('#hd-configurator-form');
|
||||
$form.action = 'index.php?module=pipedrive&action=apikey';
|
||||
$form.submit();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
init: me.init,
|
||||
addApiKey: me.addApiKey
|
||||
};
|
||||
|
||||
}(jQuery);
|
||||
|
||||
$(function () {
|
||||
if ($('#sync_pd_xt').length > 0) {
|
||||
PipedriveModule.init();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user