commit df7a781e27477356c3a358210e3624332a8dd02d Author: cyradin Date: Mon Sep 7 18:09:12 2015 +0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bab7455 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea +vendor +config/config.php +composer.lock +composer.json \ No newline at end of file diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..5150813 --- /dev/null +++ b/.htaccess @@ -0,0 +1,2 @@ +Order Deny, Allow +Deny From All \ No newline at end of file diff --git a/classes/ApiHelper.php b/classes/ApiHelper.php new file mode 100644 index 0000000..4b5cb6e --- /dev/null +++ b/classes/ApiHelper.php @@ -0,0 +1,155 @@ +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; + } + +} \ No newline at end of file diff --git a/classes/Parser.php b/classes/Parser.php new file mode 100644 index 0000000..1497559 --- /dev/null +++ b/classes/Parser.php @@ -0,0 +1,83 @@ +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 + ); + } +} \ No newline at end of file diff --git a/config/config-dist.php b/config/config-dist.php new file mode 100644 index 0000000..a72c4a6 --- /dev/null +++ b/config/config-dist.php @@ -0,0 +1,47 @@ + '', + + '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' +); \ No newline at end of file diff --git a/logs/.gitkeep b/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/run.php b/run.php new file mode 100644 index 0000000..0271f53 --- /dev/null +++ b/run.php @@ -0,0 +1,22 @@ + time()) { + echo "script is busy"; + exit(); + } +} +file_put_contents("run.lock", strtotime('+5 minutes')); + +$apiHelper = new ApiHelper($сonfig); + +if ($apiHelper->processXMLOrders()) + unlink($lockFile); \ No newline at end of file