init
This commit is contained in:
commit
df7a781e27
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.idea
|
||||||
|
vendor
|
||||||
|
config/config.php
|
||||||
|
composer.lock
|
||||||
|
composer.json
|
155
classes/ApiHelper.php
Normal file
155
classes/ApiHelper.php
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once(__DIR__ . "/../vendor/autoload.php");
|
||||||
|
require(__DIR__ . "/Parser.php");
|
||||||
|
|
||||||
|
class ApiHelper {
|
||||||
|
|
||||||
|
protected $config;
|
||||||
|
protected $crmClient;
|
||||||
|
protected $parser;
|
||||||
|
|
||||||
|
public function __construct($config) {
|
||||||
|
|
||||||
|
$this->config = $config;
|
||||||
|
|
||||||
|
$this->crmClient = new \RetailCrm\ApiClient(
|
||||||
|
$this->config['retailcrm_url'],
|
||||||
|
$this->config['retailcrm_apikey']
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->parser = new Parser($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processXMLOrders() {
|
||||||
|
$url = $this->config['tiu_xml_url'];
|
||||||
|
$xml = simplexml_load_file($url);
|
||||||
|
if (! $xml instanceof SimpleXMLElement) {
|
||||||
|
$this->writeLog("ApiHelper::processXmlOrders: cannot get XML from $url");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$orders = array();
|
||||||
|
foreach($xml as $xmlOrder) {
|
||||||
|
$order = $this->parser->parseXMLNewOrder($xmlOrder);
|
||||||
|
if ($order) {
|
||||||
|
$customerId = $this->checkCustomers($order);
|
||||||
|
if ($customerId === false) {
|
||||||
|
echo "upload failed" . PHP_EOL;
|
||||||
|
return false;
|
||||||
|
} elseif ($customerId !== 0) {
|
||||||
|
$order['customerId'] = $customerId;
|
||||||
|
}
|
||||||
|
$orders[$order['number']] = $order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$orders = $this->filterOrders($orders);
|
||||||
|
if ($this->uploadOrders($orders)) {
|
||||||
|
$a = sizeof($orders);
|
||||||
|
echo "uploaded $a orders" . PHP_EOL;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
echo "upload failed" . PHP_EOL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function uploadOrders($orders) {
|
||||||
|
if ($orders === false)
|
||||||
|
return false;
|
||||||
|
if (sizeof($orders)) {
|
||||||
|
$orders = array_chunk($orders, $this->config['retailcrm_order_chunk_size']);
|
||||||
|
foreach ($orders as $chunk) {
|
||||||
|
try {
|
||||||
|
$result = $this->crmClient->ordersUpload($chunk);
|
||||||
|
} catch (\RetailCrm\Exception\CurlException $e) {
|
||||||
|
$this->writeLog(
|
||||||
|
'\Retailcrm\ApiClient::ordersUpload: ' . $e,
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! $result->isSuccessful()) {
|
||||||
|
$this->writeLog(
|
||||||
|
'\Retailcrm\ApiClient::ordersUpload: ' . $result['errorMsg'] . (isset($result['errors']) ? ': ' . json_encode($result['errors']) : '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
time_nanosleep(0, 200000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkCustomers($order) {
|
||||||
|
$customerId = 0;
|
||||||
|
if ($order['email'] != '')
|
||||||
|
$filter = array('email' => $order['email']);
|
||||||
|
if ($order['phone'] != '')
|
||||||
|
$filter = array('name' => $order['phone']);
|
||||||
|
if (isset($filter)) {
|
||||||
|
try {
|
||||||
|
$customers = $this->crmClient->customersList($filter);
|
||||||
|
} catch (\RetailCrm\Exception\CurlException $e) {
|
||||||
|
$this->writeLog(
|
||||||
|
'\Retailcrm\ApiClient::customersList: ' . $e,
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isset($customers['customers']) && sizeof($customers['customers'] && isset($customers['customers'][0]['externalId']))) {
|
||||||
|
$customerId = $customers['customers'][0]['externalId'];
|
||||||
|
}
|
||||||
|
time_nanosleep(0, 200000000);
|
||||||
|
}
|
||||||
|
return $customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function writeLog($text, $type = null) {
|
||||||
|
if (! file_exists(__DIR__ ."/../logs"))
|
||||||
|
mkdir(__DIR__ ."/../logs");
|
||||||
|
$date = date('Y-m-d H:i:s');
|
||||||
|
file_put_contents(__DIR__ . "/../logs/error.log", "[$date]$text" . PHP_EOL, FILE_APPEND);
|
||||||
|
if ($type == 'error') {
|
||||||
|
$this->sendAlertEmail($text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendAlertEmail($text) {
|
||||||
|
mail($this->config['support_email'], $this->config['support_email_subject'], $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function filterOrders($toUpload) {
|
||||||
|
$numbers = array_keys($toUpload);
|
||||||
|
if (date_create_from_format('Y-m-d H:i:s', $this->config['filter_date'])) {
|
||||||
|
foreach ($toUpload as $i => $order) {
|
||||||
|
if ($order['createdAt'] < $this->config['filter_date']) {
|
||||||
|
unset($toUpload[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ordersListPage = 0;
|
||||||
|
do {
|
||||||
|
$ordersListPage++;
|
||||||
|
try {
|
||||||
|
$orders = $this->crmClient->ordersList(array(
|
||||||
|
'numbers' => $numbers
|
||||||
|
), $ordersListPage, 100);
|
||||||
|
} catch (\RetailCrm\Exception\CurlException $e) {
|
||||||
|
$text = '\Retailcrm\ApiClient::ordersList: ' . $e;
|
||||||
|
$this->writeLog($text, 'error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isset($orders['orders']) && sizeof($orders['orders'])) {
|
||||||
|
foreach ($orders['orders'] as $order) {
|
||||||
|
if (isset($toUpload[$order['number']]))
|
||||||
|
unset($toUpload[$order['number']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time_nanosleep(0, 200000000);
|
||||||
|
} while ($ordersListPage < $orders['pagination']['totalPageCount']);
|
||||||
|
|
||||||
|
return $toUpload;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
classes/Parser.php
Normal file
83
classes/Parser.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
|
||||||
|
const ENCODING = 'UTF-8';
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
public function __construct($config) {
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseXMLNewOrder(SimpleXMLElement $xml) {
|
||||||
|
$order = $this->explodeFIO((string)$xml->name);
|
||||||
|
|
||||||
|
foreach($xml->attributes() as $key => $val) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'id':
|
||||||
|
$val = (string)$val;
|
||||||
|
$order['number'] = $this->config['order_prefix'] . $val;
|
||||||
|
break;
|
||||||
|
case 'state':
|
||||||
|
$orderStatuses = array_flip($this->config['order_statuses']);
|
||||||
|
$order['status'] = $orderStatuses[(string)$val];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$createdAt = \DateTime::createFromFormat('d.m.y H:i', (string)$xml->date);
|
||||||
|
$createdAt = $createdAt->format('Y-m-d H:i:s');
|
||||||
|
if (isset($this->config['date_from']) && $createdAt < $this->config['date_from'])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$order = array_merge($order, array(
|
||||||
|
'email' => (string)$xml->email,
|
||||||
|
'phone' => (string)$xml->phone,
|
||||||
|
'orderMethod' => $this->config['order_method'],
|
||||||
|
'createdAt' => $createdAt,
|
||||||
|
'paymentType' => $this->config['payment'][(string)$xml->paymentType],
|
||||||
|
'delivery' => array(
|
||||||
|
'address' => array(
|
||||||
|
'text' => (string)$xml->address
|
||||||
|
),
|
||||||
|
'code' => $this->config['delivery'][(string)$xml->deliveryType]
|
||||||
|
)
|
||||||
|
));
|
||||||
|
$items = array();
|
||||||
|
$xmlItems = $xml->items;
|
||||||
|
foreach($xmlItems as $xmlItem) {
|
||||||
|
$xmlItem = $xmlItem->item;
|
||||||
|
$items[] = array(
|
||||||
|
'productId' => (string)$xmlItem->external_id,
|
||||||
|
'productName' => (string)$xmlItem->name,
|
||||||
|
'quantity' => (string)$xmlItem->quantity,
|
||||||
|
'initialPrice' => (string)$xmlItem->price
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$order['items'] = $items;
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function explodeFIO($name) {
|
||||||
|
$name = explode(' ', $name, 3);
|
||||||
|
$firstName = $lastName = $patronymic = '';
|
||||||
|
switch (sizeof($name)) {
|
||||||
|
case 1:
|
||||||
|
$firstName = $name[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$firstName = $name[0];
|
||||||
|
$lastName = $name[1];
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$firstName = $name[1];
|
||||||
|
$lastName = $name[0];
|
||||||
|
$patronymic = $name[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'firstName' => $firstName,
|
||||||
|
'lastName' => $lastName,
|
||||||
|
'patronymic' => $patronymic
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
47
config/config-dist.php
Normal file
47
config/config-dist.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
$сonfig = array(
|
||||||
|
// ссылка вида https://my.tiu.ru/cabinet/export_orders/xml/1234567?hash_tag=472g8fzb1af38d35f2c521dc8a1e1208
|
||||||
|
// берется отсюда: https://my.tiu.ru/cabinet/order/export_orders
|
||||||
|
'tiu_xml_url' => '',
|
||||||
|
|
||||||
|
'retailcrm_url' => 'https://site.retailcrm.ru',
|
||||||
|
'retailcrm_apikey' => 'qwerty123',
|
||||||
|
'retailcrm_order_chunk_size' => 50,
|
||||||
|
|
||||||
|
// загружать заказы только с определенной даты (Y-m-d H:i:s)
|
||||||
|
'date_from' => '',
|
||||||
|
|
||||||
|
'order_prefix' => 'TIU', // приписывается к номеру заказа в CRM
|
||||||
|
'set_external_ids' => false, // задавать ли externalId заказам
|
||||||
|
'order_method' => 'tiu', // способ оформления заказа
|
||||||
|
// соответствие доставок tiu => CRM
|
||||||
|
// https://my.tiu.ru/cabinet/shop_settings/delivery_options
|
||||||
|
'delivery' => array(
|
||||||
|
'Транспортная компания' => 'courier',
|
||||||
|
'Доставка курьером' => 'courier',
|
||||||
|
'Самовывоз' => 'courier'
|
||||||
|
),
|
||||||
|
|
||||||
|
// соответствие оплат tiu => CRM
|
||||||
|
// https://my.tiu.ru/cabinet/shop_settings/payment_options
|
||||||
|
'payment' => array(
|
||||||
|
'Наличными' => 'cash',
|
||||||
|
'Оплата банковской картой' => 'bank-card',
|
||||||
|
'Безналичный расчет' => 'bank-transfer'
|
||||||
|
),
|
||||||
|
|
||||||
|
// статусы заказов CRM => tiu
|
||||||
|
// https://my.tiu.ru/cabinet/order_v2
|
||||||
|
// по умолчанию заданы 4 статуса:
|
||||||
|
// 'opened' - новый, 'accepted' - принят, 'declined' - отменен, 'closed' - выполнен
|
||||||
|
'order_statuses' => array(
|
||||||
|
'new' => 'opened',
|
||||||
|
'processing' => 'accepted',
|
||||||
|
'complete' => 'closed',
|
||||||
|
'cancel-other' => 'declined',
|
||||||
|
),
|
||||||
|
|
||||||
|
// почта для логов с ошибками
|
||||||
|
'support_email' => '',
|
||||||
|
'support_email_subject' => 'tiu fail'
|
||||||
|
);
|
0
logs/.gitkeep
Normal file
0
logs/.gitkeep
Normal file
22
run.php
Normal file
22
run.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
require(__DIR__ . "/classes/ApiHelper.php");
|
||||||
|
require(__DIR__ . "/config/config.php");
|
||||||
|
|
||||||
|
if (! file_exists(__DIR__ . "/logs"))
|
||||||
|
mkdir(__DIR__ . "/logs");
|
||||||
|
if (file_exists(__DIR__ . "/logs/cookie.txt"))
|
||||||
|
unlink(__DIR__ . "/logs/cookie.txt");
|
||||||
|
|
||||||
|
$lockFile = __DIR__ . "/run.lock";
|
||||||
|
if (file_exists($lockFile)) {
|
||||||
|
if ((int)file_get_contents($lockFile) > time()) {
|
||||||
|
echo "script is busy";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_put_contents("run.lock", strtotime('+5 minutes'));
|
||||||
|
|
||||||
|
$apiHelper = new ApiHelper($сonfig);
|
||||||
|
|
||||||
|
if ($apiHelper->processXMLOrders())
|
||||||
|
unlink($lockFile);
|
Loading…
Reference in New Issue
Block a user