public function execute()
 {
     $product_id = (int) waRequest::get('id');
     $product = new shopProduct($product_id);
     $type_model = new shopTypeModel();
     $type = $type_model->getById($product['type_id']);
     if ($product['cross_selling'] === null) {
         $product['cross_selling'] = $type['cross_selling'] ? 1 : 0;
     }
     if ($product['upselling'] === null) {
         $product['upselling'] = $type['upselling'];
     }
     // if manually
     if ($product['cross_selling'] == 2 || $product['upselling'] == 2) {
         $related_model = new shopProductRelatedModel();
         $related = $related_model->getAllRelated($product_id);
     } else {
         $related = array();
     }
     if ($type['upselling']) {
         $type_upselling_model = new shopTypeUpsellingModel();
         $data = $type_upselling_model->getByType($type['id']);
         $type['upselling_html'] = shopSettingsRecommendationsAction::getConditionHTML($data);
     }
     if ($type['cross_selling'] && substr($type['cross_selling'], 0, 9) == 'category/') {
         $category_model = new shopCategoryModel();
         $type['category'] = $category_model->getById(substr($type['cross_selling'], 9));
     }
     $this->view->assign(array('type' => $type, 'product' => $product, 'related' => $related));
 }
 public function execute()
 {
     $setting = waRequest::get('setting');
     $this->type_id = waRequest::post('type_id');
     $this->type_model = new shopTypeModel();
     $this->type = $this->type_model->getById($this->type_id);
     if (!$this->type) {
         throw new waException("Type not found");
     }
     if ($setting == 'cross-selling') {
         $this->saveCrossSelling();
     } elseif ($setting == 'upselling') {
         $this->saveUpSelling();
     } else {
         throw new waException("Unknown setting");
     }
 }
 public function execute()
 {
     $id = $this->get('id', true);
     $type_model = new shopTypeModel();
     $type = $type_model->getById($id);
     if ($type) {
         $this->response = $type;
     } else {
         throw new waAPIException('invalid_param', 'Type not found', 404);
     }
 }
 /**
  * Collections /type/1
  *
  * @param int $id - type_id
  * @param bool $auto_title
  */
 protected function typePrepare($id, $auto_title = true)
 {
     $type_model = new shopTypeModel();
     $type = $type_model->getById($id);
     if (!$type) {
         $this->where[] = '0';
         return;
     }
     $this->info = $type;
     if ($auto_title) {
         $this->addTitle($type['name']);
     }
     $this->where[] = "p.type_id = " . (int) $id;
 }
 /**
  * Returns information on product's type.
  *
  * @return array|null Product type info array, or null if product has no type
  */
 public function getType()
 {
     $model = new shopTypeModel();
     return $this->type_id ? $model->getById($this->type_id) : null;
 }
 /**
  * @param string $field
  * @param mixed $value
  * @param array $info
  * @param array $data
  * @param null $sku_data
  * @return mixed|string
  */
 private function format($field, $value, $info = array(), $data = array(), $sku_data = null)
 {
     /**
      * @todo cpa field
      */
     /**
      * <yml_catalog>
      * <shop>
      * <currencies>
      * <categories>
      * <local_delivery_cost>
      * <offers>
      * <picture>
      * <description> и <name>
      * <delivery>, <pickup> и <store>
      * <adult>
      * <barcode>
      * <cpa> TODO
      * <rec>
      * <param> (name,unit,value)
      * <vendor>
      */
     static $currency_model;
     static $size;
     switch ($field) {
         case 'group_id':
             if ($value === 'auto') {
                 if (!empty($data['market_category'])) {
                     $value = $this->plugin()->isGroupedCategory($data['market_category']) ? $data['id'] : null;
                 } else {
                     $info['format'] = false;
                 }
             }
             break;
         case 'market_category':
             //it's product constant field
             //TODO verify it
             break;
         case 'name':
             if (!empty($sku_data['name']) && !empty($data['name']) && $sku_data['name'] != $data['name']) {
                 $value = sprintf('%s (%s)', $value, $sku_data['name']);
             }
             $value = preg_replace('/<br\\/?\\s*>/', "\n", $value);
             $value = preg_replace("/[\r\n]+/", "\n", $value);
             $value = strip_tags($value);
             $value = trim($value);
             if (mb_strlen($value) > 255) {
                 $value = mb_substr($value, 0, 252) . '...';
             }
             break;
         case 'description':
             $value = preg_replace('/<br\\/?\\s*>/', "\n", $value);
             $value = preg_replace("/[\r\n]+/", "\n", $value);
             $value = strip_tags($value);
             $value = trim($value);
             if (mb_strlen($value) > 512) {
                 $value = mb_substr($value, 0, 509) . '...';
             }
             break;
         case 'barcode':
             //может содержать несколько элементов
             $value = preg_replace('@\\D+@', '', $value);
             if (!in_array(strlen($value), array(8, 12, 13))) {
                 $value = null;
             }
             break;
         case 'sales_notes':
             $value = trim($value);
             if (mb_strlen($value) > 50) {
                 $value = mb_substr($value, 0, 50);
             }
             break;
         case 'typePrefix':
             $model = new shopTypeModel();
             if ($type = $model->getById($value)) {
                 $value = $type['name'];
             }
             break;
         case 'url':
             //max 512
             $value = preg_replace_callback('@([^\\[\\]a-zA-Z\\d_/-\\?=%&,\\.]+)@i', array(__CLASS__, 'rawurlencode'), $value);
             if ($this->data['utm']) {
                 $value .= (strpos($value, '?') ? '&' : '?') . $this->data['utm'];
             }
             $value = 'http://' . ifempty($this->data['base_url'], 'localhost') . $value;
             break;
         case 'oldprice':
             if (empty($value) || empty($this->data['export']['compare_price'])) {
                 $value = null;
                 break;
             }
         case 'price':
             if (!$currency_model) {
                 $currency_model = new shopCurrencyModel();
             }
             if ($sku_data) {
                 if (!in_array($data['currency'], $this->data['currency'])) {
                     $value = $currency_model->convert($value, $data['currency'], $this->data['primary_currency']);
                     $data['currency'] = $this->data['primary_currency'];
                 }
             } else {
                 if (!in_array($data['currency'], $this->data['currency'])) {
                     #value in default currency
                     if ($this->data['default_currency'] != $this->data['primary_currency']) {
                         $value = $currency_model->convert($value, $this->data['default_currency'], $this->data['primary_currency']);
                     }
                     $data['currency'] = $this->data['primary_currency'];
                 } elseif ($this->data['default_currency'] != $data['currency']) {
                     $value = $currency_model->convert($value, $this->data['default_currency'], $data['currency']);
                 }
             }
             break;
         case 'currencyId':
             if (!in_array($value, $this->data['currency'])) {
                 $value = $this->data['primary_currency'];
             }
             break;
         case 'rate':
             if (!in_array($value, array('CB', 'CBRF', 'NBU', 'NBK'))) {
                 $info['format'] = '%0.4f';
             }
             break;
         case 'available':
             if (!empty($sku_data) && isset($sku_data['available']) && empty($sku_data['available'])) {
                 $value = 'false';
             }
             if (is_object($value)) {
                 switch (get_class($value)) {
                     case 'shopBooleanValue':
                         /**
                          * @var $value shopBooleanValue
                          */
                         $value = $value->value ? 'true' : 'false';
                         break;
                 }
             }
             $value = ($value <= 0 || $value === 'false' || empty($value)) && $value !== null && $value !== 'true' ? 'false' : 'true';
             break;
         case 'store':
         case 'pickup':
         case 'delivery':
         case 'adult ':
             if (is_object($value)) {
                 switch (get_class($value)) {
                     case 'shopBooleanValue':
                         /**
                          * @var $value shopBooleanValue
                          */
                         $value = $value->value ? 'true' : 'false';
                         break;
                 }
             }
             $value = empty($value) || $value === 'false' ? 'false' : 'true';
             break;
         case 'picture':
             //max 512
             $values = array();
             $limit = 10;
             if (!empty($sku_data['image_id'])) {
                 $value = array(ifempty($value[$sku_data['image_id']]));
             }
             while (is_array($value) && ($image = array_shift($value)) && $limit--) {
                 if (!$size) {
                     $shop_config = wa('shop')->getConfig();
                     /**
                      * @var $shop_config shopConfig
                      */
                     $size = $shop_config->getImageSize('big');
                 }
                 $values[] = 'http://' . ifempty($this->data['base_url'], 'localhost') . shopImage::getUrl($image, $size);
             }
             $value = $values;
             break;
         case 'page_extent':
             $value = max(1, intval($value));
             break;
         case 'seller_warranty':
         case 'manufacturer_warranty':
         case 'expiry':
             /**
              * ISO 8601, например: P1Y2M10DT2H30M
              */
             $pattern = '@P((\\d+S)?(\\d+M)(\\d+D)?)?(T(\\d+H)?(\\d+M)(\\d+S)?)?@';
             $class = is_object($value) ? get_class($value) : false;
             switch ($class) {
                 case 'shopBooleanValue':
                     /**
                      * @var $value shopBooleanValue
                      */
                     $value = $value->value ? 'true' : 'false';
                     break;
                 case 'shopDimensionValue':
                     /**
                      * @var $value shopDimensionValue
                      */
                     $value = $value->convert('s', false);
                     /**
                      * @var $value int
                      */
                     if (empty($value)) {
                         $value = 'false';
                     } else {
                         $value = $this->formatCustom($value, 'ISO8601');
                     }
                     break;
                 default:
                     $value = (string) $value;
                     if (empty($value) || $value == 'false') {
                         $value = 'false';
                     } elseif (preg_match('@^\\d+$@', trim($value))) {
                         $value = $this->formatCustom(intval($value) * 3600 * 24, 'ISO8601');
                     } elseif (!preg_match($pattern, $value)) {
                         $value = 'true';
                     }
                     break;
             }
             break;
         case 'year':
             if (empty($value)) {
                 $value = null;
             }
             break;
         case 'ISBN':
             /**
              * @todo verify format
              * Код книги, если их несколько, то указываются через запятую.
              * Форматы ISBN и SBN проверяются на корректность. Валидация кодов происходит не только по длине,
              * также проверяется контрольная цифра (check-digit) – последняя цифра кода должна согласовываться
              * с остальными цифрами по определенной формуле. При разбиении ISBN на части при помощи дефиса
              * (например, 978-5-94878-004-7) код проверяется на соответствие дополнительным требованиям к
              * количеству цифр в каждой из частей.
              * Необязательный элемент.
              **/
             break;
         case 'recording_length':
             /**
              * Время звучания задается в формате mm.ss (минуты.секунды).
              **/
             if (is_object($value)) {
                 switch (get_class($value)) {
                     case 'shopDimensionValue':
                         /**
                          * @var $value shopDimensionValue
                          */
                         $value = $value->convert('s', false);
                         break;
                     default:
                         $value = (int) $value;
                         break;
                 }
             }
             $value = sprintf('%02d.%02d', floor($value / 60), $value % 60);
             break;
         case 'weight':
             /**
              * Элемент предназначен для указания веса товара. Вес указывается в килограммах с учетом упаковки.
              * Формат элемента: положительное число с точностью 0.001, разделитель целой и дробной части — точка.
              * При указании более высокой точности значение автоматически округляется следующим способом:
              * — если 4-ый знак после разделителя меньше 5, то 3-й знак сохраняется, а все последующие обнуляются;
              * — если 4-ый знак после разделителя больше или равен 5, то 3-й знак увеличивается на единицу, а все последующие обнуляются.
              **/
             if (is_object($value)) {
                 switch (get_class($value)) {
                     case 'shopDimensionValue':
                         /**
                          * @var $value shopDimensionValue
                          */
                         if ($value->type == 'weight') {
                             $value = $value->convert('kg', '%0.3f');
                         }
                         break;
                     default:
                         $value = floatval($value);
                         break;
                 }
             } else {
                 $value = floatval($value);
             }
             break;
         case 'dimensions':
             /**
              *
              * Элемент предназначен для указания габаритов товара (длина, ширина, высота) в упаковке. Размеры указываются в сантиметрах.
              * Формат элемента: три положительных числа с точностью 0.001, разделитель целой и дробной части — точка. Числа должны быть разделены символом «/» без пробелов.
              * При указании более высокой точности значение автоматически округляется следующим способом:
              * — если 4-ый знак после разделителя меньше 5, то 3-й знак сохраняется, а все последующие обнуляются;
              * — если 4-ый знак после разделителя больше или равен 5, то 3-й знак увеличивается на единицу, а все последующие обнуляются.
              **/
             /**
              * @todo use cm
              *
              */
             $parsed_value = array();
             $class = is_object($value) ? get_class($value) : false;
             switch ($class) {
                 case 'shopCompositeValue':
                     /**
                      * @var $value shopCompositeValue
                      */
                     for ($i = 0; $i < 3; $i++) {
                         $value_item = $value[$i];
                         $class_item = is_object($value_item) ? get_class($value_item) : false;
                         switch ($class_item) {
                             case 'shopDimensionValue':
                                 /**
                                  * @var $value_item shopDimensionValue
                                  */
                                 if ($value_item->type == '3d.length') {
                                     $parsed_value[] = $value_item->convert('cm', '%0.4f');
                                 } else {
                                     $parsed_value[] = sprintf('%0.4f', (string) $value_item);
                                 }
                                 break;
                             default:
                                 $parsed_value[] = sprintf('%0.4f', (string) $value_item);
                                 break;
                         }
                     }
                     break;
                 default:
                     $parsed_value = array_map('floatval', explode(':', preg_replace('@[^\\d\\.,]+@', ':', $value), 3));
                     break;
             }
             foreach ($parsed_value as &$p) {
                 $p = str_replace(',', '.', sprintf('%0.4f', $p));
                 unset($p);
             }
             $value = implode('/', $parsed_value);
             break;
         case 'age':
             /**
              * @todo
              * unit="year": 0, 6, 12, 16, 18
              * unit="month": 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
              */
             if (is_object($value)) {
                 switch (get_class($value)) {
                     case 'shopDimensionValue':
                         /**
                          * @var $value shopDimensionValue
                          */
                         if ($value->type == 'time') {
                             $value = $value->convert('month', false);
                         }
                         break;
                     default:
                         $value = intval($value);
                         break;
                 }
             } else {
                 /**
                  * @var $value shopDimensionValue
                  */
                 if (preg_match('@^(year|month)s?:(\\d+)$@', trim($value), $matches)) {
                     $value = array('unit' => $matches[1], 'value' => intval($matches[2]));
                 } else {
                     $value = intval($value);
                 }
             }
             if (!is_array($value)) {
                 if ($value > 12) {
                     $value = array('unit' => 'year', 'value' => floor($value / 12));
                 } else {
                     $value = array('unit' => 'month', 'value' => intval($value));
                 }
             }
             break;
         case 'country_of_origin':
             /**
              * @todo
              * @see http://partner.market.yandex.ru/pages/help/Countries.pdf
              */
             break;
         case 'local_delivery_cost':
             if ($value !== '') {
                 $value = max(0, floatval($value));
             }
             break;
         case 'days':
             $value = max(1, intval($value));
             break;
         case 'dataTour':
             /**
              * @todo
              * Даты заездов.
              * Необязательный элемент. Элемент <offer> может содержать несколько элементов <dataTour>.
              **/
             break;
         case 'hotel_stars':
             /**
              * @todo
              * Звезды отеля.
              * Необязательный элемент.
              **/
             break;
         case 'room':
             /**
              * @todo
              * Тип комнаты (SNG, DBL, ...).
              * Необязательный элемент.
              **/
             break;
         case 'meal':
             /**
              * @todo
              * Тип питания (All, HB, ...).
              * Необязательный элемент.
              **/
             break;
         case 'date':
             /**
              * @todo
              * Дата и время сеанса. Указываются в формате ISO 8601: YYYY-MM-DDThh:mm.
              **/
             break;
         case 'hall':
             /**
              * @todo
              * max 512
              * Ссылка на изображение с планом зала.
              **/
             //plan - property
             break;
         case 'param':
             $unit = null;
             $name = ifset($info['source_name'], '');
             if ($value instanceof shopDimensionValue) {
                 $unit = $value->unit_name;
                 $value = $value->format('%s');
             } elseif (is_array($value)) {
                 $_value = reset($value);
                 if ($_value instanceof shopDimensionValue) {
                     $unit = $_value->unit_name;
                     $values = array();
                     foreach ($value as $_value) {
                         /**
                          * @var shopDimensionValue $_value
                          */
                         $values[] = $_value->convert($unit, '%s');
                     }
                     $value = implode(', ', $values);
                 } else {
                     if (preg_match('@^(.+)\\s*\\(([^\\)]+)\\)\\s*$@', $name, $matches)) {
                         //feature name based unit
                         $unit = $matches[2];
                         $name = $matches[1];
                     }
                     $value = implode(', ', $value);
                 }
             } elseif (preg_match('@^(.+)\\s*\\(([^\\)]+)\\)\\s*$@', $name, $matches)) {
                 //feature name based unit
                 $unit = $matches[2];
                 $name = $matches[1];
             }
             $value = trim((string) $value);
             if (in_array($value, array(null, false, ''), true)) {
                 $value = null;
             } else {
                 $value = array('name' => $name, 'unit' => $unit, 'value' => trim((string) $value));
             }
             break;
     }
     $format = ifempty($info['format'], '%s');
     if (is_array($value)) {
         /**
          * @var $value array
          */
         reset($value);
         if (key($value) == 0) {
             foreach ($value as &$item) {
                 $item = str_replace('&nbsp;', ' ', $item);
                 $item = str_replace('&', '&amp;', $item);
                 $item = $this->sprintf($format, $item);
             }
             unset($item);
         }
         if (!in_array($field, array('email', 'picture', 'dataTour', 'additional', 'barcode', 'param', 'related_offer'))) {
             $value = implode(', ', $value);
         }
     } elseif ($value !== null) {
         /**
          * @var $value string
          */
         $value = str_replace('&nbsp;', ' ', $value);
         $value = str_replace('&', '&amp;', $value);
         $value = $this->sprintf($format, $value);
     }
     return $value;
 }
 /**
 * @param $data
 * @param array $features строка 1
 строка 2
 * @return string
 */
 public static function getConditionHTML($data, $features = array())
 {
     $result = array();
     foreach ($data as $row) {
         if (empty($row['cond'])) {
             continue;
         }
         if (!empty($row['feature_id'])) {
             if ($features) {
                 $html = $features[$row['feature_id']]['name'];
             } else {
                 $html = $row['feature_name'];
             }
         } else {
             if ($row['feature'] == 'price') {
                 $html = _w('Price');
             } elseif ($row['feature'] == 'tag') {
                 $html = _w('Tags');
             } elseif ($row['feature'] == 'type_id') {
                 $html = _w('Type');
             } else {
                 continue;
             }
         }
         $html .= ' ';
         switch ($row['cond']) {
             case 'between':
                 $v = explode(',', $row['value']);
                 $html .= '<span class="s-plus-minus">' . ($v[1] > 0 ? '+' : '') . $v[1] . '%<br>' . ($v[0] > 0 ? '+' : '') . $v[0] . '%</span>';
                 break;
             case 'contain':
                 $html .= $row['cond'] . ' "' . $row['value'] . '"';
                 break;
             case 'same':
                 $html .= _w('matches base product value');
                 break;
             case 'notsame':
                 $html .= _w('differs from base product value');
                 break;
             case 'all':
             case 'any':
             case 'is':
                 if ($row['cond'] == 'any') {
                     $html .= _w('any of selected values (OR)');
                 } elseif ($row['cond'] == 'all') {
                     $html .= _w('all of selected values (AND)');
                 } else {
                     $html .= _w($row['cond']);
                 }
                 $html .= ' ';
                 if ($row['feature'] == 'type_id') {
                     $type_model = new shopTypeModel();
                     $type = $type_model->getById($row['value']);
                     $html .= $type['name'];
                 } else {
                     $feature_values_model = shopFeatureModel::getValuesModel($features ? $features[$row['feature_id']]['type'] : $row['feature_type']);
                     if (strpos($row['value'], ',') !== false) {
                         $value_ids = explode(',', $row['value']);
                         $values = $feature_values_model->getById($value_ids);
                         foreach ($values as &$v) {
                             $v = $v['value'];
                         }
                         unset($v);
                         $html .= implode(', ', $values);
                     } else {
                         $v = $feature_values_model->getById($row['value']);
                         $html .= $v['value'];
                     }
                 }
                 break;
         }
         $result[] = $html;
     }
     return implode('; ', $result);
 }