Your IP : 216.73.216.86


Current Path : /var/www/homesaver/www/bitrix/modules/ipol.sdek/classes/general/
Upload File :
Current File : /var/www/homesaver/www/bitrix/modules/ipol.sdek/classes/general/StatusHandler.php

<?php
namespace Ipolh\SDEK;

use Ipolh\SDEK\Bitrix\Controller\Order;
use Ipolh\SDEK\Bitrix\Entity\cache;
use Ipolh\SDEK\Bitrix\Entity\encoder;
use Ipolh\SDEK\Bitrix\Entity\Options;
use Ipolh\SDEK\Bitrix\Tools;
use Ipolh\SDEK\Core\Entity\BasicResponse;
use Ipolh\SDEK\Core\Entity\Collection;
use Ipolh\SDEK\Core\Entity\Result\Error;
use Ipolh\SDEK\Core\Entity\Result\ErrorCollection;
use Ipolh\SDEK\Core\Entity\Result\Result;
use Ipolh\SDEK\Core\Entity\Result\Warning;
use Ipolh\SDEK\SDEK\SdekApplication;

use Bitrix\Main\Type\DateTime;

class StatusHandler extends abstractGeneral
{
    /**
     * Choose all orders with uid's and without sdek_id to fill it or cry loud
     */
    public static function getSendedOrdersState()
    {
        $obOrders = \sqlSdekOrders::select(array(),array('!SDEK_UID'=>false));
        $arOrders = array();
        while ($obOrder = $obOrders->getNext()){
            if(!$obOrder['SDEK_ID']){
                $acc = \sqlSdekLogs::getById($obOrder['ACCOUNT']);

                if($acc && $acc['ACTIVE'] === 'Y') {
                    $arOrders [$obOrder['ID']] = array(
                        'uid' => $obOrder['SDEK_UID'],
                        'acc' => $acc['ACCOUNT'],
                        'scr' => $acc['SECURE'],
                        'accID' => $obOrder['ACCOUNT'],
                        'oid' => $obOrder['ORDER_ID'],
                        'src' => $obOrder['SOURCE']
                    );
                }
            }
        }

        $arControllers = array();

        if(!empty($arOrders)){
            foreach ($arOrders as $dbId => $arOrder){
                if(!array_key_exists($arOrder['accID'],$arControllers)) {
                    $app = self::makeApplication($arOrder['acc'], $arOrder['scr']);
                    $arControllers[$arOrder['accID']] = new Order($app);
                }

                $result = self::_getSenderOrderState($arOrder,$arControllers[$arOrder['accID']]);
            }
        }
    }

    /**
     * @param $oId
     * @param string $mode
     * @return BasicResponse
     * checks either order with corr oId was accepted by sdek - or smth is wrong
     */
    public static function getSendedOrderStateByOid($oId, $mode='order')
    {
        $arOrder = ($mode==='order') ? \sqlSdekOrders::GetByOI($oId) : \sqlSdekOrders::GetBySI($oId);
        if($arOrder && $arOrder['SDEK_UID']){
            $result = self::getSendedOrderState($arOrder['SDEK_UID']);
        } else {
            $result = new BasicResponse();
            $errCol = new Collection('error');
            $errCol->add('No order with filled uid found with id'.$oId);
            $result->setSuccess(false)->setResponse(false)->setError($errCol);
        }

        return $result;
    }

    /**
     * Checks either order with uid was accepted by sdek - or something is wrong
     * @param $uid
     * @return BasicResponse
     */
    public static function getSendedOrderState($uid)
    {
        $obOrder = \sqlSdekOrders::GetByUId($uid);
        if($obOrder){
            $acc = \sqlSdekLogs::getById($obOrder['ACCOUNT']);

            $arOrder = array(
                'uid' => $obOrder['SDEK_UID'],
                'acc' => $acc['ACCOUNT'],
                'scr' => $acc['SECURE'],
                'accID' => $obOrder['ACCOUNT'],
                'oid' => $obOrder['ORDER_ID'],
                'src' => $obOrder['SOURCE']
            );

            $app = self::makeApplication($arOrder['acc'], $arOrder['scr']);
            $controller = new Order($app);

            $result = self::_getSenderOrderState($arOrder,$controller);
        } else {
            $result = new BasicResponse();
            $errCol = new Collection('error');
            $errCol->add('No order found with uid '.$uid);
            $result->setSuccess(false)->setResponse(false)->setError($errCol);
        }

        return $result;
    }

    /**
     * Checks state for chosen order, sets stuff for tracking number, adds in table
     * @param $arOrder - array of type uid,oid,src
     * @param Order $controller
     */
    protected static function _getSenderOrderState($arOrder, $controller)
    {
        /** @var BasicResponse $check */
        $check = $controller->checkOrderSendState($arOrder['uid']);

        $obRet = new BasicResponse();
        $obRet->setSuccess($check->isSuccess());

        if($check->isSuccess())
        {
            $sdekNumber = $check->getResponse()->getField('cdekNumber');
            if(
                $check->getResponse()->getField('state') === 'SUCCESSFUL' &&
                $sdekNumber
            ){
                \sqlSdekOrders::updateStatus(array(
                    "ORDER_ID" => $arOrder['oid'],
                    "SOURCE"   => $arOrder['src'],
                    "STATUS"   => "OK",
                    "SDEK_ID"  => $sdekNumber,
                    "MESSAGE"  => "",
                    "OK"       => true
                ));
                $obRet->setResponse($sdekNumber);

                $statusOption = !$arOrder['src'] ? 'statusOK' : 'stShipmentOK';
                $status = option::get($statusOption);
                if ($status) {
                    if (!$arOrder['src']) {
                        $order = \CSaleOrder::GetByID($arOrder['oid']);
                        if ($order['STATUS_ID'] != $status) {
                            \CSaleOrder::StatusOrder($arOrder['oid'], $status);
                        }
                    } else if (\sdekHelper::isConverted()) {
                        $shipment = \Bitrix\Sale\Shipment::getList(array('filter' => array('ID' => $arOrder['oid'])))->Fetch();
                        if ($shipment['STATUS_ID'] != $status) {
                            $order = \Bitrix\Sale\Order::load($shipment['ORDER_ID']);
                            $shipmentCollection = $order->getShipmentCollection();
                            $shipment = $shipmentCollection->getItemById($arOrder['oid']);
                            $shipment->setField('STATUS_ID', $status);
                            $order->save();
                        }
                    }
                }
                \sdekdriver::setOrderTrackingNumber($arOrder['oid'],(!$arOrder['src'])?'order':'shipment',$sdekNumber);
            } elseif($check->getResponse()->getField('state') === 'INVALID'){
                $obRet->setError($check->getError());
                $obRet->setResponse(false);

                $arErrors = array();
                if($check->getError()){
                    $check->getError()->reset();
                    while($obErr = $check->getError()->getNext()){
                        $arErrors [] = $obErr;
                    }
                }

                \sqlSdekOrders::updateStatus(array(
                    "ORDER_ID" => $arOrder['oid'],
                    "SOURCE"   => $arOrder['src'],
                    "STATUS"   => "ERROR",
                    "SDEK_ID"  => false,
                    "SDEK_UID" => '',
                    "MESSAGE"  => serialize(\sdekHelper::zaDEjsonit($arErrors)),
                    "OK"       => false
                ));
            }
        } else {
            $obRet->setError($check->getError());
        }

        return $obRet;
    }

    public static function checkCdekNumber(){
        $oId    = ($_REQUEST['mode'] === 'order') ? $_REQUEST['orderId'] : $_REQUEST['shipment'];
        $return = self::getSendedOrderStateByOid($oId,$_REQUEST['mode']);

        $obReturn = array('cdek_number'=>false,'error'=>false);
        if($return->isSuccess() && $return->getResponse()){
            $obReturn['cdek_number'] = $return->getResponse();
        } elseif($return->getError()){
            $arError = array();
            $return->getError()->reset();
            while ($obErr = $return->getError()->getNext()){
                $arError []= $obErr;
            }

            $obReturn['error'] = $arError;
        }

        echo json_encode(\sdekHelper::zaDEjsonit($obReturn));
    }

    /**
     * @return int
     * returns number of active orders - which we need to check via syncronization
     */
    public static function getNumberOfActiveOrders(){
        $orderStatusesUptime = (int)option::get('orderStatusesUptime');
        if ($orderStatusesUptime < 1)
            $orderStatusesUptime = 60;

        $dbOrders = \sqlSdekOrders::select(
            array('UPTIME', 'ASC'),
            array('OK' => true, 'STATUS' => array('OK', 'DELETE', 'STORE', 'TRANZT', 'CORIER', 'PVZ'), '>UPTIME' => strtotime('-'.$orderStatusesUptime.' days'))
        );

        return $dbOrders->SelectedRowsCount();
    }

    /**
     * Checks state of given Order. Order must be successfully sent to CDEK before check.
     * @param array $request
     * @return Result
     */
    public static function getOrderState($request)
    {
        $result = new Result();
        $resultData = [];

        if (!empty($request['DispatchNumber'])) {
            $order = \sqlSdekOrders::select([], ['SDEK_ID' => $request['DispatchNumber']])->Fetch();
            if (!empty($order) && !empty($order['ID'])) {
                $account = \sqlSdekLogs::getById($order['ACCOUNT']);
                if (!empty($account)) {
                    // 2.0 only
                    $application = new \Ipolh\SDEK\SDEK\SdekApplication(
                        $account['ACCOUNT'], $account['SECURE'],
                        false,
                        10,
                        new \Ipolh\SDEK\Bitrix\Entity\encoder(),
                        new \Ipolh\SDEK\Bitrix\Entity\cache()
                        //, new \Ipolh\SDEK\Admin\IvanInlineLoggerController()
                    );
                    $controller = new \Ipolh\SDEK\Bitrix\Controller\Order($application);

                    $getInfoResult = $controller->getOrderInfoByNumber($order['SDEK_ID']);
                    if ($getInfoResult->isSuccess()) {
                        // This one for \sdekOption::setOrderStates()
                        $successfulOrders = [];

                        $data = $getInfoResult->getData();
                        if (!empty($data['STATUSES']) && is_array($data['STATUSES'])) {
                            $resultData['STATUS_CODE'] = $data['STATUSES'][0]['STATUS'];
                            $resultData['STATUS_DATE'] = DateTime::createFromTimestamp($data['STATUSES'][0]['DATETIME']);
                            $resultData['NUMBER']      = $data['NUMBER'];

                            // 2.0 to 1.5 compatibility
                            $compatibleStatus = self::getStatusNumberByCode($data['STATUSES'][0]['STATUS']);
                            if (!empty($compatibleStatus)) {
                                $successfulOrders[] = [
                                    'DispatchNumber' => $order['SDEK_ID'],
                                    'State' => $compatibleStatus['ID'],
                                    'Number' => $data['NUMBER'],
                                    'Description' => $compatibleStatus['DESCR']
                                ];
                            }
                        } else {
                            $result->addError(new Error('Empty statuses list returns for CDEK number '.$order['SDEK_ID']));
                        }

                        if (!empty($successfulOrders)) {
                            \sdekOption::setOrderStates($successfulOrders);
                        }
                    } else {
                        $result->addErrors($getInfoResult->getErrors());
                    }
                } else {
                    $result->addError(new Error(Tools::getMessage('MESS_ORDER_INFO_UNKNOWN_ACCOUNT')));
                }
            } else {
                $result->addError(new Error('Get state failed cause no Order found by given CDEK_ID.'));
            }
        } else {
            $result->addError(new Error('Get state failed cause no Order CDEK_ID given.'));
        }
        $result->setData($resultData);

        return $result;
    }

    /**
     * Ajax wrapper for getOrderState
     * @param array $request
     * @return void
     */
    public static function getOrderStateRequest($request)
    {
        $result = ['success' => false, 'errors' => 'Unknown error.', 'status' => false];

        if (Tools::isModuleAjaxRequest()) {
            $stateResult = self::getOrderState($request);

            $result['success'] = $stateResult->isSuccess();
            $result['errors']  = $stateResult->getErrors()->isEmpty() ? '' : $stateResult->getErrorsString(Result::SEPARATOR_NEW_LINE);
            $result['status']  = $stateResult->getData()['STATUS_CODE'] ?: false;
        }

        echo Tools::jsonEncode($result);
    }

    /**
     * Checks states of Orders
     * @return Result
     */
    public static function getOrderStates()
    {
        $result = new Result();
        $resultData = ['ORDERS' => []];

        if (\sqlSdekOrders::getDataCount() > 0) {
            $orderStatusesLimit = (int)option::get('orderStatusesLimit');
            if ($orderStatusesLimit < 1)
                $orderStatusesLimit = 100;

            $orderStatusesUptime = (int)option::get('orderStatusesUptime');
            if ($orderStatusesUptime < 1)
                $orderStatusesUptime = 60;

            // Maximum orders processed per one call of Order::getOrderInfoByNumberMulti()
            $orderMultiLimit = 10;

            $dbData = \sqlSdekOrders::select(
                ['UPTIME', 'ASC'],
                ['OK' => true, 'STATUS' => ['OK', 'DELETE', 'STORE', 'TRANZT', 'CORIER', 'PVZ'], '>UPTIME' => strtotime('-'.$orderStatusesUptime.' days')],
                ['nPageSize' => $orderStatusesLimit, 'iNumPage' => 1]
            );

            $tmpOrders = [];
            while ($tmp = $dbData->Fetch()) {
                if (!empty($tmp['ACCOUNT']) && !empty($tmp['SDEK_ID'])) {
                    $tmpOrders[$tmp['ACCOUNT']][] = $tmp['SDEK_ID'];
                }
            }
            unset($dbData);

            $orders = [];
            foreach ($tmpOrders as $accountId => $cdekIds) {
                $page = 0;
                $i = 0;
                foreach ($cdekIds as $cdekId) {
                    $orders[$accountId][$page][] = $cdekId;
                    $i++;

                    if ($i === $orderMultiLimit) {
                        $i = 0;
                        $page++;
                    }
                }
            }
            unset($tmpOrders);

            if (!empty($orders)) {
                foreach ($orders as $accountId => $ordersData) {
                    $account = \sqlSdekLogs::getById($accountId);
                    if (!empty($account)) {
                        // 2.0 only
                        $application = new SdekApplication(
                            $account['ACCOUNT'], $account['SECURE'],
                            false,
                            10,
                            new \Ipolh\SDEK\Bitrix\Entity\encoder(),
                            new \Ipolh\SDEK\Bitrix\Entity\cache()
                            // , new \Ipolh\SDEK\Admin\IvanInlineLoggerController()
                        );
                        $controller = new Order($application);

                        foreach ($ordersData as $page => $ordersPack) {
                            // This one for \sdekOption::setOrderStates()
                            $successfulOrders = [];

                            $getInfoResult = $controller->getOrderInfoByNumberMulti($ordersPack);

                            foreach ($ordersPack as $cdekNumber) {
                                $resultData['ORDERS'][$cdekNumber] = ['IS_SUCCESS' => $getInfoResult->isSuccess(), 'ERRORS' => new ErrorCollection()];

                                if ($getInfoResult->isSuccess()) {
                                    $ordersResults = $getInfoResult->getData()['ORDERS'];
                                    if (array_key_exists($cdekNumber, $ordersResults)) {
                                        $orderResult = $ordersResults[$cdekNumber];

                                        if (!empty($orderResult['STATUSES']) && is_array($orderResult['STATUSES'])) {
                                            $resultData['ORDERS'][$cdekNumber]['STATUS_CODE'] = $orderResult['STATUSES'][0]['STATUS'];
                                            $resultData['ORDERS'][$cdekNumber]['STATUS_DATE'] = DateTime::createFromTimestamp($orderResult['STATUSES'][0]['DATETIME']);

                                            $resultData['ORDERS'][$cdekNumber]['NUMBER'] = $orderResult['NUMBER'];

                                            // 2.0 to 1.5 compatibility
                                            $compatibleStatus = self::getStatusNumberByCode($orderResult['STATUSES'][0]['STATUS']);
                                            if (!empty($compatibleStatus)) {
                                                $successfulOrders[] = [
                                                    'DispatchNumber' => $cdekNumber,
                                                    'State' => $compatibleStatus['ID'],
                                                    'Number' => $orderResult['NUMBER'],
                                                    'Description' => $compatibleStatus['DESCR']
                                                ];
                                            }
                                        } else if (!$orderResult['ERRORS']->isEmpty()) {
                                            $resultData['ORDERS'][$cdekNumber]['ERRORS']->append($orderResult['ERRORS']);
                                            $resultData['ORDERS'][$cdekNumber]['IS_SUCCESS'] = false;
                                        } else {
                                            $resultData['ORDERS'][$cdekNumber]['ERRORS']->add(new Error('Empty error list and requests data returns for CDEK number '.$cdekNumber));
                                            $resultData['ORDERS'][$cdekNumber]['IS_SUCCESS'] = false;
                                        }
                                    } else {
                                        // Near impossible, but who cares
                                        $resultData['ORDERS'][$cdekNumber]['ERRORS']->add(new Error('No order info returns for CDEK number '.$cdekNumber));
                                    }
                                } else {
                                    // Some serious troubles with app, like bad auth data
                                    $resultData['ORDERS'][$cdekNumber]['ERRORS']->append($getInfoResult->getErrors());
                                }
                            }

                            if (!empty($successfulOrders)) {
                                \sdekOption::setOrderStates($successfulOrders);
                            }
                        }
                    } else {
                        $result->addError(new Error(Tools::getMessage('MESS_ORDER_INFO_UNKNOWN_ACCOUNT')));
                    }
                }
            }
        } else {
            $result->addWarning(new Warning('CDEK Orders table empty.'));
        }
        $result->setData($resultData);

        if (!\sdekOption::$ERROR_REF) {
            option::set('statCync', time());
        }

        return $result;
    }

    /**
     * Get corresponding 1.5 CDEK status for given 2.0 status code
     * @param string $cdekStatusCode
     * @return array
     */
    protected static function getStatusNumberByCode($cdekStatusCode)
    {
        $map = [
            // 'ACCEPTED' => null,

            'СREATED' => 1,
            'REMOVED' => 2,
            'RECEIVED_AT_SHIPMENT_WAREHOUSE' => 3,
            'DELIVERED' => 4,
            'NOT_DELIVERED' => 5,

            'READY_TO_SHIP_AT_SENDING_OFFICE' => 6,
            'READY_FOR_SHIPMENT_IN_TRANSIT_CITY' => 6,
            'READY_FOR_SHIPMENT_IN_SENDER_CITY' => 6,

            'PASSED_TO_CARRIER_AT_SENDING_OFFICE' => 7,
            'TAKEN_BY_TRANSPORTER_FROM_SENDER_CITY' => 7,

            'SENT_TO_RECIPIENT_CITY' => 8,

            'MET_AT_RECIPIENT_OFFICE' => 9,
            'ACCEPTED_IN_RECIPIENT_CITY' => 9,

            'ACCEPTED_AT_RECIPIENT_CITY_WAREHOUSE' => 10,
            'TAKEN_BY_COURIER' => 11,
            'ACCEPTED_AT_PICK_UP_POINT' => 12,
            'ACCEPTED_AT_TRANSIT_WAREHOUSE' => 13,
            'RETURNED_TO_SENDER_CITY_WAREHOUSE' => 16,
            'RETURNED_TO_TRANSIT_WAREHOUSE' => 17,
            'RETURNED_TO_RECIPIENT_CITY_WAREHOUSE' => 18,
            'READY_TO_SHIP_IN_TRANSIT_OFFICE' => 19,

            'PASSED_TO_CARRIER_AT_TRANSIT_OFFICE' => 20,
            'TAKEN_BY_TRANSPORTER_FROM_TRANSIT_CITY' => 20,

            'SEND_TO_TRANSIT_OFFICE' => 21,
            'SENT_TO_TRANSIT_CITY' => 21,

            'MET_AT_TRANSIT_OFFICE' => 22,
            'ACCEPTED_IN_TRANSIT_CITY' => 22,

            'SENT_TO_SENDER_CITY' => 27,
            'MET_AT_SENDING_OFFICE' => 28,
        ];

        return (array_key_exists($cdekStatusCode, $map) ? ['ID' => $map[$cdekStatusCode], 'DESCR' => Tools::getMessage('STATUS_ORDER_'.$cdekStatusCode)] : []);
    }
}