/**
  * @param shopProduct $product
  * @return array|bool
  * @example <pre>
  * array(
  *  'feature_1'=>array(
  *      'selected'=>int,
  *      'values'=>array(
  *         %value_id%=> array(
  *              'value_id'=>int,
  *              'value'=>string,
  *              'selected'=>bool,
  *          ),
  *      ),
  *  ),
  * )
  * </pre>
  */
 public function getData(shopProduct $product)
 {
     $feature_model = new shopFeatureModel();
     if ($product->sku_type == shopProductModel::SKU_TYPE_SELECTABLE) {
         $selected = $this->getByProduct($product->id);
         $env = wa()->getEnv();
         if ($env == 'backend') {
             $features = $feature_model->getMultipleSelectableFeaturesByType($product->type_id);
         } else {
             $features = $feature_model->getById(array_keys($selected));
             if ($product->type_id) {
                 $types = array($product->type_id => true);
                 $type_features_model = new shopTypeFeaturesModel();
                 $type_features_model->fillTypes($features, $types);
                 foreach ($features as &$feature) {
                     unset($feature['types']);
                     $feature['sort'] = ifset($feature['sort'][$product->type_id]);
                     unset($feature);
                 }
                 uasort($features, create_function('$a,$b', 'return max(-1,min(1,$a["sort"]-$b["sort"]));'));
             }
         }
         // attach values
         $features = $feature_model->getValues($features);
         foreach ($features as &$f) {
             $count = 0;
             foreach ($f['values'] as $id => &$v) {
                 $is_selected = isset($selected[$f['id']][$id]);
                 if ($env == 'backend') {
                     if (is_object($v)) {
                         $v['selected'] = $is_selected;
                     } else {
                         $v = array('value' => htmlentities((string) $v, ENT_NOQUOTES, 'utf-8'), 'selected' => $is_selected);
                     }
                     unset($v);
                     if ($is_selected) {
                         $count += 1;
                     }
                 } elseif (!$is_selected) {
                     unset($f['values'][$id]);
                 }
             }
             $f['selected'] = $count;
             unset($f);
         }
     } else {
         if (wa()->getEnv() == 'backend') {
             $features = $feature_model->isTypeMultipleSelectable($product->type_id);
         } else {
             $features = array();
         }
     }
     return $features;
     //Frontend
     $data = $feature_model->getValues($data);
     foreach ($data as $code => $f) {
         foreach ($f['values'] as $id => $v) {
             if (!isset($v_id, $selected[$code][$id])) {
                 unset($data[$code]['values'][$id]);
             }
         }
     }
     return $data;
 }
 public function execute()
 {
     $update = waRequest::post('update');
     // just update one or any field of product
     if ($update) {
         $this->update($update);
         return;
     }
     $data = waRequest::post('product');
     $id = empty($data['id']) || !intval($data['id']) ? null : $data['id'];
     if (!$id && isset($data['id'])) {
         unset($data['id']);
     }
     # edit product info - check rights
     $product_model = new shopProductModel();
     if ($id) {
         if (!$product_model->checkRights($id)) {
             throw new waRightsException(_w("Access denied"));
         }
     } else {
         if (!$product_model->checkRights($data)) {
             throw new waRightsException(_w("Access denied"));
         }
     }
     $skus = waRequest::post('skus', array());
     if (isset($data['skus'])) {
         foreach ($skus as $s_id => $s) {
             if (isset($data['skus'][$s_id])) {
                 $data['skus'][$s_id] += $s;
             } else {
                 $data['skus'][$s_id] = $s;
             }
         }
     } else {
         $data['skus'] = $skus;
     }
     if (empty($data['categories'])) {
         $data['categories'] = array();
     }
     if (empty($data['tags'])) {
         $data['tags'] = array();
     }
     if (empty($data['features_selectable'])) {
         $data['features_selectable'] = array();
     }
     # verify sku_type before save
     if ($data['type_id']) {
         $features_model = new shopFeatureModel();
         if ($features_model->isTypeMultipleSelectable($data['type_id'])) {
             if ($data['sku_type'] == shopProductModel::SKU_TYPE_SELECTABLE) {
                 if (empty($data['features_selectable'])) {
                     throw new waException(_w("Check at least one feature value"));
                 }
             }
         } else {
             $data['sku_type'] = shopProductModel::SKU_TYPE_FLAT;
         }
     } else {
         $data['sku_type'] = shopProductModel::SKU_TYPE_FLAT;
     }
     if ($data['sku_type'] == shopProductModel::SKU_TYPE_FLAT) {
         $data['features_selectable'] = array();
     }
     try {
         $product = new shopProduct($id);
         // for logging changes in stocks
         shopProductStocksLogModel::setContext(shopProductStocksLogModel::TYPE_PRODUCT);
         if ($product->save($data, true, $this->errors)) {
             $features_counts = null;
             if ($product->sku_type == shopProductModel::SKU_TYPE_SELECTABLE) {
                 $features_counts = array();
                 foreach ($product->features_selectable as $f) {
                     if (isset($f['selected'])) {
                         $features_counts[] = $f['selected'];
                     } else {
                         $features_counts[] = count($f['values']);
                     }
                 }
                 $features_total_count = array_product($features_counts);
                 $this->response['features_selectable_strings'] = array('options' => implode(' x ', $features_counts) . ' ' . _w('option', 'options', $features_total_count), 'skus' => _w('%d SKU in total', '%d SKUs in total', $features_total_count));
             }
             shopProductStocksLogModel::clearContext();
             if ($id) {
                 $this->logAction('product_edit', $id);
             } else {
                 $this->logAction('product_add', $product->getId());
             }
             $this->response['id'] = $product->getId();
             $this->response['name'] = $product->name;
             $this->response['url'] = $product->url;
             $this->response['frontend_urls'] = $this->getUrl($product);
             $this->response['raw'] = $this->workupData($product->getData());
             $sales_rate = waRequest::post('sales_rate', 0, waRequest::TYPE_STRING_TRIM);
             $sales_rate = (double) str_replace(',', '.', $sales_rate);
             $runout = $product->getRunout($sales_rate);
             if (!empty($runout['product'])) {
                 $runout['product']['date_str'] = wa_date("humandate", $runout['product']['date']);
                 $runout['product']['days_str'] = _w('%d day', '%d days', $runout['product']['days']);
                 if ($runout['product']['days'] < 3 * 365 && $runout['product']['days'] > 0) {
                     $runout['product_str'] = sprintf(_w('Based on last 30 days sales dynamic (%d items of %s sold during last 30 days), you will run out of %s in <strong>%d days</strong> (on %s)'), $sales_rate * 30, $product->name, $product->name, $runout['product']['days'], wa_date("humandate", $runout['product']['date']));
                 }
             } else {
                 $runout['product'] = new stdClass();
                 /* {} */
             }
             if (!empty($runout['sku'])) {
                 foreach ($runout['sku'] as &$sk_r) {
                     if (empty($sk_r['stock'])) {
                         $sk_r['date_str'] = wa_date("humandate", $sk_r['date']);
                         $sk_r['days_str'] = _w('%d day', '%d days', $sk_r['days']);
                     } else {
                         foreach ($sk_r['stock'] as &$st_r) {
                             $st_r['date_str'] = wa_date("humandate", $st_r['date']);
                             $st_r['days_str'] = _w('%d day', '%d days', $st_r['days']);
                         }
                     }
                 }
                 unset($sk_r, $st_r);
             } else {
                 $runout['sku'] = new stdClass();
                 /* {} */
             }
             $this->response['raw']['runout'] = $runout;
             $this->response['storefront_map'] = $product_model->getStorefrontMap($product->id);
         }
     } catch (Exception $ex) {
         $this->setError($ex->getMessage());
     }
 }