Скидка на товары, условие!?

Условие:
Пользователь добавил в корзину несколько продуктов однной категории "Клавиатуры" + пару мышек из другой категории
Как мне написать условие?
В корзине 5 наименований, но для 3х(клавиатур) я хочу сделать скидку 10% (именно уже в самой корзине).
То есть у меня есть ID товаров 22,44,55,74,14
Так вот если в корзине лежат ID 22,44,55 у них скидка 10%. а у остальных скидки нет.

Документацию уже смотрели? http://wiki.modx-shopkeeper.ru/doku.php?id=javascript_api

Да читал.
Мне нужен метод пересчета цены к примеру одного товара который уже в корзине лежит с другими товарами.

@vectorserver так у вас на данный момент, что то уже есть? наработки какие либо? или вам нужно дать готовый ответ?)
Делитесь тем что есть, посмотрим, подумаем...

Да наробоки есть, уже сделал (пришлось немного переделать класс)

core/components/shopkeeper3/model/shopkeeper.class.php

Что изменил

  1. В функции getProductsList добавил параметры в $chunkArr
    Было
$data = array(
                    'index' => $i,
                    'num' => $i + 1,
                    'even' => ($i + 1) % 2 == 0 ? 1 : 0,
                    'comma' => ($i + 1) < count($purchasesData) ? ',' : '',
                    'plural' => self::getPlural($purchase['count'], $plural_words),
                    'price_total' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_total) : $price_total,
                    'price_count' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_count) : $price_count,
                    'price_count_total' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_count_total) : $price_count_total,
                    'url_del_item' => $this_page_url . $url_qs . "shk_action=remove&amp;n=" . $i,
                    'currency' => $this->config['currency']
                );

Стало

$data = array(
                    'index' => $i,
                    'discount_yes'=>$purchase['discount_yes'],
                    'discount_parent'=>$purchase['discount_parent'],
                    'percent_discount' => ($purchase['discount_yes']==1) ? $purchase['discount']:'', //Vectorserver Допвим холдер процетная скидка
                    'percent_discount_desc' => ($purchase['discount_yes']==1) ?$purchase['discount_desc']:'', //Vectorserver Допвим холдер процетная скидка
                    'num' => $i + 1,
                    'even' => ($i + 1) % 2 == 0 ? 1 : 0,
                    'comma' => ($i + 1) < count($purchasesData) ? ',' : '',
                    'plural' => self::getPlural($purchase['count'], $plural_words),
                    'price_total' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_total) : $price_total,
                    'price_count' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_count) : $price_count,
                    'price_count_total' => $this->config['excepDigitGroup'] ? $this->numberFormat($price_count_total) : $price_count_total,
                    'url_del_item' => $this_page_url . $url_qs . "shk_action=remove&amp;n=" . $i,
                    'currency' => $this->config['currency']
                );
  1. В функции getTotal заменил $this->data на $this->getProductsData()
    Было
foreach ($this->data as $parchase) {

Стало

foreach ($this->getProductsData() as $parchase) {
  1. Создал еще одно событие OnSHKcalcDiscount в функции getProductsData
    После кода вешаем наше событие getProductsData
        foreach ($this->data as $key => $purchase) {

            $fields_data = isset($data[$purchase['className']][$purchase['id']]) ? $data[$purchase['className']][$purchase['id']] : array();

            if (!$only_from_bd) {

                $output[$key] = array_merge($purchase, $fields_data);


                $p_options = array();
                if (!empty($purchase['options'])) {
                    $p_options = $this->getPurchasesOptionsData($purchase['options']);
                }
                $output[$key] = array_merge($output[$key], $p_options);

            } else {
                $output[$key] = $fields_data;
            }
            unset($output[$key]['options']);

        }```

Событие
    $new_output = $this->modx->invokeEvent('OnSHKcalcDiscount', array(
        'output' => $output,
    ));

Должно получится так
    foreach ($this->data as $key => $purchase) {

        $fields_data = isset($data[$purchase['className']][$purchase['id']]) ? $data[$purchase['className']][$purchase['id']] : array();

        if (!$only_from_bd) {

            $output[$key] = array_merge($purchase, $fields_data);


            $p_options = array();
            if (!empty($purchase['options'])) {
                $p_options = $this->getPurchasesOptionsData($purchase['options']);
            }
            $output[$key] = array_merge($output[$key], $p_options);

        } else {
            $output[$key] = $fields_data;
        }
        unset($output[$key]['options']);

    }
    //Vectorserver
    //Добвим  OnSHKcalcDiscount событие на обработку скидки
    $new_output = $this->modx->invokeEvent('OnSHKcalcDiscount', array(
        'output' => $output,
    ));


    if ($new_output !== false) {
        $output = $new_output[0];
    }
  1. Потом я создал ресурс в админке Управление скидками
  • создал пустой шаблон Управление скидками
  • создал TV параметры
    -- Размер скидки %
    -- Дни недели
    -- Тип скидки
    -- Список продуктов (@SELECT pagetitle, id FROM modx_site_content WHERE parent in(6,7,8,13,141,322,90,91,106,109,136,406) OR id in(183) AND published = 1)
    -- Время с по. час от и до

Скрин![alt text](image0_1479564432435_2016-11-19_19-05-10.png url)

  1. создал плагин Rules_for_Discount
    события повешал
  • OnSHKsaveOrder
  • OnSHKgetProductPrice
  • OnSHKcalcTotalPrice
  • OnSHKcalcDiscount
  • OnSHKAfterClearCart
  • OnDocUnPublished
  • OnDocFormSave
<?php
/**
 * Created by PhpStorm.
 * User: admin
 * Date: 14.11.2016
 * Time: 14:16
 * Правила для скидок
 * События Rules_for_Discount
 */

global $modx;
$user = $modx->user->id;

switch ($modx->event->name) {
    case 'OnSHKgetProductPrice'://Выбор цены товара при добавлении в корзину. $price, $id, $purchaseArray
        //Конструктор товаров
        if (@$_POST['myprice']) {
            $price = $_POST['myprice'];
            $id = $_POST['shk-id'];

            $_SESSION['shk_orderdata']['myprice'][$id] = $_POST['myprice'];

        }

        $modx->event->output($price);
        break;
    case 'OnDocFormSave':
    case 'OnDocUnPublished':
    case 'OnSHKcalcDiscount'://Рассчет полной цены товаров в корзине. $output

        //Пулучеем список параметров скидки
        $cur_day = date('w'); //день недели от 0 (воскресенье) до 6 (суббота)
        $cur_hour = date("H"); //Текущий час
        $parentDiscountParent = 418;
        $resultChilds = $modx->query("SELECT c.id, c.pagetitle, c.description FROM modx_site_content AS c WHERE c.published = 1 AND c.parent = $parentDiscountParent ORDER BY menuindex ASC");


        //Для конструкторов
        //Сессия записывается при событии OnSHKgetProductPrice

        foreach ($resultChilds->fetchAll(PDO::FETCH_ASSOC) as $data) {
            /*$tv = $modx->query("SELECT v.contentid, v.`value`, v.tmplvarid, t.`name`, t.caption FROM modx_site_tmplvar_contentvalues AS v INNER JOIN modx_site_tmplvars AS t ON t.id = v.tmplvarid WHERE  v.contentid = " . $data[id]);
            foreach ($tv->fetchAll(PDO::FETCH_ASSOC) as $k => $p) {
                $data['param'][$p['tmplvarid']] = $p;
            }*/
			
			//Получаеи спиок TV
			$products_tv = $modx->getObject('modTemplateVar', array('id' => 37));
			$discount_tv = $modx->getObject('modTemplateVar', array('id' => 38));
			$if_discount_tv = $modx->getObject('modTemplateVar', array('id' => 39));
			$days_tv = $modx->getObject('modTemplateVar', array('id' => 40));
			$times_tv = $modx->getObject('modTemplateVar', array('id' => 41));
			$sticker_tv = $modx->getObject('modTemplateVar', array('id' => 22));
			
			

            //Список продуктов  для которых действет скидка
            $products = explode("||", $products_tv->getValue($data[id]));
            //var_dump($products);

            //Размер скидки %
            $discount = (int)$discount_tv->getValue($data[id]);
            //Тип скидки
            //1 = Скидка действует к любому товару из списка выбранных
            //2 = Если 3 или более добавлены в корзину
            $if_discount = (int)$if_discount_tv->getValue($data[id]);
            //Дни недели
            $days = explode("||", $days_tv->getValue($data[id]));
            $times = explode("||", $times_tv->getValue($data[id]));
            $sticker = $sticker_tv->getValue($data[id]);
			
			 
			
			//Применяем стикеры к продуктам
			if($modx->event->name == "OnDocFormSave"){
			
		//Документы к которым прикрепляем стикер
			foreach($products as $pr){
					//Прверяем есть ли у документов стикеры с акцией
						$s_document = $modx->getObject('modResource', $pr);
						$s_document->setTVValue(22,$sticker);//Добавим стиккер к товару
						$s_document->setTVValue(18,$data['description']);//
						$s_document->save;
					}	
					
			} 
			
		if($modx->event->name == "OnDocUnPublished"){
		
			
			foreach($products as $pr){
					
						$s_document = $modx->getObject('modResource', $pr);
						$s_document->setTVValue(22,0);//Добавим стиккер к товару
						$s_document->setTVValue(18,'');
						$s_document->save;
					}	
					
			} 
			
			
			if($modx->event->name == "OnSHKcalcDiscount"){
			
			
			
			
			


            /*Проверка для Тип скидки*/
            $count_findet = 0;
            foreach ($output as $c) {
                if (in_array($c[id], $products)) {
                    $count_findet += 1;
                }
            }

            //Проверяем есть ли на сегодня скидка


            foreach ($output as $k => $item_product) {

		
                if ($_SESSION['shk_orderdata']['myprice'][$output[$k][id]]) {
                    $my_price = $_SESSION['shk_orderdata']['myprice'][$output[$k][id]];

                    if(!$output[$k]['discount_yes']){
                        $output[$k]['price'] = $my_price;
                    } else{
                        $output[$k]['old_price'] = $my_price;
                    }
                }

                //Проверяем есть ли товары в списке скидок
                if (in_array($cur_day, $days) && $discount) {
                    //Прверяем по времени
                    if ($cur_hour >= $times[0] && $cur_hour < $times[1]) {

                        if ($count_findet > 2 && $if_discount == 2 || $if_discount == 1) {

                            if (in_array($output[$k][id], $products)) {

                                //Обновляем цену, добавляем комментарии в корзину
                                $output[$k]['price'] = round($output[$k]['old_price'] * (1 - $discount / 100), 2);//Скидка 10%
                                //$output[$k]['old_price'] = $output[$k]['old_price'];
                                $output[$k]['discount'] = $discount . '%';
                                $output[$k]['discount_desc'] = $data[pagetitle];
                                $output[$k]['discount_yes'] = 1;
                                $output[$k]['discount_parent'] = $data[id];
                                $_SESSION['shk_orderdata']['note'][$k] = $output[$k];
                            }
                        }
                    }

                }
				
				$rerurn_event = $output;
            }

			
			}
        }
		
		$modx->event->output($rerurn_event);
		
        if($_GET['dbg_plg']==1){

            exit();
        }

        
        break;

    case 'OnSHKcalcTotalPrice'://Рассчет полной цены товаров в корзине. $price_total, $purchases

        if ($price_total && isset($_SESSION['shk_orderdata']['giftcard_amount'])) {
            $giftcard = $_SESSION['shk_orderdata']["giftcard"];//Номер карты
            $giftcard_amount = (int)$_SESSION['shk_orderdata']["giftcard_amount"]; //размер скидки %

            //Вычисляем окончательную обшуу сумму
            $old_price_total = $price_total;
            $price_total = round($old_price_total * (1 - $giftcard_amount / 100), 2);//Скидка


            //Запоминаем номер карты чтобы не делать других покупок после ввода промкода
            $modx->setPlaceholders(array(
                'shk.giftcard_num' => $giftcard,
                'shk.old_price_total' => $old_price_total,
                'shk.new_price_total' => $price_total,
                'shk.price_total_msg' => 'промо код №' . $giftcard,
            ));

            //Редирект на страницу оформления заказа чтобы не совершать покупок
        }

        $modx->event->output($price_total);


        break;
    case 'OnSHKsaveOrder'://Отправка заказа. $order_id

        $savedata = isset($_SESSION['shk_orderdata']['note']) ? $_SESSION['shk_orderdata']['note'] : '';
        $card_code = isset($_SESSION['shk_orderdata']['giftcard']) ? $_SESSION['shk_orderdata']['giftcard'] : '';
        $card_money = isset($_SESSION['shk_orderdata']['giftcard_amount']) ? $_SESSION['shk_orderdata']['giftcard_amount'] : 0;

        if (isset($_SESSION['shk_orderdata']['discount'])) {
            $discount = (int)$_SESSION['shk_orderdata']['discount'];
            $discount_text = $_SESSION['shk_orderdata']['discount'];
        } else {
            $discount = (int)$modx->runSnippet('userDiscount');
            $discount_text = $modx->runSnippet('userDiscount');
        }

        if (($card_code && $card_money) || $savedata) {
            $order_id = $modx->getOption('order_id', $scriptProperties, 0);
            if ($order_id && ($order = $modx->getObject('shk_order', array('id' => $order_id)))) {

                $note = $order->get('note');
                $note_json = $order->get('note_json');
                $options = $order->get('options');
                $options = $options ? json_decode($options, true) : array();

                if ($card_code) {
                    $note .= ($note ? ' ' : '') . "{$card_code} ({$card_money})";
                    $options['gift'] = min($card_money, $order->get('price'));
                }
                if ($discount) {
                    //$note .= ($note ? ' ' : '')."- {$discount}%";
                    $note .= ($note ? ' ' : '') . "{$discount_text}";
                    $options['discount'] = $discount;
                }

                //$note_json.= json_encode($_SESSION['shk_orderdata']);

                $order->set('note', $note);
                $note_json .= json_encode($_SESSION['shk_orderdata']);
                $order->set('note_json', $note_json);
                $order->set('options', ($options ? json_encode($options) : ''));
                $order->save();
                //Запишем данные по заказу в колонку note_json
                /*$save_note_json = $modx->query("UPDATE `modx_shopkeeper3_orders` SET `note_json`='".json_encode($_SESSION['shk_orderdata'])."' WHERE (`id`='$order_id')");
                if (is_object($save_note_json)) {
                    return true;
                }*/
                unset($_SESSION['shk_orderdata']['myprice'], $_SESSION['shk_orderdata']['giftcard_amount'], $_SESSION['shk_orderdata']['giftcard'], $_SESSION['shk_orderdata']['discount'], $_SESSION['shk_orderdata']['note']);
            }
        }

        break;

    case 'OnSHKAfterClearCart':
        //После очистки чищаем все
        unset($_SESSION['shk_orderdata']);
        break;


}
  1. Подрихтовал для вывода в панели заказов своих фич ```
    assets/components/shopkeeper3/mgr/js/controllers/home_controller.js

/*

homeController

*/

app.controller('homeController', function ($scope, $rootScope, $http, $templateCache, $modal, $filter, commonService, ngTableParams, usSpinnerService) {

$scope.selected_total = 0;

$scope.menu_current = 1;
$scope.settings = {};
$rootScope.loading = false;
$rootScope.tableParams = {};
$scope.grid_columns = shk_config['settings']['order_fields'];
$rootScope.statusesData = shk_config['settings']['statuses'];

angular.extend($scope, commonService);

$rootScope.startSpin = function () {
    usSpinnerService.spin('spinner-1');
}
$rootScope.stopSpin = function () {
    setTimeout(function () {
        usSpinnerService.stop('spinner-1');
    }, 500);
}

/* multiselectOptions */
$scope.multiselectOptions = {
    placeholder: shk_config.lang['shk3.select_status'],
    moreselected_text: shk_config.lang['shk3.selected'],
    selectall_text: shk_config.lang['shk3.select_all']
};

/**
 * filters
 *
 */
$rootScope.filters = {
    date: '',
    status: ''
};
$scope.filters_selected = 0;

/**
 * tableParams
 *
 */
$rootScope.tableParams = new ngTableParams({
        page: 1,
        count: 15,
        sorting: {
            id: 'desc'
        }
    }, {
        total: 0,
        counts: [15, 25, 50],
        getData: function ($defer, params) {

            var post_data = angular.copy(params.$params);
            post_data.action = 'mgr/getOrdersList';
            post_data.HTTP_MODAUTH = shk_config.auth_token;

            //get filters
            post_data.filters = $scope.getFilters();

            $rootScope.startSpin();

            $http.post(app.conf.connector_url, post_data)
                .success(function (response) {

                    if (!!response && angular.isArray(response.object)) {

                        $defer.resolve(response.object);
                        if (response.object.length == 0) {
                            response.total = 0;
                        }

                    }

                    params.total(response.total);

                    $rootScope.stopSpin();

                })
                .error(function (response) {
                    $scope.alert(shk_config.lang['shk3.message'], response.code == 401 ? 'Forbidden.' : 'Error');
                });

        }
    }
);


/**
 *
 *
 *
 */
$scope.submitFilters = function () {

    $rootScope.tableParams.reload();

}


/**
 * gridReload
 *
 */
$scope.gridReload = function () {

    $rootScope.tableParams.reload();

}

/*Обновление страныцы раз в минуту*/
setInterval($scope.gridReload, 30 * 1000);


/**
 * getSettings
 *
 */
var getSettings = function () {

    var params = {
        HTTP_MODAUTH: shk_config.auth_token,
        action: 'mgr/getSettings'
    };

    $rootScope.startSpin();

    $http.post(app.conf.connector_url, params)
        .success(function (response) {

            if (!!response && !!response.object) {

                $scope.settings = angular.copy(response.object);

            }

            $rootScope.stopSpin();

        });

};

/**
 * changeSelection
 *
 */
$scope.changeSelection = function (order) {

    var total = 0;

    for (var i in $rootScope.tableParams.data) {

        if (!$rootScope.tableParams.data.hasOwnProperty(i)) continue;

        if ($rootScope.tableParams.data[i].$selected) total++;

    }

    $scope.selected_total = total;

}

/**
 * selectAll
 *
 */
$rootScope.selectAll = function (selected) {

    if (typeof selected == 'undefined') {
        var selected = !!$rootScope.tableParams.data[0] && !!$rootScope.tableParams.data[0].$selected;
        selected = !selected;
    }

    for (var i in $rootScope.tableParams.data) {

        if (!$rootScope.tableParams.data.hasOwnProperty(i)) continue;

        $rootScope.tableParams.data[i].$selected = selected;

    }

    $scope.changeSelection();

}


/**
 * alert
 *
 */
$rootScope.alert = function (title, message) {

    var modalInstance = $modal.open({
        templateUrl: 'modals/alert.html',
        controller: function ($scope, $modalInstance) {

            $scope.title = title;
            $scope.message = message;

            $scope.cancel = function () {
                $modalInstance.close();
            };

        },
        resolve: {}
    });

}


/**
 * changeStatus
 *
 */
$rootScope.changeStatus = function (order_id) {

    var status = '';

    if (typeof order_id != 'undefined') {

        for (var i in $rootScope.tableParams.data) {

            if (!$rootScope.tableParams.data.hasOwnProperty(i)) continue;

            if ($rootScope.tableParams.data[i].id == order_id) {
                status = angular.copy($rootScope.tableParams.data[i].status);
            }

        }

        order_id = [order_id];

    } else {

        var order_id = [];

        for (var i in $rootScope.tableParams.data) {

            if (!$rootScope.tableParams.data.hasOwnProperty(i)) continue;

            if ($rootScope.tableParams.data[i].$selected) {
                order_id.push($rootScope.tableParams.data[i].id);
            }

        }

    }

    var modalInstance = $modal.open({
        templateUrl: 'modals/change_status.html',
        controller: function ($scope, $modalInstance) {

            $scope.data = {
                status: status,
                order_id: order_id,
                order_id_str: order_id.join(', '),
                message: ''
            };

            $scope.save = function () {

                var post_data = {
                    action: 'mgr/updateOrderStatus',
                    HTTP_MODAUTH: shk_config.auth_token,
                    status: $scope.data.status,
                    order_id: $scope.data.order_id
                }

                $scope.data.message = '';
                $rootScope.startSpin();
                $scope.modal_loading = true;

                $http.post(app.conf.connector_url, post_data)
                    .success(function (response) {

                        if (!!response && response.success) {

                            $rootScope.selectAll(false);
                            $rootScope.tableParams.reload();

                            $modalInstance.close();

                        }

                        if (!!response.message) {
                            $scope.data.message = response.message;
                        }

                        $rootScope.stopSpin();
                        $scope.modal_loading = false;

                    });

            };

            $scope.cancel = function () {
                $modalInstance.dismiss('cancel');
            };

        },
        resolve: {}
    });

}


/**
 * confirm
 *
 *
 */
$scope.confirm = function (action, text, header) {

    var modalInstance = $modal.open({
        templateUrl: 'modals/confirm.html',
        controller: function ($scope, $modalInstance) {

            $scope.ok = function () {
                action();
                $modalInstance.close();
            };

            $scope.cancel = function () {
                $modalInstance.dismiss('cancel');
            };

        },
        resolve: {}
    });

}


/**
 * deleteOrders
 *
 */
$scope.deleteOrders = function (id) {

    var order_ids = [];

    for (var i in $rootScope.tableParams.data) {

        if (!$rootScope.tableParams.data.hasOwnProperty(i)) continue;

        if ($rootScope.tableParams.data[i].$selected) {
            order_ids.push($rootScope.tableParams.data[i].id);
        }

    }

    var delete_func = function () {

        var post_data = {
            action: 'mgr/removeOrders',
            HTTP_MODAUTH: shk_config.auth_token,
            order_id: order_ids
        }

        $rootScope.startSpin();
        $scope.modal_loading = true;

        $http.post(app.conf.connector_url, post_data)
            .success(function (response) {

                if (!!response && response.success) {

                    $rootScope.selectAll(false);
                    $rootScope.tableParams.reload();

                }

                if (!!response.message) {
                    $rootScope.alert(( !response.success ? shk_config.lang['shk3.error'] : shk_config.lang['shk3.message'] ), response.message);
                }

                $rootScope.stopSpin();
                $scope.modal_loading = false;

            });

    };

    $scope.confirm(delete_func);

}


/**
 * viewOrder
 *
 */
$rootScope.viewOrder = function (order_id, action) {

    if (typeof action == 'undefined') {
        var action = 'view';
    }

    /* modalController */
    var modalController = function ($scope, $modalInstance) {

        $scope.data = {};
        $scope.data.total_items = 0;
        $scope.data.total_price = 0;
        $scope.data.order = {};
        $scope.modal_loading = false;
        $scope.settings = {
            payments: shk_config.settings.payments,
            delivery: shk_config.settings.delivery
        };

        var ajaxRequest = function (post_data, callback) {

            $rootScope.startSpin();
            $scope.data.message = '';
            $scope.modal_loading = true;

            $http.post(app.conf.connector_url, post_data)
                .success(function (response) {

                    if (typeof callback == 'function') {
                        callback(response);
                    }

                    if (!!response.message) {
                        $scope.data.message = response.message;
                    }

                    $rootScope.stopSpin();
                    $scope.modal_loading = false;

                })
                .error(function (response) {
                    if (response.code == 401) response.message = 'Forbidden.';
                    if (!!response.message) {
                        $scope.data.message = response.message;
                    }
                    $rootScope.stopSpin();
                    $scope.modal_loading = false;
                });

        }

        /* getOrder */
        var getOrder = function () {

            var post_data = {
                HTTP_MODAUTH: shk_config.auth_token,
                action: 'mgr/getOrder',
                order_id: order_id
            };

            var callback_func = function (response) {

                if (!!response && !!response.object) {

                    $scope.data.order = angular.copy(response.object);

                    angular.forEach($scope.data.order.purchases, function(value, key) {
                        //console.log(value);
                        var oldPrice = $scope.data.order.purchases[key].price;
                        var discount = parseInt($scope.data.order.purchases[key].discount);
                        $scope.data.order.purchases[key].price_discount = oldPrice;

                        if(discount){
                            var priceSaleDesc = oldPrice * (1 - discount / 100);
                            $scope.data.order.purchases[key].price_new_desc = '('+$scope.data.order.purchases[key].discount+')';
                            $scope.data.order.purchases[key].price_discount = priceSaleDesc;
                        }

                        //$scope.data.order.purchases[key].price = 123;
                    });


                }

            };

            ajaxRequest(post_data, callback_func);

        }

        getOrder();

        /* addOption */
        $scope.addOption = function (index) {

            if (!angular.element.isPlainObject($scope.data.order.purchases[index].options)) {
                $scope.data.order.purchases[index].options = {};
            }
            var opt_index = Object.keys($scope.data.order.purchases[index].options).length;
            var opt_name = 'shk_option' + ( opt_index + 1 );

            //если такой ключ для нового параметра уже есть, обновляем все ключи
            if ($scope.data.order.purchases[index].options[opt_name]) {
                var options = {}, ind = 0;
                for (var key in $scope.data.order.purchases[index].options) {
                    if ($scope.data.order.purchases[index].options.hasOwnProperty(key)) {
                        var key_name = key.indexOf('shk_option') > -1 ? 'shk_option' + ( ind + 1 ) : key;
                        options[key_name] = $scope.data.order.purchases[index].options[key];
                        //console.log(options[key_name]);
                    }
                    ind++;
                }
                $scope.data.order.purchases[index].options = options;
            }
            $scope.data.order.purchases[index].options[opt_name] = ['', 0];

        }

        /* edit */
        $scope.edit = function () {

            $modalInstance.close();
            $rootScope.viewOrder(order_id, 'edit');

        };

        /* view */
        $scope.view = function () {

            $modalInstance.close();
            $rootScope.viewOrder(order_id, 'view');

        };

        /* save */
        $scope.save = function () {

            var post_data = {
                HTTP_MODAUTH: shk_config.auth_token,
                action: 'mgr/saveOrder',
                order: angular.copy($scope.data.order)
            };

            var callback_func = function (response) {

                if (!!response && response.success) {

                    $rootScope.tableParams.reload();
                    $modalInstance.close();
                    //$rootScope.viewOrder( order_id, 'view' );

                }

            }

            ajaxRequest(post_data, callback_func);

        };

        /* removeRow */
        $scope.removeRow = function (index, d_name) {

            $scope.data.order[d_name].splice(index, 1);

        }

        /* addRow */
        $scope.addRow = function (d_name) {

            if (!angular.isArray($scope.data.order[d_name])) {
                $scope.data.order[d_name] = [];
            }
            $scope.data.order[d_name].push({});

        }

        /* cancel */
        $scope.cancel = function () {
            $modalInstance.dismiss('cancel');
        };

        var getTotal = function () {

            var total_items = 0;
            var total_price = 0;
            var total_price_desc = 0;
            var delivery_price = !!$scope.data.order.delivery_price && !isNaN($scope.data.order.delivery_price) ? $scope.data.order.delivery_price : 0;

            for (var i in $scope.data.order.purchases) {

                if (!$scope.data.order.purchases.hasOwnProperty(i)) continue;

                var temp_count = !!$scope.data.order.purchases[i].count ? $scope.data.order.purchases[i].count : 0;
                var temp_price = !!$scope.data.order.purchases[i].price ? $scope.data.order.purchases[i].price : 0;
                var temp_price_desc = !!$scope.data.order.purchases[i].price_discount ? $scope.data.order.purchases[i].price_discount : 0;
                //console.log(temp_price_desc);

                total_items += temp_count;
                total_price += ( temp_count * temp_price );
                total_price_desc += ( temp_count * temp_price_desc );

                if ($scope.data.order.purchases[i].options && angular.isObject($scope.data.order.purchases[i].options)) {

                    //доп параметры товара
                    for (var ii in $scope.data.order.purchases[i].options) {

                        $opt_price = $scope.data.order.purchases[i].options[ii][1] && !isNaN($scope.data.order.purchases[i].options[ii][1]) ? parseFloat($scope.data.order.purchases[i].options[ii][1]) : 0;

                        if ($opt_price > 0) {
                            total_price += $opt_price * temp_count;
                            total_price_desc += $opt_price * temp_count;
                        }

                    }

                }

            }

            $scope.data.total_items = total_items;
            $scope.data.total_price = total_price + delivery_price;
            $scope.data.total_price_desc = total_price_desc + delivery_price;

        }

        /* watch */
        $scope.$watch('data.order.purchases', getTotal, true);

        $scope.$watch('data.order.delivery_price', getTotal);

    }

    /* modalInstance */
    var modalInstance = $modal.open({
        templateUrl: 'modals/order_' + action + '.html',
        size: 'lg',
        controller: modalController,
        resolve: {}
    });

}


/**
 * watches
 *
 */
$scope.$watch('filters', function (newValue, oldValue) {

    var count = 0;
    for (var k in $scope.filters) {

        if (!$scope.filters.hasOwnProperty(k)) continue;

        if (( angular.isArray($scope.filters[k]) && $scope.filters[k].length > 0 ) || $scope.filters[k] != '') {
            count++;
        }

    }

    $scope.filters_selected = count;

}, true);

$scope.layoutInit();

});

/* renderFieldValue */
app
.directive('renderFieldValue', ['$compile', '$rootScope', function ($compile, $rootScope) {
return {
restrict: 'A',
scope: {},
template: '{{output}}',
link: function (scope, element, attrs) {

            scope.output = !!attrs.fieldvalue ? attrs.fieldvalue : 'N/a';

            //renderers
            var renderers = {
                //renderer username
                username: function (input) {

                    var template = '<a href="?a=security/user/update&id={{userid}}" target="_blank">\
                <span class="glyphicon glyphicon-user"></span>\
                {{username}}\
                </a>';
                    scope.userid = !!attrs.userid ? attrs.userid : 0;
                    scope.username = !!attrs.fieldvalue ? attrs.fieldvalue : 'N/a';

                    if (scope.userid > 0) {
                        element.html(template);
                        $compile(element.contents())(scope);
                    }


                    return true;

                },
                //renderer status
                status: function () {

                    var template = '<span class="status-button" style="background-color:{{status_color}};">\
                <span class="icon glyphicon glyphicon-retweet" ng-click="changeStatus(order_id)"></span>\
                <span class="text">{{status_label}}</span>\
                </span>';

                    scope.status_color = '';
                    scope.status_label = 'N/a';
                    scope.order_id = !!attrs.id ? attrs.id : 0;
                    scope.changeStatus = $rootScope.changeStatus;
                    var index = -1;

                    for (var i in shk_config.settings.statuses) {

                        if (shk_config.settings.statuses.hasOwnProperty(i)) {

                            if (shk_config.settings.statuses[i].id == attrs.fieldvalue) {
                                index = i;
                                break;
                            }

                        }

                    }

                    //Подключение звукового сигнала если статус новый
                    if (scope.output == 1) {
                        var srcMp3 = document.location.origin + '/sounds/magic_mirror.mp3?pp=' + scope.order_id + Math.floor(Date.now() / 1000);
                        var audiotpl = '<audio id="playID' + scope.order_id + '" src="' + srcMp3 + '" autoplay loop_off type="audio/mp3"></audio>';
                        template += audiotpl;
                    }

                    if (index > -1) {
                        scope.status_color = shk_config.settings.statuses[index].color;
                        scope.status_label = shk_config.settings.statuses[index].label;
                    } else {
                        scope.status_color = '#fff';
                        scope.status_label = 'N/a';
                    }

                    element.html(template);
                    $compile(element.contents())(scope);

                    return true;

                },
                //renderer price
                price: function () {

                    var template = "{{ output | number: 2 }}";

                    element.html(template);
                    $compile(element.contents())(scope);

                },

                /*Доабвление в список контактных данных*/
                user_contacts: function (input) {
                    scope.order_id = !!attrs.id ? attrs.id : 0;
                    var foo = $rootScope.tableParams.data;
                    var checks = foo.filter(function (person) {
                        return person.id == scope.order_id
                    });
                    var u = checks[0];

                    var fio = (u.userid > 0) ? '<a href="?a=security/user/update&id=' + u.userid + '" target="_blank">' + u['contacts.fullname'] + ' (' + u.username + ')</a>' : u['contacts.fullname'];
                    var bobusCat = (u['contacts.bonus_card']) ? u['contacts.bonus_card'] : "не указан";


                    //console.log(u);

                    //Payment
                    var payment = u['payment'];
                    switch (payment) {
                        case "AC":
                            payment = "AC (Оплата с произвольной банковской карты)";
                            break;
                        case "SB":
                            payment = "SB (Оплата через Сбербанк: оплата по SMS или Сбербанк Онлайн)";
                            break;
                        case "AB":
                            payment = "AB (Оплата через Альфа-Клик)";
                            break;
                        case "PC":
                            payment = "PC (Оплата из кошелька в Яндекс.Деньгах)";
                            break;
                        case "GP":
                            payment = "GP (Оплата наличными через кассы и терминалы)";
                            break;
                        case "PB":
                            payment = "PB (Оплата через интернет-банк Промсвязьбанка)";
                            break;
                    }

                    var too_template = '';
                    if (fio) {

                        //u['note'] = JSON.parse(u['note']);
                        //$scope.data.order.purchases

                        var note_json = (u['note_json']) ? JSON.parse(u['note_json']) : false;
                        var note = (u['note']) ? ' / <u title="Заметка"><small></small></u>' : '';

                        too_template += '<strong>Имя:</strong> ' + fio + note + '<br>';
                    }
                    if (u['contacts.phone']) {
                        too_template += '<strong>Тел.:</strong> ' + u['contacts.phone'] + '<br>';
                    }
                    if (u['contacts.address']) {
                        too_template += '<strong>Адрес:</strong> <a title="Посмотреть маршрут" target="_blank" href="https://www.google.ru/maps/dir/ул.+Циолковского,+1,+Нижний+Тагил/' + u['contacts.address'] + ',15z,+Нижний+Тагил">' + u['contacts.address'] + ' &#8663; </a><br>';
                    }
                    if (u['contacts.bonus_card']) {
                        too_template += '<strong>Номер бонусной карты:</strong> <code>' + u['contacts.bonus_card'] + '</code><br>';
                    }
                    if (payment) {
                        too_template += '<strong>Способ оплаты:</strong> <i>' + payment + '</i><br>';
                    }
                    if (u['delivery']) {
                        too_template += '<strong>Доставка:</strong> <i>' + u['delivery'] + '</i><br>';
                    }
                    if (u['contacts.pribors']) {
                        too_template += '<strong>Приборы:</strong> <i class="text-danger">' + u['contacts.pribors'] + '</i><br>';
                    }
                    if (u['contacts.comment']) {
                        too_template += '<strong>Комментарий:</strong> <i class="text-danger">' + u['contacts.comment'] + '</i><br>';
                    }
                    if (note_json && note_json.note.length) {

                        var tovar_items='',
                            count_items = note_json.note.length;
                        //console.log(note_json);
                        angular.forEach(note_json.note, function(value, key) {
                            tovar_items+='<li style="list-style-type: circle !important;"><a target="_blank" href="https://golodnaya-panda.ru/?id='+value.id+'">'+value.pagetitle+' ('+value.discount+')</a> ['+value.discount_desc+']</li>'

                        });

                        too_template += '<strong>Дополнительные скидки на товар: ('+count_items+')</strong> <ol style="margin-left: 5%">' + tovar_items + '</ol><br>';
                    }


                    var template = '<div style="font-size:13px " class="alert alert-info alert-warning">' + too_template + '</div>';
                    element.html(template);
                    $compile(element.contents())(scope);

                },
                user_phone: function () {

                    scope.order_id = !!attrs.id ? attrs.id : 0;
                    var foo = $rootScope.tableParams.data;
                    var checks = foo.filter(function (person) {
                        return person.id == scope.order_id
                    });
                    var phone = (checks[0]['contacts.phone']) ? checks[0]['contacts.phone'] : null;


                    var template = '<span>' + phone + '</span>';
                    element.html(template);
                    $compile(element.contents())(scope);
                },
                bonus_card: function () {

                    scope.order_id = !!attrs.id ? attrs.id : 0;
                    var foo = $rootScope.tableParams.data;
                    var checks = foo.filter(function (person) {
                        return person.id == scope.order_id
                    });
                    var card = (checks[0]['contacts.bonus_card']) ? checks[0]['contacts.bonus_card'] : null;
                    var template = '<span>' + card + '</span>';
                    element.html(template);
                    $compile(element.contents())(scope);
                }
            };

            if (typeof renderers[attrs.fieldname] == 'function') {

                renderers[attrs.fieldname]();

            }

        }
    };
}]);

/* orderNote */
app
.directive('orderNote', ['$compile', function ($compile) {
return {
restrict: 'A',
scope: {},
template: '{{output}}',
link: function (scope, element, attrs) {

            scope.output = '';

            if (typeof attrs.note != 'undefined' && attrs.note != '') {

                var template = '<span class="glyphicon glyphicon-info-sign" tooltip-placement="left" tooltip="' + attrs.note + '"></span>';
                element.html(template);
                $compile(element.contents())(scope);

            }

        }
    };
}]);

Зы делал все на коленке, тк, заказчику очень надо было в будущем конечно приведу все в порядок )
Рабочее решение: https://golodnaya-panda.ru/
Скриншоты того что получилось

0_1479564914052_2016-11-19_19-05-10.png
0_1479564924280_2016-11-19_18-40-08.png
0_1479564931501_2016-11-19_18-39-24.png
0_1479564941294_2016-11-19_18-38-16.png
0_1479564960573_2016-11-19_18-36-53.png
0_1479564977811_2016-11-19_18-37-33.png

Похоже, подключение к Форум | MODX Shopkeeper было разорвано, подождите, пока мы пытаемся восстановить соединение.