Version: current

      1 <?php
      2 // вариация продукта
      3 class ProductVariation extends DataObject {
      4     static $db = array(
      5         'Title' => 'Varchar(200)',     // полное название вариации для корзины
      6         'MenuTitle' => 'Varchar(200)', // краткое название вариации для списка выбора, может формироваться автоматически как склеивание пар: название параметра: значение параметра
      7
      8         'PriceType' => "Enum('own,delta,percent')", // "тип цены" с вариантами (своя цена, разница в руб., наценка в %)
      9
     10         'BasePriceDelta' => 'CatalogPrice', // разница в руб
     11         'CostPriceDelta' => 'CatalogPrice', // разница в руб
     12
     13         'BasePricePercent' => 'Float', // наценка в %
     14         'CostPricePercent' => 'Float', // наценка в %
     15
     16         'BasePriceOwn' => 'CatalogPrice', // Обычная цена
     17         'CostPriceOwn' => 'CatalogPrice', // Цена по акции
     18
     19         'VariationPrice' => 'CatalogPrice', // Итоговая цена вариации (техническое поле)
     20
     21         'Available' => 'Boolean', // Флаг доступности
     22         'AllowPurchase' => 'Boolean', // Флаг "можно заказывать"
     23
     24         'ImportID' => 'Varchar(100)', // ID-импорта, нигде не редактируется и не отображается используется только для импорта
     25         'SKU' => 'Varchar', // Артикул товара
     26
     27         'Quantity' => 'Int', // остаток на складе
     28         'Sort' => 'Int', // сортировка
     29     );
     30
     31     static $has_one = array(
     32         'Product' => 'Product',
     33     );
     34
     35     static $has_many = array(
     36         'ParamValues' => 'ProductParamValue',
     37     );
     38
     39     static $searchable_fields = array('Title', 'Available', 'AllowPurchase', 'Quantity', 'VariationPrice');
     40
     41     static $summary_fields = array('Title', 'Available', 'AllowPurchase', 'Quantity', 'CostPrice', 'BasePrice', 'VariationPrice');
     42
     43     static $default_sort = "Sort";
     44
     45     function fieldLabels() {
     46         $labels = parent::fieldLabels();
     47         $labels['CostPrice'] = $labels['CostPriceOwn'];
     48         $labels['BasePrice'] = $labels['BasePriceOwn'];
     49         return $labels;
     50     }
     51
     52     function getCMSFields() {
     53         $fields = parent::getCMSFields();
     54         $tab = $fields->findOrMakeTab('Root.Main');
     55         $fields->removeByName('VariationPrice');
     56
     57         $fields->removeByName('ParamValues');
     58         $fields->removeByName('PriceType');
     59
     60         $bp = $fields->dataFieldByName('BasePriceOwn');
     61         $cp = $fields->dataFieldByName('CostPriceOwn');
     62         $pf = new FieldGroup($bp, $cp);
     63         $fields->removeByName('BasePriceOwn');
     64         $fields->removeByName('CostPriceOwn');
     65
     66         $bpd = $fields->dataFieldByName('BasePriceDelta');
     67         $cpd = $fields->dataFieldByName('CostPriceDelta');
     68         $pfd = new FieldGroup($bpd, $cpd);
     69         $fields->removeByName('BasePriceDelta');
     70         $fields->removeByName('CostPriceDelta');
     71
     72         $bpp = $fields->dataFieldByName('BasePricePercent');
     73         $cpp = $fields->dataFieldByName('CostPricePercent');
     74         $pfp = new FieldGroup($bpp, $cpp);
     75         $fields->removeByName('BasePricePercent');
     76         $fields->removeByName('CostPricePercent');
     77
     78         $tab->insertBefore($type = new SelectionGroup('PriceType', array(
     79             'own//' . _t('ProductVariation.PriceType_own', 'Price Type Own') => $pf,
     80             'delta//' . _t('ProductVariation.PriceType_delta', 'Price Type Currency Diff') => $pfd,
     81             'percent//' . _t('ProductVariation.PriceType_percent', 'Price Type Percents Diff') => $pfp,
     82         )), 'Available');
     83
     84         if ($this->ProductID && $this->ID && $categoryParams = $this->Product()->Parent()->getVariationCatalogParams()) {
     85             foreach($categoryParams as $categoryParam) {
     86                 if ($categoryParam->Type == 'bool') {
     87                     $tab->push(new ProductParamValue_BoolValueField('ParamValues', $categoryParam, $this->ID, 'ProductVariationID'));
     88                 } else {
     89                     if ($categoryParam->MultiValues) {
     90                         if ($categoryParam->PossibleValuesList()) {
     91                             $tab->push(new ProductParamValue_MultiValueSetField('ParamValues', $categoryParam, $this->ID, 'ProductVariationID'));
     92                         } else {
     93                             $tab->push(new LiteralField('ParamTitle', "<h4>{$categoryParam->Title}</h4>"));
     94                             $rate = new ProductParamValue();
     95                             $fieldList = array(
     96                                 "Value" => $rate->fieldLabel('Value'),
     97                             );
     98                             $fieldTypes = array(
     99                                 'Value' => 'TextField',
    100                             );
    101                             $ctf = new TableField("ParamValues", "ProductParamValue", $fieldList, $fieldTypes, 'ProductParamID', $categoryParam->ID);
    102                             $params = new DataObjectSet();
    103                             if ($allParamValues = DataObject::get('ProductParamValue', "ProductParamID = {$categoryParam->ID} AND ProductID = {$this->ID}")) {
    104                                 $params = $allParamValues;
    105                             }
    106                             $ctf->setCustomSourceItems($params);
    107                             $tab->push($ctf);
    108                         }
    109                     } else {
    110                         if ($categoryParam->PossibleValuesList()) {
    111                             $tab->push(new ProductParamValue_MultiValueField('ParamValues', $categoryParam, $this->ID, 'ProductVariationID'));
    112                         } else {
    113                             $tab->push(new ProductParamValue_ValueField('ParamValues', $categoryParam, $this->ID, 'ProductVariationID'));
    114                         }
    115                     }
    116                 }
    117                 //$paramValues->push($t);
    118             }
    119         }
    120         return $fields;
    121     }
    122
    123     function OrderButtonTitle() {
    124         if ($this->MenuTitle) {
    125             return $this->MenuTitle;
    126         }
    127         return $this->Title;
    128     }
    129
    130     // флаг для верстки
    131     function IsVariation() {
    132         return true;
    133     }
    134
    135     function getPhoto() {
    136         return $this->Product()->Photo();
    137     }
    138
    139     function getBasePrice() {
    140         $price = 0;
    141         if ($this->ProductID && ($product = $this->Product())) {
    142             switch ($this->PriceType) {
    143                 case 'own':
    144                     $price = $this->BasePriceOwn;
    145                     break;
    146                 case 'delta':
    147                     $price = $product->BasePrice + $this->BasePriceDelta;
    148                     break;
    149                 case 'percent':
    150                     $price = $product->BasePrice + $product->BasePrice  / 100 * $this->BasePricePercent;
    151                     break;
    152             }
    153         }
    154         return $price;
    155     }
    156
    157     function getCostPrice() {
    158         $price = 0;
    159         if ($this->ProductID && ($product = $this->Product())) {
    160             switch ($this->PriceType) {
    161                 case 'own':
    162                     $price = $this->CostPriceOwn;
    163                     break;
    164                 case 'delta':
    165                     if ((int)$product->CostPrice) {
    166                         $price = $product->CostPrice + $this->CostPriceDelta;
    167                     }
    168                     break;
    169                 case 'percent':
    170                     if ((int)$product->CostPrice) {
    171                         $price = $product->CostPrice + $product->CostPrice  / 100 * $this->CostPricePercent;
    172                     }
    173                     break;
    174             }
    175         }
    176         return $price;
    177     }
    178
    179     function NiceBasePrice() {
    180         return DBField::create('CatalogPrice', $this->getBasePrice());
    181     }
    182
    183     function NiceCostPrice() {
    184         return DBField::create('CatalogPrice', $this->getCostPrice());
    185     }
    186
    187     function Price($onlyreal=false) {
    188         $real = $this->VariationPrice;
    189         $client = $real;
    190         $delta = 0;
    191         $percent = 0;
    192         if ($this->CostPrice == 0) {
    193             // персональная скидка
    194             $member = Member::currentUser();
    195             if ($member){
    196                 if ($member->hasMethod('getPersonalDiscount') && $percent < $member->getPersonalDiscount()) {
    197                     $percent = $member->getPersonalDiscount();
    198                 }
    199                 // групповые скидки
    200                 if ($groups = $member->Groups()) {
    201                     foreach ($groups as $g) {
    202                         if ($g->hasMethod('Discount') && $percent < $g->Discount()) {
    203                             $percent = $g->Discount();
    204                         }
    205                     }
    206                 }
    207             }
    208             $this->extend('updateDiscount', $percent);
    209         }
    210         $sc = SiteConfig::current_site_config();
    211         $ceil = $sc->CatalogDiscountCeil;
    212         if ($ceil) {
    213             $real = ceil($real);
    214             $client = ceil($client);
    215             $decimal = 0;
    216         }
    217         if ($percent) {
    218             $delta = ($ceil) ? ceil(($real / 100) * $percent) : round(($real / 100) * $percent, 2);
    219             $client = $real - $delta;
    220         }
    221         if ($onlyreal) {
    222             return $client;
    223         }
    224         return new ArrayData(array(
    225             'Real' => DBField::create('CatalogPrice', $real),
    226             'Client' => DBField::create('CatalogPrice', $client),
    227             'Delta' => DBField::create('CatalogPrice', $delta),
    228             'Percent' => $percent,
    229             'Discount' => ($percent > 0) ? 1 : 0,
    230             'Currency' => $sc->CatalogCurrency,
    231         ));
    232
    233         return DBField::create('CatalogPrice', ($this->CostPrice > 0) ? $this->CostPrice : $this->BasePrice);
    234     }
    235     /*
    236     function getVariationPrice() {
    237         return $this->Price();
    238     }
    239     */
    240
    241     function onBeforeWrite() {
    242         parent::onBeforeWrite();
    243         $this->VariationPrice = ($this->CostPrice > 0) ? $this->CostPrice : $this->BasePrice;
    244         /* !! Сомнительно, уточнить !!!
    245         if ($this->Quantity <= 0) {
    246             $this->Available = 0;
    247             $this->AllowPurchase = 0;
    248         }
    249         */
    250     }
    251
    252     function onAfterDelete() {
    253         parent::onAfterDelete();
    254
    255         DB::Query("DELETE FROM `ProductParamValue` WHERE ProductVariationID = {$this->ID}");
    256     }
    257
    258     // Список параметров для вывода на карточке товара
    259     function ParamValue($title) {
    260         if ($product = $this->Product()) {
    261             $join = "JOIN ProductParam ON ProductParam.ID = ProductParamValue.ProductParamID";
    262             if ($product->Parent()->OwnParams) {
    263                 $join .= " JOIN Catalog_EnabledParams ON ProductParam.ID = Catalog_EnabledParams.ProductParamID AND Catalog_EnabledParams.CatalogID = {$product->ParentID}";
    264             }
    265             $title = Convert::raw2sql($title);
    266             $value = DataObject::get('ProductParamValue', "ProductVariationID = {$this->ID} AND ProductParam.TechTitle = '{$title}' ", "", $join);
    267             if ($value) {
    268                 return $value->First();
    269             }
    270         }
    271         return false;
    272     }
    273
    274     /**
    275      * Возвращает класс для кнопки заказа товара в зависимости от цены и значений Product::$empty_order_button_class и Product::$order_button_class
    276      *
    277      * @return string
    278      */
    279     function orderButtonClass() {
    280         if (!$this->AllowPurchase) {
    281             return false;
    282         }
    283         if ($this->VariationPrice == 0) {
    284             if (Product::$order_empty_price)
    285                 return Product::$empty_order_button_class;
    286             return false;
    287         }
    288         $class = Product::$order_button_class;
    289         $this->extend('updateOrderButtonClass', $class);
    290         return $class;
    291     }
    292
    293     /**
    294      * Возвращает объект для генерации кнопки заказа
    295      *
    296      * @return OrderButtonInterface
    297      */
    298     function OrderButton() {
    299         $buttonClass = $this->orderButtonClass();
    300         if (!$buttonClass)
    301             return false;
    302         return new $buttonClass($this->ProductID, $this->ID);
    303     }
    304
    305     /**
    306      * Возвращает html для кнопки заказа товара с оплем для кол-ва
    307      *
    308      * @return string
    309      */
    310     function OrderButtonWithNum() {
    311         if ($button = $this->OrderButton()) {
    312             return $button->WithNum();
    313         }
    314         return false;
    315     }
    316
    317     function OrderButtonLink() {
    318         return $this->Product()->VariationOrderButtonLink($this->ID);
    319     }
    320
    321     function PurchaseStatus() {
    322         if (!$this->Available && !$this->AllowPurchase) {
    323             return 'NoOrder';
    324         }
    325         if (!$this->Available && $this->AllowPurchase) {
    326             return 'PreOrder';
    327         }
    328         if ($this->Available && !$this->AllowPurchase) {
    329             return 'NoOrder';
    330         }
    331         if ($this->Available && $this->AllowPurchase) {
    332             return 'InStock';
    333         }
    334         return 'NoOrder';
    335     }
    336
    337     function PurchaseStatusTitle() {
    338         return _t('ProductVariation.PurchaseStatus_' . $this->PurchaseStatus());
    339     }
    340
    341     function canOrder() {
    342         if (in_array($this->PurchaseStatus(), array('PreOrder', 'InStock'))) {
    343             return true;
    344         }
    345         return false;
    346     }
    347
    348     function VariationInfo() {
    349         $info = array();
    350         $info['Title'] = $this->Title;
    351         $info['Available'] = (bool)$this->Available;
    352         $info['AllowPurchase'] = (bool)$this->AllowPurchase;
    353         $info['SKU'] = $this->SKU;
    354         $info['Quantity'] = (int)$this->Quantity;
    355         $info['CostPrice'] = (float)$this->getCostPrice();
    356         $info['BasePrice'] = (float)$this->getBasePrice();
    357         $info['Price'] = (float)$this->Price(1);
    358         return $info;
    359     }
    360
    361     //this is used by TableListField to access attribute values.
    362     function AttributeProxy(){
    363         $do = new DataObject();
    364         if($this->ParamValues()->exists()){
    365             foreach($this->ParamValues() as $value){
    366                 $do->{'Val'.$value->TechTitle} = $value->Value;
    367             }
    368         }
    369         return $do;
    370     }
    371
    372 }

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -