CDEK - плагин для выгрузки заказа и получения трек-номера

На волне публикаций Паши Зарубина (тыц, тыц, тыц) решил выложить прикладное решение, которое моему клиенту немножко автоматизирует жизнь и вполне себе нравится. Началось это с того, что в msCDEK не было выгрузки заказов в личный кабинет CDEK. А закончилось тем, что Паша написал свою ГдеПосылку, которая следит за передвижениями посылки по её ID в системе CDEK в том числе. Мой плагин выполняет две задачи:
1. При установке определенного статуса (названного нами «Выгрузка в CDEK») отправляет в CDEK xml-запрос в соответствии с их документацией, по которому в системе создается накладная
2. Принимает в ответ ID накладной и пишет его в поле track заказа.

Для работы в этом плагина нужны оба дополнения — msCDEK и msGdePosylka, поскольку плагин писан немножко на коленке и второпях и некоторые функции я дергал из компонента msCDEK. При наличии времени и желания вполне можно эти функции вынести в отдельный класс и обойтись без msCDEK.


Код плагина:

<?php
/**
 * @global modX $modx
 */
switch ($modx->event->name) {

    case 'msOnChangeOrderStatus':
        /**
         * @var msOrder $order
         */
	// $status 29 - это мой ID статуса, по которому мы отправляем заказ в CDEK. 16 и 17 - мои ID способов доставки для CDEK - самовывоз из пункта выдачи и курьерская доставка
        if ($status == 29 and in_array($order->delivery, array(16, 17))) {
            $cdek = $modx->getService('msCDEK', 'msCDEK', $modx->getOption('core_path').'components/mscdek/model/mscdek/', array());
            $cdek->initialize();

            $data = array(
                'path' => 'https://integration.cdek.ru/new_orders.php',
                'opts' => array(
                    'Date' => date('Y-m-d'),
                    'Account' => $modx->getOption('mscdek_login'),
                    'http' => array(
                        'header' => 'Content-Type: application/x-www-form-urlencoded',
                    ),
                ),
            );
            $data['opts']['Secure'] = md5($data['opts']['Date'].'&'.$modx->getOption('mscdek_password'));

            $q = $modx->newQuery('msOrder', $order->id);
            $q->select('msOrder.id, msOrder.num, msOrder.delivery, msOrder.payment, msOrder.delivery_cost');
            $q->innerJoin('modUserProfile', 'modUserProfile', 'msOrder.user_id = modUserProfile.id');
            $q->select('modUserProfile.email');
            $q->innerJoin('msOrderAddress', 'msOrderAddress', 'msOrder.address = msOrderAddress.id');
            foreach (array('receiver', 'phone', 'country', 'index', 'region', 'city', 'metro', 'street', 'building', 'room', 'comment') as $field) {
                $q->select('msOrderAddress.'.$field);
            }
            $q->innerJoin('msDelivery', 'msDelivery', 'msOrder.delivery = msDelivery.id');
            $q->select('msDelivery.properties');
            if ($q->prepare() && $q->stmt->execute()) {
                $row = $q->stmt->fetch(PDO::FETCH_ASSOC);
                $row['properties'] = $modx->fromJSON($row['properties']);
                $row['city_id'] = $cdek->getCityId($row['city']);

                $values = array(
                    'Number' => 1,
                    'Date' => $data['opts']['Date'],
                    'Account' => $data['opts']['Account'],
                    'Secure' => $data['opts']['Secure'],
                    'OrderCount' => 1,
                );

                $xml = '<?xml version="1.0" encoding="UTF-8"?><DeliveryRequest></DeliveryRequest>';
                $xml = new SimpleXMLElement($xml);
                foreach ($values as $k => $v) {
                    $xml->addAttribute($k, $v);
                }

                $values = array(
                    'Number' => $row['num'],
		    // Впишите свой город отправки:
                    'SendCityCode' => $cdek->getCityId('Санкт-Петербург'),
                    'RecCityCode' => $row['city_id'],
                    'RecipientName' => $row['receiver'],
                    'RecipientEmail' => $row['email'],
                    'Phone' => $row['phone'],
                    'TariffTypeCode' => $row['properties']['tariffId'],
                    'DeliveryRecipientCost' => $row['payment'] == 3 ? 0 : $row['delivery_cost'],
                    'DeliveryRecipientVATRate' => 'VAT0',
                    'DeliveryRecipientVATSum' => 0,
                    'Comment' => $row['comment'],
                    'SellerName' => '34play',
                );

                $xmlorder = $xml->addChild('Order');
                foreach ($values as $k => $v) {
                    $xmlorder->addAttribute($k, $v);
                }
                if ($row['delivery'] == 16) {
                    $resPoints = array();
                    $dps = file_get_contents('https://integration.cdek.ru/pvzlist.php?type=PVZ&cityid=' . $row['city_id']);
                    // echo $dps;
                    $points = new SimpleXMLElement($dps);
                    foreach ($points->Pvz as $pvz) {
                        if ($row['street'] == $pvz['Address']) {
                            $row['PvzCode'] = $pvz['Code'];
                        }
                    }
                    unset($points);
                }

                $values = $row['delivery'] == 16 ? array('PvzCode' => $row['PvzCode']) : array(
                    'Street' => $row['street'],
                    'House' => $row['building'],
                    'Flat' => $row['room']
                );
                $address = $xmlorder->addChild('Address');
                foreach ($values as $k => $v) {
                    $address->addAttribute($k, $v);
                }

                $values = array(
                    'Number' => $row['num'],
                    'BarCode' => $row['num'],
                    'Weight' => 0,
                );
                $package = $xmlorder->addChild('Package');
                foreach ($values as $k => $v) {
                    $package->addAttribute($k, $v);
                }

                $q = $modx->newQuery('msOrderProduct', array('order_id' => $row['id']));
                $q->select('id,name,count,price,cost,options');
                if ($q->prepare() && $q->stmt->execute()) {
                    $weight = 0;
                    while ($item = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
                        $options = $modx->fromJSON($item['options']);
                        $values = array(
                            'WareKey' => $item['id'],
                            'Cost' => $item['cost'],
                            'Payment' => $row['payment'] == 3 ? 0 : $item['price'],
                            'Weight' => $item['weight'] ? $item['weight'] * 1000 : 500,
                            'Amount' => $item['count'],
                            'Comment' => $item['name'].', '.$options['size'],
                        );
                        $item = $package->addChild('Item');
                        foreach ($values as $k => $v) {
                            $item->addAttribute($k, $v);
                        }
                        $weight += $values['Weight'] * $values['Amount'];
                    }
                    $package['Weight'] = $weight;
                }

                foreach (array(30, 36, 37) as $code) {
                    $addService = $xmlorder->addChild('AddService');
                    $addService->addAttribute('ServiceCode', $code);
                }

                $curl = curl_init();
                curl_setopt_array($curl, array(
                    CURLOPT_URL => $data['path'],
                    CURLOPT_RETURNTRANSFER => true,
                    CURLOPT_POST => true,
                    CURLOPT_HTTPHEADER => $data['opts']['http'],
                    CURLOPT_POSTFIELDS => http_build_query(array('xml_request' => $xml->asXML())),
                ));
                $response = curl_exec($curl);
                curl_close($curl);
                $response = new SimpleXMLElement($response);
                foreach ($response->Order as $one) {
                    if (!empty($one['ErrorCode'])) {
                        $modx->log(modX::LOG_LEVEL_ERROR, $one['ErrorCode'].' - '.$one['Msg']);
                        $modx->event->output($one['Msg']);
                        break;
                    } else if (!empty($one['DispatchNumber'])) {
                        $order->set('track', $one['DispatchNumber']);
                        $order->save();
                        break;
                    }
                }
            }
        }
        break;
}
mngatoff
04 ноября 2017, 02:00
modx.pro
6
2 461
+6

Комментарии: 4

Денис
26 февраля 2019, 12:40
0
Добрый день! Ошибка авторизации, при том что в msCDEK данные для авторизации верны.
[TIMESTAMP] => 2019-02-26T09:31:40Z
    [CORRELATIONID] => dbb0a8e24567
    [ACK] => Failure
    [VERSION] => 74.0
    [BUILD] => 51975887
    [L_ERRORCODE0] => 10002
    [L_SHORTMESSAGE0] => Authentication/Authorization Failed
    [L_LONGMESSAGE0] => You do not have permissions to make this API call
    [L_SEVERITYCODE0] => Error
)
    Денис
    26 февраля 2019, 14:45
    0
    Так же ругается на эту строку
    foreach ($result['Pvz'] as $item) {
      mngatoff
      26 февраля 2019, 16:53
      0
      возможно, апи cdek с той поры поменялось. читайте доки
        mngatoff
        26 февраля 2019, 16:54
        0
        и потом, это плагин под старый компонент mscdek
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        4